Index: tools/memory_watcher/call_stack.h |
=================================================================== |
--- tools/memory_watcher/call_stack.h (revision 31097) |
+++ tools/memory_watcher/call_stack.h (working copy) |
@@ -51,9 +51,15 @@ |
// every frame in each is identical to the corresponding frame in the other. |
bool IsEqual(const CallStack &target); |
+ typedef std::basic_string<char, std::char_traits<char>, |
+ PrivateHookAllocator<char> > PrivateAllocatorString; |
+ |
// Convert the callstack to a string stored in output. |
- void CallStack::ToString(std::string* output); |
+ void CallStack::ToString(PrivateAllocatorString* output); |
+ // |
+ bool Valid() const { return valid_; } |
+ |
private: |
// The maximum number of frames to trace. |
static const int kMaxTraceFrames = 32; |
@@ -68,14 +74,67 @@ |
// Functions for manipulating the frame list. |
void ClearFrames(); |
+ // Dynamically load the DbgHelp library and supporting routines that we |
+ // will use. |
+ static bool LoadDbgHelp(); |
+ |
+ static void LockDbgHelp() { |
+ dbghelp_lock_.Acquire(); |
+ active_thread_id_ = GetCurrentThreadId(); |
+ } |
+ |
+ static void UnlockDbgHelp() { |
+ active_thread_id_ = GetCurrentThreadId(); |
+ dbghelp_lock_.Release(); |
+ } |
+ |
+ class AutoDbgHelpLock { |
+ public: |
+ AutoDbgHelpLock() { |
+ CallStack::LockDbgHelp(); |
+ } |
+ ~AutoDbgHelpLock() { |
+ CallStack::UnlockDbgHelp(); |
+ } |
+ }; |
+ |
+ // Check to see if this thread is already processing a stack. |
+ bool LockedRecursionDetected() const; |
+ |
+ // 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_; |
+ |
+ // Record the fact that dbghelp has been loaded. |
+ // Changes to this variable are protected by dbghelp_lock_. |
+ // It will only changes once... from false to true. |
+ static bool dbghelp_loaded_; |
+ |
+ // To prevent infinite recursion due to unexpected side effects in libraries, |
+ // we track the thread_id of the thread currently holding the dbghelp_lock_. |
+ // We avoid re-aquiring said lock and return an !valid_ instance when we |
+ // detect recursion. |
+ static DWORD active_thread_id_; |
+ |
int frame_count_; // Current size (in frames) |
DWORD_PTR frames_[kMaxTraceFrames]; |
int32 hash_; |
int32 id_; |
+ // Indicate is this is a valid stack. |
+ // This is false if recursion precluded a real stack generation. |
+ bool valid_; |
+ |
// Cache ProgramCounter -> Symbol lookups. |
// This cache is not thread safe. |
- typedef std::map<int32, std::string, std::less<int32>, |
+ typedef std::map<int32, PrivateAllocatorString, std::less<int32>, |
PrivateHookAllocator<int32> > SymbolCache; |
static SymbolCache* symbol_cache_; |
@@ -88,14 +147,18 @@ |
// free instances. |
class AllocationStack : public CallStack { |
public: |
- AllocationStack() : next_(NULL), CallStack() {} |
+ explicit AllocationStack(int32 size) |
+ : next_(NULL), size_(size), CallStack() {} |
// We maintain a freelist of the AllocationStacks. |
void* operator new(size_t s); |
void operator delete(void*p); |
+ int32 size() const { return size_; } |
+ |
private: |
AllocationStack* next_; // Pointer used when on the freelist. |
+ int32 size_; // Size of block allocated. |
static AllocationStack* freelist_; |
static Lock freelist_lock_; |