| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/trace_event/heap_profiler_allocation_context_tracker.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| |
| #include "base/atomicops.h" |
| #include "base/threading/thread_local_storage.h" |
| #include "base/trace_event/heap_profiler_allocation_context.h" |
| |
| namespace base { |
| namespace trace_event { |
| |
| subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0; |
| |
| namespace { |
| |
| ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER; |
| |
| // This function is added to the TLS slot to clean up the instance when the |
| // thread exits. |
| void DestructAllocationContextTracker(void* alloc_ctx_tracker) { |
| delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); |
| } |
| |
| } // namespace |
| |
| AllocationContextTracker::AllocationContextTracker() {} |
| AllocationContextTracker::~AllocationContextTracker() {} |
| |
| // static |
| AllocationContextTracker* AllocationContextTracker::GetThreadLocalTracker() { |
| auto tracker = |
| static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get()); |
| |
| if (!tracker) { |
| tracker = new AllocationContextTracker(); |
| g_tls_alloc_ctx_tracker.Set(tracker); |
| } |
| |
| return tracker; |
| } |
| |
| // static |
| void AllocationContextTracker::SetCaptureEnabled(bool enabled) { |
| // When enabling capturing, also initialize the TLS slot. This does not create |
| // a TLS instance yet. |
| if (enabled && !g_tls_alloc_ctx_tracker.initialized()) |
| g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker); |
| |
| // Release ordering ensures that when a thread observes |capture_enabled_| to |
| // be true through an acquire load, the TLS slot has been initialized. |
| subtle::Release_Store(&capture_enabled_, enabled); |
| } |
| |
| // static |
| void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { |
| auto tracker = AllocationContextTracker::GetThreadLocalTracker(); |
| |
| // Impose a limit on the height to verify that every push is popped, because |
| // in practice the pseudo stack never grows higher than ~20 frames. |
| DCHECK_LT(tracker->pseudo_stack_.size(), 128u); |
| tracker->pseudo_stack_.push_back(frame); |
| } |
| |
| // static |
| void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { |
| auto tracker = AllocationContextTracker::GetThreadLocalTracker(); |
| |
| // Guard for stack underflow. If tracing was started with a TRACE_EVENT in |
| // scope, the frame was never pushed, so it is possible that pop is called |
| // on an empty stack. |
| if (tracker->pseudo_stack_.empty()) |
| return; |
| |
| // Assert that pushes and pops are nested correctly. This DCHECK can be |
| // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call |
| // without a corresponding TRACE_EVENT_BEGIN). |
| DCHECK_EQ(frame, tracker->pseudo_stack_.back()) |
| << "Encountered an unmatched TRACE_EVENT_END"; |
| |
| tracker->pseudo_stack_.pop_back(); |
| } |
| |
| // static |
| AllocationContext AllocationContextTracker::GetContextSnapshot() { |
| AllocationContextTracker* tracker = GetThreadLocalTracker(); |
| AllocationContext ctx; |
| |
| // Fill the backtrace. |
| { |
| auto src = tracker->pseudo_stack_.begin(); |
| auto dst = std::begin(ctx.backtrace.frames); |
| auto src_end = tracker->pseudo_stack_.end(); |
| auto dst_end = std::end(ctx.backtrace.frames); |
| |
| // Copy as much of the bottom of the pseudo stack into the backtrace as |
| // possible. |
| for (; src != src_end && dst != dst_end; src++, dst++) |
| *dst = *src; |
| |
| // If there is room for more, fill the remaining slots with empty frames. |
| std::fill(dst, dst_end, nullptr); |
| } |
| |
| ctx.type_name = nullptr; |
| |
| return ctx; |
| } |
| |
| } // namespace trace_event |
| } // namespace base |