Commit 3314e2e3 authored by ruuda's avatar ruuda Committed by Commit bot

[Tracing] Enable heap dumps with both type info and backtraces

This is a major change to |HeapDumpWriter| which enables it to dump the
format described in https://goo.gl/KY7zVE. This will allow the heap
profiler to break down the heap by type or backtrace, but also
combinations of those.

This changes the interface of |HeapDumpWriter| slightly from a two-step
process (insert allocations, dump json) to a three-step process (insert
allocations, compute what to dump, convert to json). The main reason for
doing this is testability. Aggregation and json conversion are now
completely orthogonal. This means that the aggregation algorithm can be
tested without having to parse the json first, and the json conversion
is simpler to test too.

This is part of the heap profiler in chrome://tracing.

BUG=524631

Review URL: https://codereview.chromium.org/1494293005

Cr-Commit-Position: refs/heads/master@{#364444}
parent 4a36e67e
...@@ -82,12 +82,12 @@ bool BASE_EXPORT operator==(const AllocationContext& lhs, ...@@ -82,12 +82,12 @@ bool BASE_EXPORT operator==(const AllocationContext& lhs,
namespace BASE_HASH_NAMESPACE { namespace BASE_HASH_NAMESPACE {
template <> template <>
struct hash<base::trace_event::Backtrace> { struct BASE_EXPORT hash<base::trace_event::Backtrace> {
size_t operator()(const base::trace_event::Backtrace& backtrace) const; size_t operator()(const base::trace_event::Backtrace& backtrace) const;
}; };
template <> template <>
struct hash<base::trace_event::AllocationContext> { struct BASE_EXPORT hash<base::trace_event::AllocationContext> {
size_t operator()(const base::trace_event::AllocationContext& context) const; size_t operator()(const base::trace_event::AllocationContext& context) const;
}; };
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_ #ifndef BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_
#define BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_ #define BASE_TRACE_EVENT_HEAP_PROFILER_HEAP_DUMP_WRITER_H_
#include <string> #include <set>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
...@@ -20,11 +20,43 @@ class StackFrameDeduplicator; ...@@ -20,11 +20,43 @@ class StackFrameDeduplicator;
class TracedValue; class TracedValue;
class TypeNameDeduplicator; class TypeNameDeduplicator;
// Aggregates |bytes_by_context|, recursively breaks down the heap, and returns
// a traced value with an "entries" array that can be dumped in the trace log,
// following the format described in https://goo.gl/KY7zVE. The number of
// entries is kept reasonable because long tails are not included.
BASE_EXPORT scoped_refptr<TracedValue> ExportHeapDump(
const hash_map<AllocationContext, size_t>& bytes_by_context,
StackFrameDeduplicator* stack_frame_deduplicator,
TypeNameDeduplicator* type_name_deduplicator);
namespace internal {
namespace {
struct Bucket;
}
// An entry in the "entries" array as described in https://goo.gl/KY7zVE.
struct BASE_EXPORT Entry {
size_t size;
// References a backtrace in the stack frame deduplicator. -1 means empty
// backtrace (the root of the tree).
int stack_frame_id;
// References a type name in the type name deduplicator. -1 indicates that
// the size is the cumulative size for all types (the root of the tree).
int type_id;
};
// Comparison operator to enable putting |Entry| in a |std::set|.
BASE_EXPORT bool operator<(Entry lhs, Entry rhs);
// Serializes entries to an "entries" array in a traced value.
BASE_EXPORT scoped_refptr<TracedValue> Serialize(const std::set<Entry>& dump);
// Helper class to dump a snapshot of an |AllocationRegister| or other heap // Helper class to dump a snapshot of an |AllocationRegister| or other heap
// bookkeeping structure into a |TracedValue|. This class is intended to be // bookkeeping structure into a |TracedValue|. This class is intended to be
// used as a one-shot local instance on the stack. To write heap dumps, call // used as a one-shot local instance on the stack.
// |InsertAllocation| for every captured allocation, then call |WriteHeapDump|
// to do the processing and generate a heap dump value for the trace log.
class BASE_EXPORT HeapDumpWriter { class BASE_EXPORT HeapDumpWriter {
public: public:
// The |StackFrameDeduplicator| and |TypeNameDeduplicator| are not owned. The // The |StackFrameDeduplicator| and |TypeNameDeduplicator| are not owned. The
...@@ -32,30 +64,27 @@ class BASE_EXPORT HeapDumpWriter { ...@@ -32,30 +64,27 @@ class BASE_EXPORT HeapDumpWriter {
// the dump writer. // the dump writer.
HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator, HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator,
TypeNameDeduplicator* type_name_deduplicator); TypeNameDeduplicator* type_name_deduplicator);
~HeapDumpWriter();
// Inserts information from which the heap dump will be generated. This method ~HeapDumpWriter();
// does minimal processing, so it can be called when a lock is held.
void InsertAllocation(const AllocationContext& context, size_t size);
// Aggregates allocations and writes an "entries" array to a traced value. See // Aggregates allocations to compute the total size of the heap, then breaks
// https://goo.gl/jYN4Zn for a description of the format. // down the heap recursively. This produces the values that should be dumped
scoped_refptr<TracedValue> WriteHeapDump(); // in the "entries" array. The number of entries is kept reasonable because
// long tails are not included. Use |Serialize| to convert to a traced value.
const std::set<Entry>& Summarize(
const hash_map<AllocationContext, size_t>& bytes_by_context);
private: private:
// Writes a "bt" key that references a stack frame in the |stackFrames| // Inserts an |Entry| for |Bucket| into |entries_|. Returns false if the
// dictionary. // entry was present before, true if it was not.
void WriteStackFrameIndex(int index); bool AddEntryForBucket(const Bucket& bucket);
// Writes a "type" key with the stringified type ID. // Recursively breaks down a bucket into smaller buckets and adds entries for
void WriteTypeId(int type_id); // the buckets worth dumping to |entries_|.
void BreakDown(const Bucket& bucket);
// Writes a "size" key with value |size| as a hexidecimal string to the traced // The collection of entries that is filled by |Summarize|.
// value. std::set<Entry> entries_;
void WriteSize(size_t size);
// The value that this heap dumper writes to.
const scoped_refptr<TracedValue> traced_value_;
// Helper for generating the |stackFrames| dictionary. Not owned, must outlive // Helper for generating the |stackFrames| dictionary. Not owned, must outlive
// this heap dump writer instance. // this heap dump writer instance.
...@@ -65,17 +94,10 @@ class BASE_EXPORT HeapDumpWriter { ...@@ -65,17 +94,10 @@ class BASE_EXPORT HeapDumpWriter {
// dump writer instance. // dump writer instance.
TypeNameDeduplicator* const type_name_deduplicator_; TypeNameDeduplicator* const type_name_deduplicator_;
// A map of allocation context to the number of bytes allocated for that
// context.
hash_map<AllocationContext, size_t> bytes_by_context_;
// Buffer for converting integers into strings, that is re-used throughout the
// dump.
std::string buffer_;
DISALLOW_COPY_AND_ASSIGN(HeapDumpWriter); DISALLOW_COPY_AND_ASSIGN(HeapDumpWriter);
}; };
} // namespace internal
} // namespace trace_event } // namespace trace_event
} // namespace base } // namespace base
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "content/child/web_memory_dump_provider_adapter.h" #include "content/child/web_memory_dump_provider_adapter.h"
#include "base/containers/hash_tables.h"
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/trace_event/heap_profiler_allocation_context.h" #include "base/trace_event/heap_profiler_allocation_context.h"
...@@ -77,19 +78,20 @@ bool WebMemoryDumpProviderAdapter::OnMemoryDump( ...@@ -77,19 +78,20 @@ bool WebMemoryDumpProviderAdapter::OnMemoryDump(
if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED && if (args.level_of_detail == MemoryDumpLevelOfDetail::DETAILED &&
web_memory_dump_provider_->supportsHeapProfiling() && web_memory_dump_provider_->supportsHeapProfiling() &&
g_heap_profiling_enabled) { g_heap_profiling_enabled) {
HeapDumpWriter writer(pmd->session_state()->stack_frame_deduplicator(),
pmd->session_state()->type_name_deduplicator());
TraceEventMemoryOverhead overhead; TraceEventMemoryOverhead overhead;
hash_map<AllocationContext, size_t> bytes_by_context;
{ {
AutoLock lock(g_allocation_register_lock.Get()); AutoLock lock(g_allocation_register_lock.Get());
for (const auto& alloc_size : *g_allocation_register) for (const auto& alloc_size : *g_allocation_register)
writer.InsertAllocation(alloc_size.context, alloc_size.size); bytes_by_context[alloc_size.context] += alloc_size.size;
g_allocation_register->EstimateTraceMemoryOverhead(&overhead); g_allocation_register->EstimateTraceMemoryOverhead(&overhead);
} }
pmd->AddHeapDump("partition_alloc", writer.WriteHeapDump()); scoped_refptr<TracedValue> heap_dump = ExportHeapDump(
bytes_by_context, pmd->session_state()->stack_frame_deduplicator(),
pmd->session_state()->type_name_deduplicator());
pmd->AddHeapDump("partition_alloc", heap_dump);
overhead.DumpInto("tracing/heap_profiler", pmd); overhead.DumpInto("tracing/heap_profiler", pmd);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment