OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/debug/trace_memory.h" |
| 6 |
| 7 #include "base/debug/trace_event.h" |
| 8 #include "base/lazy_instance.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/message_loop.h" |
| 12 #include "base/threading/thread_local.h" |
| 13 |
| 14 #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
| 15 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" |
| 16 #endif |
| 17 |
| 18 namespace base { |
| 19 |
| 20 namespace { |
| 21 |
| 22 // Maximum number of nested TRACE_MEMORY macros. |
| 23 const int kMaxStackSize = 32; |
| 24 |
| 25 ///////////////////////////////////////////////////////////////////////////// |
| 26 // Holds a memory dump until the tracing system needs to serialize it. |
| 27 class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat { |
| 28 public: |
| 29 // Takes ownership of dump, which must be a JSON string, allocated with |
| 30 // malloc() and NULL terminated. |
| 31 explicit MemoryDumpHolder(char* dump) : dump_(dump) {} |
| 32 virtual ~MemoryDumpHolder() { free(dump_); } |
| 33 |
| 34 // base::debug::ConvertableToTraceFormat overrides: |
| 35 virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE { |
| 36 out->append(dump_); |
| 37 } |
| 38 |
| 39 private: |
| 40 char* dump_; |
| 41 |
| 42 DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder); |
| 43 }; |
| 44 |
| 45 ///////////////////////////////////////////////////////////////////////////// |
| 46 // Records a stack of TRACE_MEMORY events. One per thread is required. |
| 47 struct TraceMemoryStack { |
| 48 TraceMemoryStack() : index_(0) { |
| 49 memset(category_stack_, 0, kMaxStackSize * sizeof(const char*)); |
| 50 } |
| 51 |
| 52 // Points to the next free entry. |
| 53 int index_; |
| 54 const char* category_stack_[kMaxStackSize]; |
| 55 }; |
| 56 |
| 57 // A stack of TRACE_MEMORY events, per thread. |
| 58 LazyInstance<ThreadLocalPointer<TraceMemoryStack> >::Leaky trace_memory_stack = |
| 59 LAZY_INSTANCE_INITIALIZER; |
| 60 |
| 61 // Returns a "pseudo-stack" of pointers to trace events. |
| 62 // TODO(jamescook): Record both category and name, perhaps in a pair for speed. |
| 63 int GetPseudoStack(void** stack_out) { |
| 64 // Is this a fast enough lookup? Should we avoid LazyInstance somehow? |
| 65 TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
| 66 // TODO - figure out how to initialize this on every thread so we don't need |
| 67 // this if(). |
| 68 if (!stack) |
| 69 return 0; // could also init the stack here |
| 70 const int count = stack->index_; |
| 71 // memcpy works for zero bytes. |
| 72 memcpy(stack_out, stack->category_stack_, count * sizeof(void*)); |
| 73 return count; |
| 74 } |
| 75 |
| 76 // Polls for memory tracing being enabled. If so, dumps a memory profile every |
| 77 // few seconds. |
| 78 // TODO(jamescook): Don't poll. Get a signal when tracing is enabled. |
| 79 void DumpMemoryProfile() { |
| 80 // Don't trace allocations here in the memory tracing system. |
| 81 TRACE_MEMORY(TRACE_DISABLED_BY_DEFAULT("memory"), TRACE_MEMORY_IGNORE); |
| 82 // Check to see if tracing is enabled for the memory category. |
| 83 bool enabled; |
| 84 TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("memory"), |
| 85 &enabled); |
| 86 if (enabled) { |
| 87 // We take ownership of this string. |
| 88 char* dump = base::TraceMemoryDumpAsString(); |
| 89 scoped_ptr<MemoryDumpHolder> dump_holder(new MemoryDumpHolder(dump)); |
| 90 const int kSnapshotId = 1; |
| 91 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( |
| 92 "memory", |
| 93 "memory::Heap", |
| 94 kSnapshotId, |
| 95 dump_holder.PassAs<base::debug::ConvertableToTraceFormat>()); |
| 96 } |
| 97 base::MessageLoop::current()->PostDelayedTask( |
| 98 FROM_HERE, |
| 99 base::Bind(&DumpMemoryProfile), |
| 100 base::TimeDelta::FromSeconds(5)); |
| 101 } |
| 102 |
| 103 } // namespace |
| 104 |
| 105 ScopedTraceMemory::ScopedTraceMemory(const char* category) { |
| 106 // Get our thread's copy of the stack. |
| 107 TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
| 108 if (!stack) { |
| 109 trace_memory_stack.Get().Set(new TraceMemoryStack); |
| 110 stack = trace_memory_stack.Get().Get(); |
| 111 } |
| 112 // Bounds checking is for the weak. |
| 113 const int index = stack->index_; |
| 114 stack->category_stack_[index] = category; |
| 115 stack->index_++; |
| 116 } |
| 117 |
| 118 ScopedTraceMemory::~ScopedTraceMemory() { |
| 119 // Get our thread's copy of the stack. |
| 120 TraceMemoryStack* stack = trace_memory_stack.Get().Get(); |
| 121 stack->index_--; |
| 122 } |
| 123 |
| 124 // static |
| 125 int ScopedTraceMemory::GetStackIndexForTest() { |
| 126 return trace_memory_stack.Get().Get()->index_; |
| 127 } |
| 128 |
| 129 void TraceMemoryStart() { |
| 130 #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
| 131 DVLOG(1) << "Starting trace memory"; |
| 132 // Ensure thread-local-storage is initialized by creating a dummy event. |
| 133 ScopedTraceMemory initialize(TRACE_MEMORY_IGNORE); |
| 134 ::SetPseudoStackGenerator(&GetPseudoStack); |
| 135 ::HeapProfilerStart("/tmp/trace-memory"); |
| 136 // This won't do anything initially, but will get the polling timer going. |
| 137 DumpMemoryProfile(); |
| 138 #endif |
| 139 } |
| 140 |
| 141 void TraceMemoryDump() { |
| 142 #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
| 143 DVLOG(1) << "Dumping trace memory"; |
| 144 ::HeapProfilerDump("dump-for-trace-memory"); |
| 145 #endif |
| 146 } |
| 147 |
| 148 char* TraceMemoryDumpAsString() { |
| 149 #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
| 150 DVLOG(1) << "Getting memory dump as string"; |
| 151 return ::GetHeapProfile(); |
| 152 #endif |
| 153 return NULL; |
| 154 } |
| 155 |
| 156 void TraceMemoryStop() { |
| 157 #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX) |
| 158 DVLOG(1) << "Stopping trace memory"; |
| 159 ::HeapProfilerStop(); |
| 160 #endif |
| 161 } |
| 162 |
| 163 } // namespace base |
OLD | NEW |