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"
30 void write_stack_trace(TextOutputStream& outputStream)
32 const unsigned int MAX_SYMBOLS = 256;
33 void* symbols[MAX_SYMBOLS];
35 // get return addresses
36 int symbol_count = backtrace(symbols, MAX_SYMBOLS);
41 // resolve and print names
42 char** symbol_names = backtrace_symbols(symbols, symbol_count);
45 for(int i = 0; (i < symbol_count); ++i)
46 outputStream << symbol_names[i] << "\n";
48 // not a memleak, see www.gnu.org/software/libc/manual (Debugging Support, Backtraces)
52 #elif defined (WIN32) && defined (_MSC_VER)
62 Address(void* value) : m_value(value)
67 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
68 template<typename TextOutputStreamType>
69 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Address& p)
71 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
73 ostream.write(buf, snprintf(buf, bufferSize, "%0p", p.m_value));
81 Offset(void* value) : m_value(value)
86 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
87 template<typename TextOutputStreamType>
88 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Offset& p)
90 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
92 ostream.write(buf, snprintf(buf, bufferSize, "%X", p.m_value));
96 /// \brief Writes a WCHAR string \p s to \p ostream.
97 template<typename TextOutputStreamType>
98 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const WCHAR* s)
100 const std::size_t bufferSize = 1024;
101 char buf[bufferSize];
102 ostream.write(buf, snprintf(buf, bufferSize, "%ls", s));
106 struct EnumerateSymbolsContext
109 TextOutputStream& outputStream;
111 EnumerateSymbolsContext(STACKFRAME64& sf, TextOutputStream& outputStream) : sf(sf), outputStream(outputStream), count(0)
116 void write_symbol(PSYMBOL_INFO pSym, STACKFRAME64& sf, TextOutputStream& outputStream, std::size_t& count)
119 if ( pSym->Flags & SYMFLAG_PARAMETER )
123 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
124 TI_GET_BASETYPE, &basicType ) )
131 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
132 TI_GET_TYPEID, &typeId ))
134 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
135 TI_GET_BASETYPE, &basicType ) )
141 const char* FormatGetLastError();
142 const char* error = FormatGetLastError();
146 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
147 TI_GET_SYMNAME, &name ))
149 outputStream << name << " ";
155 const char* FormatGetLastError();
156 const char* error = FormatGetLastError();
163 const char* FormatGetLastError();
164 const char* error = FormatGetLastError();
170 outputStream << ", ";
172 outputStream << pSym->Name;
179 EnumerateSymbolsCallback(
180 PSYMBOL_INFO pSymInfo,
184 write_symbol( pSymInfo, ((EnumerateSymbolsContext*)UserContext)->sf, ((EnumerateSymbolsContext*)UserContext)->outputStream, ((EnumerateSymbolsContext*)UserContext)->count);
190 void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream)
192 HANDLE m_hProcess = GetCurrentProcess();
193 DWORD dwMachineType = 0;
195 CONTEXT context = *pContext;
197 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
198 if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) )
204 memset( &sf, 0, sizeof(sf) );
205 sf.AddrPC.Mode = AddrModeFlat;
206 sf.AddrStack.Mode = AddrModeFlat;
207 sf.AddrFrame.Mode = AddrModeFlat;
210 // Initialize the STACKFRAME structure for the first call. This is only
211 // necessary for Intel CPUs, and isn't mentioned in the documentation.
212 sf.AddrPC.Offset = context.Eip;
213 sf.AddrStack.Offset = context.Esp;
214 sf.AddrFrame.Offset = context.Ebp;
216 dwMachineType = IMAGE_FILE_MACHINE_I386;
218 sf.AddrPC.Offset = context.Rip;
219 sf.AddrStack.Offset = context.Rsp;
221 // MSDN: x64: The frame pointer is RBP or RDI. This value is not always used.
222 // very funny, we'll try Rdi for now
223 sf.AddrFrame.Offset = context.Rdi;
225 dwMachineType = IMAGE_FILE_MACHINE_AMD64;
228 const unsigned int max_sym_name = 1024;// should be enough
232 // Get the next stack frame
233 if ( ! StackWalk64( dwMachineType,
239 SymFunctionTableAccess64,
244 if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
245 break; // the frame is OK. Bail if not.
247 // Get the name of the function for this stack frame entry
248 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + max_sym_name ];
249 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
250 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
251 pSymbol->MaxNameLen = max_sym_name;
253 DWORD64 symDisplacement = 0; // Displacement of the input address,
254 // relative to the start of the symbol
256 IMAGEHLP_MODULE64 module = { sizeof(IMAGEHLP_MODULE64) };
257 if(SymGetModuleInfo64(m_hProcess, sf.AddrPC.Offset, &module))
259 outputStream << module.ModuleName << "!";
261 if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol))
263 char undecoratedName[max_sym_name];
264 UnDecorateSymbolName(pSymbol->Name, undecoratedName, max_sym_name, UNDNAME_COMPLETE);
266 outputStream << undecoratedName;
269 // Use SymSetContext to get just the locals/params for this frame
270 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
271 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
272 SymSetContext( m_hProcess, &imagehlpStackFrame, 0 );
274 // Enumerate the locals/parameters
275 EnumerateSymbolsContext context(sf, outputStream);
276 SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
279 outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement));
281 // Get the source line for this stack frame entry
282 IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
283 DWORD dwLineDisplacement;
284 if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
285 &dwLineDisplacement, &lineInfo ) )
287 outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber);
292 outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset));
296 outputStream << "\n";
299 SymCleanup(m_hProcess);
304 void write_stack_trace(TextOutputStream& outputStream)
306 __try{ RaiseException(0,0,0,0); } __except(write_stack_trace((GetExceptionInformation())->ContextRecord, outputStream), EXCEPTION_CONTINUE_EXECUTION) {}
309 #elif defined (WIN32)
310 void write_stack_trace(TextOutputStream& outputStream)
312 outputStream << "\nStacktrace is disabled on this compiler\n";
315 void write_stack_trace(TextOutputStream& outputStream)
317 outputStream << "\nStacktrace is disabled on this platform\n";