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