summaryrefslogtreecommitdiff
path: root/base/trace_event/heap_profiler_allocation_context_tracker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/trace_event/heap_profiler_allocation_context_tracker.cc')
-rw-r--r--base/trace_event/heap_profiler_allocation_context_tracker.cc115
1 files changed, 115 insertions, 0 deletions
diff --git a/base/trace_event/heap_profiler_allocation_context_tracker.cc b/base/trace_event/heap_profiler_allocation_context_tracker.cc
new file mode 100644
index 0000000000..791ab7a6fe
--- /dev/null
+++ b/base/trace_event/heap_profiler_allocation_context_tracker.cc
@@ -0,0 +1,115 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/atomicops.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/trace_event/heap_profiler_allocation_context.h"
+
+namespace base {
+namespace trace_event {
+
+subtle::Atomic32 AllocationContextTracker::capture_enabled_ = 0;
+
+namespace {
+
+ThreadLocalStorage::StaticSlot g_tls_alloc_ctx_tracker = TLS_INITIALIZER;
+
+// This function is added to the TLS slot to clean up the instance when the
+// thread exits.
+void DestructAllocationContextTracker(void* alloc_ctx_tracker) {
+ delete static_cast<AllocationContextTracker*>(alloc_ctx_tracker);
+}
+
+} // namespace
+
+AllocationContextTracker::AllocationContextTracker() {}
+AllocationContextTracker::~AllocationContextTracker() {}
+
+// static
+AllocationContextTracker* AllocationContextTracker::GetThreadLocalTracker() {
+ auto tracker =
+ static_cast<AllocationContextTracker*>(g_tls_alloc_ctx_tracker.Get());
+
+ if (!tracker) {
+ tracker = new AllocationContextTracker();
+ g_tls_alloc_ctx_tracker.Set(tracker);
+ }
+
+ return tracker;
+}
+
+// static
+void AllocationContextTracker::SetCaptureEnabled(bool enabled) {
+ // When enabling capturing, also initialize the TLS slot. This does not create
+ // a TLS instance yet.
+ if (enabled && !g_tls_alloc_ctx_tracker.initialized())
+ g_tls_alloc_ctx_tracker.Initialize(DestructAllocationContextTracker);
+
+ // 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);
+}
+
+// static
+void AllocationContextTracker::PushPseudoStackFrame(StackFrame frame) {
+ auto tracker = AllocationContextTracker::GetThreadLocalTracker();
+
+ // Impose a limit on the height to verify that every push is popped, because
+ // in practice the pseudo stack never grows higher than ~20 frames.
+ DCHECK_LT(tracker->pseudo_stack_.size(), 128u);
+ tracker->pseudo_stack_.push_back(frame);
+}
+
+// static
+void AllocationContextTracker::PopPseudoStackFrame(StackFrame frame) {
+ auto tracker = AllocationContextTracker::GetThreadLocalTracker();
+
+ // 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.
+ if (tracker->pseudo_stack_.empty())
+ return;
+
+ // 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, tracker->pseudo_stack_.back())
+ << "Encountered an unmatched TRACE_EVENT_END";
+
+ tracker->pseudo_stack_.pop_back();
+}
+
+// static
+AllocationContext AllocationContextTracker::GetContextSnapshot() {
+ AllocationContextTracker* tracker = GetThreadLocalTracker();
+ AllocationContext ctx;
+
+ // Fill the backtrace.
+ {
+ auto src = tracker->pseudo_stack_.begin();
+ auto dst = std::begin(ctx.backtrace.frames);
+ auto src_end = tracker->pseudo_stack_.end();
+ auto dst_end = std::end(ctx.backtrace.frames);
+
+ // 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, nullptr);
+ }
+
+ ctx.type_name = nullptr;
+
+ return ctx;
+}
+
+} // namespace trace_event
+} // namespace base