Index: base/debug/trace_memory.cc |
diff --git a/base/debug/trace_memory.cc b/base/debug/trace_memory.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4e2bbf25a1bd8dcb53e5758b2f3e9c760e6bdf7a |
--- /dev/null |
+++ b/base/debug/trace_memory.cc |
@@ -0,0 +1,111 @@ |
+// Copyright 2013 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 "base/debug/trace_memory.h" |
+ |
+#include "base/lazy_instance.h" |
+#include "base/logging.h" //TODO remove |
+#include "base/threading/thread_local.h" |
+ |
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
+#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" |
+#endif |
+ |
+namespace base { |
+ |
+namespace { |
+ |
+// Maximum number of nested TRACE_MEMORY macros. |
+const int kMaxStackSize = 32; |
+ |
+// Records a stack of TRACE_MEMORY events. One per thread is required. |
+struct TraceMemoryStack { |
+ TraceMemoryStack() : index_(0) { |
+ memset(category_stack_, 0, kMaxStackSize * sizeof(const char*)); |
+ } |
+ |
+ // Points to the next free entry. |
+ int index_; |
+ const char* category_stack_[kMaxStackSize]; |
+}; |
+ |
+// A stack of TRACE_MEMORY events, per thread. |
+LazyInstance<ThreadLocalPointer<TraceMemoryStack> >::Leaky trace_memory_stack = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
+// Returns a "pseudo-stack" of pointers to trace events. |
+// TODO(jamescook): Record both category and name, perhaps in a pair for speed. |
+int GetPseudoStack(void** stack_out) { |
+ // Is this a fast enough lookup? Should we avoid LazyInstance somehow? |
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
+ // TODO - figure out how to initialize this on every thread so we don't need |
+ // this if(). |
+ if (!stack) |
+ return 0; // could also init the stack here |
+ const int count = stack->index_; |
+ // memcpy works for zero bytes. |
+ memcpy(stack_out, stack->category_stack_, count * sizeof(void*)); |
+ return count; |
+} |
+ |
+} // namespace |
+ |
+ScopedTraceMemory::ScopedTraceMemory(const char* category) { |
+ // Get our thread's copy of the stack. |
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
+ if (!stack) { |
+ trace_memory_stack.Get().Set(new TraceMemoryStack); |
+ stack = trace_memory_stack.Get().Get(); |
+ } |
+ // Bounds checking is for the weak. |
+ const int index = stack->index_; |
+ stack->category_stack_[index] = category; |
+ stack->index_++; |
+} |
+ |
+ScopedTraceMemory::~ScopedTraceMemory() { |
+ // Get our thread's copy of the stack. |
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
+ stack->index_--; |
+} |
+ |
+// static |
+int ScopedTraceMemory::GetStackIndexForTest() { |
+ return trace_memory_stack.Get().Get()->index_; |
+} |
+ |
+void TraceMemoryStart() { |
+ LOG(ERROR) << "JAMESDEBUG starting trace memory"; |
+ // Ensure thread-local-storage is initialized by creating a dummy event. |
+ ScopedTraceMemory initialize(NULL); |
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
+ ::SetPseudoStackGenerator(&GetPseudoStack); |
+ ::HeapProfilerStart("/tmp/trace-memory"); |
+#endif |
+} |
+ |
+void TraceMemoryDump() { |
+ LOG(ERROR) << "JAMESDEBUG dumping trace memory"; |
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
+ ::HeapProfilerDump("dump-for-trace-memory"); |
+#endif |
+} |
+ |
+char* TraceMemoryDumpAsString() { |
+ LOG(ERROR) << "JAMESDEBUG getting memory dump as string"; |
+ // Ensure thread-local-storage is initialized by creating a dummy event. |
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
+ return ::GetHeapProfile(); |
+#endif |
+ return NULL; |
+} |
+ |
+void TraceMemoryStop() { |
+ LOG(ERROR) << "JAMESDEBUG stopping trace memory"; |
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
+ ::HeapProfilerStop(); |
+#endif |
+} |
+ |
+} // namespace base |