| 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..745fe373c219d1b94af4ee46225b534753b4c5bf
|
| --- /dev/null
|
| +++ b/base/debug/trace_memory.cc
|
| @@ -0,0 +1,163 @@
|
| +// 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/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"
|
| +
|
| +#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;
|
| +
|
| +/////////////////////////////////////////////////////////////////////////////
|
| +// 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];
|
| +};
|
| +
|
| +// 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;
|
| +}
|
| +
|
| +// Polls for memory tracing being enabled. If so, dumps a memory profile every
|
| +// few seconds.
|
| +// TODO(jamescook): Don't poll. Get a signal when tracing is enabled.
|
| +void 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) {
|
| + // We take ownership of this string.
|
| + char* dump = base::TraceMemoryDumpAsString();
|
| + scoped_ptr<MemoryDumpHolder> dump_holder(new MemoryDumpHolder(dump));
|
| + const int kSnapshotId = 1;
|
| + TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
|
| + "memory",
|
| + "memory::Heap",
|
| + kSnapshotId,
|
| + dump_holder.PassAs<base::debug::ConvertableToTraceFormat>());
|
| + }
|
| + base::MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&DumpMemoryProfile),
|
| + base::TimeDelta::FromSeconds(5));
|
| +}
|
| +
|
| +} // 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() {
|
| +#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX)
|
| + DVLOG(1) << "Starting trace memory";
|
| + // Ensure thread-local-storage is initialized by creating a dummy event.
|
| + ScopedTraceMemory initialize(TRACE_MEMORY_IGNORE);
|
| + ::SetPseudoStackGenerator(&GetPseudoStack);
|
| + ::HeapProfilerStart("/tmp/trace-memory");
|
| + // This won't do anything initially, but will get the polling timer going.
|
| + DumpMemoryProfile();
|
| +#endif
|
| +}
|
| +
|
| +void TraceMemoryDump() {
|
| +#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX)
|
| + DVLOG(1) << "Dumping trace memory";
|
| + ::HeapProfilerDump("dump-for-trace-memory");
|
| +#endif
|
| +}
|
| +
|
| +char* TraceMemoryDumpAsString() {
|
| +#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX)
|
| + DVLOG(1) << "Getting memory dump as string";
|
| + return ::GetHeapProfile();
|
| +#endif
|
| + return NULL;
|
| +}
|
| +
|
| +void TraceMemoryStop() {
|
| +#if !defined(NO_TCMALLOC) && !defined(OS_NACL) && defined(OS_LINUX)
|
| + DVLOG(1) << "Stopping trace memory";
|
| + ::HeapProfilerStop();
|
| +#endif
|
| +}
|
| +
|
| +} // namespace base
|
|
|