This source file includes following definitions.
- AppendAsTraceFormat
- DeleteStackOnThreadCleanup
- InitThreadLocalStorage
- CleanupThreadLocalStorage
- GetTraceMemoryStack
- GetPseudoStack
- weak_factory_
- OnTraceLogEnabled
- OnTraceLogDisabled
- StartProfiling
- DumpMemoryProfile
- StopProfiling
- IsTimerRunningForTest
- Initialize
- Destroy
- InitForTest
- CleanupForTest
- GetStackDepthForTest
- GetScopeDataForTest
- AppendHeapProfileAsTraceFormat
- AppendHeapProfileTotalsAsTraceFormat
- AppendHeapProfileLineAsTraceFormat
- StringFromHexAddress
#include "base/debug/trace_event_memory.h"
#include "base/debug/leak_annotations.h"
#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_local_storage.h"
namespace base {
namespace debug {
namespace {
const size_t kMaxScopeDepth = 16;
class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat {
public:
explicit MemoryDumpHolder(char* dump) : dump_(dump) {}
virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
AppendHeapProfileAsTraceFormat(dump_, out);
}
private:
virtual ~MemoryDumpHolder() { free(dump_); }
char* dump_;
DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder);
};
struct TraceMemoryStack {
TraceMemoryStack() : scope_depth(0) {
memset(scope_data, 0, kMaxScopeDepth * sizeof(scope_data[0]));
}
size_t scope_depth;
ScopedTraceMemory::ScopeData scope_data[kMaxScopeDepth];
};
base::ThreadLocalStorage::StaticSlot tls_trace_memory_stack = TLS_INITIALIZER;
void DeleteStackOnThreadCleanup(void* value) {
TraceMemoryStack* stack = static_cast<TraceMemoryStack*>(value);
delete stack;
}
bool InitThreadLocalStorage() {
if (tls_trace_memory_stack.initialized())
return true;
return tls_trace_memory_stack.Initialize(&DeleteStackOnThreadCleanup);
}
void CleanupThreadLocalStorage() {
if (!tls_trace_memory_stack.initialized())
return;
TraceMemoryStack* stack =
static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
delete stack;
tls_trace_memory_stack.Set(NULL);
}
TraceMemoryStack* GetTraceMemoryStack() {
TraceMemoryStack* stack =
static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
if (!stack) {
stack = new TraceMemoryStack;
tls_trace_memory_stack.Set(stack);
}
return stack;
}
int GetPseudoStack(int skip_count_ignored, void** stack_out) {
if (!tls_trace_memory_stack.initialized() || !tls_trace_memory_stack.Get())
return 0;
TraceMemoryStack* stack =
static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
const size_t count = std::min(stack->scope_depth, kMaxScopeDepth);
memcpy(stack_out,
stack->scope_data,
count * sizeof(stack->scope_data[0]));
return static_cast<int>(count * 2);
}
}
TraceMemoryController::TraceMemoryController(
scoped_refptr<MessageLoopProxy> message_loop_proxy,
HeapProfilerStartFunction heap_profiler_start_function,
HeapProfilerStopFunction heap_profiler_stop_function,
GetHeapProfileFunction get_heap_profile_function)
: message_loop_proxy_(message_loop_proxy),
heap_profiler_start_function_(heap_profiler_start_function),
heap_profiler_stop_function_(heap_profiler_stop_function),
get_heap_profile_function_(get_heap_profile_function),
weak_factory_(this) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("memory"), "init");
TraceLog::GetInstance()->AddEnabledStateObserver(this);
}
TraceMemoryController::~TraceMemoryController() {
if (dump_timer_.IsRunning())
StopProfiling();
TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
}
void TraceMemoryController::OnTraceLogEnabled() {
bool enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("memory"),
&enabled);
if (!enabled)
return;
DVLOG(1) << "OnTraceLogEnabled";
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&TraceMemoryController::StartProfiling,
weak_factory_.GetWeakPtr()));
}
void TraceMemoryController::OnTraceLogDisabled() {
DVLOG(1) << "OnTraceLogDisabled";
message_loop_proxy_->PostTask(
FROM_HERE,
base::Bind(&TraceMemoryController::StopProfiling,
weak_factory_.GetWeakPtr()));
}
void TraceMemoryController::StartProfiling() {
if (dump_timer_.IsRunning())
return;
DVLOG(1) << "Starting trace memory";
if (!InitThreadLocalStorage())
return;
ScopedTraceMemory::set_enabled(true);
heap_profiler_start_function_(&GetPseudoStack);
const int kDumpIntervalSeconds = 5;
dump_timer_.Start(FROM_HERE,
TimeDelta::FromSeconds(kDumpIntervalSeconds),
base::Bind(&TraceMemoryController::DumpMemoryProfile,
weak_factory_.GetWeakPtr()));
}
void TraceMemoryController::DumpMemoryProfile() {
INTERNAL_TRACE_MEMORY(TRACE_DISABLED_BY_DEFAULT("memory"),
TRACE_MEMORY_IGNORE);
DVLOG(1) << "DumpMemoryProfile";
char* dump = get_heap_profile_function_();
const int kSnapshotId = 1;
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("memory"),
"memory::Heap",
kSnapshotId,
scoped_refptr<ConvertableToTraceFormat>(new MemoryDumpHolder(dump)));
}
void TraceMemoryController::StopProfiling() {
if (!dump_timer_.IsRunning())
return;
DVLOG(1) << "Stopping trace memory";
dump_timer_.Stop();
ScopedTraceMemory::set_enabled(false);
CleanupThreadLocalStorage();
heap_profiler_stop_function_();
}
bool TraceMemoryController::IsTimerRunningForTest() const {
return dump_timer_.IsRunning();
}
bool ScopedTraceMemory::enabled_ = false;
void ScopedTraceMemory::Initialize(const char* category, const char* name) {
DCHECK(enabled_);
TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
const size_t index = trace_memory_stack->scope_depth;
if (index < kMaxScopeDepth) {
ScopeData& event = trace_memory_stack->scope_data[index];
event.category = category;
event.name = name;
}
trace_memory_stack->scope_depth++;
}
void ScopedTraceMemory::Destroy() {
DCHECK(enabled_);
TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
if (trace_memory_stack->scope_depth > 0)
trace_memory_stack->scope_depth--;
}
void ScopedTraceMemory::InitForTest() {
InitThreadLocalStorage();
enabled_ = true;
}
void ScopedTraceMemory::CleanupForTest() {
enabled_ = false;
CleanupThreadLocalStorage();
}
int ScopedTraceMemory::GetStackDepthForTest() {
TraceMemoryStack* stack = GetTraceMemoryStack();
return static_cast<int>(stack->scope_depth);
}
ScopedTraceMemory::ScopeData ScopedTraceMemory::GetScopeDataForTest(
int stack_index) {
TraceMemoryStack* stack = GetTraceMemoryStack();
return stack->scope_data[stack_index];
}
void AppendHeapProfileAsTraceFormat(const char* input, std::string* output) {
std::string input_string;
const char* mapped_libraries = strstr(input, "MAPPED_LIBRARIES");
if (mapped_libraries) {
input_string.assign(input, mapped_libraries - input);
} else {
input_string.assign(input);
}
std::vector<std::string> lines;
size_t line_count = Tokenize(input_string, "\n", &lines);
if (line_count == 0) {
DLOG(WARNING) << "No lines found";
return;
}
output->append("[");
AppendHeapProfileTotalsAsTraceFormat(lines[0], output);
for (size_t i = 1; i < line_count; ++i) {
const std::string& line = lines[i];
AppendHeapProfileLineAsTraceFormat(line, output);
}
output->append("]\n");
}
void AppendHeapProfileTotalsAsTraceFormat(const std::string& line,
std::string* output) {
std::vector<std::string> tokens;
Tokenize(line, " :[]@", &tokens);
if (tokens.size() < 4) {
DLOG(WARNING) << "Invalid totals line " << line;
return;
}
DCHECK_EQ(tokens[0], "heap");
DCHECK_EQ(tokens[1], "profile");
output->append("{\"current_allocs\": ");
output->append(tokens[2]);
output->append(", \"current_bytes\": ");
output->append(tokens[3]);
output->append(", \"trace\": \"\"}");
}
bool AppendHeapProfileLineAsTraceFormat(const std::string& line,
std::string* output) {
std::vector<std::string> tokens;
Tokenize(line, " :[]@", &tokens);
if (tokens.size() < 4) {
DLOG(WARNING) << "Invalid line " << line;
return false;
}
if (tokens[0] == "0")
return false;
output->append(",\n");
output->append("{\"current_allocs\": ");
output->append(tokens[0]);
output->append(", \"current_bytes\": ");
output->append(tokens[1]);
output->append(", \"trace\": \"");
const std::string kSingleQuote = "'";
for (size_t t = 4; t < tokens.size(); t += 2) {
const char* trace_category = StringFromHexAddress(tokens[t]);
DCHECK_LT(t + 1, tokens.size());
const char* trace_name = StringFromHexAddress(tokens[t + 1]);
std::string trace_string(trace_name);
if (!strcmp(trace_category, "toplevel"))
trace_string.append("->PostTask");
ReplaceChars(trace_string, "\"", kSingleQuote, &trace_string);
output->append(trace_string);
output->append(" ");
}
output->append("\"}");
return true;
}
const char* StringFromHexAddress(const std::string& hex_address) {
uint64 address = 0;
if (!base::HexStringToUInt64(hex_address, &address))
return "error";
if (!address)
return "null";
return reinterpret_cast<const char*>(address);
}
}
}