| 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 "base/trace_event/heap_profiler_heap_dump_writer.h" |
| 6 |
| 5 #include <stddef.h> | 7 #include <stddef.h> |
| 6 | 8 |
| 9 #include <memory> |
| 7 #include <set> | 10 #include <set> |
| 8 #include <string> | 11 #include <string> |
| 9 | 12 |
| 10 #include "base/json/json_reader.h" | 13 #include "base/json/json_reader.h" |
| 11 #include "base/macros.h" | 14 #include "base/macros.h" |
| 12 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/ptr_util.h" |
| 13 #include "base/trace_event/heap_profiler_allocation_context.h" | 16 #include "base/trace_event/heap_profiler_allocation_context.h" |
| 14 #include "base/trace_event/heap_profiler_heap_dump_writer.h" | |
| 15 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" | 17 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" |
| 16 #include "base/trace_event/heap_profiler_type_name_deduplicator.h" | 18 #include "base/trace_event/heap_profiler_type_name_deduplicator.h" |
| 17 #include "base/trace_event/trace_event_argument.h" | 19 #include "base/trace_event/trace_event_argument.h" |
| 18 #include "base/values.h" | 20 #include "base/values.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
| 20 | 22 |
| 21 namespace { | 23 namespace { |
| 22 | 24 |
| 23 // Define all strings once, because the deduplicator requires pointer equality, | 25 // Define all strings once, because the deduplicator requires pointer equality, |
| 24 // and string interning is unreliable. | 26 // and string interning is unreliable. |
| 25 const char kBrowserMain[] = "BrowserMain"; | 27 const char kBrowserMain[] = "BrowserMain"; |
| 26 const char kRendererMain[] = "RendererMain"; | 28 const char kRendererMain[] = "RendererMain"; |
| 27 const char kCreateWidget[] = "CreateWidget"; | 29 const char kCreateWidget[] = "CreateWidget"; |
| 28 const char kInitialize[] = "Initialize"; | 30 const char kInitialize[] = "Initialize"; |
| 29 const char kGetBitmap[] = "GetBitmap"; | 31 const char kGetBitmap[] = "GetBitmap"; |
| 30 | 32 |
| 31 const char kInt[] = "int"; | 33 const char kInt[] = "int"; |
| 32 const char kBool[] = "bool"; | 34 const char kBool[] = "bool"; |
| 33 const char kString[] = "string"; | 35 const char kString[] = "string"; |
| 34 | 36 |
| 35 } // namespace | 37 } // namespace |
| 36 | 38 |
| 37 namespace base { | 39 namespace base { |
| 38 namespace trace_event { | 40 namespace trace_event { |
| 39 namespace internal { | 41 namespace internal { |
| 40 | 42 |
| 41 scoped_ptr<const Value> WriteAndReadBack(const std::set<Entry>& entries) { | 43 std::unique_ptr<const Value> WriteAndReadBack(const std::set<Entry>& entries) { |
| 42 scoped_ptr<TracedValue> traced_value = Serialize(entries); | 44 std::unique_ptr<TracedValue> traced_value = Serialize(entries); |
| 43 std::string json; | 45 std::string json; |
| 44 traced_value->AppendAsTraceFormat(&json); | 46 traced_value->AppendAsTraceFormat(&json); |
| 45 return JSONReader::Read(json); | 47 return JSONReader::Read(json); |
| 46 } | 48 } |
| 47 | 49 |
| 48 scoped_ptr<const DictionaryValue> WriteAndReadBackEntry(Entry entry) { | 50 std::unique_ptr<const DictionaryValue> WriteAndReadBackEntry(Entry entry) { |
| 49 std::set<Entry> input_entries; | 51 std::set<Entry> input_entries; |
| 50 input_entries.insert(entry); | 52 input_entries.insert(entry); |
| 51 | 53 |
| 52 scoped_ptr<const Value> json_dict = WriteAndReadBack(input_entries); | 54 std::unique_ptr<const Value> json_dict = WriteAndReadBack(input_entries); |
| 53 | 55 |
| 54 // Note: Ideally these should use |ASSERT_TRUE| instead of |EXPECT_TRUE|, but | 56 // Note: Ideally these should use |ASSERT_TRUE| instead of |EXPECT_TRUE|, but |
| 55 // |ASSERT_TRUE| can only be used in void functions. | 57 // |ASSERT_TRUE| can only be used in void functions. |
| 56 const DictionaryValue* dictionary; | 58 const DictionaryValue* dictionary; |
| 57 EXPECT_TRUE(json_dict->GetAsDictionary(&dictionary)); | 59 EXPECT_TRUE(json_dict->GetAsDictionary(&dictionary)); |
| 58 | 60 |
| 59 const ListValue* json_entries; | 61 const ListValue* json_entries; |
| 60 EXPECT_TRUE(dictionary->GetList("entries", &json_entries)); | 62 EXPECT_TRUE(dictionary->GetList("entries", &json_entries)); |
| 61 | 63 |
| 62 const DictionaryValue* json_entry; | 64 const DictionaryValue* json_entry; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 << "Entry should not be present for sf = " << stack_frame_id | 101 << "Entry should not be present for sf = " << stack_frame_id |
| 100 << ", type = " << type_id << "."; | 102 << ", type = " << type_id << "."; |
| 101 } | 103 } |
| 102 | 104 |
| 103 TEST(HeapDumpWriterTest, BacktraceIndex) { | 105 TEST(HeapDumpWriterTest, BacktraceIndex) { |
| 104 Entry entry; | 106 Entry entry; |
| 105 entry.stack_frame_id = -1; // -1 means empty backtrace. | 107 entry.stack_frame_id = -1; // -1 means empty backtrace. |
| 106 entry.type_id = 0; | 108 entry.type_id = 0; |
| 107 entry.size = 1; | 109 entry.size = 1; |
| 108 | 110 |
| 109 scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry); | 111 std::unique_ptr<const DictionaryValue> json_entry = |
| 112 WriteAndReadBackEntry(entry); |
| 110 | 113 |
| 111 // For an empty backtrace, the "bt" key cannot reference a stack frame. | 114 // For an empty backtrace, the "bt" key cannot reference a stack frame. |
| 112 // Instead it should be set to the empty string. | 115 // Instead it should be set to the empty string. |
| 113 std::string backtrace_index; | 116 std::string backtrace_index; |
| 114 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index)); | 117 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index)); |
| 115 ASSERT_EQ("", backtrace_index); | 118 ASSERT_EQ("", backtrace_index); |
| 116 | 119 |
| 117 // Also verify that a non-negative backtrace index is dumped properly. | 120 // Also verify that a non-negative backtrace index is dumped properly. |
| 118 entry.stack_frame_id = 2; | 121 entry.stack_frame_id = 2; |
| 119 json_entry = WriteAndReadBackEntry(entry); | 122 json_entry = WriteAndReadBackEntry(entry); |
| 120 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index)); | 123 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index)); |
| 121 ASSERT_EQ("2", backtrace_index); | 124 ASSERT_EQ("2", backtrace_index); |
| 122 } | 125 } |
| 123 | 126 |
| 124 TEST(HeapDumpWriterTest, TypeId) { | 127 TEST(HeapDumpWriterTest, TypeId) { |
| 125 Entry entry; | 128 Entry entry; |
| 126 entry.type_id = -1; // -1 means sum over all types. | 129 entry.type_id = -1; // -1 means sum over all types. |
| 127 entry.stack_frame_id = 0; | 130 entry.stack_frame_id = 0; |
| 128 entry.size = 1; | 131 entry.size = 1; |
| 129 | 132 |
| 130 scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry); | 133 std::unique_ptr<const DictionaryValue> json_entry = |
| 134 WriteAndReadBackEntry(entry); |
| 131 | 135 |
| 132 // Entries for the cumulative size of all types should not have the "type" | 136 // Entries for the cumulative size of all types should not have the "type" |
| 133 // key set. | 137 // key set. |
| 134 ASSERT_FALSE(json_entry->HasKey("type")); | 138 ASSERT_FALSE(json_entry->HasKey("type")); |
| 135 | 139 |
| 136 // Also verify that a non-negative type ID is dumped properly. | 140 // Also verify that a non-negative type ID is dumped properly. |
| 137 entry.type_id = 2; | 141 entry.type_id = 2; |
| 138 json_entry = WriteAndReadBackEntry(entry); | 142 json_entry = WriteAndReadBackEntry(entry); |
| 139 std::string type_id; | 143 std::string type_id; |
| 140 ASSERT_TRUE(json_entry->GetString("type", &type_id)); | 144 ASSERT_TRUE(json_entry->GetString("type", &type_id)); |
| 141 ASSERT_EQ("2", type_id); | 145 ASSERT_EQ("2", type_id); |
| 142 } | 146 } |
| 143 | 147 |
| 144 TEST(HeapDumpWriterTest, SizeIsHexadecimalString) { | 148 TEST(HeapDumpWriterTest, SizeIsHexadecimalString) { |
| 145 // Take a number between 2^63 and 2^64 (or between 2^31 and 2^32 if |size_t| | 149 // Take a number between 2^63 and 2^64 (or between 2^31 and 2^32 if |size_t| |
| 146 // is not 64 bits). | 150 // is not 64 bits). |
| 147 const size_t large_value = | 151 const size_t large_value = |
| 148 sizeof(size_t) == 8 ? 0xffffffffffffffc5 : 0xffffff9d; | 152 sizeof(size_t) == 8 ? 0xffffffffffffffc5 : 0xffffff9d; |
| 149 const char* large_value_str = | 153 const char* large_value_str = |
| 150 sizeof(size_t) == 8 ? "ffffffffffffffc5" : "ffffff9d"; | 154 sizeof(size_t) == 8 ? "ffffffffffffffc5" : "ffffff9d"; |
| 151 Entry entry; | 155 Entry entry; |
| 152 entry.type_id = 0; | 156 entry.type_id = 0; |
| 153 entry.stack_frame_id = 0; | 157 entry.stack_frame_id = 0; |
| 154 entry.size = large_value; | 158 entry.size = large_value; |
| 155 | 159 |
| 156 scoped_ptr<const DictionaryValue> json_entry = WriteAndReadBackEntry(entry); | 160 std::unique_ptr<const DictionaryValue> json_entry = |
| 161 WriteAndReadBackEntry(entry); |
| 157 | 162 |
| 158 std::string size; | 163 std::string size; |
| 159 ASSERT_TRUE(json_entry->GetString("size", &size)); | 164 ASSERT_TRUE(json_entry->GetString("size", &size)); |
| 160 ASSERT_EQ(large_value_str, size); | 165 ASSERT_EQ(large_value_str, size); |
| 161 } | 166 } |
| 162 | 167 |
| 163 TEST(HeapDumpWriterTest, BacktraceTypeNameTable) { | 168 TEST(HeapDumpWriterTest, BacktraceTypeNameTable) { |
| 164 hash_map<AllocationContext, size_t> bytes_by_context; | 169 hash_map<AllocationContext, size_t> bytes_by_context; |
| 165 | 170 |
| 166 AllocationContext ctx = AllocationContext::Empty(); | 171 AllocationContext ctx = AllocationContext::Empty(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 190 // At this point the heap looks like this: | 195 // At this point the heap looks like this: |
| 191 // | 196 // |
| 192 // | | CrWidget <- BrMain | Init <- RenMain | Sum | | 197 // | | CrWidget <- BrMain | Init <- RenMain | Sum | |
| 193 // +--------+--------------------+-----------------+-----+ | 198 // +--------+--------------------+-----------------+-----+ |
| 194 // | int | 10 | 0 | 10 | | 199 // | int | 10 | 0 | 10 | |
| 195 // | bool | 18 | 30 | 48 | | 200 // | bool | 18 | 30 | 48 | |
| 196 // | string | 0 | 19 | 19 | | 201 // | string | 0 | 19 | 19 | |
| 197 // +--------+--------------------+-----------------+-----+ | 202 // +--------+--------------------+-----------------+-----+ |
| 198 // | Sum | 28 | 49 | 77 | | 203 // | Sum | 28 | 49 | 77 | |
| 199 | 204 |
| 200 auto sf_deduplicator = make_scoped_ptr(new StackFrameDeduplicator); | 205 auto sf_deduplicator = WrapUnique(new StackFrameDeduplicator); |
| 201 auto tn_deduplicator = make_scoped_ptr(new TypeNameDeduplicator); | 206 auto tn_deduplicator = WrapUnique(new TypeNameDeduplicator); |
| 202 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); | 207 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); |
| 203 const std::set<Entry>& dump = writer.Summarize(bytes_by_context); | 208 const std::set<Entry>& dump = writer.Summarize(bytes_by_context); |
| 204 | 209 |
| 205 // Get the indices of the backtraces and types by adding them again to the | 210 // Get the indices of the backtraces and types by adding them again to the |
| 206 // deduplicator. Because they were added before, the same number is returned. | 211 // deduplicator. Because they were added before, the same number is returned. |
| 207 StackFrame bt0[] = {kRendererMain, kInitialize}; | 212 StackFrame bt0[] = {kRendererMain, kInitialize}; |
| 208 StackFrame bt1[] = {kBrowserMain, kCreateWidget}; | 213 StackFrame bt1[] = {kBrowserMain, kCreateWidget}; |
| 209 int bt_renderer_main = sf_deduplicator->Insert(bt0, bt0 + 1); | 214 int bt_renderer_main = sf_deduplicator->Insert(bt0, bt0 + 1); |
| 210 int bt_browser_main = sf_deduplicator->Insert(bt1, bt1 + 1); | 215 int bt_browser_main = sf_deduplicator->Insert(bt1, bt1 + 1); |
| 211 int bt_renderer_main_initialize = sf_deduplicator->Insert(bt0, bt0 + 2); | 216 int bt_renderer_main_initialize = sf_deduplicator->Insert(bt0, bt0 + 2); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 bytes_by_context[ctx] = 512; | 260 bytes_by_context[ctx] = 512; |
| 256 | 261 |
| 257 // 1 MiB in BrowserMain -> CreateWidget -> GetBitmap. | 262 // 1 MiB in BrowserMain -> CreateWidget -> GetBitmap. |
| 258 ctx.backtrace.frames[2] = kGetBitmap; | 263 ctx.backtrace.frames[2] = kGetBitmap; |
| 259 bytes_by_context[ctx] = 1024 * 1024; | 264 bytes_by_context[ctx] = 1024 * 1024; |
| 260 | 265 |
| 261 // 0.5 KiB in BrowserMain -> CreateWidget -> Initialize. | 266 // 0.5 KiB in BrowserMain -> CreateWidget -> Initialize. |
| 262 ctx.backtrace.frames[2] = kInitialize; | 267 ctx.backtrace.frames[2] = kInitialize; |
| 263 bytes_by_context[ctx] = 512; | 268 bytes_by_context[ctx] = 512; |
| 264 | 269 |
| 265 auto sf_deduplicator = make_scoped_ptr(new StackFrameDeduplicator); | 270 auto sf_deduplicator = WrapUnique(new StackFrameDeduplicator); |
| 266 auto tn_deduplicator = make_scoped_ptr(new TypeNameDeduplicator); | 271 auto tn_deduplicator = WrapUnique(new TypeNameDeduplicator); |
| 267 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); | 272 HeapDumpWriter writer(sf_deduplicator.get(), tn_deduplicator.get()); |
| 268 const std::set<Entry>& dump = writer.Summarize(bytes_by_context); | 273 const std::set<Entry>& dump = writer.Summarize(bytes_by_context); |
| 269 | 274 |
| 270 // Get the indices of the backtraces and types by adding them again to the | 275 // Get the indices of the backtraces and types by adding them again to the |
| 271 // deduplicator. Because they were added before, the same number is returned. | 276 // deduplicator. Because they were added before, the same number is returned. |
| 272 StackFrame bt0[] = {kBrowserMain, kCreateWidget, kGetBitmap}; | 277 StackFrame bt0[] = {kBrowserMain, kCreateWidget, kGetBitmap}; |
| 273 StackFrame bt1[] = {kBrowserMain, kCreateWidget, kInitialize}; | 278 StackFrame bt1[] = {kBrowserMain, kCreateWidget, kInitialize}; |
| 274 int bt_browser_main = sf_deduplicator->Insert(bt0, bt0 + 1); | 279 int bt_browser_main = sf_deduplicator->Insert(bt0, bt0 + 1); |
| 275 int bt_create_widget = sf_deduplicator->Insert(bt0, bt0 + 2); | 280 int bt_create_widget = sf_deduplicator->Insert(bt0, bt0 + 2); |
| 276 int bt_get_bitmap = sf_deduplicator->Insert(bt0, bt0 + 3); | 281 int bt_get_bitmap = sf_deduplicator->Insert(bt0, bt0 + 3); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 288 AssertSizeEq(dump, bt_create_widget, -1, 1024 * 1024 + 512 + 512); | 293 AssertSizeEq(dump, bt_create_widget, -1, 1024 * 1024 + 512 + 512); |
| 289 AssertSizeEq(dump, bt_browser_main, -1, 1024 * 1024 + 512 + 512); | 294 AssertSizeEq(dump, bt_browser_main, -1, 1024 * 1024 + 512 + 512); |
| 290 | 295 |
| 291 // Initialize was not significant, it should not have been dumped. | 296 // Initialize was not significant, it should not have been dumped. |
| 292 AssertNotDumped(dump, bt_initialize, -1); | 297 AssertNotDumped(dump, bt_initialize, -1); |
| 293 } | 298 } |
| 294 | 299 |
| 295 } // namespace internal | 300 } // namespace internal |
| 296 } // namespace trace_event | 301 } // namespace trace_event |
| 297 } // namespace base | 302 } // namespace base |
| OLD | NEW |