// Copyright 2015 The Chromium Authors // 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 #include "base/atomicops.h" #include "base/check_op.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/threading/thread_local_storage.h" namespace base { namespace trace_event { std::atomic AllocationContextTracker::capture_mode_{ AllocationContextTracker::CaptureMode::kDisabled}; namespace { const size_t kMaxTaskDepth = 16u; AllocationContextTracker* const kInitializingSentinel = reinterpret_cast(-1); // 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(alloc_ctx_tracker); } ThreadLocalStorage::Slot& AllocationContextTrackerTLS() { static NoDestructor tls_alloc_ctx_tracker( &DestructAllocationContextTracker); return *tls_alloc_ctx_tracker; } } // namespace // static AllocationContextTracker* AllocationContextTracker::GetInstanceForCurrentThread() { AllocationContextTracker* tracker = static_cast( AllocationContextTrackerTLS().Get()); if (tracker == kInitializingSentinel) return nullptr; // Re-entrancy case. if (!tracker) { AllocationContextTrackerTLS().Set(kInitializingSentinel); tracker = new AllocationContextTracker(); AllocationContextTrackerTLS().Set(tracker); } return tracker; } AllocationContextTracker::AllocationContextTracker() { task_contexts_.reserve(kMaxTaskDepth); task_contexts_.push_back("UntrackedTask"); } AllocationContextTracker::~AllocationContextTracker() = default; // static void AllocationContextTracker::SetCurrentThreadName(const char* name) { if (name && capture_mode() != CaptureMode::kDisabled) { GetInstanceForCurrentThread()->thread_name_ = name; } } // static void AllocationContextTracker::SetCaptureMode(CaptureMode mode) { // Release ordering ensures that when a thread observes |capture_mode_| to // be true through an acquire load, the TLS slot has been initialized. capture_mode_.store(mode, std::memory_order_release); } void AllocationContextTracker::PushCurrentTaskContext(const char* context) { DCHECK(context); if (task_contexts_.size() < kMaxTaskDepth) task_contexts_.push_back(context); else NOTREACHED(); } void AllocationContextTracker::PopCurrentTaskContext(const char* context) { // Guard for stack underflow. If tracing was started with a TRACE_EVENT in // scope, the context was never pushed, so it is possible that pop is called // on an empty stack. Note that the context always contains "UntrackedTask". if (task_contexts_.size() == 1) return; DCHECK_EQ(context, task_contexts_.back()) << "Encountered an unmatched context end"; task_contexts_.pop_back(); } } // namespace trace_event } // namespace base