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; |