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

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

Powered by Google App Engine
This is Rietveld 408576698