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

Unified Diff: base/trace_event/malloc_dump_provider.cc

Issue 1675183006: tracing: add dump provider for malloc heap profiler (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shim_layer_linux
Patch Set: production version Created 4 years, 9 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/trace_event/malloc_dump_provider.cc
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 6f9aa9602f7334840489e17c1ca15c7c13198c74..314af42e94e5dc7cb00dbee55b33bf88d118e558 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -7,7 +7,14 @@
#include <stddef.h>
#include "base/allocator/allocator_extension.h"
+#include "base/allocator/allocator_shim.h"
+#include "base/allocator/features.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+#include "base/trace_event/heap_profiler_allocation_register.h"
+#include "base/trace_event/heap_profiler_heap_dump_writer.h"
#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
@@ -19,6 +26,65 @@
namespace base {
namespace trace_event {
+#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+namespace {
+
+using allocator::AllocatorDispatch;
+
+void* HookAlloc(const AllocatorDispatch* self, size_t size) {
+ const AllocatorDispatch* const next = self->next;
+ void* ptr = next->alloc_function(next, size);
+ if (ptr)
+ MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
+ return ptr;
+}
+
+void* HookZeroInitAlloc(const AllocatorDispatch* self, size_t n, size_t size) {
+ const AllocatorDispatch* const next = self->next;
+ void* ptr = next->alloc_zero_initialized_function(next, n, size);
+ if (ptr)
+ MallocDumpProvider::GetInstance()->InsertAllocation(ptr, n * size);
+ return ptr;
+}
+
+void* HookllocAligned(const AllocatorDispatch* self,
+ size_t alignment,
+ size_t size) {
+ const AllocatorDispatch* const next = self->next;
+ void* ptr = next->alloc_aligned_function(next, alignment, size);
+ if (ptr)
+ MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
+ return ptr;
+}
+
+void* HookRealloc(const AllocatorDispatch* self, void* address, size_t size) {
+ const AllocatorDispatch* const next = self->next;
+ void* ptr = next->realloc_function(next, address, size);
+ MallocDumpProvider::GetInstance()->RemoveAllocation(address);
+ if (size > 0) // realloc(size == 0) means free().
+ MallocDumpProvider::GetInstance()->InsertAllocation(ptr, size);
+ return ptr;
+}
+
+void HookFree(const AllocatorDispatch* self, void* address) {
+ if (address)
+ MallocDumpProvider::GetInstance()->RemoveAllocation(address);
+ const AllocatorDispatch* const next = self->next;
+ next->free_function(next, address);
+}
+
+AllocatorDispatch g_allocator_hooks = {
+ &HookAlloc, /* alloc_function */
+ &HookZeroInitAlloc, /* alloc_zero_initialized_function */
+ &HookllocAligned, /* alloc_aligned_function */
+ &HookRealloc, /* realloc_function */
+ &HookFree, /* free_function */
+ nullptr, /* next */
+};
+
+} // namespace
+#endif // BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+
// static
const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
@@ -28,7 +94,8 @@ MallocDumpProvider* MallocDumpProvider::GetInstance() {
LeakySingletonTraits<MallocDumpProvider>>::get();
}
-MallocDumpProvider::MallocDumpProvider() {}
+MallocDumpProvider::MallocDumpProvider()
+ : heap_profiler_enabled_(false), tid_dumping_heap_(kInvalidThreadId) {}
MallocDumpProvider::~MallocDumpProvider() {}
@@ -96,8 +163,98 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
resident_size - allocated_objects_size);
}
+ // Heap profiler dumps.
+ if (!heap_profiler_enabled_)
+ return true;
+
+ // The dumps of the heap profiler should be created only when heap profiling
+ // was enabled (--enable-heap-profiling) AND a DETAILED dump is requested.
+ // However, when enabled, the overhead of the heap profiler should be always
+ // reported (w.r.t LIGHT vs DETAILED), to avoid oscillations in the malloc()
petrcermak 2016/03/11 11:09:23 nit: s/w.r.t/w.r.t./ and s/vs/vs./
petrcermak 2016/03/11 11:09:24 supernit: I don't think there should be a comma be
petrcermak 2016/03/11 11:09:24 supernit: I think you should s/malloc()/malloc/ or
petrcermak 2016/03/11 11:09:24 What do you mean by "w.r.t LIGHT vs DETAILED"? Don
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Done.
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Ok reworded this.
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Done.
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Done.
+ // total.
+
+ tid_dumping_heap_ = PlatformThread::CurrentId();
+ // At this point the Insert/RemoveAllocation hooks will ignore this thread.
+ // Enclosing all the temporariy data structures in a scope, so that the heap
+ // profiler does not see unabalanced malloc/free calls from these containers.
+ {
+ TraceEventMemoryOverhead overhead;
+ hash_map<AllocationContext, size_t> bytes_by_context;
+ {
+ AutoLock lock(allocation_register_lock_);
+ if (allocation_register_) {
+ if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
+ for (const auto& alloc_size : *allocation_register_)
+ bytes_by_context[alloc_size.context] += alloc_size.size;
+ }
+ allocation_register_->EstimateTraceMemoryOverhead(&overhead);
+ }
+ } // lock(allocation_register_lock_)
+
+ if (!bytes_by_context.empty()) {
+ scoped_ptr<TracedValue> heap_dump = ExportHeapDump(
+ bytes_by_context, pmd->session_state()->stack_frame_deduplicator(),
+ pmd->session_state()->type_name_deduplicator());
+ pmd->AddHeapDump("malloc", std::move(heap_dump));
+ }
+ overhead.DumpInto("tracing/heap_profiler_malloc", pmd);
petrcermak 2016/03/11 11:09:24 thought: Wouldn't "tracing/malloc_heap_profiler" b
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 In the beginning it was like that, but I made it c
petrcermak 2016/03/11 14:26:31 Acknowledged.
+ }
+ tid_dumping_heap_ = kInvalidThreadId;
+
return true;
}
+void MallocDumpProvider::OnHeapProfilingEnabled(bool enabled) {
+#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATOR_SHIM)
+ if (enabled) {
+ {
+ AutoLock lock(allocation_register_lock_);
+ allocation_register_.reset(new AllocationRegister());
+ }
+ allocator::InsertAllocatorDispatch(&g_allocator_hooks);
+ } else {
+ AutoLock lock(allocation_register_lock_);
+ allocation_register_.reset();
+ // Inser/RemoveAllocation below will no-op if the register is torn down.
petrcermak 2016/03/11 11:09:24 s/Inser/Insert/
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Done.
+ }
+#endif
+ heap_profiler_enabled_ = enabled;
+}
+
+void MallocDumpProvider::InsertAllocation(void* address, size_t size) {
+ if (tid_dumping_heap_ != kInvalidThreadId &&
petrcermak 2016/03/11 11:09:24 Can it ever happen that PlatformThread::CurrentId(
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Heh I knew that you would have commented this. You
petrcermak 2016/03/11 14:26:31 Acknowledged.
+ tid_dumping_heap_ == PlatformThread::CurrentId())
+ return;
+ // AllocationContextTracker initialization is the only thing here that can
+ // cause re-entrancy.
+ switch (AllocationContextTracker::GetStateForCurrentThread()) {
+ case AllocationContextTracker::kInitialized:
+ break;
+ case AllocationContextTracker::kNotInitialized:
+ AllocationContextTracker::InitializeForCurrentThread();
+ return;
+ case AllocationContextTracker::kInitializing:
+ return;
+ }
+
+ AllocationContext context = AllocationContextTracker::GetContextSnapshot();
+
+ AutoLock lock(allocation_register_lock_);
petrcermak 2016/03/11 11:09:24 Can't the compiler move this definition to the top
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 nope. The compiler is not allowed to reorder instr
+ if (!allocation_register_)
+ return;
+
+ allocation_register_->Insert(address, size, context);
+}
+
+void MallocDumpProvider::RemoveAllocation(void* address) {
+ if (tid_dumping_heap_ != kInvalidThreadId &&
+ tid_dumping_heap_ == PlatformThread::CurrentId())
+ return;
+ AutoLock lock(allocation_register_lock_);
petrcermak 2016/03/11 11:09:24 Is it definitely the case that AllocationContextTr
Primiano Tucci (use gerrit) 2016/03/11 13:57:57 Good spot. It's definitely NOT the case (I mean, i
petrcermak 2016/03/11 14:26:31 Thanks for the detailed explanation. It might be w
Primiano Tucci (use gerrit) 2016/03/11 16:57:21 Done
+ if (!allocation_register_)
+ return;
+ allocation_register_->Remove(address);
+}
+
} // namespace trace_event
} // namespace base
« base/trace_event/malloc_dump_provider.h ('K') | « base/trace_event/malloc_dump_provider.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698