| Index: tools/memory_watcher/call_stack.cc
|
| diff --git a/tools/memory_watcher/call_stack.cc b/tools/memory_watcher/call_stack.cc
|
| deleted file mode 100644
|
| index 6f829b450178ce1b01c4a62efebb2f65f71ce561..0000000000000000000000000000000000000000
|
| --- a/tools/memory_watcher/call_stack.cc
|
| +++ /dev/null
|
| @@ -1,399 +0,0 @@
|
| -// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "tools/memory_watcher/call_stack.h"
|
| -
|
| -#include <shlwapi.h>
|
| -#include <tlhelp32.h>
|
| -
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "tools/memory_watcher/memory_hook.h"
|
| -
|
| -// Typedefs for explicit dynamic linking with functions exported from
|
| -// dbghelp.dll.
|
| -typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE,
|
| - LPSTACKFRAME64, PVOID,
|
| - PREAD_PROCESS_MEMORY_ROUTINE64,
|
| - PFUNCTION_TABLE_ACCESS_ROUTINE64,
|
| - PGET_MODULE_BASE_ROUTINE64,
|
| - PTRANSLATE_ADDRESS_ROUTINE64);
|
| -typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64);
|
| -typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64);
|
| -typedef BOOL (__stdcall *t_SymCleanup)(HANDLE);
|
| -typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64,
|
| - PDWORD64, PIMAGEHLP_SYMBOL64);
|
| -typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,
|
| - PIMAGEHLP_LINE64);
|
| -typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL);
|
| -typedef DWORD (__stdcall *t_SymGetOptions)(void);
|
| -typedef DWORD (__stdcall *t_SymSetOptions)(DWORD);
|
| -typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD);
|
| -typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR,
|
| - PCSTR, DWORD64, DWORD);
|
| -typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64,
|
| - PIMAGEHLP_MODULE64);
|
| -
|
| -// static
|
| -base::Lock CallStack::dbghelp_lock_;
|
| -// static
|
| -bool CallStack::dbghelp_loaded_ = false;
|
| -// static
|
| -DWORD CallStack::active_thread_id_ = 0;
|
| -
|
| -
|
| -static t_StackWalk64 pStackWalk64 = NULL;
|
| -static t_SymCleanup pSymCleanup = NULL;
|
| -static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL;
|
| -static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL;
|
| -static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL;
|
| -static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL;
|
| -static t_SymInitialize pSymInitialize = NULL;
|
| -static t_SymGetOptions pSymGetOptions = NULL;
|
| -static t_SymSetOptions pSymSetOptions = NULL;
|
| -static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL;
|
| -static t_SymGetSearchPath pSymGetSearchPath = NULL;
|
| -static t_SymLoadModule64 pSymLoadModule64 = NULL;
|
| -
|
| -#define LOADPROC(module, name) do { \
|
| - p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \
|
| - if (p##name == NULL) return false; \
|
| -} while (0)
|
| -
|
| -// This code has to be VERY careful to not induce any allocations, as memory
|
| -// watching code may cause recursion, which may obscure the stack for the truly
|
| -// offensive issue. We use this function to break into a debugger, and it
|
| -// is guaranteed to not do any allocations (in fact, not do anything).
|
| -static void UltraSafeDebugBreak() {
|
| - _asm int(3);
|
| -}
|
| -
|
| -// static
|
| -bool CallStack::LoadDbgHelp() {
|
| - if (!dbghelp_loaded_) {
|
| - base::AutoLock Lock(dbghelp_lock_);
|
| -
|
| - // Re-check if we've loaded successfully now that we have the lock.
|
| - if (dbghelp_loaded_)
|
| - return true;
|
| -
|
| - // Load dbghelp.dll, and obtain pointers to the exported functions that we
|
| - // will be using.
|
| - HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll");
|
| - if (dbghelp_module) {
|
| - LOADPROC(dbghelp_module, StackWalk64);
|
| - LOADPROC(dbghelp_module, SymFunctionTableAccess64);
|
| - LOADPROC(dbghelp_module, SymGetModuleBase64);
|
| - LOADPROC(dbghelp_module, SymCleanup);
|
| - LOADPROC(dbghelp_module, SymGetSymFromAddr64);
|
| - LOADPROC(dbghelp_module, SymGetLineFromAddr64);
|
| - LOADPROC(dbghelp_module, SymInitialize);
|
| - LOADPROC(dbghelp_module, SymGetOptions);
|
| - LOADPROC(dbghelp_module, SymSetOptions);
|
| - LOADPROC(dbghelp_module, SymGetModuleInfo64);
|
| - LOADPROC(dbghelp_module, SymGetSearchPath);
|
| - LOADPROC(dbghelp_module, SymLoadModule64);
|
| - dbghelp_loaded_ = true;
|
| - } else {
|
| - UltraSafeDebugBreak();
|
| - return false;
|
| - }
|
| - }
|
| - return dbghelp_loaded_;
|
| -}
|
| -
|
| -// Load the symbols for generating stack traces.
|
| -static bool LoadSymbols(HANDLE process_handle) {
|
| - static bool symbols_loaded = false;
|
| - if (symbols_loaded) return true;
|
| -
|
| - BOOL ok;
|
| -
|
| - // Initialize the symbol engine.
|
| - ok = pSymInitialize(process_handle, /* hProcess */
|
| - NULL, /* UserSearchPath */
|
| - FALSE); /* fInvadeProcess */
|
| - if (!ok) return false;
|
| -
|
| - DWORD options = pSymGetOptions();
|
| - options |= SYMOPT_LOAD_LINES;
|
| - options |= SYMOPT_FAIL_CRITICAL_ERRORS;
|
| - options |= SYMOPT_UNDNAME;
|
| - options = pSymSetOptions(options);
|
| -
|
| - const DWORD kMaxSearchPath = 1024;
|
| - TCHAR buf[kMaxSearchPath] = {0};
|
| - ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath);
|
| - if (!ok)
|
| - return false;
|
| -
|
| - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
|
| - GetCurrentProcessId());
|
| - if (snapshot == INVALID_HANDLE_VALUE)
|
| - return false;
|
| -
|
| - MODULEENTRY32W module;
|
| - module.dwSize = sizeof(module); // Set the size of the structure.
|
| - BOOL cont = Module32FirstW(snapshot, &module);
|
| - while (cont) {
|
| - DWORD64 base;
|
| - // NOTE the SymLoadModule64 function has the peculiarity of accepting a
|
| - // both unicode and ASCII strings even though the parameter is PSTR.
|
| - base = pSymLoadModule64(process_handle,
|
| - 0,
|
| - reinterpret_cast<PSTR>(module.szExePath),
|
| - reinterpret_cast<PSTR>(module.szModule),
|
| - reinterpret_cast<DWORD64>(module.modBaseAddr),
|
| - module.modBaseSize);
|
| - if (base == 0) {
|
| - int err = GetLastError();
|
| - if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE)
|
| - return false;
|
| - }
|
| - cont = Module32NextW(snapshot, &module);
|
| - }
|
| - CloseHandle(snapshot);
|
| -
|
| - symbols_loaded = true;
|
| - return true;
|
| -}
|
| -
|
| -
|
| -CallStack::SymbolCache* CallStack::symbol_cache_;
|
| -
|
| -bool CallStack::Initialize() {
|
| - // We need to delay load the symbol cache until after
|
| - // the MemoryHook heap is alive.
|
| - symbol_cache_ = new SymbolCache();
|
| - return LoadDbgHelp();
|
| -}
|
| -
|
| -CallStack::CallStack() {
|
| - static LONG callstack_id = 0;
|
| - frame_count_ = 0;
|
| - hash_ = 0;
|
| - id_ = InterlockedIncrement(&callstack_id);
|
| - valid_ = false;
|
| -
|
| - if (!dbghelp_loaded_) {
|
| - UltraSafeDebugBreak(); // Initialize should have been called.
|
| - return;
|
| - }
|
| -
|
| - GetStackTrace();
|
| -}
|
| -
|
| -bool CallStack::IsEqual(const CallStack &target) {
|
| - if (frame_count_ != target.frame_count_)
|
| - return false; // They can't be equal if the sizes are different.
|
| -
|
| - // Walk the frames array until we
|
| - // either find a mismatch, or until we reach the end of the call stacks.
|
| - for (int index = 0; index < frame_count_; index++) {
|
| - if (frames_[index] != target.frames_[index])
|
| - return false; // Found a mismatch. They are not equal.
|
| - }
|
| -
|
| - // Reached the end of the call stacks. They are equal.
|
| - return true;
|
| -}
|
| -
|
| -void CallStack::AddFrame(DWORD_PTR pc) {
|
| - DCHECK(frame_count_ < kMaxTraceFrames);
|
| - frames_[frame_count_++] = pc;
|
| -
|
| - // Create a unique id for this CallStack.
|
| - pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack.
|
| - hash_ = ~hash_ + (pc << 15);
|
| - hash_ = hash_ ^ (pc >> 12);
|
| - hash_ = hash_ + (pc << 2);
|
| - hash_ = hash_ ^ (pc >> 4);
|
| - hash_ = hash_ * 2057;
|
| - hash_ = hash_ ^ (pc >> 16);
|
| -}
|
| -
|
| -bool CallStack::LockedRecursionDetected() const {
|
| - if (!active_thread_id_) return false;
|
| - DWORD thread_id = GetCurrentThreadId();
|
| - // TODO(jar): Perchance we should use atomic access to member.
|
| - return thread_id == active_thread_id_;
|
| -}
|
| -
|
| -bool CallStack::GetStackTrace() {
|
| - if (LockedRecursionDetected())
|
| - return false;
|
| -
|
| - // Initialize the context record.
|
| - CONTEXT context;
|
| - memset(&context, 0, sizeof(context));
|
| - context.ContextFlags = CONTEXT_FULL;
|
| - __asm call x
|
| - __asm x: pop eax
|
| - __asm mov context.Eip, eax
|
| - __asm mov context.Ebp, ebp
|
| - __asm mov context.Esp, esp
|
| -
|
| - STACKFRAME64 frame;
|
| - memset(&frame, 0, sizeof(frame));
|
| -
|
| -#ifdef _M_IX86
|
| - DWORD image_type = IMAGE_FILE_MACHINE_I386;
|
| - frame.AddrPC.Offset = context.Eip;
|
| - frame.AddrPC.Mode = AddrModeFlat;
|
| - frame.AddrFrame.Offset = context.Ebp;
|
| - frame.AddrFrame.Mode = AddrModeFlat;
|
| - frame.AddrStack.Offset = context.Esp;
|
| - frame.AddrStack.Mode = AddrModeFlat;
|
| -#elif
|
| - NOT IMPLEMENTED!
|
| -#endif
|
| -
|
| - HANDLE current_process = GetCurrentProcess();
|
| - HANDLE current_thread = GetCurrentThread();
|
| -
|
| - // Walk the stack.
|
| - unsigned int count = 0;
|
| - {
|
| - AutoDbgHelpLock thread_monitoring_lock;
|
| -
|
| - while (count < kMaxTraceFrames) {
|
| - count++;
|
| - if (!pStackWalk64(image_type,
|
| - current_process,
|
| - current_thread,
|
| - &frame,
|
| - &context,
|
| - 0,
|
| - pSymFunctionTableAccess64,
|
| - pSymGetModuleBase64,
|
| - NULL))
|
| - break; // Couldn't trace back through any more frames.
|
| -
|
| - if (frame.AddrFrame.Offset == 0)
|
| - continue; // End of stack.
|
| -
|
| - // Push this frame's program counter onto the provided CallStack.
|
| - AddFrame((DWORD_PTR)frame.AddrPC.Offset);
|
| - }
|
| - valid_ = true;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void CallStack::ToString(PrivateAllocatorString* output) {
|
| - static const int kStackWalkMaxNameLen = MAX_SYM_NAME;
|
| - HANDLE current_process = GetCurrentProcess();
|
| -
|
| - if (!LoadSymbols(current_process)) {
|
| - *output = "Error";
|
| - return;
|
| - }
|
| -
|
| - base::AutoLock lock(dbghelp_lock_);
|
| -
|
| - // Iterate through each frame in the call stack.
|
| - for (int32 index = 0; index < frame_count_; index++) {
|
| - PrivateAllocatorString line;
|
| -
|
| - DWORD_PTR intruction_pointer = frame(index);
|
| -
|
| - SymbolCache::iterator it;
|
| - it = symbol_cache_->find(intruction_pointer);
|
| - if (it != symbol_cache_->end()) {
|
| - line = it->second;
|
| - } else {
|
| - // Try to locate a symbol for this frame.
|
| - DWORD64 symbol_displacement = 0;
|
| - ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) +
|
| - sizeof(TCHAR)*kStackWalkMaxNameLen +
|
| - sizeof(ULONG64) - 1) / sizeof(ULONG64)];
|
| - IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
|
| - memset(buffer, 0, sizeof(buffer));
|
| - symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
| - symbol->MaxNameLength = kStackWalkMaxNameLen;
|
| - BOOL ok = pSymGetSymFromAddr64(current_process, // hProcess
|
| - intruction_pointer, // Address
|
| - &symbol_displacement, // Displacement
|
| - symbol); // Symbol
|
| - if (ok) {
|
| - // Try to locate more source information for the symbol.
|
| - IMAGEHLP_LINE64 Line;
|
| - memset(&Line, 0, sizeof(Line));
|
| - Line.SizeOfStruct = sizeof(Line);
|
| - DWORD line_displacement;
|
| - ok = pSymGetLineFromAddr64(current_process,
|
| - intruction_pointer,
|
| - &line_displacement,
|
| - &Line);
|
| - if (ok) {
|
| - // Skip junk symbols from our internal stuff.
|
| - if (strstr(symbol->Name, "CallStack::") ||
|
| - strstr(symbol->Name, "MemoryWatcher::") ||
|
| - strstr(symbol->Name, "Perftools_") ||
|
| - strstr(symbol->Name, "MemoryHook::") ) {
|
| - // Just record a blank string.
|
| - (*symbol_cache_)[intruction_pointer] = "";
|
| - continue;
|
| - }
|
| -
|
| - line += " ";
|
| - line += static_cast<char*>(Line.FileName);
|
| - line += " (";
|
| - // TODO(jar): get something like this template to work :-/
|
| - // line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber);
|
| - // ...and then delete this line, which uses std::string.
|
| - line += base::IntToString(Line.LineNumber).c_str();
|
| - line += "): ";
|
| - line += symbol->Name;
|
| - line += "\n";
|
| - } else {
|
| - line += " unknown (0):";
|
| - line += symbol->Name;
|
| - line += "\n";
|
| - }
|
| - } else {
|
| - // OK - couldn't get any info. Try for the module.
|
| - IMAGEHLP_MODULE64 module_info;
|
| - module_info.SizeOfStruct = sizeof(module_info);
|
| - if (pSymGetModuleInfo64(current_process, intruction_pointer,
|
| - &module_info)) {
|
| - line += " (";
|
| - line += static_cast<char*>(module_info.ModuleName);
|
| - line += ")\n";
|
| - } else {
|
| - line += " ???\n";
|
| - }
|
| - }
|
| - }
|
| -
|
| - (*symbol_cache_)[intruction_pointer] = line;
|
| - *output += line;
|
| - }
|
| - *output += "==================\n";
|
| -}
|
| -
|
| -
|
| -base::Lock AllocationStack::freelist_lock_;
|
| -AllocationStack* AllocationStack::freelist_ = NULL;
|
| -
|
| -void* AllocationStack::operator new(size_t size) {
|
| - DCHECK(size == sizeof(AllocationStack));
|
| - {
|
| - base::AutoLock lock(freelist_lock_);
|
| - if (freelist_ != NULL) {
|
| - AllocationStack* stack = freelist_;
|
| - freelist_ = freelist_->next_;
|
| - stack->next_ = NULL;
|
| - return stack;
|
| - }
|
| - }
|
| - return MemoryHook::Alloc(size);
|
| -}
|
| -
|
| -void AllocationStack::operator delete(void* ptr) {
|
| - AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr);
|
| - base::AutoLock lock(freelist_lock_);
|
| - DCHECK(stack->next_ == NULL);
|
| - stack->next_ = freelist_;
|
| - freelist_ = stack;
|
| -}
|
|
|