• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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