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