| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stdlib.h> | 5 #include <stdlib.h> |
| 6 #include <string> | 6 #include <string> |
| 7 | 7 |
| 8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
| 9 #include "base/memory/ref_counted.h" | 9 #include "base/memory/ref_counted.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/trace_event/heap_profiler_allocation_context.h" | 11 #include "base/trace_event/heap_profiler_allocation_context.h" |
| 12 #include "base/trace_event/heap_profiler_heap_dump_writer.h" | 12 #include "base/trace_event/heap_profiler_heap_dump_writer.h" |
| 13 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" | 13 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" |
| 14 #include "base/trace_event/heap_profiler_type_name_deduplicator.h" |
| 14 #include "base/trace_event/trace_event_argument.h" | 15 #include "base/trace_event/trace_event_argument.h" |
| 15 #include "base/values.h" | 16 #include "base/values.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" | 17 #include "testing/gtest/include/gtest/gtest.h" |
| 17 | 18 |
| 18 namespace { | 19 namespace { |
| 19 | 20 |
| 20 // Define all strings once, because the deduplicator requires pointer equality, | 21 // Define all strings once, because the deduplicator requires pointer equality, |
| 21 // and string interning is unreliable. | 22 // and string interning is unreliable. |
| 22 const char kBrowserMain[] = "BrowserMain"; | 23 const char kBrowserMain[] = "BrowserMain"; |
| 23 const char kRendererMain[] = "RendererMain"; | 24 const char kRendererMain[] = "RendererMain"; |
| 24 const char kCreateWidget[] = "CreateWidget"; | 25 const char kCreateWidget[] = "CreateWidget"; |
| 25 const char kInitialize[] = "Initialize"; | 26 const char kInitialize[] = "Initialize"; |
| 26 | 27 |
| 28 const char kInt[] = "int"; |
| 29 const char kBool[] = "bool"; |
| 30 const char kString[] = "string"; |
| 31 |
| 27 } // namespace | 32 } // namespace |
| 28 | 33 |
| 29 namespace base { | 34 namespace base { |
| 30 namespace trace_event { | 35 namespace trace_event { |
| 31 | 36 |
| 32 void AssertStringEq(const DictionaryValue* entry, | |
| 33 const char* key, | |
| 34 const char* expected_value) { | |
| 35 std::string str; | |
| 36 ASSERT_TRUE(entry->GetString(key, &str)); | |
| 37 ASSERT_EQ(expected_value, str); | |
| 38 } | |
| 39 | |
| 40 // Asserts that an integer stored in the json as a string has the correct value. | 37 // Asserts that an integer stored in the json as a string has the correct value. |
| 41 void AssertIntEq(const DictionaryValue* entry, | 38 void AssertIntEq(const DictionaryValue* entry, |
| 42 const char* key, | 39 const char* key, |
| 43 int expected_value) { | 40 int expected_value) { |
| 44 std::string str; | 41 std::string str; |
| 45 ASSERT_TRUE(entry->GetString(key, &str)); | 42 ASSERT_TRUE(entry->GetString(key, &str)); |
| 46 ASSERT_EQ(expected_value, atoi(str.c_str())); | 43 ASSERT_EQ(expected_value, atoi(str.c_str())); |
| 47 } | 44 } |
| 48 | 45 |
| 49 scoped_ptr<Value> DumpAndReadBack(HeapDumpWriter* writer) { | 46 scoped_ptr<Value> DumpAndReadBack(HeapDumpWriter* writer) { |
| 50 scoped_refptr<TracedValue> traced_value = writer->WriteHeapDump(); | 47 scoped_refptr<TracedValue> traced_value = writer->WriteHeapDump(); |
| 51 std::string json; | 48 std::string json; |
| 52 traced_value->AppendAsTraceFormat(&json); | 49 traced_value->AppendAsTraceFormat(&json); |
| 53 return JSONReader::Read(json); | 50 return JSONReader::Read(json); |
| 54 } | 51 } |
| 55 | 52 |
| 56 TEST(HeapDumpWriterTest, BacktraceTypeIdTable) { | 53 TEST(HeapDumpWriterTest, BacktraceTypeNameTable) { |
| 57 auto deduplicator = make_scoped_refptr(new StackFrameDeduplicator); | 54 auto sf_deduplicator = make_scoped_refptr(new StackFrameDeduplicator); |
| 58 HeapDumpWriter writer(deduplicator.get()); | 55 auto tn_deduplicator = make_scoped_refptr(new TypeNameDeduplicator); |
| 56 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); |
| 59 | 57 |
| 60 AllocationContext ctx = AllocationContext::Empty(); | 58 AllocationContext ctx = AllocationContext::Empty(); |
| 61 ctx.backtrace.frames[0] = kBrowserMain; | 59 ctx.backtrace.frames[0] = kBrowserMain; |
| 62 ctx.backtrace.frames[1] = kCreateWidget; | 60 ctx.backtrace.frames[1] = kCreateWidget; |
| 63 ctx.type_id = 1; | 61 ctx.type_name = kInt; |
| 64 | 62 |
| 65 // 10 bytes with context { type: 1, bt: [BrowserMain, CreateWidget] }. | 63 // 10 bytes with context { type: int, bt: [BrowserMain, CreateWidget] }. |
| 66 writer.InsertAllocation(ctx, 2); | 64 writer.InsertAllocation(ctx, 2); |
| 67 writer.InsertAllocation(ctx, 3); | 65 writer.InsertAllocation(ctx, 3); |
| 68 writer.InsertAllocation(ctx, 5); | 66 writer.InsertAllocation(ctx, 5); |
| 69 | 67 |
| 70 ctx.type_id = 2; | 68 ctx.type_name = kBool; |
| 71 | 69 |
| 72 // 18 bytes with context { type: 2, bt: [BrowserMain, CreateWidget] }. | 70 // 18 bytes with context { type: bool, bt: [BrowserMain, CreateWidget] }. |
| 73 writer.InsertAllocation(ctx, 7); | 71 writer.InsertAllocation(ctx, 7); |
| 74 writer.InsertAllocation(ctx, 11); | 72 writer.InsertAllocation(ctx, 11); |
| 75 | 73 |
| 76 ctx.backtrace.frames[0] = kRendererMain; | 74 ctx.backtrace.frames[0] = kRendererMain; |
| 77 ctx.backtrace.frames[1] = kInitialize; | 75 ctx.backtrace.frames[1] = kInitialize; |
| 78 | 76 |
| 79 // 30 bytes with context { type: 2, bt: [RendererMain, Initialize] }. | 77 // 30 bytes with context { type: bool, bt: [RendererMain, Initialize] }. |
| 80 writer.InsertAllocation(ctx, 13); | 78 writer.InsertAllocation(ctx, 13); |
| 81 writer.InsertAllocation(ctx, 17); | 79 writer.InsertAllocation(ctx, 17); |
| 82 | 80 |
| 83 ctx.type_id = 3; | 81 ctx.type_name = kString; |
| 84 | 82 |
| 85 // 19 bytes with context { type: 3, bt: [RendererMain, Initialize] }. | 83 // 19 bytes with context { type: string, bt: [RendererMain, Initialize] }. |
| 86 writer.InsertAllocation(ctx, 19); | 84 writer.InsertAllocation(ctx, 19); |
| 87 | 85 |
| 88 // At this point the heap looks like this: | 86 // At this point the heap looks like this: |
| 89 // | 87 // |
| 90 // | | CrWidget <- BrMain | Init <- RenMain | Sum | | 88 // | | CrWidget <- BrMain | Init <- RenMain | Sum | |
| 91 // +--------+--------------------+-----------------+-----+ | 89 // +--------+--------------------+-----------------+-----+ |
| 92 // | Type 1 | 10 | 0 | 10 | | 90 // | int | 10 | 0 | 10 | |
| 93 // | Type 2 | 18 | 30 | 48 | | 91 // | bool | 18 | 30 | 48 | |
| 94 // | Type 3 | 0 | 19 | 19 | | 92 // | string | 0 | 19 | 19 | |
| 95 // +--------+--------------------+-----------------+-----+ | 93 // +--------+--------------------+-----------------+-----+ |
| 96 // | Sum | 28 | 49 | 77 | | 94 // | Sum | 28 | 49 | 77 | |
| 97 | 95 |
| 98 scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer); | 96 scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer); |
| 99 | 97 |
| 100 // The json heap dump should at least include this: | 98 // The json heap dump should at least include this: |
| 101 // { | 99 // { |
| 102 // "entries": [ | 100 // "entries": [ |
| 103 // { "size": "4d" }, // 77 = 0x4d. | 101 // { "size": "4d" }, // 77 = 0x4d. |
| 104 // { "size": "31", "bt": "id_of(Init <- RenMain)" }, // 49 = 0x31. | 102 // { "size": "31", "bt": "id_of(Init <- RenMain)" }, // 49 = 0x31. |
| 105 // { "size": "1c", "bt": "id_of(CrWidget <- BrMain)" }, // 28 = 0x1c. | 103 // { "size": "1c", "bt": "id_of(CrWidget <- BrMain)" }, // 28 = 0x1c. |
| 106 // { "size": "30", "type": "2" }, // 48 = 0x30. | 104 // { "size": "30", "type": "id_of(bool)" }, // 48 = 0x30. |
| 107 // { "size": "13", "type": "3" }, // 19 = 0x13. | 105 // { "size": "13", "type": "id_of(string)" }, // 19 = 0x13. |
| 108 // { "size": "a", "type": "1" } // 10 = 0xa. | 106 // { "size": "a", "type": "id_of(int)" } // 10 = 0xa. |
| 109 // ] | 107 // ] |
| 110 // } | 108 // } |
| 111 | 109 |
| 112 // Get the indices of the backtraces by adding them again to the deduplicator. | 110 // Get the indices of the backtraces and types by adding them again to the |
| 113 // Because they were added before, the same number is returned. | 111 // deduplicator. Because they were added before, the same number is returned. |
| 114 int bt_renderer_main_initialize = | 112 int bt_renderer_main_initialize = |
| 115 deduplicator->Insert({{kRendererMain, kInitialize}}); | 113 sf_deduplicator->Insert({{kRendererMain, kInitialize}}); |
| 116 int bt_browser_main_create_widget = | 114 int bt_browser_main_create_widget = |
| 117 deduplicator->Insert({{kBrowserMain, kCreateWidget}}); | 115 sf_deduplicator->Insert({{kBrowserMain, kCreateWidget}}); |
| 116 int type_id_int = tn_deduplicator->Insert(kInt); |
| 117 int type_id_bool = tn_deduplicator->Insert(kBool); |
| 118 int type_id_string = tn_deduplicator->Insert(kString); |
| 118 | 119 |
| 119 const DictionaryValue* dictionary; | 120 const DictionaryValue* dictionary; |
| 120 ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary)); | 121 ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary)); |
| 121 | 122 |
| 122 const ListValue* entries; | 123 const ListValue* entries; |
| 123 ASSERT_TRUE(dictionary->GetList("entries", &entries)); | 124 ASSERT_TRUE(dictionary->GetList("entries", &entries)); |
| 124 | 125 |
| 125 // Keep counters to verify that every entry is present exactly once. | 126 // Keep counters to verify that every entry is present exactly once. |
| 126 int x4d_seen = 0; | 127 int x4d_seen = 0; |
| 127 int x31_seen = 0; | 128 int x31_seen = 0; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 147 // Entry for backtrace "Initialize <- RendererMain". | 148 // Entry for backtrace "Initialize <- RendererMain". |
| 148 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. | 149 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. |
| 149 AssertIntEq(entry, "bt", bt_renderer_main_initialize); | 150 AssertIntEq(entry, "bt", bt_renderer_main_initialize); |
| 150 x31_seen++; | 151 x31_seen++; |
| 151 } else if (size == "1c") { | 152 } else if (size == "1c") { |
| 152 // Entry for backtrace "CreateWidget <- BrowserMain". | 153 // Entry for backtrace "CreateWidget <- BrowserMain". |
| 153 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. | 154 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. |
| 154 AssertIntEq(entry, "bt", bt_browser_main_create_widget); | 155 AssertIntEq(entry, "bt", bt_browser_main_create_widget); |
| 155 x1c_seen++; | 156 x1c_seen++; |
| 156 } else if (size == "30") { | 157 } else if (size == "30") { |
| 157 // Entry for type ID 2. | 158 // Entry for type bool. |
| 158 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. | 159 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. |
| 159 AssertStringEq(entry, "type", "2"); | 160 AssertIntEq(entry, "type", type_id_bool); |
| 160 x30_seen++; | 161 x30_seen++; |
| 161 } else if (size == "13") { | 162 } else if (size == "13") { |
| 162 // Entry for type ID 3. | 163 // Entry for type string. |
| 163 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. | 164 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. |
| 164 AssertStringEq(entry, "type", "3"); | 165 AssertIntEq(entry, "type", type_id_string); |
| 165 x13_seen++; | 166 x13_seen++; |
| 166 } else if (size == "a") { | 167 } else if (size == "a") { |
| 167 // Entry for type ID 1. | 168 // Entry for type int. |
| 168 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. | 169 ASSERT_EQ(2u, entry->size()); // Dictionary must have two elements. |
| 169 AssertStringEq(entry, "type", "1"); | 170 AssertIntEq(entry, "type", type_id_int); |
| 170 xa_seen++; | 171 xa_seen++; |
| 171 } | 172 } |
| 172 } | 173 } |
| 173 | 174 |
| 174 ASSERT_EQ(1, x4d_seen); | 175 ASSERT_EQ(1, x4d_seen); |
| 175 ASSERT_EQ(1, x31_seen); | 176 ASSERT_EQ(1, x31_seen); |
| 176 ASSERT_EQ(1, x1c_seen); | 177 ASSERT_EQ(1, x1c_seen); |
| 177 ASSERT_EQ(1, x30_seen); | 178 ASSERT_EQ(1, x30_seen); |
| 178 ASSERT_EQ(1, x13_seen); | 179 ASSERT_EQ(1, x13_seen); |
| 179 ASSERT_EQ(1, xa_seen); | 180 ASSERT_EQ(1, xa_seen); |
| 180 } | 181 } |
| 181 | 182 |
| 182 // Test that the entry for the empty backtrace ends up in the json with the | 183 // Test that the entry for the empty backtrace ends up in the json with the |
| 183 // "bt" field set to the empty string. Also test that the entry for "unknown | 184 // "bt" field set to the empty string. Also test that an entry for "unknown |
| 184 // type" (type ID 0) ends up in the json with the "type" field set to the empty | 185 // type" (nullptr type name) does not dereference the null pointer when writing |
| 185 // string. | 186 // the type names, and that the type ID is 0. |
| 186 TEST(HeapDumpWriterTest, EmptyBacktraceIndexAndUnknownTypeIdAreEmptyString) { | 187 TEST(HeapDumpWriterTest, EmptyBacktraceIndexIsEmptyString) { |
| 187 auto deduplicator = make_scoped_refptr(new StackFrameDeduplicator); | 188 auto sf_deduplicator = make_scoped_refptr(new StackFrameDeduplicator); |
| 188 HeapDumpWriter writer(deduplicator.get()); | 189 auto tn_deduplicator = make_scoped_refptr(new TypeNameDeduplicator); |
| 190 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); |
| 189 | 191 |
| 190 // A context with empty backtrace and type ID 0 (unknown type). | 192 // A context with empty backtrace and unknown type (nullptr). |
| 191 AllocationContext ctx = AllocationContext::Empty(); | 193 AllocationContext ctx = AllocationContext::Empty(); |
| 192 | 194 |
| 193 writer.InsertAllocation(ctx, 1); | 195 writer.InsertAllocation(ctx, 1); |
| 194 | 196 |
| 195 scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer); | 197 scoped_ptr<Value> heap_dump = DumpAndReadBack(&writer); |
| 196 | 198 |
| 197 const DictionaryValue* dictionary; | 199 const DictionaryValue* dictionary; |
| 198 ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary)); | 200 ASSERT_TRUE(heap_dump->GetAsDictionary(&dictionary)); |
| 199 | 201 |
| 200 const ListValue* entries; | 202 const ListValue* entries; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 211 if (entry->HasKey("bt") && entry->size() == 2) { | 213 if (entry->HasKey("bt") && entry->size() == 2) { |
| 212 std::string backtrace; | 214 std::string backtrace; |
| 213 ASSERT_TRUE(entry->GetString("bt", &backtrace)); | 215 ASSERT_TRUE(entry->GetString("bt", &backtrace)); |
| 214 ASSERT_EQ("", backtrace); | 216 ASSERT_EQ("", backtrace); |
| 215 empty_backtrace_seen++; | 217 empty_backtrace_seen++; |
| 216 } | 218 } |
| 217 | 219 |
| 218 if (entry->HasKey("type") && entry->size() == 2) { | 220 if (entry->HasKey("type") && entry->size() == 2) { |
| 219 std::string type_id; | 221 std::string type_id; |
| 220 ASSERT_TRUE(entry->GetString("type", &type_id)); | 222 ASSERT_TRUE(entry->GetString("type", &type_id)); |
| 221 ASSERT_EQ("", type_id); | 223 ASSERT_EQ("0", type_id); |
| 222 unknown_type_seen++; | 224 unknown_type_seen++; |
| 223 } | 225 } |
| 224 } | 226 } |
| 225 | 227 |
| 226 ASSERT_EQ(1, unknown_type_seen); | 228 ASSERT_EQ(1, unknown_type_seen); |
| 227 ASSERT_EQ(1, empty_backtrace_seen); | 229 ASSERT_EQ(1, empty_backtrace_seen); |
| 228 } | 230 } |
| 229 | 231 |
| 230 } // namespace trace_event | 232 } // namespace trace_event |
| 231 } // namespace base | 233 } // namespace base |
| OLD | NEW |