Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Unified Diff: base/debug/trace_memory.cc

Issue 15418002: Record Chrome trace events in tcmalloc heap profiles (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase, cleanup Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..65bec56be135a3354ff3e02acf59651c9995c55f
--- /dev/null
+++ b/base/debug/trace_memory.cc
@@ -0,0 +1,236 @@
+// 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/debug/leak_annotations.h"
+#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/threading/thread_local.h"
+
+// TODO(jamescook): Windows support for memory tracing.
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && (defined(OS_LINUX) || defined(OS_ANDROID))
dsinclair 2013/06/18 15:30:15 Could this be done through gyp? Have a trace_memor
James Cook 2013/06/29 00:02:42 I think we follow this pattern of using ifdefs oth
+#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+// Maximum number of nested TRACE_MEMORY scopes to record. Must be greater than
+// or equal to HeapProfileTable::kMaxStackDepth.
+const int kMaxStackSize = 32;
+
+/////////////////////////////////////////////////////////////////////////////
+// Holds a memory dump until the tracing system needs to serialize it.
+class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat {
+ public:
+ // Takes ownership of dump, which must be a JSON string, allocated with
+ // malloc() and NULL terminated.
+ explicit MemoryDumpHolder(char* dump) : dump_(dump) {}
+ virtual ~MemoryDumpHolder() { free(dump_); }
+
+ // base::debug::ConvertableToTraceFormat overrides:
+ virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
+ out->append(dump_);
+ }
+
+ private:
+ char* dump_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// 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];
+};
+
+// One stack of TRACE_MEMORY event data per thread.
+LazyInstance<ThreadLocalPointer<TraceMemoryStack> >::Leaky trace_memory_stack =
dsinclair 2013/06/18 15:30:15 Not sure if it's in the styleguide, but in trace_e
James Cook 2013/06/29 00:02:42 Style guide says it is optional to use g_, but I l
+ LAZY_INSTANCE_INITIALIZER;
+
+// Initializes the thread-local trace memory stack and returns it.
+TraceMemoryStack* InitTraceMemoryStack() {
+ DCHECK(!trace_memory_stack.Get().Get());
+ // Intentionally leak one stack per thread.
+ TraceMemoryStack* leaked_stack = new TraceMemoryStack;
+ ANNOTATE_LEAKING_OBJECT_PTR(leaked_stack);
+ trace_memory_stack.Get().Set(leaked_stack);
+ return trace_memory_stack.Get().Get();
+}
+
+// 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) {
dsinclair 2013/06/18 15:30:15 category_stack_ has const char*, why use void* ins
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get();
+ // If the tracing system isn't fully initialized, just skip this allocation.
+ // Attempting to initialize will allocate memory, causing this function to
+ // be called recursively from inside the allocator.
+ if (!stack)
+ return 0;
+ // Copy out a maximum of kMaxStackSize stack entries.
+ const int count =
+ stack->index_ < kMaxStackSize ? stack->index_ : kMaxStackSize;
+ // Notes that memcpy() works for zero bytes.
+ memcpy(stack_out, stack->category_stack_, count * sizeof(void*));
+ return count;
+}
+
+// Caller owns the returned char* and must release it with free().
+char* TraceMemoryDumpAsString() {
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && (defined(OS_LINUX) || defined(OS_ANDROID))
+ DVLOG(1) << "TraceMemoryDumpAsString";
+ return ::GetHeapProfile();
+#else
+ NOTREACHED();
+ return NULL;
+#endif
+}
+
+// If memory tracing is enabled, dumps a memory profile to the tracing system.
+void DumpMemoryProfile() {
+ DVLOG(1) << "DumpMemoryProfile";
+ // Don't trace allocations here in the memory tracing system.
+ TRACE_MEMORY(TRACE_DISABLED_BY_DEFAULT("memory"), TRACE_MEMORY_IGNORE);
+ // Check to see if tracing is enabled for the memory category.
+ bool enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("memory"),
+ &enabled);
+ if (enabled) {
+ // MemoryDumpHolder takes ownership of this string.
+ char* dump = TraceMemoryDumpAsString();
+ scoped_ptr<MemoryDumpHolder> dump_holder(new MemoryDumpHolder(dump));
+ const int kSnapshotId = 1;
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ "memory",
dsinclair 2013/06/18 15:30:15 TRACE_DISABLED_BY_DEFAULT("memory") ?
James Cook 2013/06/29 00:02:42 Yes, good catch.
+ "memory::Heap",
+ kSnapshotId,
+ dump_holder.PassAs<base::debug::ConvertableToTraceFormat>());
+ }
+}
+
+void TraceMemoryStart() {
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && (defined(OS_LINUX) || defined(OS_ANDROID))
+ DVLOG(1) << "Starting trace memory";
+ // Ensure thread-local-storage is initialized by creating a dummy event.
+ ScopedTraceMemory initialize(TRACE_MEMORY_IGNORE);
dsinclair 2013/06/18 15:30:15 Wouldn't it be easier to get just get the stack an
James Cook 2013/06/29 00:02:42 Changed InitTraceMemoryStack() to GetTraceMemorySt
+ ::SetPseudoStackGenerator(&GetPseudoStack);
+ ::HeapProfilerStart(NULL);
+#else
+ NOTREACHED();
+#endif
+}
+
+void TraceMemoryStop() {
+#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && (defined(OS_LINUX) || defined(OS_ANDROID))
+ DVLOG(1) << "Stopping trace memory";
+ ::HeapProfilerStop();
+#else
+ NOTREACHED();
+#endif
+}
+
+} // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+
+TraceMemoryTraceLogObserver::TraceMemoryTraceLogObserver(
+ scoped_refptr<MessageLoopProxy> message_loop_proxy)
+ : message_loop_proxy_(message_loop_proxy),
+ weak_factory_(this) {
+ TraceLog::GetInstance()->AddEnabledStateObserver(this);
+}
+
+TraceMemoryTraceLogObserver::~TraceMemoryTraceLogObserver() {
+ if (dump_timer_.IsRunning())
+ StopProfiling();
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
+}
+
+ // base::debug::TraceLog::EnabledStateChangedObserver overrides:
+void TraceMemoryTraceLogObserver::OnTraceLogEnabled() {
+ DVLOG(1) << "OnTraceLogEnabled";
+ DCHECK(message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&TraceMemoryTraceLogObserver::StartProfiling,
+ weak_factory_.GetWeakPtr())));
+}
+
+void TraceMemoryTraceLogObserver::OnTraceLogDisabled() {
+ DVLOG(1) << "OnTraceLogDisabled";
+ DCHECK(message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&TraceMemoryTraceLogObserver::StopProfiling,
+ weak_factory_.GetWeakPtr())));
+}
+
+void TraceMemoryTraceLogObserver::StartProfiling() {
+ TraceMemoryStart();
dsinclair 2013/06/18 15:30:15 Should this do a: if (dump_timer_.IsRunning())
James Cook 2013/06/29 00:02:42 Good idea. Done.
+ const int kDumpIntervalSeconds = 5;
+ dump_timer_.Start(FROM_HERE,
+ TimeDelta::FromSeconds(kDumpIntervalSeconds),
+ base::Bind(&DumpMemoryProfile));
+}
+
+void TraceMemoryTraceLogObserver::StopProfiling() {
+ dump_timer_.Stop();
+ TraceMemoryStop();
+}
+
+bool TraceMemoryTraceLogObserver::IsTimerRunningForTest() const {
+ return dump_timer_.IsRunning();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+ScopedTraceMemory::ScopedTraceMemory(const char* category) {
+ // Get our thread's copy of the stack.
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get();
+ if (!stack)
+ stack = InitTraceMemoryStack();
dsinclair 2013/06/18 15:30:15 If InitTraceMemoryStack() always returns this can
James Cook 2013/06/29 00:02:42 Done.
+ const int index = stack->index_;
+ // Allow deep nesting of stacks (needed for tests), but only record
+ // |kMaxStackSize| entries.
+ if (index < kMaxStackSize)
+ 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_--;
+ DCHECK_GE(stack->index_, 0) << "stack underflow";
+}
+
+// static
+int ScopedTraceMemory::GetStackIndexForTest() {
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get();
+ if (!stack)
+ stack = InitTraceMemoryStack();
+ return stack->index_;
+}
+
+// static
+const char* ScopedTraceMemory::GetItemForTest(int index) {
+ TraceMemoryStack* stack = trace_memory_stack.Get().Get();
+ if (!stack)
+ stack = InitTraceMemoryStack();
+ return stack->category_stack_[index];
+}
+
+} // namespace debug
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698