| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "tools/memory_watcher/call_stack.h" | |
| 6 | |
| 7 #include <shlwapi.h> | |
| 8 #include <tlhelp32.h> | |
| 9 | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "tools/memory_watcher/memory_hook.h" | |
| 12 | |
| 13 // Typedefs for explicit dynamic linking with functions exported from | |
| 14 // dbghelp.dll. | |
| 15 typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE, | |
| 16 LPSTACKFRAME64, PVOID, | |
| 17 PREAD_PROCESS_MEMORY_ROUTINE64, | |
| 18 PFUNCTION_TABLE_ACCESS_ROUTINE64, | |
| 19 PGET_MODULE_BASE_ROUTINE64, | |
| 20 PTRANSLATE_ADDRESS_ROUTINE64); | |
| 21 typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64); | |
| 22 typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64); | |
| 23 typedef BOOL (__stdcall *t_SymCleanup)(HANDLE); | |
| 24 typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64, | |
| 25 PDWORD64, PIMAGEHLP_SYMBOL64); | |
| 26 typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, | |
| 27 PIMAGEHLP_LINE64); | |
| 28 typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL); | |
| 29 typedef DWORD (__stdcall *t_SymGetOptions)(void); | |
| 30 typedef DWORD (__stdcall *t_SymSetOptions)(DWORD); | |
| 31 typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD); | |
| 32 typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR, | |
| 33 PCSTR, DWORD64, DWORD); | |
| 34 typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64, | |
| 35 PIMAGEHLP_MODULE64); | |
| 36 | |
| 37 // static | |
| 38 base::Lock CallStack::dbghelp_lock_; | |
| 39 // static | |
| 40 bool CallStack::dbghelp_loaded_ = false; | |
| 41 // static | |
| 42 DWORD CallStack::active_thread_id_ = 0; | |
| 43 | |
| 44 | |
| 45 static t_StackWalk64 pStackWalk64 = NULL; | |
| 46 static t_SymCleanup pSymCleanup = NULL; | |
| 47 static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL; | |
| 48 static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL; | |
| 49 static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL; | |
| 50 static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL; | |
| 51 static t_SymInitialize pSymInitialize = NULL; | |
| 52 static t_SymGetOptions pSymGetOptions = NULL; | |
| 53 static t_SymSetOptions pSymSetOptions = NULL; | |
| 54 static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL; | |
| 55 static t_SymGetSearchPath pSymGetSearchPath = NULL; | |
| 56 static t_SymLoadModule64 pSymLoadModule64 = NULL; | |
| 57 | |
| 58 #define LOADPROC(module, name) do { \ | |
| 59 p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \ | |
| 60 if (p##name == NULL) return false; \ | |
| 61 } while (0) | |
| 62 | |
| 63 // This code has to be VERY careful to not induce any allocations, as memory | |
| 64 // watching code may cause recursion, which may obscure the stack for the truly | |
| 65 // offensive issue. We use this function to break into a debugger, and it | |
| 66 // is guaranteed to not do any allocations (in fact, not do anything). | |
| 67 static void UltraSafeDebugBreak() { | |
| 68 _asm int(3); | |
| 69 } | |
| 70 | |
| 71 // static | |
| 72 bool CallStack::LoadDbgHelp() { | |
| 73 if (!dbghelp_loaded_) { | |
| 74 base::AutoLock Lock(dbghelp_lock_); | |
| 75 | |
| 76 // Re-check if we've loaded successfully now that we have the lock. | |
| 77 if (dbghelp_loaded_) | |
| 78 return true; | |
| 79 | |
| 80 // Load dbghelp.dll, and obtain pointers to the exported functions that we | |
| 81 // will be using. | |
| 82 HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll"); | |
| 83 if (dbghelp_module) { | |
| 84 LOADPROC(dbghelp_module, StackWalk64); | |
| 85 LOADPROC(dbghelp_module, SymFunctionTableAccess64); | |
| 86 LOADPROC(dbghelp_module, SymGetModuleBase64); | |
| 87 LOADPROC(dbghelp_module, SymCleanup); | |
| 88 LOADPROC(dbghelp_module, SymGetSymFromAddr64); | |
| 89 LOADPROC(dbghelp_module, SymGetLineFromAddr64); | |
| 90 LOADPROC(dbghelp_module, SymInitialize); | |
| 91 LOADPROC(dbghelp_module, SymGetOptions); | |
| 92 LOADPROC(dbghelp_module, SymSetOptions); | |
| 93 LOADPROC(dbghelp_module, SymGetModuleInfo64); | |
| 94 LOADPROC(dbghelp_module, SymGetSearchPath); | |
| 95 LOADPROC(dbghelp_module, SymLoadModule64); | |
| 96 dbghelp_loaded_ = true; | |
| 97 } else { | |
| 98 UltraSafeDebugBreak(); | |
| 99 return false; | |
| 100 } | |
| 101 } | |
| 102 return dbghelp_loaded_; | |
| 103 } | |
| 104 | |
| 105 // Load the symbols for generating stack traces. | |
| 106 static bool LoadSymbols(HANDLE process_handle) { | |
| 107 static bool symbols_loaded = false; | |
| 108 if (symbols_loaded) return true; | |
| 109 | |
| 110 BOOL ok; | |
| 111 | |
| 112 // Initialize the symbol engine. | |
| 113 ok = pSymInitialize(process_handle, /* hProcess */ | |
| 114 NULL, /* UserSearchPath */ | |
| 115 FALSE); /* fInvadeProcess */ | |
| 116 if (!ok) return false; | |
| 117 | |
| 118 DWORD options = pSymGetOptions(); | |
| 119 options |= SYMOPT_LOAD_LINES; | |
| 120 options |= SYMOPT_FAIL_CRITICAL_ERRORS; | |
| 121 options |= SYMOPT_UNDNAME; | |
| 122 options = pSymSetOptions(options); | |
| 123 | |
| 124 const DWORD kMaxSearchPath = 1024; | |
| 125 TCHAR buf[kMaxSearchPath] = {0}; | |
| 126 ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath); | |
| 127 if (!ok) | |
| 128 return false; | |
| 129 | |
| 130 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | |
| 131 GetCurrentProcessId()); | |
| 132 if (snapshot == INVALID_HANDLE_VALUE) | |
| 133 return false; | |
| 134 | |
| 135 MODULEENTRY32W module; | |
| 136 module.dwSize = sizeof(module); // Set the size of the structure. | |
| 137 BOOL cont = Module32FirstW(snapshot, &module); | |
| 138 while (cont) { | |
| 139 DWORD64 base; | |
| 140 // NOTE the SymLoadModule64 function has the peculiarity of accepting a | |
| 141 // both unicode and ASCII strings even though the parameter is PSTR. | |
| 142 base = pSymLoadModule64(process_handle, | |
| 143 0, | |
| 144 reinterpret_cast<PSTR>(module.szExePath), | |
| 145 reinterpret_cast<PSTR>(module.szModule), | |
| 146 reinterpret_cast<DWORD64>(module.modBaseAddr), | |
| 147 module.modBaseSize); | |
| 148 if (base == 0) { | |
| 149 int err = GetLastError(); | |
| 150 if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE) | |
| 151 return false; | |
| 152 } | |
| 153 cont = Module32NextW(snapshot, &module); | |
| 154 } | |
| 155 CloseHandle(snapshot); | |
| 156 | |
| 157 symbols_loaded = true; | |
| 158 return true; | |
| 159 } | |
| 160 | |
| 161 | |
| 162 CallStack::SymbolCache* CallStack::symbol_cache_; | |
| 163 | |
| 164 bool CallStack::Initialize() { | |
| 165 // We need to delay load the symbol cache until after | |
| 166 // the MemoryHook heap is alive. | |
| 167 symbol_cache_ = new SymbolCache(); | |
| 168 return LoadDbgHelp(); | |
| 169 } | |
| 170 | |
| 171 CallStack::CallStack() { | |
| 172 static LONG callstack_id = 0; | |
| 173 frame_count_ = 0; | |
| 174 hash_ = 0; | |
| 175 id_ = InterlockedIncrement(&callstack_id); | |
| 176 valid_ = false; | |
| 177 | |
| 178 if (!dbghelp_loaded_) { | |
| 179 UltraSafeDebugBreak(); // Initialize should have been called. | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 GetStackTrace(); | |
| 184 } | |
| 185 | |
| 186 bool CallStack::IsEqual(const CallStack &target) { | |
| 187 if (frame_count_ != target.frame_count_) | |
| 188 return false; // They can't be equal if the sizes are different. | |
| 189 | |
| 190 // Walk the frames array until we | |
| 191 // either find a mismatch, or until we reach the end of the call stacks. | |
| 192 for (int index = 0; index < frame_count_; index++) { | |
| 193 if (frames_[index] != target.frames_[index]) | |
| 194 return false; // Found a mismatch. They are not equal. | |
| 195 } | |
| 196 | |
| 197 // Reached the end of the call stacks. They are equal. | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 void CallStack::AddFrame(DWORD_PTR pc) { | |
| 202 DCHECK(frame_count_ < kMaxTraceFrames); | |
| 203 frames_[frame_count_++] = pc; | |
| 204 | |
| 205 // Create a unique id for this CallStack. | |
| 206 pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack. | |
| 207 hash_ = ~hash_ + (pc << 15); | |
| 208 hash_ = hash_ ^ (pc >> 12); | |
| 209 hash_ = hash_ + (pc << 2); | |
| 210 hash_ = hash_ ^ (pc >> 4); | |
| 211 hash_ = hash_ * 2057; | |
| 212 hash_ = hash_ ^ (pc >> 16); | |
| 213 } | |
| 214 | |
| 215 bool CallStack::LockedRecursionDetected() const { | |
| 216 if (!active_thread_id_) return false; | |
| 217 DWORD thread_id = GetCurrentThreadId(); | |
| 218 // TODO(jar): Perchance we should use atomic access to member. | |
| 219 return thread_id == active_thread_id_; | |
| 220 } | |
| 221 | |
| 222 bool CallStack::GetStackTrace() { | |
| 223 if (LockedRecursionDetected()) | |
| 224 return false; | |
| 225 | |
| 226 // Initialize the context record. | |
| 227 CONTEXT context; | |
| 228 memset(&context, 0, sizeof(context)); | |
| 229 context.ContextFlags = CONTEXT_FULL; | |
| 230 __asm call x | |
| 231 __asm x: pop eax | |
| 232 __asm mov context.Eip, eax | |
| 233 __asm mov context.Ebp, ebp | |
| 234 __asm mov context.Esp, esp | |
| 235 | |
| 236 STACKFRAME64 frame; | |
| 237 memset(&frame, 0, sizeof(frame)); | |
| 238 | |
| 239 #ifdef _M_IX86 | |
| 240 DWORD image_type = IMAGE_FILE_MACHINE_I386; | |
| 241 frame.AddrPC.Offset = context.Eip; | |
| 242 frame.AddrPC.Mode = AddrModeFlat; | |
| 243 frame.AddrFrame.Offset = context.Ebp; | |
| 244 frame.AddrFrame.Mode = AddrModeFlat; | |
| 245 frame.AddrStack.Offset = context.Esp; | |
| 246 frame.AddrStack.Mode = AddrModeFlat; | |
| 247 #elif | |
| 248 NOT IMPLEMENTED! | |
| 249 #endif | |
| 250 | |
| 251 HANDLE current_process = GetCurrentProcess(); | |
| 252 HANDLE current_thread = GetCurrentThread(); | |
| 253 | |
| 254 // Walk the stack. | |
| 255 unsigned int count = 0; | |
| 256 { | |
| 257 AutoDbgHelpLock thread_monitoring_lock; | |
| 258 | |
| 259 while (count < kMaxTraceFrames) { | |
| 260 count++; | |
| 261 if (!pStackWalk64(image_type, | |
| 262 current_process, | |
| 263 current_thread, | |
| 264 &frame, | |
| 265 &context, | |
| 266 0, | |
| 267 pSymFunctionTableAccess64, | |
| 268 pSymGetModuleBase64, | |
| 269 NULL)) | |
| 270 break; // Couldn't trace back through any more frames. | |
| 271 | |
| 272 if (frame.AddrFrame.Offset == 0) | |
| 273 continue; // End of stack. | |
| 274 | |
| 275 // Push this frame's program counter onto the provided CallStack. | |
| 276 AddFrame((DWORD_PTR)frame.AddrPC.Offset); | |
| 277 } | |
| 278 valid_ = true; | |
| 279 } | |
| 280 return true; | |
| 281 } | |
| 282 | |
| 283 void CallStack::ToString(PrivateAllocatorString* output) { | |
| 284 static const int kStackWalkMaxNameLen = MAX_SYM_NAME; | |
| 285 HANDLE current_process = GetCurrentProcess(); | |
| 286 | |
| 287 if (!LoadSymbols(current_process)) { | |
| 288 *output = "Error"; | |
| 289 return; | |
| 290 } | |
| 291 | |
| 292 base::AutoLock lock(dbghelp_lock_); | |
| 293 | |
| 294 // Iterate through each frame in the call stack. | |
| 295 for (int32 index = 0; index < frame_count_; index++) { | |
| 296 PrivateAllocatorString line; | |
| 297 | |
| 298 DWORD_PTR intruction_pointer = frame(index); | |
| 299 | |
| 300 SymbolCache::iterator it; | |
| 301 it = symbol_cache_->find(intruction_pointer); | |
| 302 if (it != symbol_cache_->end()) { | |
| 303 line = it->second; | |
| 304 } else { | |
| 305 // Try to locate a symbol for this frame. | |
| 306 DWORD64 symbol_displacement = 0; | |
| 307 ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) + | |
| 308 sizeof(TCHAR)*kStackWalkMaxNameLen + | |
| 309 sizeof(ULONG64) - 1) / sizeof(ULONG64)]; | |
| 310 IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer); | |
| 311 memset(buffer, 0, sizeof(buffer)); | |
| 312 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); | |
| 313 symbol->MaxNameLength = kStackWalkMaxNameLen; | |
| 314 BOOL ok = pSymGetSymFromAddr64(current_process, // hProcess | |
| 315 intruction_pointer, // Address | |
| 316 &symbol_displacement, // Displacement | |
| 317 symbol); // Symbol | |
| 318 if (ok) { | |
| 319 // Try to locate more source information for the symbol. | |
| 320 IMAGEHLP_LINE64 Line; | |
| 321 memset(&Line, 0, sizeof(Line)); | |
| 322 Line.SizeOfStruct = sizeof(Line); | |
| 323 DWORD line_displacement; | |
| 324 ok = pSymGetLineFromAddr64(current_process, | |
| 325 intruction_pointer, | |
| 326 &line_displacement, | |
| 327 &Line); | |
| 328 if (ok) { | |
| 329 // Skip junk symbols from our internal stuff. | |
| 330 if (strstr(symbol->Name, "CallStack::") || | |
| 331 strstr(symbol->Name, "MemoryWatcher::") || | |
| 332 strstr(symbol->Name, "Perftools_") || | |
| 333 strstr(symbol->Name, "MemoryHook::") ) { | |
| 334 // Just record a blank string. | |
| 335 (*symbol_cache_)[intruction_pointer] = ""; | |
| 336 continue; | |
| 337 } | |
| 338 | |
| 339 line += " "; | |
| 340 line += static_cast<char*>(Line.FileName); | |
| 341 line += " ("; | |
| 342 // TODO(jar): get something like this template to work :-/ | |
| 343 // line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber); | |
| 344 // ...and then delete this line, which uses std::string. | |
| 345 line += base::IntToString(Line.LineNumber).c_str(); | |
| 346 line += "): "; | |
| 347 line += symbol->Name; | |
| 348 line += "\n"; | |
| 349 } else { | |
| 350 line += " unknown (0):"; | |
| 351 line += symbol->Name; | |
| 352 line += "\n"; | |
| 353 } | |
| 354 } else { | |
| 355 // OK - couldn't get any info. Try for the module. | |
| 356 IMAGEHLP_MODULE64 module_info; | |
| 357 module_info.SizeOfStruct = sizeof(module_info); | |
| 358 if (pSymGetModuleInfo64(current_process, intruction_pointer, | |
| 359 &module_info)) { | |
| 360 line += " ("; | |
| 361 line += static_cast<char*>(module_info.ModuleName); | |
| 362 line += ")\n"; | |
| 363 } else { | |
| 364 line += " ???\n"; | |
| 365 } | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 (*symbol_cache_)[intruction_pointer] = line; | |
| 370 *output += line; | |
| 371 } | |
| 372 *output += "==================\n"; | |
| 373 } | |
| 374 | |
| 375 | |
| 376 base::Lock AllocationStack::freelist_lock_; | |
| 377 AllocationStack* AllocationStack::freelist_ = NULL; | |
| 378 | |
| 379 void* AllocationStack::operator new(size_t size) { | |
| 380 DCHECK(size == sizeof(AllocationStack)); | |
| 381 { | |
| 382 base::AutoLock lock(freelist_lock_); | |
| 383 if (freelist_ != NULL) { | |
| 384 AllocationStack* stack = freelist_; | |
| 385 freelist_ = freelist_->next_; | |
| 386 stack->next_ = NULL; | |
| 387 return stack; | |
| 388 } | |
| 389 } | |
| 390 return MemoryHook::Alloc(size); | |
| 391 } | |
| 392 | |
| 393 void AllocationStack::operator delete(void* ptr) { | |
| 394 AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr); | |
| 395 base::AutoLock lock(freelist_lock_); | |
| 396 DCHECK(stack->next_ == NULL); | |
| 397 stack->next_ = freelist_; | |
| 398 freelist_ = stack; | |
| 399 } | |
| OLD | NEW |