| Index: components/tracing/docs/heap_profiler_internals.md
|
| diff --git a/components/tracing/docs/heap_profiler_internals.md b/components/tracing/docs/heap_profiler_internals.md
|
| index d1019c8d2126b02dce0dbb01222a72a4f45496f9..a54e7f307101d355955cef5f38a27c10c95d1d2e 100644
|
| --- a/components/tracing/docs/heap_profiler_internals.md
|
| +++ b/components/tracing/docs/heap_profiler_internals.md
|
| @@ -1,184 +1,2 @@
|
| -# Heap Profiler Internals
|
| +This document has moved to [//docs/memory-infra/heap_profiler_internals.md](/docs/memory-infra/heap_profiler_internals.md).
|
|
|
| -This document describes how the heap profiler works and how to add heap
|
| -profiling support to your allocator. If you just want to know how to use it,
|
| -see [Heap Profiling with MemoryInfra](heap_profiler.md)
|
| -
|
| -[TOC]
|
| -
|
| -## Overview
|
| -
|
| -The heap profiler consists of tree main components:
|
| -
|
| - * **The Context Tracker**: Responsible for providing context (pseudo stack
|
| - backtrace) when an allocation occurs.
|
| - * **The Allocation Register**: A specialized hash table that stores allocation
|
| - details by address.
|
| - * **The Heap Dump Writer**: Extracts the most important information from a set
|
| - of recorded allocations and converts it into a format that can be dumped into
|
| - the trace log.
|
| -
|
| -These components are designed to work well together, but to be usable
|
| -independently as well.
|
| -
|
| -When there is a way to get notified of all allocations and frees, this is the
|
| -normal flow:
|
| -
|
| - 1. When an allocation occurs, call
|
| - [`AllocationContextTracker::GetInstanceForCurrentThread()->GetContextSnapshot()`][context-tracker]
|
| - to get an [`AllocationContext`][alloc-context].
|
| - 2. Insert that context together with the address and size into an
|
| - [`AllocationRegister`][alloc-register] by calling `Insert()`.
|
| - 3. When memory is freed, remove it from the register with `Remove()`.
|
| - 4. On memory dump, collect the allocations from the register, call
|
| - [`ExportHeapDump()`][export-heap-dump], and add the generated heap dump to
|
| - the memory dump.
|
| -
|
| -[context-tracker]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_context_tracker.h
|
| -[alloc-context]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_context.h
|
| -[alloc-register]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_allocation_register.h
|
| -[export-heap-dump]: https://chromium.googlesource.com/chromium/src/+/master/base/trace_event/heap_profiler_heap_dump_writer.h
|
| -
|
| -*** aside
|
| -An allocator can skip step 2 and 3 if it is able to store the context itself,
|
| -and if it is able to enumerate all allocations for step 4.
|
| -***
|
| -
|
| -When heap profiling is enabled (the `--enable-heap-profiling` flag is passed),
|
| -the memory dump manager calls `OnHeapProfilingEnabled()` on every
|
| -`MemoryDumpProvider` as early as possible, so allocators can start recording
|
| -allocations. This should be done even when tracing has not been started,
|
| -because these allocations might still be around when a heap dump happens during
|
| -tracing.
|
| -
|
| -## Context Tracker
|
| -
|
| -The [`AllocationContextTracker`][context-tracker] is a thread-local object. Its
|
| -main purpose is to keep track of a pseudo stack of trace events. Chrome has
|
| -been instrumented with lots of `TRACE_EVENT` macros. These trace events push
|
| -their name to a thread-local stack when they go into scope, and pop when they
|
| -go out of scope, if all of the following conditions have been met:
|
| -
|
| - * A trace is being recorded.
|
| - * The category of the event is enabled in the trace config.
|
| - * Heap profiling is enabled (with the `--enable-heap-profiling` flag).
|
| -
|
| -This means that allocations that occur before tracing is started will not have
|
| -backtrace information in their context.
|
| -
|
| -A thread-local instance of the context tracker is initialized lazily when it is
|
| -first accessed. This might be because a trace event pushed or popped, or because
|
| -`GetContextSnapshot()` was called when an allocation occurred.
|
| -
|
| -[`AllocationContext`][alloc-context] is what is used to group and break down
|
| -allocations. Currently `AllocationContext` has the following fields:
|
| -
|
| - * Backtrace: filled by the context tracker, obtained from the thread-local
|
| - pseudo stack.
|
| - * Type name: to be filled in at a point where the type of a pointer is known,
|
| - set to _[unknown]_ by default.
|
| -
|
| -It is possible to modify this context after insertion into the register, for
|
| -instance to set the type name if it was not known at the time of allocation.
|
| -
|
| -## Allocation Register
|
| -
|
| -The [`AllocationRegister`][alloc-register] is a hash table specialized for
|
| -storing `(size, AllocationContext)` pairs by address. It has been optimized for
|
| -Chrome's typical number of unfreed allocations, and it is backed by `mmap`
|
| -memory directly so there are no reentrancy issues when using it to record
|
| -`malloc` allocations.
|
| -
|
| -The allocation register is threading-agnostic. Access must be synchronised
|
| -properly.
|
| -
|
| -## Heap Dump Writer
|
| -
|
| -Dumping every single allocation in the allocation register straight into the
|
| -trace log is not an option due to the sheer volume (~300k unfreed allocations).
|
| -The role of the [`ExportHeapDump()`][export-heap-dump] function is to group
|
| -allocations, striking a balance between trace log size and detail.
|
| -
|
| -See the [Heap Dump Format][heap-dump-format] document for more details about the
|
| -structure of the heap dump in the trace log.
|
| -
|
| -[heap-dump-format]: https://docs.google.com/document/d/1NqBg1MzVnuMsnvV1AKLdKaPSPGpd81NaMPVk5stYanQ
|
| -
|
| -## Instrumenting an Allocator
|
| -
|
| -Below is an example of adding heap profiling support to an allocator that has
|
| -an existing memory dump provider.
|
| -
|
| -```cpp
|
| -class FooDumpProvider : public MemoryDumpProvider {
|
| -
|
| - // Kept as pointer because |AllocationRegister| allocates a lot of virtual
|
| - // address space when constructed, so only construct it when heap profiling is
|
| - // enabled.
|
| - scoped_ptr<AllocationRegister> allocation_register_;
|
| - Lock allocation_register_lock_;
|
| -
|
| - static FooDumpProvider* GetInstance();
|
| -
|
| - void InsertAllocation(void* address, size_t size) {
|
| - AllocationContext context = AllocationContextTracker::GetInstanceForCurrentThread()->GetContextSnapshot();
|
| - AutoLock lock(allocation_register_lock_);
|
| - allocation_register_->Insert(address, size, context);
|
| - }
|
| -
|
| - void RemoveAllocation(void* address) {
|
| - AutoLock lock(allocation_register_lock_);
|
| - allocation_register_->Remove(address);
|
| - }
|
| -
|
| - // Will be called as early as possible by the memory dump manager.
|
| - void OnHeapProfilingEnabled(bool enabled) override {
|
| - AutoLock lock(allocation_register_lock_);
|
| - allocation_register_.reset(new AllocationRegister());
|
| -
|
| - // At this point, make sure that from now on, for every allocation and
|
| - // free, |FooDumpProvider::GetInstance()->InsertAllocation()| and
|
| - // |RemoveAllocation| are called.
|
| - }
|
| -
|
| - bool OnMemoryDump(const MemoryDumpArgs& args,
|
| - ProcessMemoryDump& pmd) override {
|
| - // Do regular dumping here.
|
| -
|
| - // Dump the heap only for detailed dumps.
|
| - if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
|
| - TraceEventMemoryOverhead overhead;
|
| - hash_map<AllocationContext, size_t> bytes_by_context;
|
| -
|
| - {
|
| - AutoLock lock(allocation_register_lock_);
|
| - if (allocation_register_) {
|
| - // Group allocations in the register into |bytes_by_context|, but do
|
| - // no additional processing inside the lock.
|
| - for (const auto& alloc_size : *allocation_register_)
|
| - bytes_by_context[alloc_size.context] += alloc_size.size;
|
| -
|
| - allocation_register_->EstimateTraceMemoryOverhead(&overhead);
|
| - }
|
| - }
|
| -
|
| - if (!bytes_by_context.empty()) {
|
| - scoped_refptr<TracedValue> heap_dump = ExportHeapDump(
|
| - bytes_by_context,
|
| - pmd->session_state()->stack_frame_deduplicator(),
|
| - pmb->session_state()->type_name_deduplicator());
|
| - pmd->AddHeapDump("foo_allocator", heap_dump);
|
| - overhead.DumpInto("tracing/heap_profiler", pmd);
|
| - }
|
| - }
|
| -
|
| - return true;
|
| - }
|
| -};
|
| -
|
| -```
|
| -
|
| -*** aside
|
| -The implementation for `malloc` is more complicated because it needs to deal
|
| -with reentrancy.
|
| -***
|
|
|