Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Side by Side Diff: base/trace_event/heap_profiler_allocation_context_tracker.cc

Issue 1839503002: [tracing] Add native allocation tracing mode. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: WalkStackFrames (wants frame pointers) Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/trace_event/heap_profiler_allocation_context_tracker.h" 5 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 9
10 #include "base/allocator/features.h"
10 #include "base/atomicops.h" 11 #include "base/atomicops.h"
12 #include "base/debug/stack_trace.h"
11 #include "base/threading/thread_local_storage.h" 13 #include "base/threading/thread_local_storage.h"
12 #include "base/trace_event/heap_profiler_allocation_context.h" 14 #include "base/trace_event/heap_profiler_allocation_context.h"
13 15
14 namespace base { 16 namespace base {
15 namespace trace_event { 17 namespace trace_event {
16 18
17 subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0; 19 subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0;
18 20
19 namespace { 21 namespace {
20 22
21 const size_t kMaxStackDepth = 128u; 23 const size_t kMaxStackDepth = 128u;
22 AllocationContextTracker* const kInitializingSentinel = 24 AllocationContextTracker* const kInitializingSentinel =
23 reinterpret_cast<AllocationContextTracker*>(-1); 25 reinterpret_cast<AllocationContextTracker*>(-1);
24 26
25 ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER; 27 ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER;
26 28
27 // This function is added to the TLS slot to clean up the instance when the 29 // This function is added to the TLS slot to clean up the instance when the
28 // thread exits. 30 // thread exits.
29 void DestructAllocationContextTracker(void* alloc_ctx_tracker) { 31 void DestructAllocationContextTracker(void* alloc_ctx_tracker) {
30 delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); 32 delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker);
31 } 33 }
32 34
35 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL)
36
37 // Walks stack frames and collects a trace. Unlike base::debug::StackTrace
38 // (which uses unwinding) this function uses frame pointers and is fast.
39 // However, by default frame pointers are not enabled in optimized builds
40 // unless -fno-omit-frame-pointer flag is specified.
41 // Adapted from TCMalloc's stacktrace_x86-inl.h
42 size_t WalkStackFrames(const void** trace,
Primiano Tucci (use gerrit) 2016/04/01 15:56:28 would be nice to make this available as a general
Dmitry Skiba 2016/04/04 22:38:34 Yes, I would prefer to make it available through S
43 size_t max_depth,
44 size_t skip_count) {
45 uintptr_t sp = reinterpret_cast<uintptr_t>(
46 __builtin_frame_address(0));
47
48 size_t n = 0;
49 while (sp && n < max_depth) {
50 if (reinterpret_cast<void**>(sp)[1] == nullptr) {
51 // In 64-bit code, we often see a frame that
52 // points to itself and has a return address of 0.
53 break;
54 }
55
56 if (skip_count > 0) {
57 skip_count--;
58 } else {
59 trace[n] = reinterpret_cast<void**>(sp)[1];
60 n++;
61 }
62
63 {
64 uintptr_t next_sp = reinterpret_cast<uintptr_t*>(sp)[0];
65
66 // With the stack growing downwards, older stack frame must be
67 // at a greater address that the current one.
68 if (next_sp <= sp) break;
69
70 // Assume stack frames larger than 100,000 bytes are bogus.
71 if (next_sp - sp > 100000) break;
72
73 // Check alignment.
74 if (next_sp & (sizeof(void*) - 1)) break;
75
76 #ifdef __i386__
77 // On 64-bit machines, the stack pointer can be very close to
78 // 0xffffffff, so we explicitly check for a pointer into the
79 // last two pages in the address space
80 if (next_sp >= 0xffffe000) break;
81 #endif
82
83 sp = next_sp;
84 }
85 }
86
87 return n;
88 }
89
90 #endif // USE_EXPERIMENTAL_ALLOCATION_TRACING
91
33 } // namespace 92 } // namespace
34 93
35 // static 94 // static
36 AllocationContextTracker* 95 AllocationContextTracker*
37 AllocationContextTracker::GetInstanceForCurrentThread() { 96 AllocationContextTracker::GetInstanceForCurrentThread() {
38 AllocationContextTracker* tracker = 97 AllocationContextTracker* tracker =
39 static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get()); 98 static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get());
40 if (tracker == kInitializingSentinel) 99 if (tracker == kInitializingSentinel)
41 return nullptr; // Re-entrancy case. 100 return nullptr; // Re-entrancy case.
42 101
(...skipping 14 matching lines...) Expand all
57 // static 116 // static
58 void AllocationContextTracker::SetCaptureEnabled(bool enabled) { 117 void AllocationContextTracker::SetCaptureEnabled(bool enabled) {
59 // When enabling capturing, also initialize the TLS slot. This does not create 118 // When enabling capturing, also initialize the TLS slot. This does not create
60 // a TLS instance yet. 119 // a TLS instance yet.
61 if (enabled && !g_tls_alloc_ctx_tracker.initialized()) 120 if (enabled && !g_tls_alloc_ctx_tracker.initialized())
62 g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker); 121 g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker);
63 122
64 // Release ordering ensures that when a thread observes |capture_enabled_| to 123 // Release ordering ensures that when a thread observes |capture_enabled_| to
65 // be true through an acquire load, the TLS slot has been initialized. 124 // be true through an acquire load, the TLS slot has been initialized.
66 subtle::Release_Store(&capture_enabled_, enabled); 125 subtle::Release_Store(&capture_enabled_, enabled);
126
127 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL)
128 if (enabled) {
129 // Dummy StackTrace usage, just in case stack trace machinery needs to
130 // allocate memory during initialization (e.g. backtrace() does that).
Primiano Tucci (use gerrit) 2016/04/01 15:56:28 where is backtrace() called from?
Dmitry Skiba 2016/04/04 22:38:34 From StackTrace constructor. StackTrace is actuall
131 debug::StackTrace warmup;
132 }
133 #endif
67 } 134 }
68 135
69 void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { 136 void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) {
70 // Impose a limit on the height to verify that every push is popped, because 137 // Impose a limit on the height to verify that every push is popped, because
71 // in practice the pseudo stack never grows higher than ~20 frames. 138 // in practice the pseudo stack never grows higher than ~20 frames.
72 if (pseudo_stack_.size() < kMaxStackDepth) 139 if (pseudo_stack_.size() < kMaxStackDepth)
73 pseudo_stack_.push_back(frame); 140 pseudo_stack_.push_back(frame);
74 else 141 else
75 NOTREACHED(); 142 NOTREACHED();
76 } 143 }
77 144
78 // static
79 void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { 145 void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) {
80 // Guard for stack underflow. If tracing was started with a TRACE_EVENT in 146 // Guard for stack underflow. If tracing was started with a TRACE_EVENT in
81 // scope, the frame was never pushed, so it is possible that pop is called 147 // scope, the frame was never pushed, so it is possible that pop is called
82 // on an empty stack. 148 // on an empty stack.
83 if (pseudo_stack_.empty()) 149 if (pseudo_stack_.empty())
84 return; 150 return;
85 151
86 // Assert that pushes and pops are nested correctly. This DCHECK can be 152 // Assert that pushes and pops are nested correctly. This DCHECK can be
87 // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call 153 // hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call
88 // without a corresponding TRACE_EVENT_BEGIN). 154 // without a corresponding TRACE_EVENT_BEGIN).
89 DCHECK_EQ(frame, pseudo_stack_.back()) 155 DCHECK_EQ(frame, pseudo_stack_.back())
90 << "Encountered an unmatched TRACE_EVENT_END"; 156 << "Encountered an unmatched TRACE_EVENT_END";
91 157
92 pseudo_stack_.pop_back(); 158 pseudo_stack_.pop_back();
93 } 159 }
94 160
95 // static
96 AllocationContext AllocationContextTracker::GetContextSnapshot() { 161 AllocationContext AllocationContextTracker::GetContextSnapshot() {
97 AllocationContext ctx; 162 AllocationContext ctx;
98 163
99 // Fill the backtrace. 164 #if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL)
165 // Ignore pseudo stack and return the real thing
166
167 // base::debug::StackTrace::StackTrace()
168 // base::trace_event::AllocationContextTracker::GetContextSnapshot()
169 // base::trace_event::MallocDumpProvider::InsertAllocation(...)
170 // base::trace_event::(anonymous namespace)::HookXXX(...)
171 // ShimXXX
172 const size_t known_frame_count = 5;
173
174 const size_t max_backtrace_count = arraysize(ctx.backtrace.frames);
175 size_t backtrace_count = WalkStackFrames(ctx.backtrace.frames,
176 max_backtrace_count,
177 known_frame_count + 1);
178
179 std::reverse(ctx.backtrace.frames,
Primiano Tucci (use gerrit) 2016/04/01 15:56:28 I wonder if we could avoid this reverse and just w
Dmitry Skiba 2016/04/04 22:38:34 OK
180 ctx.backtrace.frames + backtrace_count);
181
182 std::fill(ctx.backtrace.frames + backtrace_count,
183 ctx.backtrace.frames + max_backtrace_count,
184 nullptr);
185
186 ctx.backtrace.frame_type = STACK_FRAME_TYPE_PC;
187
188 #else
189 // Return pseudo stack trace
190
100 { 191 {
101 auto src = pseudo_stack_.begin(); 192 auto src = pseudo_stack_.begin();
102 auto dst = std::begin(ctx.backtrace.frames); 193 auto dst = std::begin(ctx.backtrace.frames);
103 auto src_end = pseudo_stack_.end(); 194 auto src_end = pseudo_stack_.end();
104 auto dst_end = std::end(ctx.backtrace.frames); 195 auto dst_end = std::end(ctx.backtrace.frames);
105 196
106 // Copy as much of the bottom of the pseudo stack into the backtrace as 197 // Copy as much of the bottom of the pseudo stack into the backtrace as
107 // possible. 198 // possible.
108 for (; src != src_end && dst != dst_end; src++, dst++) 199 for (; src != src_end && dst != dst_end; src++, dst++)
109 *dst = *src; 200 *dst = *src;
110 201
111 // If there is room for more, fill the remaining slots with empty frames. 202 // If there is room for more, fill the remaining slots with empty frames.
112 std::fill(dst, dst_end, nullptr); 203 std::fill(dst, dst_end, nullptr);
113 } 204 }
114 205
206 ctx.backtrace.frame_type = STACK_FRAME_TYPE_SYMBOL;
207
208 #endif // USE_EXPERIMENTAL_ALLOCATION_TRACING
209
115 ctx.type_name = nullptr; 210 ctx.type_name = nullptr;
116 211
117 return ctx; 212 return ctx;
118 } 213 }
119 214
120 } // namespace trace_event 215 } // namespace trace_event
121 } // namespace base 216 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698