Index: base/trace_event/heap_profiler_allocation_context_tracker.cc |
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc |
index 1fc8bc008a522419f7b1eb454c9b45a6d8c739b0..13e13c4cc349a6ed12745cadf55a9777e33d32e0 100644 |
--- a/base/trace_event/heap_profiler_allocation_context_tracker.cc |
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc |
@@ -7,6 +7,7 @@ |
#include <algorithm> |
#include <iterator> |
+#include "base/allocator/features.h" |
#include "base/atomicops.h" |
#include "base/threading/thread_local_storage.h" |
#include "base/trace_event/heap_profiler_allocation_context.h" |
@@ -14,7 +15,8 @@ |
namespace base { |
namespace trace_event { |
-subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0; |
+subtle::Atomic32 AllocationContextTracker::capture_mode_ = |
+ AllocationContextTracker::DONT_CAPTURE; |
namespace { |
@@ -31,6 +33,59 @@ void DestructAllocationContextTracker(void* alloc_ctx_tracker) { |
delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); |
} |
+// Walks stack frames and collects a trace. Unlike base::debug::StackTrace |
+// (which uses unwinding) this function uses frame pointers and is fast. |
+// However, by default frame pointers are not enabled in optimized builds |
+// unless -fno-omit-frame-pointer flag is specified. |
+// Adapted from TCMalloc's stacktrace_x86-inl.h |
+size_t WalkStackFrames(const void** trace, |
+ size_t max_depth, |
+ size_t skip_count) { |
+ uintptr_t sp = reinterpret_cast<uintptr_t>( |
Primiano Tucci (use gerrit)
2016/04/07 15:51:56
Should this have a
#if !BUILDFLAG(ENABLE_PROFILIN
|
+ __builtin_frame_address(0)); |
+ |
+ size_t n = 0; |
+ while (sp && n < max_depth) { |
+ if (reinterpret_cast<void**>(sp)[1] == nullptr) { |
+ // In 64-bit code, we often see a frame that |
+ // points to itself and has a return address of 0. |
+ break; |
+ } |
+ |
+ if (skip_count > 0) { |
+ skip_count--; |
+ } else { |
+ trace[n] = reinterpret_cast<void**>(sp)[1]; |
+ n++; |
+ } |
+ |
+ { |
+ uintptr_t next_sp = reinterpret_cast<uintptr_t*>(sp)[0]; |
+ |
+ // With the stack growing downwards, older stack frame must be |
+ // at a greater address that the current one. |
+ if (next_sp <= sp) break; |
+ |
+ // Assume stack frames larger than 100,000 bytes are bogus. |
+ if (next_sp - sp > 100000) break; |
+ |
+ // Check alignment. |
+ if (next_sp & (sizeof(void*) - 1)) break; |
+ |
+#ifdef __i386__ |
+ // On 64-bit machines, the stack pointer can be very close to |
+ // 0xffffffff, so we explicitly check for a pointer into the |
+ // last two pages in the address space |
+ if (next_sp >= 0xffffe000) break; |
+#endif |
+ |
+ sp = next_sp; |
+ } |
+ } |
+ |
+ return n; |
+} |
+ |
} // namespace |
// static |
@@ -64,15 +119,19 @@ void AllocationContextTracker::SetCurrentThreadName(const char* name) { |
} |
// static |
-void AllocationContextTracker::SetCaptureEnabled(bool enabled) { |
+void AllocationContextTracker::SetCaptureMode(CaptureMode mode) { |
// When enabling capturing, also initialize the TLS slot. This does not create |
// a TLS instance yet. |
- if (enabled && !g_tls_alloc_ctx_tracker.initialized()) |
+ if (mode != DONT_CAPTURE && !g_tls_alloc_ctx_tracker.initialized()) |
g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker); |
- // Release ordering ensures that when a thread observes |capture_enabled_| to |
+ // Release ordering ensures that when a thread observes |capture_mode_| to |
// be true through an acquire load, the TLS slot has been initialized. |
- subtle::Release_Store(&capture_enabled_, enabled); |
+ subtle::Release_Store(&capture_mode_, mode); |
+} |
+ |
+void AllocationContextTracker::PushPseudoStackFrame(const char* frame_value) { |
+ PushPseudoStackFrame(StackFrame::FromSymbol(frame_value)); |
} |
void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { |
@@ -84,8 +143,7 @@ void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { |
NOTREACHED(); |
} |
-// static |
-void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { |
+void AllocationContextTracker::PopPseudoStackFrame(const void* frame_value) { |
// Guard for stack underflow. If tracing was started with a TRACE_EVENT in |
// scope, the frame was never pushed, so it is possible that pop is called |
// on an empty stack. |
@@ -95,7 +153,7 @@ void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { |
// Assert that pushes and pops are nested correctly. This DCHECK can be |
// hit if some TRACE_EVENT macro is unbalanced (a TRACE_EVENT_END* call |
// without a corresponding TRACE_EVENT_BEGIN). |
- DCHECK_EQ(frame, pseudo_stack_.back()) |
+ DCHECK_EQ(frame_value, pseudo_stack_.back().value) |
<< "Encountered an unmatched TRACE_EVENT_END"; |
pseudo_stack_.pop_back(); |
@@ -115,31 +173,77 @@ void AllocationContextTracker::PopCurrentTaskContext(const char* context) { |
task_contexts_.pop_back(); |
} |
-// static |
AllocationContext AllocationContextTracker::GetContextSnapshot() { |
AllocationContext ctx; |
- // Fill the backtrace. |
- { |
- auto src = pseudo_stack_.begin(); |
- auto dst = std::begin(ctx.backtrace.frames); |
- auto src_end = pseudo_stack_.end(); |
- auto dst_end = std::end(ctx.backtrace.frames); |
- |
- // Add the thread name as the first enrty in the backtrace. |
- if (thread_name_) { |
- DCHECK(dst < dst_end); |
- *dst = thread_name_; |
- ++dst; |
- } |
+ CaptureMode mode = static_cast<CaptureMode>( |
+ subtle::Acquire_Load(&capture_mode_)); |
Primiano Tucci (use gerrit)
2016/04/07 15:51:56
We are going to call this on each malloc. I'd keep
Dmitry Skiba
2016/04/12 18:22:11
Done.
|
+ |
+ switch (mode) { |
+ case DONT_CAPTURE: |
+ { |
+ NOTREACHED(); |
+ return AllocationContext::Empty(); |
+ } |
+ case CAPTURE_PSEUDO_STACK: |
+ { |
+ auto src = pseudo_stack_.begin(); |
Primiano Tucci (use gerrit)
2016/04/07 15:51:56
I think there is some duplicated logic between thi
Dmitry Skiba
2016/04/12 18:22:11
Done.
|
+ auto dst = std::begin(ctx.backtrace.frames); |
+ auto src_end = pseudo_stack_.end(); |
+ auto dst_end = std::end(ctx.backtrace.frames); |
+ |
+ // Add the thread name as the first enrty in the backtrace. |
+ if (thread_name_) { |
+ DCHECK(dst < dst_end); |
+ *dst = StackFrame::FromThreadName(thread_name_); |
+ ++dst; |
+ } |
+ |
+ // Copy as much of the bottom of the pseudo stack into the |
+ // backtrace as possible. |
+ for (; src != src_end && dst != dst_end; src++, dst++) |
+ *dst = *src; |
+ |
+ // If there is room for more, fill the remaining slots with |
+ // empty frames. |
+ std::fill(dst, dst_end, StackFrame::Empty()); |
+ |
+ break; |
+ } |
+ case CAPTURE_NATIVE_STACK: |
+ { |
+ // WalkStackFrames() |
+ // base::trace_event::AllocationContextTracker::GetContextSnapshot() |
+ const size_t known_frame_count = 2; |
+ |
+ const size_t max_frame_count = arraysize(ctx.backtrace.frames); |
+ const void* frames[max_frame_count]; |
+ size_t frame_count = WalkStackFrames(frames, |
+ max_frame_count, |
+ known_frame_count); |
+ |
+ size_t frame_shift = 0; |
+ if (thread_name_) { |
+ ctx.backtrace.frames[frame_shift++] = |
+ StackFrame::FromThreadName(thread_name_); |
+ } |
+ |
+ if (frame_count + frame_shift > max_frame_count) { |
+ frame_count = max_frame_count - frame_shift; |
+ } |
- // Copy as much of the bottom of the pseudo stack into the backtrace as |
- // possible. |
- for (; src != src_end && dst != dst_end; src++, dst++) |
- *dst = *src; |
+ // Copy frames backwards |
+ for (size_t i = 0; i != frame_count; ++i) { |
+ ctx.backtrace.frames[i + frame_shift] = |
+ StackFrame::FromPC(frames[frame_count - 1 - i]); |
+ } |
- // If there is room for more, fill the remaining slots with empty frames. |
- std::fill(dst, dst_end, nullptr); |
+ // Fill remainder with empty frames |
+ std::fill(ctx.backtrace.frames + frame_shift + frame_count, |
+ ctx.backtrace.frames + max_frame_count, |
+ StackFrame::Empty()); |
+ break; |
+ } |
} |
// TODO(ssid): Fix crbug.com/594803 to add file name as 3rd dimension |