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 2877d2af91b27ace28839ebaac7468322cf4823f..607d500d2dfcbaebaec42a61578a4fd271a593e6 100644 |
| --- a/base/trace_event/heap_profiler_allocation_context_tracker.cc |
| +++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc |
| @@ -7,7 +7,9 @@ |
| #include <algorithm> |
| #include <iterator> |
| +#include "base/allocator/features.h" |
| #include "base/atomicops.h" |
| +#include "base/debug/stack_trace.h" |
| #include "base/threading/thread_local_storage.h" |
| #include "base/trace_event/heap_profiler_allocation_context.h" |
| @@ -30,6 +32,63 @@ void DestructAllocationContextTracker(void* alloc_ctx_tracker) { |
| delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker); |
| } |
| +#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL) |
| + |
| +// 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, |
|
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
|
| + size_t max_depth, |
| + size_t skip_count) { |
| + uintptr_t sp = reinterpret_cast<uintptr_t>( |
| + __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; |
| +} |
| + |
| +#endif // USE_EXPERIMENTAL_ALLOCATION_TRACING |
| + |
| } // namespace |
| // static |
| @@ -64,6 +123,14 @@ void AllocationContextTracker::SetCaptureEnabled(bool enabled) { |
| // Release ordering ensures that when a thread observes |capture_enabled_| to |
| // be true through an acquire load, the TLS slot has been initialized. |
| subtle::Release_Store(&capture_enabled_, enabled); |
| + |
| +#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL) |
| + if (enabled) { |
| + // Dummy StackTrace usage, just in case stack trace machinery needs to |
| + // 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
|
| + debug::StackTrace warmup; |
| + } |
| +#endif |
| } |
| void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { |
| @@ -75,7 +142,6 @@ void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) { |
| NOTREACHED(); |
| } |
| -// static |
| void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { |
| // 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 |
| @@ -92,11 +158,36 @@ void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) { |
| pseudo_stack_.pop_back(); |
| } |
| -// static |
| AllocationContext AllocationContextTracker::GetContextSnapshot() { |
| AllocationContext ctx; |
| - // Fill the backtrace. |
| +#if BUILDFLAG(USE_EXPERIMENTAL_ALLOCATION_TRACING) && !defined(OS_NACL) |
| + // Ignore pseudo stack and return the real thing |
| + |
| + // base::debug::StackTrace::StackTrace() |
| + // base::trace_event::AllocationContextTracker::GetContextSnapshot() |
| + // base::trace_event::MallocDumpProvider::InsertAllocation(...) |
| + // base::trace_event::(anonymous namespace)::HookXXX(...) |
| + // ShimXXX |
| + const size_t known_frame_count = 5; |
| + |
| + const size_t max_backtrace_count = arraysize(ctx.backtrace.frames); |
| + size_t backtrace_count = WalkStackFrames(ctx.backtrace.frames, |
| + max_backtrace_count, |
| + known_frame_count + 1); |
| + |
| + 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
|
| + ctx.backtrace.frames + backtrace_count); |
| + |
| + std::fill(ctx.backtrace.frames + backtrace_count, |
| + ctx.backtrace.frames + max_backtrace_count, |
| + nullptr); |
| + |
| + ctx.backtrace.frame_type = STACK_FRAME_TYPE_PC; |
| + |
| +#else |
| + // Return pseudo stack trace |
| + |
| { |
| auto src = pseudo_stack_.begin(); |
| auto dst = std::begin(ctx.backtrace.frames); |
| @@ -112,6 +203,10 @@ AllocationContext AllocationContextTracker::GetContextSnapshot() { |
| std::fill(dst, dst_end, nullptr); |
| } |
| + ctx.backtrace.frame_type = STACK_FRAME_TYPE_SYMBOL; |
| + |
| +#endif // USE_EXPERIMENTAL_ALLOCATION_TRACING |
| + |
| ctx.type_name = nullptr; |
| return ctx; |