forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Tracing] Clean up heap profiler files
Over time, base/trace_event/memory_profiler_allocation_context.h had become a god header that contained nearly all heap profiler classes. This CL splits the file (along with its tests) into multiple smaller files. Furthermore, there are small comment and readability fixes. No behavior is changed by this CL. * Change prefix of heap profiler files from |memory_profiler_*| to |heap_profiler_*|. * Move |AllocationContextTracker| and its tests into its own file. * Move |StackFrameDeduplicator| and its tests into its own file. * Remove |AllocationStack|, which was just a very thin wrapper around |std::vector|. Inline its logic into |AllocationContextTracker| instead. * Rename the |SortDescending| function in the heap dump writer source file to |SortBySizeDescending|. * Fix variable names in comments in |AllocationRegister|. * Fix and clarify comments in |AllocationContextTracker| and |StackFrameDeduplicator|. * Include what you use fixes. BUG=524631 Review URL: https://codereview.chromium.org/1431103004 Cr-Commit-Position: refs/heads/master@{#359813}
- Loading branch information
Showing
23 changed files
with
712 additions
and
631 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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.h" | ||
|
||
#include <cstring> | ||
|
||
#include "base/hash.h" | ||
|
||
namespace base { | ||
namespace trace_event { | ||
|
||
bool operator==(const Backtrace& lhs, const Backtrace& rhs) { | ||
// Pointer equality of the stack frames is assumed, so instead of doing a deep | ||
// string comparison on all of the frames, a |memcmp| suffices. | ||
return std::memcmp(lhs.frames, rhs.frames, sizeof(lhs.frames)) == 0; | ||
} | ||
|
||
bool operator==(const AllocationContext& lhs, const AllocationContext& rhs) { | ||
return (lhs.backtrace == rhs.backtrace) && (lhs.type_id == rhs.type_id); | ||
} | ||
|
||
} // namespace trace_event | ||
} // namespace base | ||
|
||
namespace BASE_HASH_NAMESPACE { | ||
using base::trace_event::AllocationContext; | ||
using base::trace_event::Backtrace; | ||
|
||
size_t hash<Backtrace>::operator()(const Backtrace& backtrace) const { | ||
return base::SuperFastHash(reinterpret_cast<const char*>(backtrace.frames), | ||
sizeof(backtrace.frames)); | ||
} | ||
|
||
size_t hash<AllocationContext>::operator()(const AllocationContext& ctx) const { | ||
size_t ctx_hash = hash<Backtrace>()(ctx.backtrace); | ||
|
||
// Multiply one side to break the commutativity of +. Multiplication with a | ||
// number coprime to |numeric_limits<size_t>::max() + 1| is bijective so | ||
// randomness is preserved. The type ID is assumed to be distributed randomly | ||
// already so there is no need to hash it. | ||
return (ctx_hash * 3) + static_cast<size_t>(ctx.type_id); | ||
} | ||
|
||
} // BASE_HASH_NAMESPACE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// 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. | ||
|
||
#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ | ||
#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ | ||
|
||
#include <stdint.h> | ||
|
||
#include "base/base_export.h" | ||
#include "base/containers/hash_tables.h" | ||
|
||
namespace base { | ||
namespace trace_event { | ||
|
||
// When heap profiling is enabled, tracing keeps track of the allocation | ||
// context for each allocation intercepted. It is generated by the | ||
// |AllocationContextTracker| which keeps stacks of context in TLS. | ||
// The tracker is initialized lazily. | ||
|
||
// The backtrace in the allocation context is a snapshot of the stack. For now, | ||
// this is the pseudo stack where frames are created by trace event macros. In | ||
// the future, we might add the option to use the native call stack. In that | ||
// case, |Backtrace| and |AllocationContextTracker::GetContextSnapshot| might | ||
// have different implementations that can be selected by a compile time flag. | ||
|
||
// The number of stack frames stored in the backtrace is a trade off between | ||
// memory used for tracing and accuracy. Measurements done on a prototype | ||
// revealed that: | ||
// | ||
// - In 60 percent of the cases, stack depth <= 7. | ||
// - In 87 percent of the cases, stack depth <= 9. | ||
// - In 95 percent of the cases, stack depth <= 11. | ||
// | ||
// See the design doc (https://goo.gl/4s7v7b) for more details. | ||
|
||
using StackFrame = const char*; | ||
|
||
struct BASE_EXPORT Backtrace { | ||
// Unused backtrace frames are filled with nullptr frames. If the stack is | ||
// higher than what can be stored here, the bottom frames are stored. Based | ||
// on the data above, a depth of 12 captures the full stack in the vast | ||
// majority of the cases. | ||
StackFrame frames[12]; | ||
}; | ||
|
||
bool BASE_EXPORT operator==(const Backtrace& lhs, const Backtrace& rhs); | ||
|
||
// The |AllocationContext| is context metadata that is kept for every allocation | ||
// when heap profiling is enabled. To simplify memory management for book- | ||
// keeping, this struct has a fixed size. All |const char*|s here must have | ||
// static lifetime. | ||
// TODO(ruuda): Make the default constructor private to avoid accidentally | ||
// constructing an instance and forgetting to initialize it. Only | ||
// |AllocationContextTracker| should be able to construct. (And tests.) | ||
struct BASE_EXPORT AllocationContext { | ||
// A type ID is a number that is unique for every C++ type. A type ID is | ||
// stored instead of the type name to avoid inflating the binary with type | ||
// name strings. There is an out of band lookup table mapping IDs to the type | ||
// names. A value of 0 means that the type is not known. | ||
using TypeId = uint16_t; | ||
|
||
Backtrace backtrace; | ||
TypeId type_id; | ||
}; | ||
|
||
bool BASE_EXPORT operator==(const AllocationContext& lhs, | ||
const AllocationContext& rhs); | ||
|
||
} // namespace trace_event | ||
} // namespace base | ||
|
||
namespace BASE_HASH_NAMESPACE { | ||
|
||
template <> | ||
struct hash<base::trace_event::Backtrace> { | ||
size_t operator()(const base::trace_event::Backtrace& backtrace) const; | ||
}; | ||
|
||
template <> | ||
struct hash<base::trace_event::AllocationContext> { | ||
size_t operator()(const base::trace_event::AllocationContext& context) const; | ||
}; | ||
|
||
} // BASE_HASH_NAMESPACE | ||
|
||
#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_H_ |
121 changes: 121 additions & 0 deletions
121
base/trace_event/heap_profiler_allocation_context_tracker.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// 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 "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); | ||
} | ||
|
||
// Returns a pointer past the end of the fixed-size array |array| of |T| of | ||
// length |N|, identical to C++11 |std::end|. | ||
template <typename T, int N> | ||
T* End(T(&array)[N]) { | ||
return array + N; | ||
} | ||
|
||
} // 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 = ctx.backtrace.frames; | ||
auto src_end = tracker->pseudo_stack_.end(); | ||
auto dst_end = 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_id = 0; | ||
|
||
return ctx; | ||
} | ||
|
||
} // namespace trace_event | ||
} // namespace base |
73 changes: 73 additions & 0 deletions
73
base/trace_event/heap_profiler_allocation_context_tracker.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// 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. | ||
|
||
#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ | ||
#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ | ||
|
||
#include <vector> | ||
|
||
#include "base/atomicops.h" | ||
#include "base/base_export.h" | ||
#include "base/logging.h" | ||
#include "base/macros.h" | ||
#include "base/trace_event/heap_profiler_allocation_context.h" | ||
|
||
namespace base { | ||
namespace trace_event { | ||
|
||
// The allocation context tracker keeps track of thread-local context for heap | ||
// profiling. It includes a pseudo stack of trace events. On every allocation | ||
// the tracker provides a snapshot of its context in the form of an | ||
// |AllocationContext| that is to be stored together with the allocation | ||
// details. | ||
class BASE_EXPORT AllocationContextTracker { | ||
public: | ||
// Globally enables capturing allocation context. | ||
// TODO(ruuda): Should this be replaced by |EnableCapturing| in the future? | ||
// Or at least have something that guards agains enable -> disable -> enable? | ||
static void SetCaptureEnabled(bool enabled); | ||
|
||
// Returns whether capturing allocation context is enabled globally. | ||
inline static bool capture_enabled() { | ||
// A little lag after heap profiling is enabled or disabled is fine, it is | ||
// more important that the check is as cheap as possible when capturing is | ||
// not enabled, so do not issue a memory barrier in the fast path. | ||
if (subtle::NoBarrier_Load(&capture_enabled_) == 0) | ||
return false; | ||
|
||
// In the slow path, an acquire load is required to pair with the release | ||
// store in |SetCaptureEnabled|. This is to ensure that the TLS slot for | ||
// the thread-local allocation context tracker has been initialized if | ||
// |capture_enabled| returns true. | ||
return subtle::Acquire_Load(&capture_enabled_) != 0; | ||
} | ||
|
||
// Pushes a frame onto the thread-local pseudo stack. | ||
static void PushPseudoStackFrame(StackFrame frame); | ||
|
||
// Pops a frame from the thread-local pseudo stack. | ||
static void PopPseudoStackFrame(StackFrame frame); | ||
|
||
// Returns a snapshot of the current thread-local context. | ||
static AllocationContext GetContextSnapshot(); | ||
|
||
~AllocationContextTracker(); | ||
|
||
private: | ||
AllocationContextTracker(); | ||
|
||
static AllocationContextTracker* GetThreadLocalTracker(); | ||
|
||
static subtle::Atomic32 capture_enabled_; | ||
|
||
// The pseudo stack where frames are |TRACE_EVENT| names. | ||
std::vector<StackFrame> pseudo_stack_; | ||
|
||
DISALLOW_COPY_AND_ASSIGN(AllocationContextTracker); | ||
}; | ||
|
||
} // namespace trace_event | ||
} // namespace base | ||
|
||
#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_ |
Oops, something went wrong.