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 <stddef.h>
6
7 #include <iterator>
8
9 #include "base/memory/ref_counted.h"
10 #include "base/pending_task.h"
11 #include "base/trace_event/heap_profiler.h"
12 #include "base/trace_event/heap_profiler_allocation_context.h"
13 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
14 #include "base/trace_event/memory_dump_manager.h"
15 #include "base/trace_event/trace_event.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19 namespace trace_event {
20
21 // Define all strings once, because the pseudo stack requires pointer equality,
22 // and string interning is unreliable.
23 const char kThreadName[] = "TestThread";
24 const char kCupcake[] = "Cupcake";
25 const char kDonut[] = "Donut";
26 const char kEclair[] = "Eclair";
27 const char kFroyo[] = "Froyo";
28 const char kGingerbread[] = "Gingerbread";
29
30 const char kFilteringTraceConfig[] =
31 "{"
32 " \"event_filters\": ["
33 " {"
34 " \"excluded_categories\": [],"
35 " \"filter_args\": {},"
36 " \"filter_predicate\": \"heap_profiler_predicate\","
37 " \"included_categories\": ["
38 " \"*\","
39 " \"" TRACE_DISABLED_BY_DEFAULT("Testing") "\"]"
40 " }"
41 " ]"
42 "}";
43
44 // Asserts that the fixed-size array |expected_backtrace| matches the backtrace
45 // in |AllocationContextTracker::GetContextSnapshot|.
46 template <size_t N>
AssertBacktraceEquals(const StackFrame (& expected_backtrace)[N])47 void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) {
48 AllocationContext ctx;
49 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
50 ->GetContextSnapshot(&ctx));
51
52 auto* actual = std::begin(ctx.backtrace.frames);
53 auto* actual_bottom = actual + ctx.backtrace.frame_count;
54 auto expected = std::begin(expected_backtrace);
55 auto expected_bottom = std::end(expected_backtrace);
56
57 // Note that this requires the pointers to be equal, this is not doing a deep
58 // string comparison.
59 for (; actual != actual_bottom && expected != expected_bottom;
60 actual++, expected++)
61 ASSERT_EQ(*expected, *actual);
62
63 // Ensure that the height of the stacks is the same.
64 ASSERT_EQ(actual, actual_bottom);
65 ASSERT_EQ(expected, expected_bottom);
66 }
67
AssertBacktraceContainsOnlyThreadName()68 void AssertBacktraceContainsOnlyThreadName() {
69 StackFrame t = StackFrame::FromThreadName(kThreadName);
70 AllocationContext ctx;
71 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
72 ->GetContextSnapshot(&ctx));
73
74 ASSERT_EQ(1u, ctx.backtrace.frame_count);
75 ASSERT_EQ(t, ctx.backtrace.frames[0]);
76 }
77
78 class AllocationContextTrackerTest : public testing::Test {
79 public:
SetUp()80 void SetUp() override {
81 AllocationContextTracker::SetCaptureMode(
82 AllocationContextTracker::CaptureMode::PSEUDO_STACK);
83 // Enabling memory-infra category sets default memory dump config which
84 // includes filters for capturing pseudo stack.
85 TraceConfig config(kFilteringTraceConfig);
86 TraceLog::GetInstance()->SetEnabled(config, TraceLog::FILTERING_MODE);
87 AllocationContextTracker::SetCurrentThreadName(kThreadName);
88 }
89
TearDown()90 void TearDown() override {
91 AllocationContextTracker::SetCaptureMode(
92 AllocationContextTracker::CaptureMode::DISABLED);
93 TraceLog::GetInstance()->SetDisabled(TraceLog::FILTERING_MODE);
94 }
95 };
96
97 // Check that |TRACE_EVENT| macros push and pop to the pseudo stack correctly.
TEST_F(AllocationContextTrackerTest,PseudoStackScopedTrace)98 TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) {
99 StackFrame t = StackFrame::FromThreadName(kThreadName);
100 StackFrame c = StackFrame::FromTraceEventName(kCupcake);
101 StackFrame d = StackFrame::FromTraceEventName(kDonut);
102 StackFrame e = StackFrame::FromTraceEventName(kEclair);
103 StackFrame f = StackFrame::FromTraceEventName(kFroyo);
104
105 AssertBacktraceContainsOnlyThreadName();
106
107 {
108 TRACE_EVENT0("Testing", kCupcake);
109 StackFrame frame_c[] = {t, c};
110 AssertBacktraceEquals(frame_c);
111
112 {
113 TRACE_EVENT0("Testing", kDonut);
114 StackFrame frame_cd[] = {t, c, d};
115 AssertBacktraceEquals(frame_cd);
116 }
117
118 AssertBacktraceEquals(frame_c);
119
120 {
121 TRACE_EVENT0("Testing", kEclair);
122 StackFrame frame_ce[] = {t, c, e};
123 AssertBacktraceEquals(frame_ce);
124 }
125
126 {
127 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("NotTesting"), kDonut);
128 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("Testing"), kCupcake);
129 StackFrame frame_cc[] = {t, c, c};
130 AssertBacktraceEquals(frame_cc);
131 }
132
133 AssertBacktraceEquals(frame_c);
134 }
135
136 AssertBacktraceContainsOnlyThreadName();
137
138 {
139 TRACE_EVENT0("Testing", kFroyo);
140 StackFrame frame_f[] = {t, f};
141 AssertBacktraceEquals(frame_f);
142 }
143
144 AssertBacktraceContainsOnlyThreadName();
145 }
146
147 // Same as |PseudoStackScopedTrace|, but now test the |TRACE_EVENT_BEGIN| and
148 // |TRACE_EVENT_END| macros.
TEST_F(AllocationContextTrackerTest,PseudoStackBeginEndTrace)149 TEST_F(AllocationContextTrackerTest, PseudoStackBeginEndTrace) {
150 StackFrame t = StackFrame::FromThreadName(kThreadName);
151 StackFrame c = StackFrame::FromTraceEventName(kCupcake);
152 StackFrame d = StackFrame::FromTraceEventName(kDonut);
153 StackFrame e = StackFrame::FromTraceEventName(kEclair);
154 StackFrame f = StackFrame::FromTraceEventName(kFroyo);
155
156 StackFrame frame_c[] = {t, c};
157 StackFrame frame_cd[] = {t, c, d};
158 StackFrame frame_ce[] = {t, c, e};
159 StackFrame frame_f[] = {t, f};
160
161 AssertBacktraceContainsOnlyThreadName();
162
163 TRACE_EVENT_BEGIN0("Testing", kCupcake);
164 AssertBacktraceEquals(frame_c);
165
166 TRACE_EVENT_BEGIN0("Testing", kDonut);
167 AssertBacktraceEquals(frame_cd);
168 TRACE_EVENT_END0("Testing", kDonut);
169
170 AssertBacktraceEquals(frame_c);
171
172 TRACE_EVENT_BEGIN0("Testing", kEclair);
173 AssertBacktraceEquals(frame_ce);
174 TRACE_EVENT_END0("Testing", kEclair);
175
176 AssertBacktraceEquals(frame_c);
177 TRACE_EVENT_END0("Testing", kCupcake);
178
179 AssertBacktraceContainsOnlyThreadName();
180
181 TRACE_EVENT_BEGIN0("Testing", kFroyo);
182 AssertBacktraceEquals(frame_f);
183 TRACE_EVENT_END0("Testing", kFroyo);
184
185 AssertBacktraceContainsOnlyThreadName();
186 }
187
TEST_F(AllocationContextTrackerTest,PseudoStackMixedTrace)188 TEST_F(AllocationContextTrackerTest, PseudoStackMixedTrace) {
189 StackFrame t = StackFrame::FromThreadName(kThreadName);
190 StackFrame c = StackFrame::FromTraceEventName(kCupcake);
191 StackFrame d = StackFrame::FromTraceEventName(kDonut);
192 StackFrame e = StackFrame::FromTraceEventName(kEclair);
193 StackFrame f = StackFrame::FromTraceEventName(kFroyo);
194
195 StackFrame frame_c[] = {t, c};
196 StackFrame frame_cd[] = {t, c, d};
197 StackFrame frame_e[] = {t, e};
198 StackFrame frame_ef[] = {t, e, f};
199
200 AssertBacktraceContainsOnlyThreadName();
201
202 TRACE_EVENT_BEGIN0("Testing", kCupcake);
203 AssertBacktraceEquals(frame_c);
204
205 {
206 TRACE_EVENT0("Testing", kDonut);
207 AssertBacktraceEquals(frame_cd);
208 }
209
210 AssertBacktraceEquals(frame_c);
211 TRACE_EVENT_END0("Testing", kCupcake);
212 AssertBacktraceContainsOnlyThreadName();
213
214 {
215 TRACE_EVENT0("Testing", kEclair);
216 AssertBacktraceEquals(frame_e);
217
218 TRACE_EVENT_BEGIN0("Testing", kFroyo);
219 AssertBacktraceEquals(frame_ef);
220 TRACE_EVENT_END0("Testing", kFroyo);
221 AssertBacktraceEquals(frame_e);
222 }
223
224 AssertBacktraceContainsOnlyThreadName();
225 }
226
TEST_F(AllocationContextTrackerTest,MixedStackWithProgramCounter)227 TEST_F(AllocationContextTrackerTest, MixedStackWithProgramCounter) {
228 StackFrame t = StackFrame::FromThreadName(kThreadName);
229 StackFrame c = StackFrame::FromTraceEventName(kCupcake);
230 StackFrame f = StackFrame::FromTraceEventName(kFroyo);
231 const void* pc1 = reinterpret_cast<void*>(0x1000);
232 const void* pc2 = reinterpret_cast<void*>(0x2000);
233 StackFrame n1 = StackFrame::FromProgramCounter(pc1);
234 StackFrame n2 = StackFrame::FromProgramCounter(pc2);
235
236 StackFrame frame_c[] = {t, c};
237 StackFrame frame_cd[] = {t, c, n1};
238 StackFrame frame_e[] = {t, n2, n1};
239 StackFrame frame_ef[] = {t, n2, n1, f};
240
241 AssertBacktraceContainsOnlyThreadName();
242
243 AllocationContextTracker::SetCaptureMode(
244 AllocationContextTracker::CaptureMode::MIXED_STACK);
245
246 TRACE_EVENT_BEGIN0("Testing", kCupcake);
247 AssertBacktraceEquals(frame_c);
248
249 {
250 TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e1(pc1);
251 AssertBacktraceEquals(frame_cd);
252 }
253
254 AssertBacktraceEquals(frame_c);
255 TRACE_EVENT_END0("Testing", kCupcake);
256 AssertBacktraceContainsOnlyThreadName();
257
258 {
259 TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e1(pc2);
260 TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER e2(pc1);
261 AssertBacktraceEquals(frame_e);
262
263 TRACE_EVENT0("Testing", kFroyo);
264 AssertBacktraceEquals(frame_ef);
265 }
266
267 AssertBacktraceContainsOnlyThreadName();
268 AllocationContextTracker::SetCaptureMode(
269 AllocationContextTracker::CaptureMode::DISABLED);
270 }
271
TEST_F(AllocationContextTrackerTest,BacktraceTakesTop)272 TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) {
273 StackFrame t = StackFrame::FromThreadName(kThreadName);
274 StackFrame c = StackFrame::FromTraceEventName(kCupcake);
275 StackFrame f = StackFrame::FromTraceEventName(kFroyo);
276
277 // Push 11 events onto the pseudo stack.
278 TRACE_EVENT0("Testing", kCupcake);
279 TRACE_EVENT0("Testing", kCupcake);
280 TRACE_EVENT0("Testing", kCupcake);
281
282 TRACE_EVENT0("Testing", kCupcake);
283 TRACE_EVENT0("Testing", kCupcake);
284 TRACE_EVENT0("Testing", kCupcake);
285 TRACE_EVENT0("Testing", kCupcake);
286
287 TRACE_EVENT0("Testing", kCupcake);
288 TRACE_EVENT0("Testing", kDonut);
289 TRACE_EVENT0("Testing", kEclair);
290 TRACE_EVENT0("Testing", kFroyo);
291
292 {
293 TRACE_EVENT0("Testing", kGingerbread);
294 AllocationContext ctx;
295 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
296 ->GetContextSnapshot(&ctx));
297
298 // The pseudo stack relies on pointer equality, not deep string comparisons.
299 ASSERT_EQ(t, ctx.backtrace.frames[0]);
300 ASSERT_EQ(c, ctx.backtrace.frames[1]);
301 ASSERT_EQ(f, ctx.backtrace.frames[11]);
302 }
303
304 {
305 AllocationContext ctx;
306 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
307 ->GetContextSnapshot(&ctx));
308 ASSERT_EQ(t, ctx.backtrace.frames[0]);
309 ASSERT_EQ(c, ctx.backtrace.frames[1]);
310 ASSERT_EQ(f, ctx.backtrace.frames[11]);
311 }
312 }
313
TEST_F(AllocationContextTrackerTest,TrackCategoryName)314 TEST_F(AllocationContextTrackerTest, TrackCategoryName) {
315 const char kContext1[] = "context1";
316 const char kContext2[] = "context2";
317 {
318 // The context from the scoped task event should be used as type name.
319 TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event1(kContext1);
320 AllocationContext ctx1;
321 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
322 ->GetContextSnapshot(&ctx1));
323 ASSERT_EQ(kContext1, ctx1.type_name);
324
325 // In case of nested events, the last event's context should be used.
326 TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event2(kContext2);
327 AllocationContext ctx2;
328 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
329 ->GetContextSnapshot(&ctx2));
330 ASSERT_EQ(kContext2, ctx2.type_name);
331 }
332
333 // Type should be nullptr without task event.
334 AllocationContext ctx;
335 ASSERT_TRUE(AllocationContextTracker::GetInstanceForCurrentThread()
336 ->GetContextSnapshot(&ctx));
337 ASSERT_FALSE(ctx.type_name);
338 }
339
TEST_F(AllocationContextTrackerTest,IgnoreAllocationTest)340 TEST_F(AllocationContextTrackerTest, IgnoreAllocationTest) {
341 TRACE_EVENT0("Testing", kCupcake);
342 TRACE_EVENT0("Testing", kDonut);
343 HEAP_PROFILER_SCOPED_IGNORE;
344 AllocationContext ctx;
345 ASSERT_FALSE(AllocationContextTracker::GetInstanceForCurrentThread()
346 ->GetContextSnapshot(&ctx));
347 }
348
349 } // namespace trace_event
350 } // namespace base
351