1bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved. 2bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Use of this source code is governed by a BSD-style license that can be 3bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// found in the LICENSE file. 4bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 5bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/debug/trace_event_memory.h" 6bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 7bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/debug/leak_annotations.h" 8bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/debug/trace_event.h" 9bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/lazy_instance.h" 10bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/logging.h" 11bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/memory/scoped_ptr.h" 12bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/message_loop/message_loop.h" 13bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/strings/string_number_conversions.h" 14bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/strings/string_util.h" 15bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch#include "base/threading/thread_local_storage.h" 16bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 17bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochnamespace base { 18bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochnamespace debug { 19bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 20bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochnamespace { 21bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 22424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Maximum number of nested TRACE_EVENT scopes to record. Must be less than 23424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// or equal to HeapProfileTable::kMaxStackDepth / 2 because we record two 24424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// entries on the pseudo-stack per scope. 25424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)const size_t kMaxScopeDepth = 16; 26bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 27bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch///////////////////////////////////////////////////////////////////////////// 28bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Holds a memory dump until the tracing system needs to serialize it. 29bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochclass MemoryDumpHolder : public base::debug::ConvertableToTraceFormat { 30bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch public: 31bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Takes ownership of dump, which must be a JSON string, allocated with 32bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // malloc() and NULL terminated. 33bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch explicit MemoryDumpHolder(char* dump) : dump_(dump) {} 34bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 35bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // base::debug::ConvertableToTraceFormat overrides: 36bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE { 37bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch AppendHeapProfileAsTraceFormat(dump_, out); 38bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 39bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 40bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch private: 414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) virtual ~MemoryDumpHolder() { free(dump_); } 424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 43bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch char* dump_; 44bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 45bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder); 46bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch}; 47bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 48bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch///////////////////////////////////////////////////////////////////////////// 49bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Records a stack of TRACE_MEMORY events. One per thread is required. 50bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochstruct TraceMemoryStack { 51424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) TraceMemoryStack() : scope_depth(0) { 52424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) memset(scope_data, 0, kMaxScopeDepth * sizeof(scope_data[0])); 53bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 54bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 55424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Depth of the currently nested TRACE_EVENT scopes. Allowed to be greater 56424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // than kMaxScopeDepth so we can match scope pushes and pops even if we don't 57424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // have enough space to store the EventData. 58424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) size_t scope_depth; 59424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 60424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Stack of categories and names. 61424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ScopedTraceMemory::ScopeData scope_data[kMaxScopeDepth]; 62bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch}; 63bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 64bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Pointer to a TraceMemoryStack per thread. 65bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbase::ThreadLocalStorage::StaticSlot tls_trace_memory_stack = TLS_INITIALIZER; 66bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 67bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Clean up memory pointed to by our thread-local storage. 68bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid DeleteStackOnThreadCleanup(void* value) { 69bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = static_cast<TraceMemoryStack*>(value); 70bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch delete stack; 71bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 72bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 73bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Initializes the thread-local TraceMemoryStack pointer. Returns true on 74bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// success or if it is already initialized. 75bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbool InitThreadLocalStorage() { 76bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (tls_trace_memory_stack.initialized()) 77bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return true; 78bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Initialize the thread-local storage key, returning true on success. 79bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return tls_trace_memory_stack.Initialize(&DeleteStackOnThreadCleanup); 80bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 81bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 82bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Clean up thread-local-storage in the main thread. 83bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid CleanupThreadLocalStorage() { 84bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!tls_trace_memory_stack.initialized()) 85bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 86bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = 87bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get()); 88bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch delete stack; 89bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch tls_trace_memory_stack.Set(NULL); 90bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Intentionally do not release the thread-local-storage key here, that is, 91bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // do not call tls_trace_memory_stack.Free(). Other threads have lazily 92bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // created pointers in thread-local-storage via GetTraceMemoryStack() below. 93bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Those threads need to run the DeleteStack() destructor function when they 94bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // exit. If we release the key the destructor will not be called and those 95bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // threads will not clean up their memory. 96bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 97bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 98bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// Returns the thread-local trace memory stack for the current thread, creating 99bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// one if needed. Returns NULL if the thread-local storage key isn't 100bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// initialized, which indicates that heap profiling isn't running. 101bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben MurdochTraceMemoryStack* GetTraceMemoryStack() { 102bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = 103bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get()); 104bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Lazily initialize TraceMemoryStack objects for new threads. 105bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!stack) { 106bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch stack = new TraceMemoryStack; 107bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch tls_trace_memory_stack.Set(stack); 108bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 109bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return stack; 110bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 111bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 112424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Returns a "pseudo-stack" of pointers to trace event categories and names. 113424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Because tcmalloc stores one pointer per stack frame this converts N nested 114424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// trace events into N * 2 pseudo-stack entries. Thus this macro invocation: 115424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// TRACE_EVENT0("category1", "name1"); 116424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// TRACE_EVENT0("category2", "name2"); 117424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// becomes this pseudo-stack: 118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// stack_out[0] = "category1" 119424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// stack_out[1] = "name1" 120424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// stack_out[2] = "category2" 121424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// stack_out[3] = "name2" 122424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// Returns int instead of size_t to match the signature required by tcmalloc. 123bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochint GetPseudoStack(int skip_count_ignored, void** stack_out) { 124bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // If the tracing system isn't fully initialized, just skip this allocation. 125bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Attempting to initialize will allocate memory, causing this function to 126bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // be called recursively from inside the allocator. 127bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!tls_trace_memory_stack.initialized() || !tls_trace_memory_stack.Get()) 128bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return 0; 129bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = 130bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get()); 131424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Copy at most kMaxScopeDepth scope entries. 132424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const size_t count = std::min(stack->scope_depth, kMaxScopeDepth); 133bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Notes that memcpy() works for zero bytes. 134bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch memcpy(stack_out, 135424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) stack->scope_data, 136424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) count * sizeof(stack->scope_data[0])); 137424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Each item in the trace event stack contains both name and category so tell 138424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // tcmalloc that we have returned |count| * 2 stack frames. 139424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return static_cast<int>(count * 2); 140bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 141bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 142bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} // namespace 143bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 144bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch////////////////////////////////////////////////////////////////////////////// 145bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 146bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben MurdochTraceMemoryController::TraceMemoryController( 147bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch scoped_refptr<MessageLoopProxy> message_loop_proxy, 148bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch HeapProfilerStartFunction heap_profiler_start_function, 149bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch HeapProfilerStopFunction heap_profiler_stop_function, 150bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch GetHeapProfileFunction get_heap_profile_function) 151bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch : message_loop_proxy_(message_loop_proxy), 152bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch heap_profiler_start_function_(heap_profiler_start_function), 153bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch heap_profiler_stop_function_(heap_profiler_stop_function), 154bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch get_heap_profile_function_(get_heap_profile_function), 155bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch weak_factory_(this) { 156bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Force the "memory" category to show up in the trace viewer. 157bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("memory"), "init"); 158bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Watch for the tracing system being enabled. 159bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceLog::GetInstance()->AddEnabledStateObserver(this); 160bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 161bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 162bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben MurdochTraceMemoryController::~TraceMemoryController() { 163bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (dump_timer_.IsRunning()) 164bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch StopProfiling(); 165bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceLog::GetInstance()->RemoveEnabledStateObserver(this); 166bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 167bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 168bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // base::debug::TraceLog::EnabledStateChangedObserver overrides: 169bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid TraceMemoryController::OnTraceLogEnabled() { 170bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Check to see if tracing is enabled for the memory category. 171bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch bool enabled; 172bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("memory"), 173bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch &enabled); 174bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!enabled) 175bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 176bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DVLOG(1) << "OnTraceLogEnabled"; 177bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch message_loop_proxy_->PostTask( 178bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch FROM_HERE, 179bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch base::Bind(&TraceMemoryController::StartProfiling, 180bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch weak_factory_.GetWeakPtr())); 181bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 182bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 183bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid TraceMemoryController::OnTraceLogDisabled() { 184bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // The memory category is always disabled before OnTraceLogDisabled() is 185bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // called, so we cannot tell if it was enabled before. Always try to turn 186bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // off profiling. 187bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DVLOG(1) << "OnTraceLogDisabled"; 188bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch message_loop_proxy_->PostTask( 189bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch FROM_HERE, 190bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch base::Bind(&TraceMemoryController::StopProfiling, 191bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch weak_factory_.GetWeakPtr())); 192bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 193bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 194bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid TraceMemoryController::StartProfiling() { 195bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Watch for the tracing framework sending enabling more than once. 196bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (dump_timer_.IsRunning()) 197bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 198bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DVLOG(1) << "Starting trace memory"; 199bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!InitThreadLocalStorage()) 200bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 201bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch ScopedTraceMemory::set_enabled(true); 202bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Call ::HeapProfilerWithPseudoStackStart(). 203bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch heap_profiler_start_function_(&GetPseudoStack); 204bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch const int kDumpIntervalSeconds = 5; 205bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch dump_timer_.Start(FROM_HERE, 206bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TimeDelta::FromSeconds(kDumpIntervalSeconds), 207bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch base::Bind(&TraceMemoryController::DumpMemoryProfile, 208bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch weak_factory_.GetWeakPtr())); 209bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 210bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 211bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid TraceMemoryController::DumpMemoryProfile() { 212bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Don't trace allocations here in the memory tracing system. 213bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch INTERNAL_TRACE_MEMORY(TRACE_DISABLED_BY_DEFAULT("memory"), 214bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TRACE_MEMORY_IGNORE); 215bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 216bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DVLOG(1) << "DumpMemoryProfile"; 217bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // MemoryDumpHolder takes ownership of this string. See GetHeapProfile() in 218bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // tcmalloc for details. 219bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch char* dump = get_heap_profile_function_(); 220bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch const int kSnapshotId = 1; 221bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( 222bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TRACE_DISABLED_BY_DEFAULT("memory"), 223bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch "memory::Heap", 224bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch kSnapshotId, 2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) scoped_refptr<ConvertableToTraceFormat>(new MemoryDumpHolder(dump))); 226bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 227bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 228bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid TraceMemoryController::StopProfiling() { 229bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Watch for the tracing framework sending disabled more than once. 230bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (!dump_timer_.IsRunning()) 231bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 232bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DVLOG(1) << "Stopping trace memory"; 233bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch dump_timer_.Stop(); 234bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch ScopedTraceMemory::set_enabled(false); 235bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch CleanupThreadLocalStorage(); 236bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Call ::HeapProfilerStop(). 237bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch heap_profiler_stop_function_(); 238bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 239bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 240bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbool TraceMemoryController::IsTimerRunningForTest() const { 241bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return dump_timer_.IsRunning(); 242bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 243bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 244bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch///////////////////////////////////////////////////////////////////////////// 245bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 246bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static 247bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbool ScopedTraceMemory::enabled_ = false; 248bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 2491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void ScopedTraceMemory::Initialize(const char* category, const char* name) { 2501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) DCHECK(enabled_); 251bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Get our thread's copy of the stack. 252bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack(); 253424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const size_t index = trace_memory_stack->scope_depth; 254424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Don't record data for deeply nested scopes, but continue to increment 255424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // |stack_depth| so we can match pushes and pops. 256424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (index < kMaxScopeDepth) { 257424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ScopeData& event = trace_memory_stack->scope_data[index]; 258424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) event.category = category; 259424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) event.name = name; 260424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) } 261424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) trace_memory_stack->scope_depth++; 262bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 263bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 2641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void ScopedTraceMemory::Destroy() { 2651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) DCHECK(enabled_); 266bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Get our thread's copy of the stack. 267bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack(); 268bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // The tracing system can be turned on with ScopedTraceMemory objects 269bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // allocated on the stack, so avoid potential underflow as they are destroyed. 270424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (trace_memory_stack->scope_depth > 0) 271424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) trace_memory_stack->scope_depth--; 272bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 273bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 274bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static 275bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid ScopedTraceMemory::InitForTest() { 276bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch InitThreadLocalStorage(); 277bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch enabled_ = true; 278bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 279bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 280bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static 281bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid ScopedTraceMemory::CleanupForTest() { 282bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch enabled_ = false; 283bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch CleanupThreadLocalStorage(); 284bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 285bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 286bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static 287424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)int ScopedTraceMemory::GetStackDepthForTest() { 288bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = GetTraceMemoryStack(); 289424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return static_cast<int>(stack->scope_depth); 290bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 291bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 292bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch// static 293424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)ScopedTraceMemory::ScopeData ScopedTraceMemory::GetScopeDataForTest( 294424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) int stack_index) { 295bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch TraceMemoryStack* stack = GetTraceMemoryStack(); 296424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return stack->scope_data[stack_index]; 297bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 298bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 299bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch///////////////////////////////////////////////////////////////////////////// 300bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 301bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid AppendHeapProfileAsTraceFormat(const char* input, std::string* output) { 302bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Heap profile output has a header total line, then a list of stacks with 303bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // memory totals, like this: 304bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 305bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile 306bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 95: 40940 [ 649: 114260] @ 0x7fa7f4b3be13 307bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 77: 32546 [ 742: 106234] @ 308bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 68: 4195 [ 1087: 98009] @ 0x7fa7fa9b9ba0 0x7fa7f4b3be13 309bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 310bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // MAPPED_LIBRARIES: 311bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 1be411fc1000-1be4139e4000 rw-p 00000000 00:00 0 312bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 1be4139e4000-1be4139e5000 ---p 00000000 00:00 0 313bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // ... 314bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 315bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Skip input after MAPPED_LIBRARIES. 316bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::string input_string; 317bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch const char* mapped_libraries = strstr(input, "MAPPED_LIBRARIES"); 318bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (mapped_libraries) { 319bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch input_string.assign(input, mapped_libraries - input); 320bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } else { 321bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch input_string.assign(input); 322bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 323bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 324bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::vector<std::string> lines; 325bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch size_t line_count = Tokenize(input_string, "\n", &lines); 326bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (line_count == 0) { 327bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DLOG(WARNING) << "No lines found"; 328bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 329bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 330bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 331bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Handle the initial summary line. 332bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append("["); 333bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch AppendHeapProfileTotalsAsTraceFormat(lines[0], output); 334bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 335bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Handle the following stack trace lines. 336bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch for (size_t i = 1; i < line_count; ++i) { 337bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch const std::string& line = lines[i]; 338bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch AppendHeapProfileLineAsTraceFormat(line, output); 339bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 340bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append("]\n"); 341bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 342bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 343bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochvoid AppendHeapProfileTotalsAsTraceFormat(const std::string& line, 344bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::string* output) { 345bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // This is what a line looks like: 346bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile 347bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 348bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // The numbers represent total allocations since profiling was enabled. 349bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // From the example above: 350bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 357 = Outstanding allocations (mallocs - frees) 351bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 55227 = Outstanding bytes (malloc bytes - free bytes) 352bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 14653 = Total allocations (mallocs) 353bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 2624014 = Total bytes (malloc bytes) 354bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::vector<std::string> tokens; 355bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch Tokenize(line, " :[]@", &tokens); 356bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (tokens.size() < 4) { 357bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DLOG(WARNING) << "Invalid totals line " << line; 358bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return; 359bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 360bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DCHECK_EQ(tokens[0], "heap"); 361bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DCHECK_EQ(tokens[1], "profile"); 362bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append("{\"current_allocs\": "); 363bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(tokens[2]); 364bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(", \"current_bytes\": "); 365bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(tokens[3]); 366bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(", \"trace\": \"\"}"); 367bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 368bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 369bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdochbool AppendHeapProfileLineAsTraceFormat(const std::string& line, 370bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::string* output) { 371bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // This is what a line looks like: 372bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 68: 4195 [ 1087: 98009] @ 0x7fa7fa9b9ba0 0x7fa7f4b3be13 373bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 374bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // The numbers represent allocations for a particular stack trace since 375bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // profiling was enabled. From the example above: 376bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 68 = Outstanding allocations (mallocs - frees) 377bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 4195 = Outstanding bytes (malloc bytes - free bytes) 378bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 1087 = Total allocations (mallocs) 379bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 98009 = Total bytes (malloc bytes) 380bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 381bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // 0x7fa7fa9b9ba0 0x7fa7f4b3be13 = Stack trace represented as pointers to 382424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // static strings from trace event categories 383424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // and names. 384bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch std::vector<std::string> tokens; 385bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch Tokenize(line, " :[]@", &tokens); 386bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // It's valid to have no stack addresses, so only require 4 tokens. 387bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (tokens.size() < 4) { 388bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch DLOG(WARNING) << "Invalid line " << line; 389bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return false; 390bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 391bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Don't bother with stacks that have no current allocations. 392bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch if (tokens[0] == "0") 393bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return false; 394bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(",\n"); 395bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append("{\"current_allocs\": "); 396bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(tokens[0]); 397bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(", \"current_bytes\": "); 398bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(tokens[1]); 399bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(", \"trace\": \""); 400bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 401424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Convert pairs of "stack addresses" into category and name strings. 402bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch const std::string kSingleQuote = "'"; 403424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) for (size_t t = 4; t < tokens.size(); t += 2) { 404424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Casting strings into pointers is ugly but otherwise tcmalloc would need 405424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // to gain a special output serializer just for pseudo-stacks. 406424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const char* trace_category = StringFromHexAddress(tokens[t]); 407424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) DCHECK_LT(t + 1, tokens.size()); 408424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const char* trace_name = StringFromHexAddress(tokens[t + 1]); 409424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 410424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // TODO(jamescook): Report the trace category and name separately to the 411424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // trace viewer and allow it to decide what decorations to apply. For now 4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // just hard-code a decoration for posted tasks (toplevel). 413424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) std::string trace_string(trace_name); 4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!strcmp(trace_category, "toplevel")) 415424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) trace_string.append("->PostTask"); 416bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 417bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Some trace name strings have double quotes, convert them to single. 418424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) ReplaceChars(trace_string, "\"", kSingleQuote, &trace_string); 419bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 420424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) output->append(trace_string); 421bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 422bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch // Trace viewer expects a trailing space. 423bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append(" "); 424bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch } 425bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch output->append("\"}"); 426bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch return true; 427bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} 428bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch 429424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)const char* StringFromHexAddress(const std::string& hex_address) { 430424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) uint64 address = 0; 431424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!base::HexStringToUInt64(hex_address, &address)) 432424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return "error"; 433424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (!address) 434424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return "null"; 435424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) // Note that this cast handles 64-bit to 32-bit conversion if necessary. 436424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) return reinterpret_cast<const char*>(address); 437424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 438424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 439bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} // namespace debug 440bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch} // namespace base 441