summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/trace_event/BUILD.gn3
-rw-r--r--base/trace_event/heap_profiler_allocation_context.cc17
-rw-r--r--base/trace_event/heap_profiler_allocation_context.h15
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker.cc2
-rw-r--r--base/trace_event/heap_profiler_heap_dump_writer.cc32
-rw-r--r--base/trace_event/heap_profiler_heap_dump_writer.h17
-rw-r--r--base/trace_event/heap_profiler_heap_dump_writer_unittest.cc88
-rw-r--r--base/trace_event/heap_profiler_type_name_deduplicator.cc75
-rw-r--r--base/trace_event/heap_profiler_type_name_deduplicator.h46
-rw-r--r--base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc71
-rw-r--r--base/trace_event/memory_allocator_dump_unittest.cc4
-rw-r--r--base/trace_event/memory_dump_manager.cc14
-rw-r--r--base/trace_event/memory_dump_session_state.cc6
-rw-r--r--base/trace_event/memory_dump_session_state.h14
-rw-r--r--base/trace_event/trace_event.gypi3
-rw-r--r--base/trace_event/winheap_dump_provider_win_unittest.cc2
16 files changed, 319 insertions, 90 deletions
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn
index 493316e..07d28fe 100644
--- a/base/trace_event/BUILD.gn
+++ b/base/trace_event/BUILD.gn
@@ -17,6 +17,8 @@ source_set("trace_event") {
"heap_profiler_heap_dump_writer.h",
"heap_profiler_stack_frame_deduplicator.cc",
"heap_profiler_stack_frame_deduplicator.h",
+ "heap_profiler_type_name_deduplicator.cc",
+ "heap_profiler_type_name_deduplicator.h",
"java_heap_dump_provider_android.cc",
"java_heap_dump_provider_android.h",
"memory_allocator_dump.cc",
@@ -116,6 +118,7 @@ source_set("trace_event_unittests") {
"heap_profiler_allocation_register_unittest.cc",
"heap_profiler_heap_dump_writer_unittest.cc",
"heap_profiler_stack_frame_deduplicator_unittest.cc",
+ "heap_profiler_type_name_deduplicator_unittest.cc",
"java_heap_dump_provider_android_unittest.cc",
"memory_allocator_dump_unittest.cc",
"memory_dump_manager_unittest.cc",
diff --git a/base/trace_event/heap_profiler_allocation_context.cc b/base/trace_event/heap_profiler_allocation_context.cc
index ce8e50d..038c083 100644
--- a/base/trace_event/heap_profiler_allocation_context.cc
+++ b/base/trace_event/heap_profiler_allocation_context.cc
@@ -22,7 +22,7 @@ AllocationContext AllocationContext::Empty() {
for (size_t i = 0; i < arraysize(ctx.backtrace.frames); i++)
ctx.backtrace.frames[i] = nullptr;
- ctx.type_id = 0;
+ ctx.type_name = nullptr;
return ctx;
}
@@ -34,7 +34,7 @@ bool operator==(const Backtrace& lhs, const Backtrace& rhs) {
}
bool operator==(const AllocationContext& lhs, const AllocationContext& rhs) {
- return (lhs.backtrace == rhs.backtrace) && (lhs.type_id == rhs.type_id);
+ return (lhs.backtrace == rhs.backtrace) && (lhs.type_name == rhs.type_name);
}
} // namespace trace_event
@@ -50,13 +50,18 @@ size_t hash<Backtrace>::operator()(const Backtrace& backtrace) const {
}
size_t hash<AllocationContext>::operator()(const AllocationContext& ctx) const {
- size_t ctx_hash = hash<Backtrace>()(ctx.backtrace);
+ size_t backtrace_hash = hash<Backtrace>()(ctx.backtrace);
+
+ // Multiplicative hash from [Knuth 1998]. Works best if |size_t| is 32 bits,
+ // because the magic number is a prime very close to 2^32 / golden ratio, but
+ // will still redistribute keys bijectively on 64-bit architectures because
+ // the magic number is coprime to 2^64.
+ size_t type_hash = reinterpret_cast<size_t>(ctx.type_name) * 2654435761;
// Multiply one side to break the commutativity of +. Multiplication with a
// number coprime to |numeric_limits<size_t>::max() + 1| is bijective so
- // randomness is preserved. The type ID is assumed to be distributed randomly
- // already so there is no need to hash it.
- return (ctx_hash * 3) + static_cast<size_t>(ctx.type_id);
+ // randomness is preserved.
+ return (backtrace_hash * 3) + type_hash;
}
} // BASE_HASH_NAMESPACE
diff --git a/base/trace_event/heap_profiler_allocation_context.h b/base/trace_event/heap_profiler_allocation_context.h
index c5f1fdf..6a46d03 100644
--- a/base/trace_event/heap_profiler_allocation_context.h
+++ b/base/trace_event/heap_profiler_allocation_context.h
@@ -52,17 +52,16 @@ bool BASE_EXPORT operator==(const Backtrace& lhs, const Backtrace& rhs);
// static lifetime.
struct BASE_EXPORT AllocationContext {
public:
- // A type ID is a number that is unique for every C++ type. A type ID is
- // stored instead of the type name to avoid inflating the binary with type
- // name strings. There is an out of band lookup table mapping IDs to the type
- // names. A value of 0 means that the type is not known.
- using TypeId = uint16_t;
-
- // An allocation context with empty backtrace and type ID 0 (unknown type).
+ // An allocation context with empty backtrace and unknown type.
static AllocationContext Empty();
Backtrace backtrace;
- TypeId type_id;
+
+ // Type name of the type stored in the allocated memory. A null pointer
+ // indicates "unknown type". Grouping is done by comparing pointers, not by
+ // deep string comparison. In a component build, where a type name can have a
+ // string literal in several dynamic libraries, this may distort grouping.
+ const char* type_name;
private:
friend class AllocationContextTracker;
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
index 51e4590..791ab7a 100644
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -106,7 +106,7 @@ AllocationContext AllocationContextTracker::GetContextSnapshot() {
std::fill(dst, dst_end, nullptr);
}
- ctx.type_id = 0;
+ ctx.type_name = nullptr;
return ctx;
}
diff --git a/base/trace_event/heap_profiler_heap_dump_writer.cc b/base/trace_event/heap_profiler_heap_dump_writer.cc
index dc27823..d69ed7b 100644
--- a/base/trace_event/heap_profiler_heap_dump_writer.cc
+++ b/base/trace_event/heap_profiler_heap_dump_writer.cc
@@ -12,13 +12,12 @@
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
+#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
#include "base/trace_event/trace_event_argument.h"
namespace base {
namespace trace_event {
-using TypeId = AllocationContext::TypeId;
-
namespace {
template <typename T>
@@ -41,9 +40,11 @@ std::vector<std::pair<T, size_t>> SortBySizeDescending(
} // namespace
-HeapDumpWriter::HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator)
+HeapDumpWriter::HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator,
+ TypeNameDeduplicator* type_name_deduplicator)
: traced_value_(new TracedValue()),
- stack_frame_deduplicator_(stack_frame_deduplicator) {}
+ stack_frame_deduplicator_(stack_frame_deduplicator),
+ type_name_deduplicator_(type_name_deduplicator) {}
HeapDumpWriter::~HeapDumpWriter() {}
@@ -57,12 +58,12 @@ scoped_refptr<TracedValue> HeapDumpWriter::WriteHeapDump() {
// iterating anyway.
size_t total_size = 0;
hash_map<Backtrace, size_t> bytes_by_backtrace;
- hash_map<TypeId, size_t> bytes_by_type;
+ hash_map<const char*, size_t> bytes_by_type;
for (auto context_size : bytes_by_context_) {
total_size += context_size.second;
bytes_by_backtrace[context_size.first.backtrace] += context_size.second;
- bytes_by_type[context_size.first.type_id] += context_size.second;
+ bytes_by_type[context_size.first.type_name] += context_size.second;
}
auto sorted_bytes_by_backtrace = SortBySizeDescending(bytes_by_backtrace);
@@ -90,7 +91,9 @@ scoped_refptr<TracedValue> HeapDumpWriter::WriteHeapDump() {
// Entries with the size per type.
for (const auto& entry : sorted_bytes_by_type) {
traced_value_->BeginDictionary();
- WriteTypeId(entry.first);
+ // Insert a forward reference to the type name that will be written to the
+ // trace when it is flushed.
+ WriteTypeId(type_name_deduplicator_->Insert(entry.first));
WriteSize(entry.second);
traced_value_->EndDictionary();
}
@@ -113,17 +116,10 @@ void HeapDumpWriter::WriteStackFrameIndex(int index) {
}
}
-void HeapDumpWriter::WriteTypeId(TypeId type_id) {
- if (type_id == 0) {
- // Type ID 0 represents "unknown type". Instead of writing it as "0" which
- // could be mistaken for an actual type ID, an unknown type is represented
- // by the empty string.
- traced_value_->SetString("type", "");
- } else {
- // Format the type ID as a string.
- SStringPrintf(&buffer_, "%" PRIu16, type_id);
- traced_value_->SetString("type", buffer_);
- }
+void HeapDumpWriter::WriteTypeId(int type_id) {
+ // Format the type ID as a string.
+ SStringPrintf(&buffer_, "%i", type_id);
+ traced_value_->SetString("type", buffer_);
}
void HeapDumpWriter::WriteSize(size_t size) {
diff --git a/base/trace_event/heap_profiler_heap_dump_writer.h b/base/trace_event/heap_profiler_heap_dump_writer.h
index 4b99543..c3a7767 100644
--- a/base/trace_event/heap_profiler_heap_dump_writer.h
+++ b/base/trace_event/heap_profiler_heap_dump_writer.h
@@ -16,8 +16,9 @@
namespace base {
namespace trace_event {
-class TracedValue;
class StackFrameDeduplicator;
+class TracedValue;
+class TypeNameDeduplicator;
// Helper class to dump a snapshot of an |AllocationRegister| or other heap
// bookkeeping structure into a |TracedValue|. This class is intended to be
@@ -26,9 +27,11 @@ class StackFrameDeduplicator;
// to do the processing and generate a heap dump value for the trace log.
class BASE_EXPORT HeapDumpWriter {
public:
- // The |StackFrameDeduplicator| is not owned. The heap dump writer assumes
- // exclusive access to it during the lifetime of the dump writer.
- HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator);
+ // The |StackFrameDeduplicator| and |TypeNameDeduplicator| are not owned. The
+ // heap dump writer assumes exclusive access to them during the lifetime of
+ // the dump writer.
+ HeapDumpWriter(StackFrameDeduplicator* stack_frame_deduplicator,
+ TypeNameDeduplicator* type_name_deduplicator);
~HeapDumpWriter();
// Inserts information from which the heap dump will be generated. This method
@@ -45,7 +48,7 @@ class BASE_EXPORT HeapDumpWriter {
void WriteStackFrameIndex(int index);
// Writes a "type" key with the stringified type ID.
- void WriteTypeId(AllocationContext::TypeId type_id);
+ void WriteTypeId(int type_id);
// Writes a "size" key with value |size| as a hexidecimal string to the traced
// value.
@@ -58,6 +61,10 @@ class BASE_EXPORT HeapDumpWriter {
// this heap dump writer instance.
StackFrameDeduplicator* const stack_frame_deduplicator_;
+ // Helper for converting type names to IDs. Not owned, must outlive this heap
+ // dump writer instance.
+ 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_;
diff --git a/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc b/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
index 2edf863..c936f4e 100644
--- a/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
+++ b/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
@@ -11,6 +11,7 @@
#include "base/trace_event/heap_profiler_allocation_context.h"
#include "base/trace_event/heap_profiler_heap_dump_writer.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
+#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
#include "base/trace_event/trace_event_argument.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,19 +25,15 @@ const char kRendererMain[] = "RendererMain";
const char kCreateWidget[] = "CreateWidget";
const char kInitialize[] = "Initialize";
+const char kInt[] = "int";
+const char kBool[] = "bool";
+const char kString[] = "string";
+
} // namespace
namespace base {
namespace trace_event {
-void AssertStringEq(const DictionaryValue* entry,
- const char* key,
- const char* expected_value) {
- std::string str;
- ASSERT_TRUE(entry->GetString(key, &str));
- ASSERT_EQ(expected_value, str);
-}
-
// Asserts that an integer stored in the json as a string has the correct value.
void AssertIntEq(const DictionaryValue* entry,
const char* key,
@@ -53,45 +50,46 @@ scoped_ptr<Value> DumpAndReadBack(HeapDumpWriter* writer) {
return JSONReader::Read(json);
}
-TEST(HeapDumpWriterTest, BacktraceTypeIdTable) {
- auto deduplicator = make_scoped_refptr(new StackFrameDeduplicator);
- HeapDumpWriter writer(deduplicator.get());
+TEST(HeapDumpWriterTest, BacktraceTypeNameTable) {
+ auto sf_deduplicator = make_scoped_refptr(new StackFrameDeduplicator);
+ auto tn_deduplicator = make_scoped_refptr(new TypeNameDeduplicator);
+ HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get());
AllocationContext ctx = AllocationContext::Empty();
ctx.backtrace.frames[0] = kBrowserMain;
ctx.backtrace.frames[1] = kCreateWidget;
- ctx.type_id = 1;
+ ctx.type_name = kInt;
- // 10 bytes with context { type: 1, bt: [BrowserMain, CreateWidget] }.
+ // 10 bytes with context { type: int, bt: [BrowserMain, CreateWidget] }.
writer.InsertAllocation(ctx, 2);
writer.InsertAllocation(ctx, 3);
writer.InsertAllocation(ctx, 5);
- ctx.type_id = 2;
+ ctx.type_name = kBool;
- // 18 bytes with context { type: 2, bt: [BrowserMain, CreateWidget] }.
+ // 18 bytes with context { type: bool, bt: [BrowserMain, CreateWidget] }.
writer.InsertAllocation(ctx, 7);
writer.InsertAllocation(ctx, 11);
ctx.backtrace.frames[0] = kRendererMain;
ctx.backtrace.frames[1] = kInitialize;
- // 30 bytes with context { type: 2, bt: [RendererMain, Initialize] }.
+ // 30 bytes with context { type: bool, bt: [RendererMain, Initialize] }.
writer.InsertAllocation(ctx, 13);
writer.InsertAllocation(ctx, 17);
- ctx.type_id = 3;
+ ctx.type_name = kString;
- // 19 bytes with context { type: 3, bt: [RendererMain, Initialize] }.
+ // 19 bytes with context { type: string, bt: [RendererMain, Initialize] }.
writer.InsertAllocation(ctx, 19);
// At this point the heap looks like this:
//
// | | CrWidget <- BrMain | Init <- RenMain | Sum |
// +--------+--------------------+-----------------+-----+
- // | Type 1 | 10 | 0 | 10 |
- // | Type 2 | 18 | 30 | 48 |
- // | Type 3 | 0 | 19 | 19 |
+ // | int | 10 | 0 | 10 |
+ // | bool | 18 | 30 | 48 |
+ // | string | 0 | 19 | 19 |
// +--------+--------------------+-----------------+-----+
// | Sum | 28 | 49 | 77 |
@@ -103,18 +101,21 @@ TEST(HeapDumpWriterTest, BacktraceTypeIdTable) {
// { "size": "4d" }, // 77 = 0x4d.
// { "size": "31", "bt": "id_of(Init <- RenMain)" }, // 49 = 0x31.
// { "size": "1c", "bt": "id_of(CrWidget <- BrMain)" }, // 28 = 0x1c.
- // { "size": "30", "type": "2" }, // 48 = 0x30.
- // { "size": "13", "type": "3" }, // 19 = 0x13.
- // { "size": "a", "type": "1" } // 10 = 0xa.
+ // { "size": "30", "type": "id_of(bool)" }, // 48 = 0x30.
+ // { "size": "13", "type": "id_of(string)" }, // 19 = 0x13.
+ // { "size": "a", "type": "id_of(int)" } // 10 = 0xa.
// ]
// }
- // Get the indices of the backtraces by adding them again to the deduplicator.
- // Because they were added before, the same number is returned.
+ // Get the indices of the backtraces and types by adding them again to the
+ // deduplicator. Because they were added before, the same number is returned.
int bt_renderer_main_initialize =
- deduplicator->Insert({{kRendererMain, kInitialize}});
+ sf_deduplicator->Insert({{kRendererMain, kInitialize}});
int bt_browser_main_create_widget =
- deduplicator->Insert({{kBrowserMain, kCreateWidget}});
+ sf_deduplicator->Insert({{kBrowserMain, kCreateWidget}});
+ int type_id_int = tn_deduplicator->Insert(kInt);
+ int type_id_bool = tn_deduplicator->Insert(kBool);
+ int type_id_string = tn_deduplicator->Insert(kString);
const DictionaryValue* dictionary;
ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary));
@@ -154,19 +155,19 @@ TEST(HeapDumpWriterTest, BacktraceTypeIdTable) {
AssertIntEq(entry, "bt", bt_browser_main_create_widget);
x1c_seen++;
} else if (size == "30") {
- // Entry for type ID 2.
+ // Entry for type bool.
ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertStringEq(entry, "type", "2");
+ AssertIntEq(entry, "type", type_id_bool);
x30_seen++;
} else if (size == "13") {
- // Entry for type ID 3.
+ // Entry for type string.
ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertStringEq(entry, "type", "3");
+ AssertIntEq(entry, "type", type_id_string);
x13_seen++;
} else if (size == "a") {
- // Entry for type ID 1.
+ // Entry for type int.
ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertStringEq(entry, "type", "1");
+ AssertIntEq(entry, "type", type_id_int);
xa_seen++;
}
}
@@ -180,14 +181,15 @@ TEST(HeapDumpWriterTest, BacktraceTypeIdTable) {
}
// Test that the entry for the empty backtrace ends up in the json with the
-// "bt" field set to the empty string. Also test that the entry for "unknown
-// type" (type ID 0) ends up in the json with the "type" field set to the empty
-// string.
-TEST(HeapDumpWriterTest, EmptyBacktraceIndexAndUnknownTypeIdAreEmptyString) {
- auto deduplicator = make_scoped_refptr(new StackFrameDeduplicator);
- HeapDumpWriter writer(deduplicator.get());
-
- // A context with empty backtrace and type ID 0 (unknown type).
+// "bt" field set to the empty string. Also test that an entry for "unknown
+// type" (nullptr type name) does not dereference the null pointer when writing
+// the type names, and that the type ID is 0.
+TEST(HeapDumpWriterTest, EmptyBacktraceIndexIsEmptyString) {
+ auto sf_deduplicator = make_scoped_refptr(new StackFrameDeduplicator);
+ auto tn_deduplicator = make_scoped_refptr(new TypeNameDeduplicator);
+ HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get());
+
+ // A context with empty backtrace and unknown type (nullptr).
AllocationContext ctx = AllocationContext::Empty();
writer.InsertAllocation(ctx, 1);
@@ -218,7 +220,7 @@ TEST(HeapDumpWriterTest, EmptyBacktraceIndexAndUnknownTypeIdAreEmptyString) {
if (entry->HasKey("type") && entry->size() == 2) {
std::string type_id;
ASSERT_TRUE(entry->GetString("type", &type_id));
- ASSERT_EQ("", type_id);
+ ASSERT_EQ("0", type_id);
unknown_type_seen++;
}
}
diff --git a/base/trace_event/heap_profiler_type_name_deduplicator.cc b/base/trace_event/heap_profiler_type_name_deduplicator.cc
new file mode 100644
index 0000000..17c3cee
--- /dev/null
+++ b/base/trace_event/heap_profiler_type_name_deduplicator.cc
@@ -0,0 +1,75 @@
+// Copyright 2015 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/trace_event/heap_profiler_type_name_deduplicator.h"
+
+#include <stdlib.h>
+#include <string>
+#include <utility>
+
+#include "base/json/string_escape.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
+
+namespace base {
+namespace trace_event {
+
+TypeNameDeduplicator::TypeNameDeduplicator() {
+ // A null pointer has type ID 0 ("unknown type");
+ type_ids_.insert(std::make_pair(nullptr, 0));
+}
+
+TypeNameDeduplicator::~TypeNameDeduplicator() {}
+
+int TypeNameDeduplicator::Insert(const char* type_name) {
+ auto result = type_ids_.insert(std::make_pair(type_name, 0));
+ auto& elem = result.first;
+ bool did_not_exist_before = result.second;
+
+ if (did_not_exist_before) {
+ // The type IDs are assigned sequentially and they are zero-based, so
+ // |size() - 1| is the ID of the new element.
+ elem->second = static_cast<int>(type_ids_.size() - 1);
+ }
+
+ return elem->second;
+}
+
+void TypeNameDeduplicator::AppendAsTraceFormat(std::string* out) const {
+ out->append("{"); // Begin the type names dictionary.
+
+ auto it = type_ids_.begin();
+ std::string buffer;
+
+ // Write the first entry manually; the null pointer must not be dereferenced.
+ // (The first entry is the null pointer because a |std::map| is ordered.)
+ it++;
+ out->append("\"0\":\"[unknown]\"");
+
+ for (; it != type_ids_.end(); it++) {
+ // Type IDs in the trace are strings, write them as stringified keys of
+ // a dictionary.
+ SStringPrintf(&buffer, ",\"%d\":", it->second);
+
+ // |EscapeJSONString| appends, it does not overwrite |buffer|.
+ bool put_in_quotes = true;
+ EscapeJSONString(it->first, put_in_quotes, &buffer);
+ out->append(buffer);
+ }
+
+ out->append("}"); // End the type names dictionary.
+}
+
+void TypeNameDeduplicator::EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) {
+ // The size here is only an estimate; it fails to take into account the size
+ // of the tree nodes for the map, but as an estimate this should be fine.
+ size_t map_size = type_ids_.size() * sizeof(std::pair<const char*, int>);
+
+ overhead->Add("TypeNameDeduplicator",
+ sizeof(TypeNameDeduplicator) + map_size);
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/heap_profiler_type_name_deduplicator.h b/base/trace_event/heap_profiler_type_name_deduplicator.h
new file mode 100644
index 0000000..317ea5e
--- /dev/null
+++ b/base/trace_event/heap_profiler_type_name_deduplicator.h
@@ -0,0 +1,46 @@
+// Copyright 2015 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.
+
+#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_
+#define BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_
+
+#include <map>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/trace_event/trace_event_impl.h"
+
+namespace base {
+namespace trace_event {
+
+class TraceEventMemoryOverhead;
+
+// Data structure that assigns a unique numeric ID to |const char*|s.
+class BASE_EXPORT TypeNameDeduplicator : public ConvertableToTraceFormat {
+ public:
+ TypeNameDeduplicator();
+
+ // Inserts a type name and returns its ID.
+ int Insert(const char* type_name);
+
+ // Estimates memory overhead including |sizeof(TypeNameDeduplicator)|.
+ void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
+
+ private:
+ ~TypeNameDeduplicator() override;
+
+ // Writes the type ID -> type name mapping to the trace log.
+ void AppendAsTraceFormat(std::string* out) const override;
+
+ // Map from type name to type ID.
+ std::map<const char*, int> type_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(TypeNameDeduplicator);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_HEAP_PROFILER_TYPE_NAME_DEDUPLICATOR_H_
diff --git a/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc b/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc
new file mode 100644
index 0000000..82c8fb5
--- /dev/null
+++ b/base/trace_event/heap_profiler_type_name_deduplicator_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 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 <string>
+
+#include "base/json/json_reader.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+// Define all strings once, because the deduplicator requires pointer equality,
+// and string interning is unreliable.
+const char kInt[] = "int";
+const char kBool[] = "bool";
+const char kString[] = "string";
+const char kNeedsEscape[] = "\"quotes\"";
+
+scoped_ptr<Value> DumpAndReadBack(const ConvertableToTraceFormat& convertable) {
+ std::string json;
+ convertable.AppendAsTraceFormat(&json);
+ return JSONReader::Read(json);
+}
+
+TEST(TypeNameDeduplicatorTest, Deduplication) {
+ // The type IDs should be like this:
+ // 0: [unknown]
+ // 1: int
+ // 2: bool
+ // 3: string
+
+ scoped_refptr<TypeNameDeduplicator> dedup = new TypeNameDeduplicator;
+ ASSERT_EQ(1, dedup->Insert(kInt));
+ ASSERT_EQ(2, dedup->Insert(kBool));
+ ASSERT_EQ(3, dedup->Insert(kString));
+
+ // Inserting again should return the same IDs.
+ ASSERT_EQ(2, dedup->Insert(kBool));
+ ASSERT_EQ(1, dedup->Insert(kInt));
+ ASSERT_EQ(3, dedup->Insert(kString));
+
+ // A null pointer should yield type ID 0.
+ ASSERT_EQ(0, dedup->Insert(nullptr));
+}
+
+TEST(TypeNameDeduplicatorTest, EscapeTypeName) {
+ scoped_refptr<TypeNameDeduplicator> dedup = new TypeNameDeduplicator;
+ ASSERT_EQ(1, dedup->Insert(kNeedsEscape));
+
+ // Reading json should not fail, because the type name should have been
+ // escaped properly.
+ scoped_ptr<Value> type_names = DumpAndReadBack(*dedup);
+ ASSERT_NE(nullptr, type_names);
+
+ const DictionaryValue* dictionary;
+ ASSERT_TRUE(type_names->GetAsDictionary(&dictionary));
+
+ // When the type name was inserted, it got ID 1. The exported key "1"
+ // should contain the name, with quotes.
+ std::string type_name;
+ ASSERT_TRUE(dictionary->GetString("1", &type_name));
+ ASSERT_EQ("\"quotes\"", type_name);
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
index 8d8d147..124de0d 100644
--- a/base/trace_event/memory_allocator_dump_unittest.cc
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -125,7 +125,7 @@ TEST(MemoryAllocatorDumpTest, GuidGeneration) {
TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
FakeMemoryAllocatorDumpProvider fmadp;
- ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr));
+ ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr, nullptr));
MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
fmadp.OnMemoryDump(dump_args, &pmd);
@@ -172,7 +172,7 @@ TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) {
FakeMemoryAllocatorDumpProvider fmadp;
- ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr));
+ ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr, nullptr));
pmd.CreateAllocatorDump("foo_allocator");
pmd.CreateAllocatorDump("bar_allocator/heap");
ASSERT_DEATH(pmd.CreateAllocatorDump("foo_allocator"), "");
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index e6a1cc3..7b46e41 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -15,6 +15,7 @@
#include "base/threading/thread.h"
#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
+#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
#include "base/trace_event/malloc_dump_provider.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_session_state.h"
@@ -519,19 +520,26 @@ void MemoryDumpManager::OnTraceLogEnabled() {
DCHECK(delegate_); // At this point we must have a delegate.
scoped_refptr<StackFrameDeduplicator> stack_frame_deduplicator = nullptr;
+ scoped_refptr<TypeNameDeduplicator> type_name_deduplicator = nullptr;
if (heap_profiling_enabled_) {
- // If heap profiling is enabled, the stack frame deduplicator will be in
- // use. Add a metadata event to write its frames.
+ // If heap profiling is enabled, the stack frame deduplicator and type name
+ // deduplicator will be in use. Add a metadata events to write the frames
+ // and type IDs.
stack_frame_deduplicator = new StackFrameDeduplicator;
+ type_name_deduplicator = new TypeNameDeduplicator;
TRACE_EVENT_API_ADD_METADATA_EVENT(
"stackFrames", "stackFrames",
scoped_refptr<ConvertableToTraceFormat>(stack_frame_deduplicator));
+ TRACE_EVENT_API_ADD_METADATA_EVENT(
+ "typeNames", "typeNames",
+ scoped_refptr<ConvertableToTraceFormat>(type_name_deduplicator));
}
DCHECK(!dump_thread_);
dump_thread_ = std::move(dump_thread);
- session_state_ = new MemoryDumpSessionState(stack_frame_deduplicator);
+ session_state_ = new MemoryDumpSessionState(stack_frame_deduplicator,
+ type_name_deduplicator);
for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
it->disabled = false;
diff --git a/base/trace_event/memory_dump_session_state.cc b/base/trace_event/memory_dump_session_state.cc
index b9b0d1e..5aa79b1 100644
--- a/base/trace_event/memory_dump_session_state.cc
+++ b/base/trace_event/memory_dump_session_state.cc
@@ -8,8 +8,10 @@ namespace base {
namespace trace_event {
MemoryDumpSessionState::MemoryDumpSessionState(
- const scoped_refptr<StackFrameDeduplicator>& stack_frame_deduplicator)
- : stack_frame_deduplicator_(stack_frame_deduplicator) {}
+ const scoped_refptr<StackFrameDeduplicator>& stack_frame_deduplicator,
+ const scoped_refptr<TypeNameDeduplicator>& type_name_deduplicator)
+ : stack_frame_deduplicator_(stack_frame_deduplicator),
+ type_name_deduplicator_(type_name_deduplicator) {}
MemoryDumpSessionState::~MemoryDumpSessionState() {
}
diff --git a/base/trace_event/memory_dump_session_state.h b/base/trace_event/memory_dump_session_state.h
index 8a03207..6834471 100644
--- a/base/trace_event/memory_dump_session_state.h
+++ b/base/trace_event/memory_dump_session_state.h
@@ -8,6 +8,7 @@
#include "base/base_export.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
+#include "base/trace_event/heap_profiler_type_name_deduplicator.h"
namespace base {
namespace trace_event {
@@ -18,7 +19,8 @@ class BASE_EXPORT MemoryDumpSessionState
: public RefCountedThreadSafe<MemoryDumpSessionState> {
public:
MemoryDumpSessionState(
- const scoped_refptr<StackFrameDeduplicator>& stack_frame_deduplicator);
+ const scoped_refptr<StackFrameDeduplicator>& stack_frame_deduplicator,
+ const scoped_refptr<TypeNameDeduplicator>& type_name_deduplicator);
// Returns the stack frame deduplicator that should be used by memory dump
// providers when doing a heap dump.
@@ -26,6 +28,12 @@ class BASE_EXPORT MemoryDumpSessionState
return stack_frame_deduplicator_.get();
}
+ // Returns the type name deduplicator that should be used by memory dump
+ // providers when doing a heap dump.
+ TypeNameDeduplicator* type_name_deduplicator() {
+ return type_name_deduplicator_.get();
+ }
+
private:
friend class RefCountedThreadSafe<MemoryDumpSessionState>;
~MemoryDumpSessionState();
@@ -33,6 +41,10 @@ class BASE_EXPORT MemoryDumpSessionState
// Deduplicates backtraces in heap dumps so they can be written once when the
// trace is finalized.
scoped_refptr<StackFrameDeduplicator> stack_frame_deduplicator_;
+
+ // Deduplicates type names in heap dumps so they can be written once when the
+ // trace is finalized.
+ scoped_refptr<TypeNameDeduplicator> type_name_deduplicator_;
};
} // namespace trace_event
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
index 2e163c3..ca37015 100644
--- a/base/trace_event/trace_event.gypi
+++ b/base/trace_event/trace_event.gypi
@@ -17,6 +17,8 @@
'trace_event/heap_profiler_heap_dump_writer.h',
'trace_event/heap_profiler_stack_frame_deduplicator.cc',
'trace_event/heap_profiler_stack_frame_deduplicator.h',
+ 'trace_event/heap_profiler_type_name_deduplicator.cc',
+ 'trace_event/heap_profiler_type_name_deduplicator.h',
'trace_event/java_heap_dump_provider_android.cc',
'trace_event/java_heap_dump_provider_android.h',
'trace_event/memory_allocator_dump.cc',
@@ -72,6 +74,7 @@
'trace_event/heap_profiler_allocation_register_unittest.cc',
'trace_event/heap_profiler_heap_dump_writer_unittest.cc',
'trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc',
+ 'trace_event/heap_profiler_type_name_deduplicator_unittest.cc',
'trace_event/java_heap_dump_provider_android_unittest.cc',
'trace_event/memory_allocator_dump_unittest.cc',
'trace_event/memory_dump_manager_unittest.cc',
diff --git a/base/trace_event/winheap_dump_provider_win_unittest.cc b/base/trace_event/winheap_dump_provider_win_unittest.cc
index fac002f..c74a7b7 100644
--- a/base/trace_event/winheap_dump_provider_win_unittest.cc
+++ b/base/trace_event/winheap_dump_provider_win_unittest.cc
@@ -14,7 +14,7 @@ namespace base {
namespace trace_event {
TEST(WinHeapDumpProviderTest, OnMemoryDump) {
- ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr));
+ ProcessMemoryDump pmd(new MemoryDumpSessionState(nullptr, nullptr));
MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
WinHeapDumpProvider* winheap_dump_provider =