summaryrefslogtreecommitdiffstats
path: root/tools/memory_watcher/call_stack.h
diff options
context:
space:
mode:
Diffstat (limited to 'tools/memory_watcher/call_stack.h')
-rw-r--r--tools/memory_watcher/call_stack.h69
1 files changed, 66 insertions, 3 deletions
diff --git a/tools/memory_watcher/call_stack.h b/tools/memory_watcher/call_stack.h
index 5af7ed6..2c026bc 100644
--- a/tools/memory_watcher/call_stack.h
+++ b/tools/memory_watcher/call_stack.h
@@ -51,8 +51,14 @@ class CallStack {
// 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.
@@ -68,14 +74,67 @@ class CallStack {
// 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 @@ class CallStack {
// 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_;