1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
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.
11
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.
16
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
20 */
21
22 #include "stacktrace.h"
23 #include "stream/textstream.h"
24
25 #include "environment.h"
26
27 #if defined (WIN32) && defined (_MSC_VER)
28
29 #include "windows.h"
30 #include "winnt.h"
31 #include "dbghelp.h"
32
33 class Address
34 {
35 public:
36 void* m_value;
Address(void * value)37 Address(void* value) : m_value(value)
38 {
39 }
40 };
41
42 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
43 template<typename TextOutputStreamType>
ostream_write(TextOutputStreamType & ostream,const Address & p)44 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Address& p)
45 {
46 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
47 char buf[bufferSize];
48 ostream.write(buf, snprintf(buf, bufferSize, "%0p", p.m_value));
49 return ostream;
50 }
51
52 class Offset
53 {
54 public:
55 void* m_value;
Offset(void * value)56 Offset(void* value) : m_value(value)
57 {
58 }
59 };
60
61 /// \brief Writes an address \p p to \p ostream in hexadecimal form.
62 template<typename TextOutputStreamType>
ostream_write(TextOutputStreamType & ostream,const Offset & p)63 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const Offset& p)
64 {
65 const std::size_t bufferSize = (sizeof(void*) * 2) + 1;
66 char buf[bufferSize];
67 ostream.write(buf, snprintf(buf, bufferSize, "%X", p.m_value));
68 return ostream;
69 }
70
71 /// \brief Writes a WCHAR string \p s to \p ostream.
72 template<typename TextOutputStreamType>
ostream_write(TextOutputStreamType & ostream,const WCHAR * s)73 inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const WCHAR* s)
74 {
75 const std::size_t bufferSize = 1024;
76 char buf[bufferSize];
77 ostream.write(buf, snprintf(buf, bufferSize, "%ls", s));
78 return ostream;
79 }
80
81 struct EnumerateSymbolsContext
82 {
83 STACKFRAME& sf;
84 TextOutputStream& outputStream;
85 std::size_t count;
EnumerateSymbolsContextEnumerateSymbolsContext86 EnumerateSymbolsContext(STACKFRAME& sf, TextOutputStream& outputStream) : sf(sf), outputStream(outputStream), count(0)
87 {
88 }
89 };
90
write_symbol(PSYMBOL_INFO pSym,STACKFRAME & sf,TextOutputStream & outputStream,std::size_t & count)91 void write_symbol(PSYMBOL_INFO pSym, STACKFRAME& sf, TextOutputStream& outputStream, std::size_t& count)
92 {
93 if ( pSym->Flags & SYMFLAG_PARAMETER )
94 {
95 #if 0
96 DWORD basicType;
97 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
98 TI_GET_BASETYPE, &basicType ) )
99 {
100 int bleh = 0;
101 }
102 else
103 {
104 DWORD typeId;
105 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
106 TI_GET_TYPEID, &typeId ))
107 {
108 if ( SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, pSym->TypeIndex,
109 TI_GET_BASETYPE, &basicType ) )
110 {
111 int bleh = 0;
112 }
113 else
114 {
115 const char* FormatGetLastError();
116 const char* error = FormatGetLastError();
117 int bleh = 0;
118
119 WCHAR* name;
120 if(SymGetTypeInfo( GetCurrentProcess(), pSym->ModBase, typeId,
121 TI_GET_SYMNAME, &name ))
122 {
123 outputStream << name << " ";
124 LocalFree(name);
125 int bleh = 0;
126 }
127 else
128 {
129 const char* FormatGetLastError();
130 const char* error = FormatGetLastError();
131 int bleh = 0;
132 }
133 }
134 }
135 else
136 {
137 const char* FormatGetLastError();
138 const char* error = FormatGetLastError();
139 int bleh = 0;
140 }
141 }
142 #endif
143 if(count != 0)
144 {
145 outputStream << ", ";
146 }
147 outputStream << pSym->Name;
148 ++count;
149 }
150 }
151
152 BOOL CALLBACK
EnumerateSymbolsCallback(PSYMBOL_INFO pSymInfo,ULONG SymbolSize,PVOID UserContext)153 EnumerateSymbolsCallback(
154 PSYMBOL_INFO pSymInfo,
155 ULONG SymbolSize,
156 PVOID UserContext )
157 {
158 write_symbol( pSymInfo, ((EnumerateSymbolsContext*)UserContext)->sf, ((EnumerateSymbolsContext*)UserContext)->outputStream, ((EnumerateSymbolsContext*)UserContext)->count);
159
160
161 return TRUE;
162 }
163
write_stack_trace(PCONTEXT pContext,TextOutputStream & outputStream)164 void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream)
165 {
166 HANDLE m_hProcess = GetCurrentProcess();
167 DWORD dwMachineType = 0;
168
169 CONTEXT context = *pContext;
170
171 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
172 if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) )
173 {
174 return;
175 }
176
177 STACKFRAME sf;
178 memset( &sf, 0, sizeof(sf) );
179
180 #ifdef _M_IX86
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;
189
190 dwMachineType = IMAGE_FILE_MACHINE_I386;
191 #endif
192
193 while ( 1 )
194 {
195 // Get the next stack frame
196 if ( ! StackWalk( dwMachineType,
197 m_hProcess,
198 GetCurrentThread(),
199 &sf,
200 &context,
201 0,
202 SymFunctionTableAccess,
203 SymGetModuleBase,
204 0 ) )
205 break;
206
207 if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure
208 break; // the frame is OK. Bail if not.
209
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;
215
216 DWORD64 symDisplacement = 0; // Displacement of the input address,
217 // relative to the start of the symbol
218
219 IMAGEHLP_MODULE module = { sizeof(IMAGEHLP_MODULE) };
220 if(SymGetModuleInfo(m_hProcess, sf.AddrPC.Offset, &module))
221 {
222 outputStream << module.ModuleName << "!";
223
224 if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol))
225 {
226 char undecoratedName[MAX_SYM_NAME];
227 UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE);
228
229 outputStream << undecoratedName;
230
231 outputStream << "(";
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 );
236
237 // Enumerate the locals/parameters
238 EnumerateSymbolsContext context(sf, outputStream);
239 SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context );
240 outputStream << ")";
241
242 outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement));
243
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 ) )
249 {
250 outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber);
251 }
252 }
253 else
254 {
255 outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset));
256 }
257 }
258
259 outputStream << "\n";
260 }
261
262 SymCleanup(m_hProcess);
263
264 return;
265 }
266
write_stack_trace(TextOutputStream & outputStream)267 void write_stack_trace(TextOutputStream& outputStream)
268 {
269 __try{ RaiseException(0,0,0,0); } __except(write_stack_trace((GetExceptionInformation())->ContextRecord, outputStream), EXCEPTION_CONTINUE_EXECUTION) {}
270 }
271
272 #endif
273