| 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 |