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

Side by Side Diff: base/trace_event/heap_profiler_event_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_event_writer.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10
11 #include "base/memory/ptr_util.h"
12 #include "base/trace_event/heap_profiler_allocation_context.h"
13 #include "base/trace_event/heap_profiler_allocation_register.h"
14 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
15 #include "base/trace_event/heap_profiler_type_name_deduplicator.h"
16 #include "base/trace_event/memory_dump_session_state.h"
17 #include "base/trace_event/trace_event_argument.h"
18 #include "base/values.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace base {
22 namespace trace_event {
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
39 class AllocationRegisterHelper : public AllocationRegister {
40 public:
41 AllocationRegisterHelper()
42 : AllocationRegister(kAllocationCapacity, kBacktraceCapacity),
43 next_address_(0x100000) {}
44
45 void Allocate(size_t size,
46 const char* type_name,
47 std::initializer_list<StackFrame> backtrace) {
48 AllocationContext context;
49 context.backtrace.frame_count = backtrace.size();
50 std::copy(backtrace.begin(), backtrace.end(),
51 std::begin(context.backtrace.frames));
52 context.type_name = type_name;
53 Insert(reinterpret_cast<void*>(next_address_), size, context);
54 next_address_ += size;
55 }
56
57 private:
58 constexpr static size_t kAllocationCapacity = 100;
59 constexpr static size_t kBacktraceCapacity = 10;
60
61 uintptr_t next_address_;
62 };
63
64 struct HeapDumpEntry {
65 int backtrace_id;
66 int type_id;
67 int size;
68 int count;
69
70 bool operator==(const HeapDumpEntry& other) const {
71 return backtrace_id == other.backtrace_id && type_id == other.type_id &&
72 size == other.size && count == other.count;
73 }
74 };
75
76 ::testing::AssertionResult AssertHeapDump(
77 const DictionaryValue& heap_dump,
78 std::initializer_list<HeapDumpEntry> expected_entries) {
79 auto get_list_value = [&](const char* list_name, size_t index,
80 int* value) -> ::testing::AssertionResult {
81 const ListValue* list = nullptr;
82 if (!heap_dump.GetList(list_name, &list)) {
83 return ::testing::AssertionFailure()
84 << "'" << list_name << "' doesn't exist or is not a list";
85 }
86 if (list->GetSize() != expected_entries.size()) {
87 return ::testing::AssertionFailure()
88 << "size of '" << list_name << "' is " << list->GetSize()
89 << ", expected " << expected_entries.size();
90 }
91 if (!list->GetInteger(index, value)) {
92 return ::testing::AssertionFailure()
93 << "'" << list_name << "' value at index " << index
94 << " is not an integer";
95 }
96 return ::testing::AssertionSuccess();
97 };
98
99 constexpr size_t kValueCount = 4; // nodes, types, counts, sizes
100 if (heap_dump.size() != kValueCount) {
101 return ::testing::AssertionFailure()
102 << "heap dump has " << heap_dump.size() << " values"
103 << ", expected " << kValueCount;
104 }
105
106 for (size_t i = 0; i != expected_entries.size(); ++i) {
107 HeapDumpEntry entry;
108
109 ::testing::AssertionResult assertion = ::testing::AssertionSuccess();
110 if (!(assertion = get_list_value("nodes", i, &entry.backtrace_id)) ||
111 !(assertion = get_list_value("types", i, &entry.type_id)) ||
112 !(assertion = get_list_value("sizes", i, &entry.size)) ||
113 !(assertion = get_list_value("counts", i, &entry.count))) {
114 return assertion;
115 }
116
117 auto* entry_iter =
118 std::find(expected_entries.begin(), expected_entries.end(), entry);
119 if (entry_iter == expected_entries.end()) {
120 return ::testing::AssertionFailure()
121 << "unexpected HeapDumpEntry{" << entry.backtrace_id << ", "
122 << entry.type_id << ", " << entry.size << ", " << entry.count
123 << "} at index " << i;
124 }
125 }
126
127 return ::testing::AssertionSuccess();
128 }
129
130 std::unique_ptr<DictionaryValue> ToDictionary(
131 const std::unique_ptr<TracedValue>& traced_value) {
132 if (!traced_value) {
133 return nullptr;
134 }
135 return DictionaryValue::From(traced_value->ToBaseValue());
136 }
137
138 } // namespace
139
140 TEST(EventWriterTest, HeapDumpNoBacktraceNoType) {
141 AllocationRegisterHelper allocation_register;
142 auto bt = {kBrowserMain};
143 std::initializer_list<StackFrame> empty_bt = {};
144 allocation_register.Allocate(10, nullptr, bt);
145 allocation_register.Allocate(100, kInt, empty_bt);
146 allocation_register.Allocate(1000, nullptr, empty_bt);
147
148 auto state = make_scoped_refptr(new MemoryDumpSessionState);
149 state->CreateDeduplicators();
150 auto heap_dump = ToDictionary(SerializeHeapDump(allocation_register, *state));
151 ASSERT_TRUE(heap_dump);
152
153 int bt_id =
154 state->stack_frame_deduplicator()->Insert(std::begin(bt), std::end(bt));
155 int int_id = state->type_name_deduplicator()->Insert(kInt);
156
157 // NULL type and empty backtrace IDs should be 0.
158 auto expected_entries = {
159 HeapDumpEntry{bt_id, 0, 10, 1}, // no type
160 HeapDumpEntry{0, int_id, 100, 1}, // no backtrace
161 HeapDumpEntry{0, 0, 1000, 1}, // no type, no backtrace
162 };
163 ASSERT_TRUE(AssertHeapDump(*heap_dump, expected_entries))
164 << "heap_dump = " << *heap_dump;
165 }
166
167 TEST(EventWriterTest, HeapDumpAggregation) {
168 //
169 // |- (no backtrace) int*1, int*2, bool*3,
170 // | (no type)*4, (no type)*5
171 // |
172 // |- kBrowserMain (no type)*6, (no type)*7, (no type)*8
173 // |-- kCreateWidget int*10, bool*20
174 // |---- kGetBitmap int*100, int*200, bool*300
175 //
176 // Aggregation is done by {backtrace_id, type_id}, so the following
177 // entries should be aggregated:
178 // - int*1 + int*2
179 // - (no type)*4 + (no type)*5
180 // - (no type)*6 + (no type)*7 + (no type)*8
181 // - int*100 + int*200
182
183 AllocationRegisterHelper allocation_register;
184
185 std::initializer_list<StackFrame> empty_bt = {};
186 allocation_register.Allocate(1, kInt, empty_bt);
187 allocation_register.Allocate(2, kInt, empty_bt);
188 allocation_register.Allocate(3, kBool, empty_bt);
189 allocation_register.Allocate(4, nullptr, empty_bt);
190 allocation_register.Allocate(5, nullptr, empty_bt);
191
192 auto bt1 = {kBrowserMain};
193 allocation_register.Allocate(6, nullptr, bt1);
194 allocation_register.Allocate(7, nullptr, bt1);
195 allocation_register.Allocate(8, nullptr, bt1);
196
197 auto bt2 = {kBrowserMain, kCreateWidget};
198 allocation_register.Allocate(10, kInt, bt2);
199 allocation_register.Allocate(20, kBool, bt2);
200
201 auto bt3 = {kBrowserMain, kCreateWidget, kGetBitmap};
202 allocation_register.Allocate(100, kInt, bt3);
203 allocation_register.Allocate(200, kInt, bt3);
204 allocation_register.Allocate(300, kBool, bt3);
205
206 auto state = make_scoped_refptr(new MemoryDumpSessionState);
207 state->CreateDeduplicators();
208
209 auto heap_dump = ToDictionary(SerializeHeapDump(allocation_register, *state));
210 ASSERT_TRUE(heap_dump);
211
212 int bt1_id =
213 state->stack_frame_deduplicator()->Insert(std::begin(bt1), std::end(bt1));
214 int bt2_id =
215 state->stack_frame_deduplicator()->Insert(std::begin(bt2), std::end(bt2));
216 int bt3_id =
217 state->stack_frame_deduplicator()->Insert(std::begin(bt3), std::end(bt3));
218
219 int int_id = state->type_name_deduplicator()->Insert(kInt);
220 int bool_id = state->type_name_deduplicator()->Insert(kBool);
221
222 auto expected_entries = {
223 HeapDumpEntry{0, int_id, 3, 2},
224 HeapDumpEntry{0, bool_id, 3, 1},
225 HeapDumpEntry{0, 0, 9, 2},
226 HeapDumpEntry{bt1_id, 0, 21, 3},
227 HeapDumpEntry{bt2_id, int_id, 10, 1},
228 HeapDumpEntry{bt2_id, bool_id, 20, 1},
229 HeapDumpEntry{bt3_id, int_id, 300, 2},
230 HeapDumpEntry{bt3_id, bool_id, 300, 1},
231 };
232 ASSERT_TRUE(AssertHeapDump(*heap_dump, expected_entries))
233 << "heap_dump = " << *heap_dump;
234 }
235
236 TEST(EventWriterTest, SerializeHeapProfileEventData) {
237 AllocationRegisterHelper foo_register;
238 foo_register.Allocate(10, "Widget", {kBrowserMain, kCreateWidget});
239 foo_register.Allocate(16, "int[]", {kBrowserMain, kCreateWidget});
240
241 AllocationRegisterHelper bar_register;
242 bar_register.Allocate(10, "Widget", {kRendererMain, kCreateWidget});
243 bar_register.Allocate(71, "char[]", {kRendererMain});
244
245 auto state = make_scoped_refptr(new MemoryDumpSessionState);
246 state->CreateDeduplicators();
247
248 SerializedHeapDumpsMap heap_dumps;
249 heap_dumps["foo"] = SerializeHeapDump(foo_register, *state);
250 heap_dumps["bar"] = SerializeHeapDump(bar_register, *state);
251
252 auto event_data =
253 ToDictionary(SerializeHeapProfileEventData(heap_dumps, *state));
254 ASSERT_TRUE(event_data);
255
256 constexpr size_t kTopCount = 3; // version, allocators, maps
257 ASSERT_EQ(kTopCount, event_data->size());
258
259 int version;
260 ASSERT_TRUE(event_data->GetInteger("version", &version));
261 ASSERT_EQ(1, version);
262
263 const DictionaryValue* allocators;
264 ASSERT_TRUE(event_data->GetDictionary("allocators", &allocators));
265 {
266 constexpr size_t kAllocatorCount = 2; // foo, bar
267 ASSERT_EQ(kAllocatorCount, allocators->size());
268
269 const DictionaryValue* foo_dump;
270 ASSERT_TRUE(allocators->GetDictionary("foo", &foo_dump));
271 ASSERT_TRUE(ToDictionary(heap_dumps["foo"])->Equals(foo_dump));
272
273 const DictionaryValue* bar_dump;
274 ASSERT_TRUE(allocators->GetDictionary("bar", &bar_dump));
275 ASSERT_TRUE(ToDictionary(heap_dumps["bar"])->Equals(bar_dump));
276 }
277
278 const DictionaryValue* maps;
279 ASSERT_TRUE(event_data->GetDictionary("maps", &maps));
280 {
281 constexpr size_t kMapCount = 3; // nodes, types, strings
282 ASSERT_EQ(kMapCount, maps->size());
283
284 ASSERT_TRUE(maps->HasKey("nodes"));
285 ASSERT_TRUE(maps->HasKey("types"));
286 ASSERT_TRUE(maps->HasKey("strings"));
287 }
288 }
289
290 } // namespace trace_event
291 } // namespace base
OLDNEW
« no previous file with comments | « base/trace_event/heap_profiler_event_writer.cc ('k') | base/trace_event/heap_profiler_heap_dump_writer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698