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