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/debug/leak_annotations.h"
     12 #include "base/threading/platform_thread.h"
     13 #include "base/threading/thread_local_storage.h"
     14 #include "base/trace_event/heap_profiler_allocation_context.h"
     15 
     16 #if defined(OS_LINUX) || defined(OS_ANDROID)
     17 #include <sys/prctl.h>
     18 #endif
     19 
     20 namespace base {
     21 namespace trace_event {
     22 
     23 subtle::Atomic32 AllocationContextTracker::capture_mode_ =
     24     static_cast<int32_t>(AllocationContextTracker::CaptureMode::DISABLED);
     25 
     26 namespace {
     27 
     28 const size_t kMaxStackDepth = 128u;
     29 const size_t kMaxTaskDepth = 16u;
     30 AllocationContextTracker* const kInitializingSentinel =
     31     reinterpret_cast<AllocationContextTracker*>(-1);
     32 
     33 ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER;
     34 
     35 // This function is added to the TLS slot to clean up the instance when the
     36 // thread exits.
     37 void DestructAllocationContextTracker(void* alloc_ctx_tracker) {
     38   delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker);
     39 }
     40 
     41 // Cannot call ThreadIdNameManager::GetName because it holds a lock and causes
     42 // deadlock when lock is already held by ThreadIdNameManager before the current
     43 // allocation. Gets the thread name from kernel if available or returns a string
     44 // with id. This function intenionally leaks the allocated strings since they
     45 // are used to tag allocations even after the thread dies.
     46 const char* GetAndLeakThreadName() {
     47   char name[16];
     48 #if defined(OS_LINUX) || defined(OS_ANDROID)
     49   // If the thread name is not set, try to get it from prctl. Thread name might
     50   // not be set in cases where the thread started before heap profiling was
     51   // enabled.
     52   int err = prctl(PR_GET_NAME, name);
     53   if (!err) {
     54     return strdup(name);
     55   }
     56 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
     57 
     58   // Use tid if we don't have a thread name.
     59   snprintf(name, sizeof(name), "%lu",
     60            static_cast<unsigned long>(PlatformThread::CurrentId()));
     61   return strdup(name);
     62 }
     63 
     64 }  // namespace
     65 
     66 // static
     67 AllocationContextTracker*
     68 AllocationContextTracker::GetInstanceForCurrentThread() {
     69   AllocationContextTracker* tracker =
     70       static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get());
     71   if (tracker == kInitializingSentinel)
     72     return nullptr;  // Re-entrancy case.
     73 
     74   if (!tracker) {
     75     g_tls_alloc_ctx_tracker.Set(kInitializingSentinel);
     76     tracker = new AllocationContextTracker();
     77     g_tls_alloc_ctx_tracker.Set(tracker);
     78   }
     79 
     80   return tracker;
     81 }
     82 
     83 AllocationContextTracker::AllocationContextTracker()
     84     : thread_name_(nullptr), ignore_scope_depth_(0) {
     85   pseudo_stack_.reserve(kMaxStackDepth);
     86   task_contexts_.reserve(kMaxTaskDepth);
     87 }
     88 AllocationContextTracker::~AllocationContextTracker() {}
     89 
     90 // static
     91 void AllocationContextTracker::SetCurrentThreadName(const char* name) {
     92   if (name && capture_mode() != CaptureMode::DISABLED) {
     93     GetInstanceForCurrentThread()->thread_name_ = name;
     94   }
     95 }
     96 
     97 // static
     98 void AllocationContextTracker::SetCaptureMode(CaptureMode mode) {
     99   // When enabling capturing, also initialize the TLS slot. This does not create
    100   // a TLS instance yet.
    101   if (mode != CaptureMode::DISABLED && !g_tls_alloc_ctx_tracker.initialized())
    102     g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker);
    103 
    104   // Release ordering ensures that when a thread observes |capture_mode_| to
    105   // be true through an acquire load, the TLS slot has been initialized.
    106   subtle::Release_Store(&capture_mode_, static_cast<int32_t>(mode));
    107 }
    108 
    109 void AllocationContextTracker::PushPseudoStackFrame(
    110     AllocationContextTracker::PseudoStackFrame stack_frame) {
    111   // Impose a limit on the height to verify that every push is popped, because
    112   // in practice the pseudo stack never grows higher than ~20 frames.
    113   if (pseudo_stack_.size() < kMaxStackDepth)
    114     pseudo_stack_.push_back(stack_frame);
    115   else
    116     NOTREACHED();
    117 }
    118 
    119 void AllocationContextTracker::PopPseudoStackFrame(
    120     AllocationContextTracker::PseudoStackFrame stack_frame) {
    121   // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
    122   // scope, the frame was never pushed, so it is possible that pop is called
    123   // on an empty stack.
    124   if (pseudo_stack_.empty())
    125     return;
    126 
    127   // Assert that pushes and pops are nested correctly. This DCHECK can be
    128   // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call
    129   // without a corresponding TRACE_EVENT_BEGIN).
    130   DCHECK(stack_frame == pseudo_stack_.back())
    131       << "Encountered an unmatched TRACE_EVENT_END: "
    132       << stack_frame.trace_event_name
    133       << " vs event in stack: " << pseudo_stack_.back().trace_event_name;
    134 
    135   pseudo_stack_.pop_back();
    136 }
    137 
    138 void AllocationContextTracker::PushCurrentTaskContext(const char* context) {
    139   DCHECK(context);
    140   if (task_contexts_.size() < kMaxTaskDepth)
    141     task_contexts_.push_back(context);
    142   else
    143     NOTREACHED();
    144 }
    145 
    146 void AllocationContextTracker::PopCurrentTaskContext(const char* context) {
    147   // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
    148   // scope, the context was never pushed, so it is possible that pop is called
    149   // on an empty stack.
    150   if (task_contexts_.empty())
    151     return;
    152 
    153   DCHECK_EQ(context, task_contexts_.back())
    154       << "Encountered an unmatched context end";
    155   task_contexts_.pop_back();
    156 }
    157 
    158 // static
    159 bool AllocationContextTracker::GetContextSnapshot(AllocationContext* ctx) {
    160   if (ignore_scope_depth_)
    161     return false;
    162 
    163   CaptureMode mode = static_cast<CaptureMode>(
    164       subtle::NoBarrier_Load(&capture_mode_));
    165 
    166   auto* backtrace = std::begin(ctx->backtrace.frames);
    167   auto* backtrace_end = std::end(ctx->backtrace.frames);
    168 
    169   if (!thread_name_) {
    170     // Ignore the string allocation made by GetAndLeakThreadName to avoid
    171     // reentrancy.
    172     ignore_scope_depth_++;
    173     thread_name_ = GetAndLeakThreadName();
    174     ANNOTATE_LEAKING_OBJECT_PTR(thread_name_);
    175     DCHECK(thread_name_);
    176     ignore_scope_depth_--;
    177   }
    178 
    179   // Add the thread name as the first entry in pseudo stack.
    180   if (thread_name_) {
    181     *backtrace++ = StackFrame::FromThreadName(thread_name_);
    182   }
    183 
    184   switch (mode) {
    185     case CaptureMode::DISABLED:
    186       {
    187         break;
    188       }
    189     case CaptureMode::PSEUDO_STACK:
    190       {
    191         for (const PseudoStackFrame& stack_frame : pseudo_stack_) {
    192           if (backtrace == backtrace_end) {
    193             break;
    194           }
    195           *backtrace++ =
    196               StackFrame::FromTraceEventName(stack_frame.trace_event_name);
    197         }
    198         break;
    199       }
    200     case CaptureMode::NATIVE_STACK:
    201       {
    202         // Backtrace contract requires us to return bottom frames, i.e.
    203         // from main() and up. Stack unwinding produces top frames, i.e.
    204         // from this point and up until main(). We request many frames to
    205         // make sure we reach main(), and then copy bottom portion of them.
    206         const void* frames[128];
    207         static_assert(arraysize(frames) >= Backtrace::kMaxFrameCount,
    208                       "not requesting enough frames to fill Backtrace");
    209 #if HAVE_TRACE_STACK_FRAME_POINTERS && !defined(OS_NACL)
    210         size_t frame_count = debug::TraceStackFramePointers(
    211             frames,
    212             arraysize(frames),
    213             1 /* exclude this function from the trace */ );
    214 #else
    215         size_t frame_count = 0;
    216         NOTREACHED();
    217 #endif
    218 
    219         // Copy frames backwards
    220         size_t backtrace_capacity = backtrace_end - backtrace;
    221         int32_t top_frame_index = (backtrace_capacity >= frame_count)
    222                                       ? 0
    223                                       : frame_count - backtrace_capacity;
    224         for (int32_t i = frame_count - 1; i >= top_frame_index; --i) {
    225           const void* frame = frames[i];
    226           *backtrace++ = StackFrame::FromProgramCounter(frame);
    227         }
    228         break;
    229       }
    230   }
    231 
    232   ctx->backtrace.frame_count = backtrace - std::begin(ctx->backtrace.frames);
    233 
    234   // TODO(ssid): Fix crbug.com/594803 to add file name as 3rd dimension
    235   // (component name) in the heap profiler and not piggy back on the type name.
    236   if (!task_contexts_.empty()) {
    237     ctx->type_name = task_contexts_.back();
    238   } else if (!pseudo_stack_.empty()) {
    239     // If task context was unavailable, then the category names are taken from
    240     // trace events.
    241     ctx->type_name = pseudo_stack_.back().trace_event_category;
    242   } else {
    243     ctx->type_name = nullptr;
    244   }
    245 
    246   return true;
    247 }
    248 
    249 }  // namespace trace_event
    250 }  // namespace base
    251