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