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/trace_event/heap_profiler_allocation_context.h"
11 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
12 #include "base/trace_event/trace_event.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace base {
16 namespace trace_event {
17 
18 // Define all strings once, because the pseudo stack requires pointer equality,
19 // and string interning is unreliable.
20 const char kCupcake[] = "Cupcake";
21 const char kDonut[] = "Donut";
22 const char kEclair[] = "Eclair";
23 const char kFroyo[] = "Froyo";
24 const char kGingerbread[] = "Gingerbread";
25 
26 // Asserts that the fixed-size array |expected_backtrace| matches the backtrace
27 // in |AllocationContextTracker::GetContextSnapshot|.
28 template <size_t N>
AssertBacktraceEquals(const StackFrame (& expected_backtrace)[N])29 void AssertBacktraceEquals(const StackFrame(&expected_backtrace)[N]) {
30   AllocationContext ctx = AllocationContextTracker::GetContextSnapshot();
31 
32   auto actual = std::begin(ctx.backtrace.frames);
33   auto actual_bottom = std::end(ctx.backtrace.frames);
34   auto expected = std::begin(expected_backtrace);
35   auto expected_bottom = std::end(expected_backtrace);
36 
37   // Note that this requires the pointers to be equal, this is not doing a deep
38   // string comparison.
39   for (; actual != actual_bottom && expected != expected_bottom;
40        actual++, expected++)
41     ASSERT_EQ(*expected, *actual);
42 
43   // Ensure that the height of the stacks is the same.
44   ASSERT_EQ(actual, actual_bottom);
45   ASSERT_EQ(expected, expected_bottom);
46 }
47 
AssertBacktraceEmpty()48 void AssertBacktraceEmpty() {
49   AllocationContext ctx = AllocationContextTracker::GetContextSnapshot();
50 
51   for (StackFrame frame : ctx.backtrace.frames)
52     ASSERT_EQ(nullptr, frame);
53 }
54 
55 class AllocationContextTrackerTest : public testing::Test {
56  public:
SetUp()57   void SetUp() override {
58     TraceConfig config("");
59     TraceLog::GetInstance()->SetEnabled(config, TraceLog::RECORDING_MODE);
60     AllocationContextTracker::SetCaptureEnabled(true);
61   }
62 
TearDown()63   void TearDown() override {
64     AllocationContextTracker::SetCaptureEnabled(false);
65     TraceLog::GetInstance()->SetDisabled();
66   }
67 };
68 
69 // Check that |TRACE_EVENT| macros push and pop to the pseudo stack correctly.
70 // Also check that |GetContextSnapshot| fills the backtrace with null pointers
71 // when the pseudo stack height is less than the capacity.
TEST_F(AllocationContextTrackerTest,PseudoStackScopedTrace)72 TEST_F(AllocationContextTrackerTest, PseudoStackScopedTrace) {
73   StackFrame c = kCupcake;
74   StackFrame d = kDonut;
75   StackFrame e = kEclair;
76   StackFrame f = kFroyo;
77 
78   AssertBacktraceEmpty();
79 
80   {
81     TRACE_EVENT0("Testing", kCupcake);
82     StackFrame frame_c[] = {c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
83     AssertBacktraceEquals(frame_c);
84 
85     {
86       TRACE_EVENT0("Testing", kDonut);
87       StackFrame frame_cd[] = {c, d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
88       AssertBacktraceEquals(frame_cd);
89     }
90 
91     AssertBacktraceEquals(frame_c);
92 
93     {
94       TRACE_EVENT0("Testing", kEclair);
95       StackFrame frame_ce[] = {c, e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
96       AssertBacktraceEquals(frame_ce);
97     }
98 
99     AssertBacktraceEquals(frame_c);
100   }
101 
102   AssertBacktraceEmpty();
103 
104   {
105     TRACE_EVENT0("Testing", kFroyo);
106     StackFrame frame_f[] = {f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
107     AssertBacktraceEquals(frame_f);
108   }
109 
110   AssertBacktraceEmpty();
111 }
112 
113 // Same as |PseudoStackScopedTrace|, but now test the |TRACE_EVENT_BEGIN| and
114 // |TRACE_EVENT_END| macros.
TEST_F(AllocationContextTrackerTest,PseudoStackBeginEndTrace)115 TEST_F(AllocationContextTrackerTest, PseudoStackBeginEndTrace) {
116   StackFrame c = kCupcake;
117   StackFrame d = kDonut;
118   StackFrame e = kEclair;
119   StackFrame f = kFroyo;
120 
121   StackFrame frame_c[] = {c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
122   StackFrame frame_cd[] = {c, d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
123   StackFrame frame_ce[] = {c, e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
124   StackFrame frame_f[] = {f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
125 
126   AssertBacktraceEmpty();
127 
128   TRACE_EVENT_BEGIN0("Testing", kCupcake);
129   AssertBacktraceEquals(frame_c);
130 
131   TRACE_EVENT_BEGIN0("Testing", kDonut);
132   AssertBacktraceEquals(frame_cd);
133   TRACE_EVENT_END0("Testing", kDonut);
134 
135   AssertBacktraceEquals(frame_c);
136 
137   TRACE_EVENT_BEGIN0("Testing", kEclair);
138   AssertBacktraceEquals(frame_ce);
139   TRACE_EVENT_END0("Testing", kEclair);
140 
141   AssertBacktraceEquals(frame_c);
142   TRACE_EVENT_END0("Testing", kCupcake);
143 
144   AssertBacktraceEmpty();
145 
146   TRACE_EVENT_BEGIN0("Testing", kFroyo);
147   AssertBacktraceEquals(frame_f);
148   TRACE_EVENT_END0("Testing", kFroyo);
149 
150   AssertBacktraceEmpty();
151 }
152 
TEST_F(AllocationContextTrackerTest,PseudoStackMixedTrace)153 TEST_F(AllocationContextTrackerTest, PseudoStackMixedTrace) {
154   StackFrame c = kCupcake;
155   StackFrame d = kDonut;
156   StackFrame e = kEclair;
157   StackFrame f = kFroyo;
158 
159   StackFrame frame_c[] = {c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
160   StackFrame frame_cd[] = {c, d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
161   StackFrame frame_e[] = {e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
162   StackFrame frame_ef[] = {e, f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
163 
164   AssertBacktraceEmpty();
165 
166   TRACE_EVENT_BEGIN0("Testing", kCupcake);
167   AssertBacktraceEquals(frame_c);
168 
169   {
170     TRACE_EVENT0("Testing", kDonut);
171     AssertBacktraceEquals(frame_cd);
172   }
173 
174   AssertBacktraceEquals(frame_c);
175   TRACE_EVENT_END0("Testing", kCupcake);
176   AssertBacktraceEmpty();
177 
178   {
179     TRACE_EVENT0("Testing", kEclair);
180     AssertBacktraceEquals(frame_e);
181 
182     TRACE_EVENT_BEGIN0("Testing", kFroyo);
183     AssertBacktraceEquals(frame_ef);
184     TRACE_EVENT_END0("Testing", kFroyo);
185     AssertBacktraceEquals(frame_e);
186   }
187 
188   AssertBacktraceEmpty();
189 }
190 
TEST_F(AllocationContextTrackerTest,BacktraceTakesTop)191 TEST_F(AllocationContextTrackerTest, BacktraceTakesTop) {
192   // Push 12 events onto the pseudo stack.
193   TRACE_EVENT0("Testing", kCupcake);
194   TRACE_EVENT0("Testing", kCupcake);
195   TRACE_EVENT0("Testing", kCupcake);
196   TRACE_EVENT0("Testing", kCupcake);
197 
198   TRACE_EVENT0("Testing", kCupcake);
199   TRACE_EVENT0("Testing", kCupcake);
200   TRACE_EVENT0("Testing", kCupcake);
201   TRACE_EVENT0("Testing", kCupcake);
202 
203   TRACE_EVENT0("Testing", kCupcake);
204   TRACE_EVENT0("Testing", kDonut);
205   TRACE_EVENT0("Testing", kEclair);
206   TRACE_EVENT0("Testing", kFroyo);
207 
208   {
209     TRACE_EVENT0("Testing", kGingerbread);
210     AllocationContext ctx = AllocationContextTracker::GetContextSnapshot();
211 
212     // The pseudo stack relies on pointer equality, not deep string comparisons.
213     ASSERT_EQ(kCupcake, ctx.backtrace.frames[0]);
214     ASSERT_EQ(kFroyo, ctx.backtrace.frames[11]);
215   }
216 
217   {
218     AllocationContext ctx = AllocationContextTracker::GetContextSnapshot();
219     ASSERT_EQ(kCupcake, ctx.backtrace.frames[0]);
220     ASSERT_EQ(kFroyo, ctx.backtrace.frames[11]);
221   }
222 }
223 
224 }  // namespace trace_event
225 }  // namespace base
226