OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "call_stack.h" | 5 #include "call_stack.h" |
6 #include <shlwapi.h> | 6 #include <shlwapi.h> |
7 #include <tlhelp32.h> | 7 #include <tlhelp32.h> |
8 | 8 |
9 #include "memory_hook.h" | 9 #include "memory_hook.h" |
10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
(...skipping 15 matching lines...) Expand all Loading... |
26 PIMAGEHLP_LINE64); | 26 PIMAGEHLP_LINE64); |
27 typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL); | 27 typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL); |
28 typedef DWORD (__stdcall *t_SymGetOptions)(void); | 28 typedef DWORD (__stdcall *t_SymGetOptions)(void); |
29 typedef DWORD (__stdcall *t_SymSetOptions)(DWORD); | 29 typedef DWORD (__stdcall *t_SymSetOptions)(DWORD); |
30 typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD); | 30 typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD); |
31 typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR, | 31 typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR, |
32 PCSTR, DWORD64, DWORD); | 32 PCSTR, DWORD64, DWORD); |
33 typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64, | 33 typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64, |
34 PIMAGEHLP_MODULE64); | 34 PIMAGEHLP_MODULE64); |
35 | 35 |
36 // According to http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx | 36 // static |
37 // "All DbgHelp functions, such as this one, are single threaded. Therefore, | 37 Lock CallStack::dbghelp_lock_; |
38 // calls from more than one thread to this function will likely result in | 38 // static |
39 // unexpected behavior or memory corruption. To avoid this, you must | 39 bool CallStack::dbghelp_loaded_ = false; |
40 // synchromize all concurrent calls from one thread to this function." | 40 // static |
41 // | 41 DWORD CallStack::active_thread_id_ = 0; |
42 // dbghelp_lock_ is used to serialize access across all calls to the DbgHelp | 42 |
43 // library. This may be overly conservative (serializing them all together), | |
44 // but does guarantee correctness. | |
45 static Lock dbghelp_lock_; | |
46 | 43 |
47 static t_StackWalk64 pStackWalk64 = NULL; | 44 static t_StackWalk64 pStackWalk64 = NULL; |
48 static t_SymCleanup pSymCleanup = NULL; | 45 static t_SymCleanup pSymCleanup = NULL; |
49 static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL; | 46 static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL; |
50 static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL; | 47 static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL; |
51 static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL; | 48 static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL; |
52 static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL; | 49 static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL; |
53 static t_SymInitialize pSymInitialize = NULL; | 50 static t_SymInitialize pSymInitialize = NULL; |
54 static t_SymGetOptions pSymGetOptions = NULL; | 51 static t_SymGetOptions pSymGetOptions = NULL; |
55 static t_SymSetOptions pSymSetOptions = NULL; | 52 static t_SymSetOptions pSymSetOptions = NULL; |
56 static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL; | 53 static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL; |
57 static t_SymGetSearchPath pSymGetSearchPath = NULL; | 54 static t_SymGetSearchPath pSymGetSearchPath = NULL; |
58 static t_SymLoadModule64 pSymLoadModule64 = NULL; | 55 static t_SymLoadModule64 pSymLoadModule64 = NULL; |
59 | 56 |
60 #define LOADPROC(module, name) do { \ | 57 #define LOADPROC(module, name) do { \ |
61 p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \ | 58 p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \ |
62 if (p##name == NULL) return false; \ | 59 if (p##name == NULL) return false; \ |
63 } while (0) | 60 } while (0) |
64 | 61 |
65 // Dynamically load the DbgHelp library and supporting routines that we | 62 // This code has to be VERY careful to not induce any allocations, as memory |
66 // will use. | 63 // watching code may cause recursion, which may obscure the stack for the truly |
67 static bool LoadDbgHelp() { | 64 // offensive issue. We use this function to break into a debugger, and it |
68 static bool loaded = false; | 65 // is guaranteed to not do any allocations (in fact, not do anything). |
69 if (!loaded) { | 66 static void UltraSafeDebugBreak() { |
| 67 _asm int(3); |
| 68 } |
| 69 |
| 70 // static |
| 71 bool CallStack::LoadDbgHelp() { |
| 72 if (!dbghelp_loaded_) { |
70 AutoLock Lock(dbghelp_lock_); | 73 AutoLock Lock(dbghelp_lock_); |
71 | 74 |
72 // Re-check if we've loaded successfully now that we have the lock. | 75 // Re-check if we've loaded successfully now that we have the lock. |
73 if (loaded) | 76 if (dbghelp_loaded_) |
74 return true; | 77 return true; |
75 | 78 |
76 // Load dbghelp.dll, and obtain pointers to the exported functions that we | 79 // Load dbghelp.dll, and obtain pointers to the exported functions that we |
77 // will be using. | 80 // will be using. |
78 HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll"); | 81 HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll"); |
79 if (dbghelp_module) { | 82 if (dbghelp_module) { |
80 LOADPROC(dbghelp_module, StackWalk64); | 83 LOADPROC(dbghelp_module, StackWalk64); |
81 LOADPROC(dbghelp_module, SymFunctionTableAccess64); | 84 LOADPROC(dbghelp_module, SymFunctionTableAccess64); |
82 LOADPROC(dbghelp_module, SymGetModuleBase64); | 85 LOADPROC(dbghelp_module, SymGetModuleBase64); |
83 LOADPROC(dbghelp_module, SymCleanup); | 86 LOADPROC(dbghelp_module, SymCleanup); |
84 LOADPROC(dbghelp_module, SymGetSymFromAddr64); | 87 LOADPROC(dbghelp_module, SymGetSymFromAddr64); |
85 LOADPROC(dbghelp_module, SymGetLineFromAddr64); | 88 LOADPROC(dbghelp_module, SymGetLineFromAddr64); |
86 LOADPROC(dbghelp_module, SymInitialize); | 89 LOADPROC(dbghelp_module, SymInitialize); |
87 LOADPROC(dbghelp_module, SymGetOptions); | 90 LOADPROC(dbghelp_module, SymGetOptions); |
88 LOADPROC(dbghelp_module, SymSetOptions); | 91 LOADPROC(dbghelp_module, SymSetOptions); |
89 LOADPROC(dbghelp_module, SymGetModuleInfo64); | 92 LOADPROC(dbghelp_module, SymGetModuleInfo64); |
90 LOADPROC(dbghelp_module, SymGetSearchPath); | 93 LOADPROC(dbghelp_module, SymGetSearchPath); |
91 LOADPROC(dbghelp_module, SymLoadModule64); | 94 LOADPROC(dbghelp_module, SymLoadModule64); |
92 loaded = true; | 95 dbghelp_loaded_ = true; |
93 } else { | 96 } else { |
| 97 UltraSafeDebugBreak(); |
94 return false; | 98 return false; |
95 } | 99 } |
96 } | 100 } |
97 return loaded; | 101 return dbghelp_loaded_; |
98 } | 102 } |
99 | 103 |
100 // Load the symbols for generating stack traces. | 104 // Load the symbols for generating stack traces. |
101 static bool LoadSymbols(HANDLE process_handle) { | 105 static bool LoadSymbols(HANDLE process_handle) { |
102 static bool symbols_loaded = false; | 106 static bool symbols_loaded = false; |
103 if (symbols_loaded) return true; | 107 if (symbols_loaded) return true; |
104 | 108 |
105 BOOL ok; | 109 BOOL ok; |
106 | 110 |
107 // Initialize the symbol engine. | 111 // Initialize the symbol engine. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 // the MemoryHook heap is alive. | 165 // the MemoryHook heap is alive. |
162 symbol_cache_ = new SymbolCache(); | 166 symbol_cache_ = new SymbolCache(); |
163 return LoadDbgHelp(); | 167 return LoadDbgHelp(); |
164 } | 168 } |
165 | 169 |
166 CallStack::CallStack() { | 170 CallStack::CallStack() { |
167 static LONG callstack_id = 0; | 171 static LONG callstack_id = 0; |
168 frame_count_ = 0; | 172 frame_count_ = 0; |
169 hash_ = 0; | 173 hash_ = 0; |
170 id_ = InterlockedIncrement(&callstack_id); | 174 id_ = InterlockedIncrement(&callstack_id); |
| 175 valid_ = false; |
171 | 176 |
172 LoadDbgHelp(); | 177 if (!dbghelp_loaded_) { |
173 CHECK(GetStackTrace()); | 178 UltraSafeDebugBreak(); // Initialize should have been called. |
| 179 return; |
| 180 } |
| 181 |
| 182 GetStackTrace(); |
174 } | 183 } |
175 | 184 |
176 bool CallStack::IsEqual(const CallStack &target) { | 185 bool CallStack::IsEqual(const CallStack &target) { |
177 if (frame_count_ != target.frame_count_) | 186 if (frame_count_ != target.frame_count_) |
178 return false; // They can't be equal if the sizes are different. | 187 return false; // They can't be equal if the sizes are different. |
179 | 188 |
180 // Walk the frames array until we | 189 // Walk the frames array until we |
181 // either find a mismatch, or until we reach the end of the call stacks. | 190 // either find a mismatch, or until we reach the end of the call stacks. |
182 for (int index = 0; index < frame_count_; index++) { | 191 for (int index = 0; index < frame_count_; index++) { |
183 if (frames_[index] != target.frames_[index]) | 192 if (frames_[index] != target.frames_[index]) |
(...skipping 11 matching lines...) Expand all Loading... |
195 // Create a unique id for this CallStack. | 204 // Create a unique id for this CallStack. |
196 pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack. | 205 pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack. |
197 hash_ = ~hash_ + (pc << 15); | 206 hash_ = ~hash_ + (pc << 15); |
198 hash_ = hash_ ^ (pc >> 12); | 207 hash_ = hash_ ^ (pc >> 12); |
199 hash_ = hash_ + (pc << 2); | 208 hash_ = hash_ + (pc << 2); |
200 hash_ = hash_ ^ (pc >> 4); | 209 hash_ = hash_ ^ (pc >> 4); |
201 hash_ = hash_ * 2057; | 210 hash_ = hash_ * 2057; |
202 hash_ = hash_ ^ (pc >> 16); | 211 hash_ = hash_ ^ (pc >> 16); |
203 } | 212 } |
204 | 213 |
| 214 bool CallStack::LockedRecursionDetected() const { |
| 215 if (!active_thread_id_) return false; |
| 216 DWORD thread_id = GetCurrentThreadId(); |
| 217 // TODO(jar): Perchance we should use atomic access to member. |
| 218 return thread_id == active_thread_id_; |
| 219 } |
| 220 |
205 bool CallStack::GetStackTrace() { | 221 bool CallStack::GetStackTrace() { |
| 222 if (LockedRecursionDetected()) |
| 223 return false; |
| 224 |
206 // Initialize the context record. | 225 // Initialize the context record. |
207 CONTEXT context; | 226 CONTEXT context; |
208 memset(&context, 0, sizeof(context)); | 227 memset(&context, 0, sizeof(context)); |
209 context.ContextFlags = CONTEXT_FULL; | 228 context.ContextFlags = CONTEXT_FULL; |
210 __asm call x | 229 __asm call x |
211 __asm x: pop eax | 230 __asm x: pop eax |
212 __asm mov context.Eip, eax | 231 __asm mov context.Eip, eax |
213 __asm mov context.Ebp, ebp | 232 __asm mov context.Ebp, ebp |
214 __asm mov context.Esp, esp | 233 __asm mov context.Esp, esp |
215 | 234 |
(...skipping 11 matching lines...) Expand all Loading... |
227 #elif | 246 #elif |
228 NOT IMPLEMENTED! | 247 NOT IMPLEMENTED! |
229 #endif | 248 #endif |
230 | 249 |
231 HANDLE current_process = GetCurrentProcess(); | 250 HANDLE current_process = GetCurrentProcess(); |
232 HANDLE current_thread = GetCurrentThread(); | 251 HANDLE current_thread = GetCurrentThread(); |
233 | 252 |
234 // Walk the stack. | 253 // Walk the stack. |
235 unsigned int count = 0; | 254 unsigned int count = 0; |
236 { | 255 { |
237 AutoLock lock(dbghelp_lock_); | 256 AutoDbgHelpLock thread_monitoring_lock; |
238 | 257 |
239 while (count < kMaxTraceFrames) { | 258 while (count < kMaxTraceFrames) { |
240 count++; | 259 count++; |
241 if (!pStackWalk64(image_type, | 260 if (!pStackWalk64(image_type, |
242 current_process, | 261 current_process, |
243 current_thread, | 262 current_thread, |
244 &frame, | 263 &frame, |
245 &context, | 264 &context, |
246 0, | 265 0, |
247 pSymFunctionTableAccess64, | 266 pSymFunctionTableAccess64, |
248 pSymGetModuleBase64, | 267 pSymGetModuleBase64, |
249 NULL)) | 268 NULL)) |
250 break; // Couldn't trace back through any more frames. | 269 break; // Couldn't trace back through any more frames. |
251 | 270 |
252 if (frame.AddrFrame.Offset == 0) | 271 if (frame.AddrFrame.Offset == 0) |
253 continue; // End of stack. | 272 continue; // End of stack. |
254 | 273 |
255 // Push this frame's program counter onto the provided CallStack. | 274 // Push this frame's program counter onto the provided CallStack. |
256 AddFrame((DWORD_PTR)frame.AddrPC.Offset); | 275 AddFrame((DWORD_PTR)frame.AddrPC.Offset); |
257 } | 276 } |
| 277 valid_ = true; |
258 } | 278 } |
259 return true; | 279 return true; |
260 } | 280 } |
261 | 281 |
262 void CallStack::ToString(std::string* output) { | 282 void CallStack::ToString(PrivateAllocatorString* output) { |
263 static const int kStackWalkMaxNameLen = MAX_SYM_NAME; | 283 static const int kStackWalkMaxNameLen = MAX_SYM_NAME; |
264 HANDLE current_process = GetCurrentProcess(); | 284 HANDLE current_process = GetCurrentProcess(); |
265 | 285 |
266 if (!LoadSymbols(current_process)) { | 286 if (!LoadSymbols(current_process)) { |
267 *output = "Error"; | 287 *output = "Error"; |
268 return; | 288 return; |
269 } | 289 } |
270 | 290 |
271 AutoLock lock(dbghelp_lock_); | 291 AutoLock lock(dbghelp_lock_); |
272 | 292 |
273 // Iterate through each frame in the call stack. | 293 // Iterate through each frame in the call stack. |
274 for (int32 index = 0; index < frame_count_; index++) { | 294 for (int32 index = 0; index < frame_count_; index++) { |
275 std::string line; | 295 PrivateAllocatorString line; |
276 | 296 |
277 DWORD_PTR intruction_pointer = frame(index); | 297 DWORD_PTR intruction_pointer = frame(index); |
278 | 298 |
279 SymbolCache::iterator it; | 299 SymbolCache::iterator it; |
280 it = symbol_cache_->find( intruction_pointer ); | 300 it = symbol_cache_->find(intruction_pointer); |
281 if (it != symbol_cache_->end()) { | 301 if (it != symbol_cache_->end()) { |
282 line = it->second; | 302 line = it->second; |
283 } else { | 303 } else { |
284 // Try to locate a symbol for this frame. | 304 // Try to locate a symbol for this frame. |
285 DWORD64 symbol_displacement = 0; | 305 DWORD64 symbol_displacement = 0; |
286 ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) + | 306 ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) + |
287 sizeof(TCHAR)*kStackWalkMaxNameLen + | 307 sizeof(TCHAR)*kStackWalkMaxNameLen + |
288 sizeof(ULONG64) - 1) / sizeof(ULONG64)]; | 308 sizeof(ULONG64) - 1) / sizeof(ULONG64)]; |
289 IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer); | 309 IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer); |
290 memset(buffer, 0, sizeof(buffer)); | 310 memset(buffer, 0, sizeof(buffer)); |
(...skipping 13 matching lines...) Expand all Loading... |
304 intruction_pointer, | 324 intruction_pointer, |
305 &line_displacement, | 325 &line_displacement, |
306 &Line); | 326 &Line); |
307 if (ok) { | 327 if (ok) { |
308 // Skip junk symbols from our internal stuff. | 328 // Skip junk symbols from our internal stuff. |
309 if (strstr(symbol->Name, "CallStack::") || | 329 if (strstr(symbol->Name, "CallStack::") || |
310 strstr(symbol->Name, "MemoryWatcher::") || | 330 strstr(symbol->Name, "MemoryWatcher::") || |
311 strstr(symbol->Name, "Perftools_") || | 331 strstr(symbol->Name, "Perftools_") || |
312 strstr(symbol->Name, "MemoryHook::") ) { | 332 strstr(symbol->Name, "MemoryHook::") ) { |
313 // Just record a blank string. | 333 // Just record a blank string. |
314 (*symbol_cache_)[intruction_pointer] = std::string(""); | 334 (*symbol_cache_)[intruction_pointer] = ""; |
315 continue; | 335 continue; |
316 } | 336 } |
317 | 337 |
318 line += " "; | 338 line += " "; |
319 line += static_cast<char*>(Line.FileName); | 339 line += static_cast<char*>(Line.FileName); |
320 line += " ("; | 340 line += " ("; |
321 line += IntToString(Line.LineNumber); | 341 // TODO(jar): get something like this template to work :-/ |
| 342 // line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber); |
| 343 // ...and then delete this line, which uses std::string. |
| 344 line += IntToString(Line.LineNumber).c_str(); |
322 line += "): "; | 345 line += "): "; |
323 line += symbol->Name; | 346 line += symbol->Name; |
324 line += "\n"; | 347 line += "\n"; |
325 } else { | 348 } else { |
326 line += " unknown (0):"; | 349 line += " unknown (0):"; |
327 line += symbol->Name; | 350 line += symbol->Name; |
328 line += "\n"; | 351 line += "\n"; |
329 } | 352 } |
330 } else { | 353 } else { |
331 // OK - couldn't get any info. Try for the module. | 354 // OK - couldn't get any info. Try for the module. |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 return MemoryHook::Alloc(size); | 389 return MemoryHook::Alloc(size); |
367 } | 390 } |
368 | 391 |
369 void AllocationStack::operator delete(void* ptr) { | 392 void AllocationStack::operator delete(void* ptr) { |
370 AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr); | 393 AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr); |
371 AutoLock lock(freelist_lock_); | 394 AutoLock lock(freelist_lock_); |
372 DCHECK(stack->next_ == NULL); | 395 DCHECK(stack->next_ == NULL); |
373 stack->next_ = freelist_; | 396 stack->next_ = freelist_; |
374 freelist_ = stack; | 397 freelist_ = stack; |
375 } | 398 } |
OLD | NEW |