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