Home | History | Annotate | Download | only in trace_event
      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