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.
DestructAllocationContextTracker(void * alloc_ctx_tracker)25 void DestructAllocationContextTracker(void* alloc_ctx_tracker) {
26   delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker);
27 }
28 
29 }  // namespace
30 
AllocationContextTracker()31 AllocationContextTracker::AllocationContextTracker() {}
~AllocationContextTracker()32 AllocationContextTracker::~AllocationContextTracker() {}
33 
34 // static
GetThreadLocalTracker()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
SetCaptureEnabled(bool enabled)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
PushPseudoStackFrame(StackFrame frame)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
PopPseudoStackFrame(StackFrame frame)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
GetContextSnapshot()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