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