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 "base/trace_event/heap_profiler_allocation_context_tracker.h" 6 7 #include <algorithm> 8 #include <iterator> 9 10 #include "base/atomicops.h" 11 #include "base/threading/thread_local_storage.h" 12 #include "base/trace_event/heap_profiler_allocation_context.h" 13 14 namespace base { 15 namespace trace_event { 16 17 subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0; 18 19 namespace { 20 21 ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER; 22 23 // This function is added to the TLS slot to clean up the instance when the 24 // thread exits. 25 void DestructAllocationContextTracker(void* alloc_ctx_tracker) { 26 delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); 27 } 28 29 } // namespace 30 31 AllocationContextTracker::AllocationContextTracker() {} 32 AllocationContextTracker::~AllocationContextTracker() {} 33 34 // static 35 AllocationContextTracker* AllocationContextTracker::GetThreadLocalTracker() { 36 auto tracker = 37 static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get()); 38 39 if (!tracker) { 40 tracker = new AllocationContextTracker(); 41 g_tls_alloc_ctx_tracker.Set(tracker); 42 } 43 44 return tracker; 45 } 46 47 // static 48 void AllocationContextTracker::SetCaptureEnabled(bool enabled) { 49 // When enabling capturing, also initialize the TLS slot. This does not create 50 // a TLS instance yet. 51 if (enabled && !g_tls_alloc_ctx_tracker.initialized()) 52 g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker); 53 54 // Release ordering ensures that when a thread observes |capture_enabled_| to 55 // be true through an acquire load, the TLS slot has been initialized. 56 subtle::Release_Store(&capture_enabled_, enabled); 57 } 58 59 // static 60 void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { 61 auto tracker = AllocationContextTracker::GetThreadLocalTracker(); 62 63 // Impose a limit on the height to verify that every push is popped, because 64 // in practice the pseudo stack never grows higher than ~20 frames. 65 DCHECK_LT(tracker->pseudo_stack_.size(), 128u); 66 tracker->pseudo_stack_.push_back(frame); 67 } 68 69 // static 70 void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { 71 auto tracker = AllocationContextTracker::GetThreadLocalTracker(); 72 73 // Guard for stack underflow. If tracing was started with a TRACE_EVENT in 74 // scope, the frame was never pushed, so it is possible that pop is called 75 // on an empty stack. 76 if (tracker->pseudo_stack_.empty()) 77 return; 78 79 // Assert that pushes and pops are nested correctly. This DCHECK can be 80 // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call 81 // without a corresponding TRACE_EVENT_BEGIN). 82 DCHECK_EQ(frame, tracker->pseudo_stack_.back()) 83 << "Encountered an unmatched TRACE_EVENT_END"; 84 85 tracker->pseudo_stack_.pop_back(); 86 } 87 88 // static 89 AllocationContext AllocationContextTracker::GetContextSnapshot() { 90 AllocationContextTracker* tracker = GetThreadLocalTracker(); 91 AllocationContext ctx; 92 93 // Fill the backtrace. 94 { 95 auto src = tracker->pseudo_stack_.begin(); 96 auto dst = std::begin(ctx.backtrace.frames); 97 auto src_end = tracker->pseudo_stack_.end(); 98 auto dst_end = std::end(ctx.backtrace.frames); 99 100 // Copy as much of the bottom of the pseudo stack into the backtrace as 101 // possible. 102 for (; src != src_end && dst != dst_end; src++, dst++) 103 *dst = *src; 104 105 // If there is room for more, fill the remaining slots with empty frames. 106 std::fill(dst, dst_end, nullptr); 107 } 108 109 ctx.type_name = nullptr; 110 111 return ctx; 112 } 113 114 } // namespace trace_event 115 } // namespace base 116