2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "stacktrace.h"
23 #include "stream/textstream.h"
25 #include "environment.h"
27 #if defined (WIN32) && defined (_MSC_VER)
37 Address(void* value) : m_value(value)
42 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
43 template<typename TextOutputStreamType>
44 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Address& p)
46 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
48 ostream.write(buf, snprintf(buf, bufferSize, "%0p", p.m_value));
56 Offset(void* value) : m_value(value)
61 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
62 template<typename TextOutputStreamType>
63 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Offset& p)
65 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
67 ostream.write(buf, snprintf(buf, bufferSize, "%X", p.m_value));
71 /// \brief Writes a WCHAR string \p s to \p ostream.
72 template<typename TextOutputStreamType>
73 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const WCHAR* s)
75 const std::size_t bufferSize = 1024;
77 ostream.write(buf, snprintf(buf, bufferSize, "%ls", s));
81 struct EnumerateSymbolsContext
84 TextOutputStream& outputStream;
86 EnumerateSymbolsContext(STACKFRAME& sf, TextOutputStream& outputStream) : sf(sf), outputStream(outputStream), count(0)
91 void write_symbol(PSYMBOL_INFO pSym, STACKFRAME& sf, TextOutputStream& outputStream, std::size_t& count)
93 if ( pSym->Flags & SYMFLAG_PARAMETER )
97 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
98 TI_GET_BASETYPE, &basicType ) )
105 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
106 TI_GET_TYPEID, &typeId ))
108 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
109 TI_GET_BASETYPE, &basicType ) )
115 const char* FormatGetLastError();
116 const char* error = FormatGetLastError();
120 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
121 TI_GET_SYMNAME, &name ))
123 outputStream << name << " ";
129 const char* FormatGetLastError();
130 const char* error = FormatGetLastError();
137 const char* FormatGetLastError();
138 const char* error = FormatGetLastError();
145 outputStream << ", ";
147 outputStream << pSym->Name;
153 EnumerateSymbolsCallback(
154 PSYMBOL_INFO pSymInfo,
158 write_symbol( pSymInfo, ((EnumerateSymbolsContext*)UserContext)->sf, ((EnumerateSymbolsContext*)UserContext)->outputStream, ((EnumerateSymbolsContext*)UserContext)->count);
164 void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream)
166 HANDLE m_hProcess = GetCurrentProcess();
167 DWORD dwMachineType = 0;
169 CONTEXT context = *pContext;
171 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
172 if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) )
178 memset( &sf, 0, sizeof(sf) );
181 // Initialize the STACKFRAME structure for the first call. This is only
182 // necessary for Intel CPUs, and isn't mentioned in the documentation.
183 sf.AddrPC.Offset = context.Eip;
184 sf.AddrPC.Mode = AddrModeFlat;
185 sf.AddrStack.Offset = context.Esp;
186 sf.AddrStack.Mode = AddrModeFlat;
187 sf.AddrFrame.Offset = context.Ebp;
188 sf.AddrFrame.Mode = AddrModeFlat;
190 dwMachineType = IMAGE_FILE_MACHINE_I386;
195 // Get the next stack frame
196 if ( ! StackWalk( dwMachineType,
202 SymFunctionTableAccess,
207 if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
208 break; // the frame is OK. Bail if not.
210 // Get the name of the function for this stack frame entry
211 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + MAX_SYM_NAME ];
212 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
213 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
214 pSymbol->MaxNameLen = MAX_SYM_NAME;
216 DWORD64 symDisplacement = 0; // Displacement of the input address,
217 // relative to the start of the symbol
219 IMAGEHLP_MODULE module = { sizeof(IMAGEHLP_MODULE) };
220 if(SymGetModuleInfo(m_hProcess, sf.AddrPC.Offset, &module))
222 outputStream << module.ModuleName << "!";
224 if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol))
226 char undecoratedName[MAX_SYM_NAME];
227 UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE);
229 outputStream << undecoratedName;
232 // Use SymSetContext to get just the locals/params for this frame
233 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
234 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
235 SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
237 // Enumerate the locals/parameters
238 EnumerateSymbolsContext context(sf, outputStream);
239 SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
242 outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement));
244 // Get the source line for this stack frame entry
245 IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
246 DWORD dwLineDisplacement;
247 if ( SymGetLineFromAddr( m_hProcess, sf.AddrPC.Offset,
248 &dwLineDisplacement, &lineInfo ) )
250 outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber);
255 outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset));
259 outputStream << "\n";
262 SymCleanup(m_hProcess);
267 void write_stack_trace(TextOutputStream& outputStream)
269 __try{ RaiseException(0,0,0,0); } __except(write_stack_trace((GetExceptionInformation())->ContextRecord, outputStream), EXCEPTION_CONTINUE_EXECUTION) {}