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