Index: tools/memory_watcher/call_stack.cc |
=================================================================== |
--- tools/memory_watcher/call_stack.cc (revision 31097) |
+++ tools/memory_watcher/call_stack.cc (working copy) |
@@ -33,17 +33,14 @@ |
typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64, |
PIMAGEHLP_MODULE64); |
-// According to http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx |
-// "All DbgHelp functions, such as this one, are single threaded. Therefore, |
-// calls from more than one thread to this function will likely result in |
-// unexpected behavior or memory corruption. To avoid this, you must |
-// synchromize all concurrent calls from one thread to this function." |
-// |
-// dbghelp_lock_ is used to serialize access across all calls to the DbgHelp |
-// library. This may be overly conservative (serializing them all together), |
-// but does guarantee correctness. |
-static Lock dbghelp_lock_; |
+// static |
+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; |
@@ -62,15 +59,21 @@ |
if (p##name == NULL) return false; \ |
} while (0) |
-// Dynamically load the DbgHelp library and supporting routines that we |
-// will use. |
-static bool LoadDbgHelp() { |
- static bool loaded = false; |
- if (!loaded) { |
+// 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_) { |
AutoLock Lock(dbghelp_lock_); |
// Re-check if we've loaded successfully now that we have the lock. |
- if (loaded) |
+ if (dbghelp_loaded_) |
return true; |
// Load dbghelp.dll, and obtain pointers to the exported functions that we |
@@ -89,12 +92,13 @@ |
LOADPROC(dbghelp_module, SymGetModuleInfo64); |
LOADPROC(dbghelp_module, SymGetSearchPath); |
LOADPROC(dbghelp_module, SymLoadModule64); |
- loaded = true; |
+ dbghelp_loaded_ = true; |
} else { |
+ UltraSafeDebugBreak(); |
return false; |
} |
} |
- return loaded; |
+ return dbghelp_loaded_; |
} |
// Load the symbols for generating stack traces. |
@@ -168,9 +172,14 @@ |
frame_count_ = 0; |
hash_ = 0; |
id_ = InterlockedIncrement(&callstack_id); |
+ valid_ = false; |
- LoadDbgHelp(); |
- CHECK(GetStackTrace()); |
+ if (!dbghelp_loaded_) { |
+ UltraSafeDebugBreak(); // Initialize should have been called. |
+ return; |
+ } |
+ |
+ GetStackTrace(); |
} |
bool CallStack::IsEqual(const CallStack &target) { |
@@ -202,7 +211,17 @@ |
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)); |
@@ -234,7 +253,7 @@ |
// Walk the stack. |
unsigned int count = 0; |
{ |
- AutoLock lock(dbghelp_lock_); |
+ AutoDbgHelpLock thread_monitoring_lock; |
while (count < kMaxTraceFrames) { |
count++; |
@@ -255,11 +274,12 @@ |
// Push this frame's program counter onto the provided CallStack. |
AddFrame((DWORD_PTR)frame.AddrPC.Offset); |
} |
+ valid_ = true; |
} |
return true; |
} |
-void CallStack::ToString(std::string* output) { |
+void CallStack::ToString(PrivateAllocatorString* output) { |
static const int kStackWalkMaxNameLen = MAX_SYM_NAME; |
HANDLE current_process = GetCurrentProcess(); |
@@ -272,12 +292,12 @@ |
// Iterate through each frame in the call stack. |
for (int32 index = 0; index < frame_count_; index++) { |
- std::string line; |
+ PrivateAllocatorString line; |
DWORD_PTR intruction_pointer = frame(index); |
SymbolCache::iterator it; |
- it = symbol_cache_->find( intruction_pointer ); |
+ it = symbol_cache_->find(intruction_pointer); |
if (it != symbol_cache_->end()) { |
line = it->second; |
} else { |
@@ -311,14 +331,17 @@ |
strstr(symbol->Name, "Perftools_") || |
strstr(symbol->Name, "MemoryHook::") ) { |
// Just record a blank string. |
- (*symbol_cache_)[intruction_pointer] = std::string(""); |
+ (*symbol_cache_)[intruction_pointer] = ""; |
continue; |
} |
line += " "; |
line += static_cast<char*>(Line.FileName); |
line += " ("; |
- line += IntToString(Line.LineNumber); |
+ // TODO(jar): get something like this template to work :-/ |
+ // line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber); |
+ // ...and then delete this line, which uses std::string. |
+ line += IntToString(Line.LineNumber).c_str(); |
line += "): "; |
line += symbol->Name; |
line += "\n"; |