Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: base/trace_event/heap_profiler_heap_dump_writer_unittest.cc

Issue 2650863003: [tracing] Switch to new heap dump format. (Closed)
Patch Set: Address comments (heaps_v2) Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "base/trace_event/heap_profiler_heap_dump_writer.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <set>
11 #include <string>
12
13 #include "base/json/json_reader.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/trace_event/heap_profiler_allocation_context.h"
17 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
18 #include "base/trace_event/heap_profiler_type_name_deduplicator.h"
19 #include "base/trace_event/memory_dump_session_state.h"
20 #include "base/trace_event/trace_event_argument.h"
21 #include "base/values.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 namespace {
25
26 using base::trace_event::StackFrame;
27
28 // Define all strings once, because the deduplicator requires pointer equality,
29 // and string interning is unreliable.
30 StackFrame kBrowserMain = StackFrame::FromTraceEventName("BrowserMain");
31 StackFrame kRendererMain = StackFrame::FromTraceEventName("RendererMain");
32 StackFrame kCreateWidget = StackFrame::FromTraceEventName("CreateWidget");
33 StackFrame kInitialize = StackFrame::FromTraceEventName("Initialize");
34 StackFrame kGetBitmap = StackFrame::FromTraceEventName("GetBitmap");
35
36 const char kInt[] = "int";
37 const char kBool[] = "bool";
38 const char kString[] = "string";
39
40 } // namespace
41
42 namespace base {
43 namespace trace_event {
44 namespace internal {
45
46 std::unique_ptr<const Value> WriteAndReadBack(const std::set<Entry>& entries) {
47 std::unique_ptr<TracedValue> traced_value = Serialize(entries);
48 std::string json;
49 traced_value->AppendAsTraceFormat(&json);
50 return JSONReader::Read(json);
51 }
52
53 std::unique_ptr<const DictionaryValue> WriteAndReadBackEntry(Entry entry) {
54 std::set<Entry> input_entries;
55 input_entries.insert(entry);
56
57 std::unique_ptr<const Value> json_dict = WriteAndReadBack(input_entries);
58
59 // Note: Ideally these should use |ASSERT_TRUE| instead of |EXPECT_TRUE|, but
60 // |ASSERT_TRUE| can only be used in void functions.
61 const DictionaryValue* dictionary;
62 EXPECT_TRUE(json_dict->GetAsDictionary(&dictionary));
63
64 const ListValue* json_entries;
65 EXPECT_TRUE(dictionary->GetList("entries", &json_entries));
66
67 const DictionaryValue* json_entry;
68 EXPECT_TRUE(json_entries->GetDictionary(0, &json_entry));
69
70 return json_entry->CreateDeepCopy();
71 }
72
73 // Given a desired stack frame ID and type ID, looks up the entry in the set and
74 // asserts that it is present and has the expected size and count.
75 void AssertSizeAndCountEq(const std::set<Entry>& entries,
76 int stack_frame_id,
77 int type_id,
78 const AllocationMetrics& expected) {
79 // The comparison operator for |Entry| does not take size into account, so by
80 // setting only stack frame ID and type ID, the real entry can be found.
81 Entry entry;
82 entry.stack_frame_id = stack_frame_id;
83 entry.type_id = type_id;
84 auto it = entries.find(entry);
85
86 ASSERT_NE(entries.end(), it) << "No entry found for sf = " << stack_frame_id
87 << ", type = " << type_id << ".";
88 ASSERT_EQ(expected.size, it->size) << "Wrong size for sf = " << stack_frame_id
89 << ", type = " << type_id << ".";
90 ASSERT_EQ(expected.count, it->count)
91 << "Wrong count for sf = " << stack_frame_id << ", type = " << type_id
92 << ".";
93 }
94
95 // Given a desired stack frame ID and type ID, asserts that no entry was dumped
96 // for that that particular combination of stack frame and type.
97 void AssertNotDumped(const std::set<Entry>& entries,
98 int stack_frame_id,
99 int type_id) {
100 // The comparison operator for |Entry| does not take size into account, so by
101 // setting only stack frame ID and type ID, the real entry can be found.
102 Entry entry;
103 entry.stack_frame_id = stack_frame_id;
104 entry.type_id = type_id;
105 auto it = entries.find(entry);
106 ASSERT_EQ(entries.end(), it)
107 << "Entry should not be present for sf = " << stack_frame_id
108 << ", type = " << type_id << ".";
109 }
110
111 TEST(HeapDumpWriterTest, BacktraceIndex) {
112 Entry entry;
113 entry.stack_frame_id = -1; // -1 means empty backtrace.
114 entry.type_id = 0;
115 entry.size = 1;
116 entry.count = 1;
117
118 std::unique_ptr<const DictionaryValue> json_entry =
119 WriteAndReadBackEntry(entry);
120
121 // For an empty backtrace, the "bt" key cannot reference a stack frame.
122 // Instead it should be set to the empty string.
123 std::string backtrace_index;
124 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index));
125 ASSERT_EQ("", backtrace_index);
126
127 // Also verify that a non-negative backtrace index is dumped properly.
128 entry.stack_frame_id = 2;
129 json_entry = WriteAndReadBackEntry(entry);
130 ASSERT_TRUE(json_entry->GetString("bt", &backtrace_index));
131 ASSERT_EQ("2", backtrace_index);
132 }
133
134 TEST(HeapDumpWriterTest, TypeId) {
135 Entry entry;
136 entry.type_id = -1; // -1 means sum over all types.
137 entry.stack_frame_id = 0;
138 entry.size = 1;
139 entry.count = 1;
140
141 std::unique_ptr<const DictionaryValue> json_entry =
142 WriteAndReadBackEntry(entry);
143
144 // Entries for the cumulative size of all types should not have the "type"
145 // key set.
146 ASSERT_FALSE(json_entry->HasKey("type"));
147
148 // Also verify that a non-negative type ID is dumped properly.
149 entry.type_id = 2;
150 json_entry = WriteAndReadBackEntry(entry);
151 std::string type_id;
152 ASSERT_TRUE(json_entry->GetString("type", &type_id));
153 ASSERT_EQ("2", type_id);
154 }
155
156 TEST(HeapDumpWriterTest, SizeAndCountAreHexadecimal) {
157 // Take a number between 2^63 and 2^64 (or between 2^31 and 2^32 if |size_t|
158 // is not 64 bits).
159 const size_t large_value =
160 sizeof(size_t) == 8 ? 0xffffffffffffffc5 : 0xffffff9d;
161 const char* large_value_str =
162 sizeof(size_t) == 8 ? "ffffffffffffffc5" : "ffffff9d";
163 Entry entry;
164 entry.type_id = 0;
165 entry.stack_frame_id = 0;
166 entry.size = large_value;
167 entry.count = large_value;
168
169 std::unique_ptr<const DictionaryValue> json_entry =
170 WriteAndReadBackEntry(entry);
171
172 std::string size;
173 ASSERT_TRUE(json_entry->GetString("size", &size));
174 ASSERT_EQ(large_value_str, size);
175
176 std::string count;
177 ASSERT_TRUE(json_entry->GetString("count", &count));
178 ASSERT_EQ(large_value_str, count);
179 }
180
181 TEST(HeapDumpWriterTest, BacktraceTypeNameTable) {
182 hash_map<AllocationContext, AllocationMetrics> metrics_by_context;
183
184 AllocationContext ctx;
185 ctx.backtrace.frames[0] = kBrowserMain;
186 ctx.backtrace.frames[1] = kCreateWidget;
187 ctx.backtrace.frame_count = 2;
188 ctx.type_name = kInt;
189
190 // 10 bytes with context { type: int, bt: [BrowserMain, CreateWidget] }.
191 metrics_by_context[ctx] = {10, 5};
192
193 ctx.type_name = kBool;
194
195 // 18 bytes with context { type: bool, bt: [BrowserMain, CreateWidget] }.
196 metrics_by_context[ctx] = {18, 18};
197
198 ctx.backtrace.frames[0] = kRendererMain;
199 ctx.backtrace.frames[1] = kInitialize;
200 ctx.backtrace.frame_count = 2;
201
202 // 30 bytes with context { type: bool, bt: [RendererMain, Initialize] }.
203 metrics_by_context[ctx] = {30, 30};
204
205 ctx.type_name = kString;
206
207 // 19 bytes with context { type: string, bt: [RendererMain, Initialize] }.
208 metrics_by_context[ctx] = {19, 4};
209
210 // At this point the heap looks like this:
211 //
212 // | | CrWidget <- BrMain | Init <- RenMain | Sum |
213 // +--------+--------------------+-----------------+-------------+
214 // | | size count | size count | size count |
215 // | int | 10 5 | 0 0 | 10 5 |
216 // | bool | 18 18 | 30 30 | 48 48 |
217 // | string | 0 0 | 19 4 | 19 4 |
218 // +--------+--------------------+-----------------+-------------+
219 // | Sum | 28 23 | 49 34 | 77 57 |
220
221 auto stack_frame_deduplicator = WrapUnique(new StackFrameDeduplicator);
222 auto type_name_deduplicator = WrapUnique(new TypeNameDeduplicator);
223 HeapDumpWriter writer(stack_frame_deduplicator.get(),
224 type_name_deduplicator.get(),
225 10u);
226 const std::set<Entry>& dump = writer.Summarize(metrics_by_context);
227
228 // Get the indices of the backtraces and types by adding them again to the
229 // deduplicator. Because they were added before, the same number is returned.
230 StackFrame bt0[] = {kRendererMain, kInitialize};
231 StackFrame bt1[] = {kBrowserMain, kCreateWidget};
232 int bt_renderer_main = stack_frame_deduplicator->Insert(bt0, bt0 + 1);
233 int bt_browser_main = stack_frame_deduplicator->Insert(bt1, bt1 + 1);
234 int bt_renderer_main_initialize =
235 stack_frame_deduplicator->Insert(bt0, bt0 + 2);
236 int bt_browser_main_create_widget =
237 stack_frame_deduplicator->Insert(bt1, bt1 + 2);
238 int type_id_int = type_name_deduplicator->Insert(kInt);
239 int type_id_bool = type_name_deduplicator->Insert(kBool);
240 int type_id_string = type_name_deduplicator->Insert(kString);
241
242 // Full heap should have size 77.
243 AssertSizeAndCountEq(dump, -1, -1, {77, 57});
244
245 // 49 bytes in 34 chunks were allocated in RendererMain and children. Also
246 // check the type breakdown.
247 AssertSizeAndCountEq(dump, bt_renderer_main, -1, {49, 34});
248 AssertSizeAndCountEq(dump, bt_renderer_main, type_id_bool, {30, 30});
249 AssertSizeAndCountEq(dump, bt_renderer_main, type_id_string, {19, 4});
250
251 // 28 bytes in 23 chunks were allocated in BrowserMain and children. Also
252 // check the type breakdown.
253 AssertSizeAndCountEq(dump, bt_browser_main, -1, {28, 23});
254 AssertSizeAndCountEq(dump, bt_browser_main, type_id_int, {10, 5});
255 AssertSizeAndCountEq(dump, bt_browser_main, type_id_bool, {18, 18});
256
257 // In this test all bytes are allocated in leaf nodes, so check again one
258 // level deeper.
259 AssertSizeAndCountEq(dump, bt_renderer_main_initialize, -1, {49, 34});
260 AssertSizeAndCountEq(dump, bt_renderer_main_initialize, type_id_bool,
261 {30, 30});
262 AssertSizeAndCountEq(dump, bt_renderer_main_initialize, type_id_string,
263 {19, 4});
264 AssertSizeAndCountEq(dump, bt_browser_main_create_widget, -1, {28, 23});
265 AssertSizeAndCountEq(dump, bt_browser_main_create_widget, type_id_int,
266 {10, 5});
267 AssertSizeAndCountEq(dump, bt_browser_main_create_widget, type_id_bool,
268 {18, 18});
269
270 // The type breakdown of the entrie heap should have been dumped as well.
271 AssertSizeAndCountEq(dump, -1, type_id_int, {10, 5});
272 AssertSizeAndCountEq(dump, -1, type_id_bool, {48, 48});
273 AssertSizeAndCountEq(dump, -1, type_id_string, {19, 4});
274 }
275
276 TEST(HeapDumpWriterTest, InsignificantValuesNotDumped) {
277 hash_map<AllocationContext, AllocationMetrics> metrics_by_context;
278
279 AllocationContext ctx;
280 ctx.backtrace.frames[0] = kBrowserMain;
281 ctx.backtrace.frames[1] = kCreateWidget;
282 ctx.backtrace.frame_count = 2;
283
284 // 0.5 KiB and 1 chunk in BrowserMain -> CreateWidget itself.
285 metrics_by_context[ctx] = {512, 1};
286
287 // 1 MiB and 1 chunk in BrowserMain -> CreateWidget -> GetBitmap.
288 ctx.backtrace.frames[2] = kGetBitmap;
289 ctx.backtrace.frame_count = 3;
290 metrics_by_context[ctx] = {1024 * 1024, 1};
291
292 // 400B and 1 chunk in BrowserMain -> CreateWidget -> Initialize.
293 ctx.backtrace.frames[2] = kInitialize;
294 ctx.backtrace.frame_count = 3;
295 metrics_by_context[ctx] = {400, 1};
296
297 auto stack_frame_deduplicator = WrapUnique(new StackFrameDeduplicator);
298 auto type_name_deduplicator = WrapUnique(new TypeNameDeduplicator);
299 HeapDumpWriter writer(stack_frame_deduplicator.get(),
300 type_name_deduplicator.get(),
301 512u);
302 const std::set<Entry>& dump = writer.Summarize(metrics_by_context);
303
304 // Get the indices of the backtraces and types by adding them again to the
305 // deduplicator. Because they were added before, the same number is returned.
306 StackFrame bt0[] = {kBrowserMain, kCreateWidget, kGetBitmap};
307 StackFrame bt1[] = {kBrowserMain, kCreateWidget, kInitialize};
308 int bt_browser_main = stack_frame_deduplicator->Insert(bt0, bt0 + 1);
309 int bt_create_widget = stack_frame_deduplicator->Insert(bt0, bt0 + 2);
310 int bt_get_bitmap = stack_frame_deduplicator->Insert(bt0, bt0 + 3);
311 int bt_initialize = stack_frame_deduplicator->Insert(bt1, bt1 + 3);
312
313 // Full heap should have size of 1 MiB + .9 KiB and 3 chunks.
314 AssertSizeAndCountEq(dump, -1, -1 /* No type specified */,
315 {1024 * 1024 + 512 + 400, 3});
316
317 // |GetBitmap| allocated 1 MiB and 1 chunk.
318 AssertSizeAndCountEq(dump, bt_get_bitmap, -1, {1024 * 1024, 1});
319
320 // Because |GetBitmap| was dumped, all of its parent nodes should have been
321 // dumped too. |CreateWidget| has 1 MiB in |GetBitmap|, 400 bytes in
322 // |Initialize|, and 512 bytes of its own and each in 1 chunk.
323 AssertSizeAndCountEq(dump, bt_create_widget, -1,
324 {1024 * 1024 + 400 + 512, 3});
325 AssertSizeAndCountEq(dump, bt_browser_main, -1, {1024 * 1024 + 400 + 512, 3});
326
327 // Initialize was not significant, it should not have been dumped.
328 AssertNotDumped(dump, bt_initialize, -1);
329 }
330
331 } // namespace internal
332 } // namespace trace_event
333 } // namespace base
OLDNEW
« no previous file with comments | « base/trace_event/heap_profiler_heap_dump_writer.cc ('k') | base/trace_event/heap_profiler_stack_frame_deduplicator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698