summaryrefslogtreecommitdiffstats
path: root/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/trace_event/heap_profiler_heap_dump_writer_unittest.cc')
-rw-r--r--base/trace_event/heap_profiler_heap_dump_writer_unittest.cc299
1 files changed, 148 insertions, 151 deletions
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 841611a..ba632da 100644
--- a/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
+++ b/base/trace_event/heap_profiler_heap_dump_writer_unittest.cc
@@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <stdlib.h>
-
-#include <iterator>
+#include <set>
#include <string>
#include "base/json/json_reader.h"
+#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/trace_event/heap_profiler_allocation_context.h"
@@ -35,27 +34,116 @@ const char kString[] = "string";
namespace base {
namespace trace_event {
+namespace internal {
-// Asserts that an integer stored in the json as a string has the correct value.
-void AssertIntEq(const DictionaryValue* entry,
- const char* key,
- int expected_value) {
- std::string str;
- ASSERT_TRUE(entry->GetString(key, &str));
- ASSERT_EQ(expected_value, atoi(str.c_str()));
-}
-
-scoped_ptr<Value> DumpAndReadBack(HeapDumpWriter* writer) {
- scoped_refptr<TracedValue> traced_value = writer->WriteHeapDump();
+scoped_ptr<const Value> WriteAndReadBack(const std::set<Entry>& entries) {
+ scoped_refptr<TracedValue> traced_value = Serialize(entries);
std::string json;
traced_value->AppendAsTraceFormat(&json);
return JSONReader::Read(json);
}
+scoped_ptr<const DictionaryValue> WriteAndReadBackEntry(Entry entry) {
+ std::set<Entry> input_entries;
+ input_entries.insert(entry);
+
+ scoped_ptr<const Value> json_dict = WriteAndReadBack(input_entries);
+
+ // Note: Ideally these should use |ASSERT_TRUE| instead of |EXPECT_TRUE|, but
+ // |ASSERT_TRUE| can only be used in void functions.
+ const DictionaryValue* dictionary;
+ EXPECT_TRUE(json_dict->GetAsDictionary(&dictionary));
+
+ const ListValue* json_entries;
+ EXPECT_TRUE(dictionary->GetList("entries", &json_entries));
+
+ const DictionaryValue* json_entry;
+ EXPECT_TRUE(json_entries->GetDictionary(0, &json_entry));
+
+ return json_entry->CreateDeepCopy();
+}
+
+// Given a desired stack frame ID and type ID, looks up the entry in the set and
+// asserts that it is present and has the expected size.
+void AssertSizeEq(const std::set<Entry>& entries,
+ int stack_frame_id,
+ int type_id,
+ size_t expected_size) {
+ // The comparison operator for |Entry| does not take size into account, so by
+ // setting only stack frame ID and type ID, the real entry can be found.
+ Entry entry;
+ entry.stack_frame_id = stack_frame_id;
+ entry.type_id = type_id;
+ auto it = entries.find(entry);
+
+ ASSERT_NE(entries.end(), it) << "No entry found for sf = " << stack_frame_id
+ << ", type = " << type_id << ".";
+ ASSERT_EQ(expected_size, it->size) << "Wrong size for sf = " << stack_frame_id
+ << ", type = " << type_id << ".";
+}
+
+TEST(HeapDumpWriterTest, BacktraceIndex) {
+ Entry entry;
+ entry.stack_frame_id = -1; // -1 means empty backtrace.
+ entry.type_id = 0;
+ entry.size = 1;
+
+ scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry);
+
+ // For an empty backtrace, the "bt" key cannot reference a stack frame.
+ // Instead it should be set to the empty string.
+ std::string backtrace_index;
+ ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index));
+ ASSERT_EQ("", backtrace_index);
+
+ // Also verify that a non-negative backtrace index is dumped properly.
+ entry.stack_frame_id = 2;
+ json_entry = WriteAndReadBackEntry(entry);
+ ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index));
+ ASSERT_EQ("2", backtrace_index);
+}
+
+TEST(HeapDumpWriterTest, TypeId) {
+ Entry entry;
+ entry.type_id = -1; // -1 means sum over all types.
+ entry.stack_frame_id = 0;
+ entry.size = 1;
+
+ scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry);
+
+ // Entries for the cumulative size of all types should not have the "type"
+ // key set.
+ ASSERT_FALSE(json_entry->HasKey("type"));
+
+ // Also verify that a non-negative type ID is dumped properly.
+ entry.type_id = 2;
+ json_entry = WriteAndReadBackEntry(entry);
+ std::string type_id;
+ ASSERT_TRUE(json_entry->GetString("type", &type_id));
+ ASSERT_EQ("2", type_id);
+}
+
+TEST(HeapDumpWriterTest, SizeIsHexadecimalString) {
+ // Take a number between 2^63 and 2^64 (or between 2^31 and 2^32 if |size_t|
+ // is not 64 bits).
+ const size_t large_value =
+ sizeof(size_t) == 8 ? 0xffffffffffffffc5 : 0xffffff9d;
+ const char* large_value_str =
+ sizeof(size_t) == 8 ? "ffffffffffffffc5" : "ffffff9d";
+ Entry entry;
+ entry.type_id = 0;
+ entry.stack_frame_id = 0;
+ entry.size = large_value;
+
+ scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry);
+
+ std::string size;
+ ASSERT_TRUE(json_entry->GetString("size", &size));
+ ASSERT_EQ(large_value_str, size);
+}
+
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());
+ hash_map<AllocationContext, size_t> bytes_by_context;
AllocationContext ctx = AllocationContext::Empty();
ctx.backtrace.frames[0] = kBrowserMain;
@@ -63,27 +151,23 @@ TEST(HeapDumpWriterTest, BacktraceTypeNameTable) {
ctx.type_name = kInt;
// 10 bytes with context { type: int, bt: [BrowserMain, CreateWidget] }.
- writer.InsertAllocation(ctx, 2);
- writer.InsertAllocation(ctx, 3);
- writer.InsertAllocation(ctx, 5);
+ bytes_by_context[ctx] = 10;
ctx.type_name = kBool;
// 18 bytes with context { type: bool, bt: [BrowserMain, CreateWidget] }.
- writer.InsertAllocation(ctx, 7);
- writer.InsertAllocation(ctx, 11);
+ bytes_by_context[ctx] = 18;
ctx.backtrace.frames[0] = kRendererMain;
ctx.backtrace.frames[1] = kInitialize;
// 30 bytes with context { type: bool, bt: [RendererMain, Initialize] }.
- writer.InsertAllocation(ctx, 13);
- writer.InsertAllocation(ctx, 17);
+ bytes_by_context[ctx] = 30;
ctx.type_name = kString;
// 19 bytes with context { type: string, bt: [RendererMain, Initialize] }.
- writer.InsertAllocation(ctx, 19);
+ bytes_by_context[ctx] = 19;
// At this point the heap looks like this:
//
@@ -95,143 +179,56 @@ TEST(HeapDumpWriterTest, BacktraceTypeNameTable) {
// +--------+--------------------+-----------------+-----+
// | Sum | 28 | 49 | 77 |
- scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer);
-
- // The json heap dump should at least include this:
- // {
- // "entries": [
- // { "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": "id_of(bool)" }, // 48 = 0x30.
- // { "size": "13", "type": "id_of(string)" }, // 19 = 0x13.
- // { "size": "a", "type": "id_of(int)" } // 10 = 0xa.
- // ]
- // }
+ auto sf_deduplicator = make_scoped_refptr(new StackFrameDeduplicator);
+ auto tn_deduplicator = make_scoped_refptr(new TypeNameDeduplicator);
+ HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get());
+ const std::set<Entry>& dump = writer.Summarize(bytes_by_context);
// 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.
StackFrame bt0[] = {kRendererMain, kInitialize};
StackFrame bt1[] = {kBrowserMain, kCreateWidget};
- int bt_renderer_main_initialize =
- sf_deduplicator->Insert(std::begin(bt0), std::end(bt0));
- int bt_browser_main_create_widget =
- sf_deduplicator->Insert(std::begin(bt1), std::end(bt1));
+ int bt_renderer_main = sf_deduplicator->Insert(bt0, bt0 + 1);
+ int bt_browser_main = sf_deduplicator->Insert(bt1, bt1 + 1);
+ int bt_renderer_main_initialize = sf_deduplicator->Insert(bt0, bt0 + 2);
+ int bt_browser_main_create_widget = sf_deduplicator->Insert(bt1, bt1 + 2);
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));
-
- const ListValue* entries;
- ASSERT_TRUE(dictionary->GetList("entries", &entries));
-
- // Keep counters to verify that every entry is present exactly once.
- int x4d_seen = 0;
- int x31_seen = 0;
- int x1c_seen = 0;
- int x30_seen = 0;
- int x13_seen = 0;
- int xa_seen = 0;
-
- for (const Value* entry_as_value : *entries) {
- const DictionaryValue* entry;
- ASSERT_TRUE(entry_as_value->GetAsDictionary(&entry));
-
- // The "size" field, not to be confused with |entry->size()| which is the
- // number of elements in the dictionary.
- std::string size;
- ASSERT_TRUE(entry->GetString("size", &size));
-
- if (size == "4d") {
- // Total size, should not include any other field.
- ASSERT_EQ(1u, entry->size()); // Dictionary must have one element.
- x4d_seen++;
- } else if (size == "31") {
- // Entry for backtrace "Initialize <- RendererMain".
- ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertIntEq(entry, "bt", bt_renderer_main_initialize);
- x31_seen++;
- } else if (size == "1c") {
- // Entry for backtrace "CreateWidget <- BrowserMain".
- ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertIntEq(entry, "bt", bt_browser_main_create_widget);
- x1c_seen++;
- } else if (size == "30") {
- // Entry for type bool.
- ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertIntEq(entry, "type", type_id_bool);
- x30_seen++;
- } else if (size == "13") {
- // Entry for type string.
- ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertIntEq(entry, "type", type_id_string);
- x13_seen++;
- } else if (size == "a") {
- // Entry for type int.
- ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements.
- AssertIntEq(entry, "type", type_id_int);
- xa_seen++;
- }
- }
-
- ASSERT_EQ(1, x4d_seen);
- ASSERT_EQ(1, x31_seen);
- ASSERT_EQ(1, x1c_seen);
- ASSERT_EQ(1, x30_seen);
- ASSERT_EQ(1, x13_seen);
- ASSERT_EQ(1, xa_seen);
+ // Full heap should have size 77.
+ AssertSizeEq(dump, -1, -1, 77);
+
+ // 49 bytes were allocated in RendererMain and children. Also check the type
+ // breakdown.
+ AssertSizeEq(dump, bt_renderer_main, -1, 49);
+ AssertSizeEq(dump, bt_renderer_main, type_id_bool, 30);
+ AssertSizeEq(dump, bt_renderer_main, type_id_string, 19);
+
+ // 28 bytes were allocated in BrowserMain and children. Also check the type
+ // breakdown.
+ AssertSizeEq(dump, bt_browser_main, -1, 28);
+ AssertSizeEq(dump, bt_browser_main, type_id_int, 10);
+ AssertSizeEq(dump, bt_browser_main, type_id_bool, 18);
+
+ // In this test all bytes are allocated in leaf nodes, so check again one
+ // level deeper.
+ AssertSizeEq(dump, bt_renderer_main_initialize, -1, 49);
+ AssertSizeEq(dump, bt_renderer_main_initialize, type_id_bool, 30);
+ AssertSizeEq(dump, bt_renderer_main_initialize, type_id_string, 19);
+ AssertSizeEq(dump, bt_browser_main_create_widget, -1, 28);
+ AssertSizeEq(dump, bt_browser_main_create_widget, type_id_int, 10);
+ AssertSizeEq(dump, bt_browser_main_create_widget, type_id_bool, 18);
+
+ // The type breakdown of the entrie heap should have been dumped as well.
+ AssertSizeEq(dump, -1, type_id_int, 10);
+ AssertSizeEq(dump, -1, type_id_bool, 48);
+ AssertSizeEq(dump, -1, type_id_string, 19);
}
-// 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 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);
-
- scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer);
-
- const DictionaryValue* dictionary;
- ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary));
-
- const ListValue* entries;
- ASSERT_TRUE(dictionary->GetList("entries", &entries));
-
- int empty_backtrace_seen = 0;
- int unknown_type_seen = 0;
-
- for (const Value* entry_as_value : *entries) {
- const DictionaryValue* entry;
- ASSERT_TRUE(entry_as_value->GetAsDictionary(&entry));
-
- // Note that |entry->size()| is the number of elements in the dictionary.
- if (entry->HasKey("bt") && entry->size() == 2) {
- std::string backtrace;
- ASSERT_TRUE(entry->GetString("bt", &backtrace));
- ASSERT_EQ("", backtrace);
- empty_backtrace_seen++;
- }
-
- if (entry->HasKey("type") && entry->size() == 2) {
- std::string type_id;
- ASSERT_TRUE(entry->GetString("type", &type_id));
- ASSERT_EQ("0", type_id);
- unknown_type_seen++;
- }
- }
-
- ASSERT_EQ(1, unknown_type_seen);
- ASSERT_EQ(1, empty_backtrace_seen);
-}
+// TODO(ruuda): Verify that cumulative sizes are computed correctly.
+// TODO(ruuda): Verify that insignificant values are not dumped.
+} // namespace internal
} // namespace trace_event
} // namespace base