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
15namespace base {
16namespace trace_event {
17
18// Define all strings once, because the pseudo stack requires pointer equality,
19// and string interning is unreliable.
20const char kCupcake[] = "Cupcake";
21const char kDonut[] = "Donut";
22const char kEclair[] = "Eclair";
23const char kFroyo[] = "Froyo";
24const char kGingerbread[] = "Gingerbread";
25
26// Asserts that the fixed-size array |expected_backtrace| matches the backtrace
27// in |AllocationContextTracker::GetContextSnapshot|.
28template <size_t N>
29void 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
48void AssertBacktraceEmpty() {
49  AllocationContext ctx = AllocationContextTracker::GetContextSnapshot();
50
51  for (StackFrame frame : ctx.backtrace.frames)
52    ASSERT_EQ(nullptr, frame);
53}
54
55class AllocationContextTrackerTest : public testing::Test {
56 public:
57  void SetUp() override {
58    TraceConfig config("");
59    TraceLog::GetInstance()->SetEnabled(config, TraceLog::RECORDING_MODE);
60    AllocationContextTracker::SetCaptureEnabled(true);
61  }
62
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.
72TEST_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.
115TEST_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
153TEST_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
191TEST_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