Chromium Code Reviews| 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 |