Move prefs::kLastPolicyStatisticsUpdate to the policy component.
[chromium-blink-merge.git] / base / debug / trace_event_memory.cc
blob42655b310e582f29cdf0b31ded547c71936149ea
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/debug/trace_event_memory.h"
7 #include "base/debug/leak_annotations.h"
8 #include "base/debug/trace_event.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/threading/thread_local_storage.h"
17 namespace base {
18 namespace debug {
20 namespace {
22 // Maximum number of nested TRACE_EVENT scopes to record. Must be less than
23 // or equal to HeapProfileTable::kMaxStackDepth / 2 because we record two
24 // entries on the pseudo-stack per scope.
25 const size_t kMaxScopeDepth = 16;
27 /////////////////////////////////////////////////////////////////////////////
28 // Holds a memory dump until the tracing system needs to serialize it.
29 class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat {
30 public:
31 // Takes ownership of dump, which must be a JSON string, allocated with
32 // malloc() and NULL terminated.
33 explicit MemoryDumpHolder(char* dump) : dump_(dump) {}
34 virtual ~MemoryDumpHolder() { free(dump_); }
36 // base::debug::ConvertableToTraceFormat overrides:
37 virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
38 AppendHeapProfileAsTraceFormat(dump_, out);
41 private:
42 char* dump_;
44 DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder);
47 /////////////////////////////////////////////////////////////////////////////
48 // Records a stack of TRACE_MEMORY events. One per thread is required.
49 struct TraceMemoryStack {
50 TraceMemoryStack() : scope_depth(0) {
51 memset(scope_data, 0, kMaxScopeDepth * sizeof(scope_data[0]));
54 // Depth of the currently nested TRACE_EVENT scopes. Allowed to be greater
55 // than kMaxScopeDepth so we can match scope pushes and pops even if we don't
56 // have enough space to store the EventData.
57 size_t scope_depth;
59 // Stack of categories and names.
60 ScopedTraceMemory::ScopeData scope_data[kMaxScopeDepth];
63 // Pointer to a TraceMemoryStack per thread.
64 base::ThreadLocalStorage::StaticSlot tls_trace_memory_stack = TLS_INITIALIZER;
66 // Clean up memory pointed to by our thread-local storage.
67 void DeleteStackOnThreadCleanup(void* value) {
68 TraceMemoryStack* stack = static_cast<TraceMemoryStack*>(value);
69 delete stack;
72 // Initializes the thread-local TraceMemoryStack pointer. Returns true on
73 // success or if it is already initialized.
74 bool InitThreadLocalStorage() {
75 if (tls_trace_memory_stack.initialized())
76 return true;
77 // Initialize the thread-local storage key, returning true on success.
78 return tls_trace_memory_stack.Initialize(&DeleteStackOnThreadCleanup);
81 // Clean up thread-local-storage in the main thread.
82 void CleanupThreadLocalStorage() {
83 if (!tls_trace_memory_stack.initialized())
84 return;
85 TraceMemoryStack* stack =
86 static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
87 delete stack;
88 tls_trace_memory_stack.Set(NULL);
89 // Intentionally do not release the thread-local-storage key here, that is,
90 // do not call tls_trace_memory_stack.Free(). Other threads have lazily
91 // created pointers in thread-local-storage via GetTraceMemoryStack() below.
92 // Those threads need to run the DeleteStack() destructor function when they
93 // exit. If we release the key the destructor will not be called and those
94 // threads will not clean up their memory.
97 // Returns the thread-local trace memory stack for the current thread, creating
98 // one if needed. Returns NULL if the thread-local storage key isn't
99 // initialized, which indicates that heap profiling isn't running.
100 TraceMemoryStack* GetTraceMemoryStack() {
101 TraceMemoryStack* stack =
102 static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
103 // Lazily initialize TraceMemoryStack objects for new threads.
104 if (!stack) {
105 stack = new TraceMemoryStack;
106 tls_trace_memory_stack.Set(stack);
108 return stack;
111 // Returns a "pseudo-stack" of pointers to trace event categories and names.
112 // Because tcmalloc stores one pointer per stack frame this converts N nested
113 // trace events into N * 2 pseudo-stack entries. Thus this macro invocation:
114 // TRACE_EVENT0("category1", "name1");
115 // TRACE_EVENT0("category2", "name2");
116 // becomes this pseudo-stack:
117 // stack_out[0] = "category1"
118 // stack_out[1] = "name1"
119 // stack_out[2] = "category2"
120 // stack_out[3] = "name2"
121 // Returns int instead of size_t to match the signature required by tcmalloc.
122 int GetPseudoStack(int skip_count_ignored, void** stack_out) {
123 // If the tracing system isn't fully initialized, just skip this allocation.
124 // Attempting to initialize will allocate memory, causing this function to
125 // be called recursively from inside the allocator.
126 if (!tls_trace_memory_stack.initialized() || !tls_trace_memory_stack.Get())
127 return 0;
128 TraceMemoryStack* stack =
129 static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
130 // Copy at most kMaxScopeDepth scope entries.
131 const size_t count = std::min(stack->scope_depth, kMaxScopeDepth);
132 // Notes that memcpy() works for zero bytes.
133 memcpy(stack_out,
134 stack->scope_data,
135 count * sizeof(stack->scope_data[0]));
136 // Each item in the trace event stack contains both name and category so tell
137 // tcmalloc that we have returned |count| * 2 stack frames.
138 return static_cast<int>(count * 2);
141 } // namespace
143 //////////////////////////////////////////////////////////////////////////////
145 TraceMemoryController::TraceMemoryController(
146 scoped_refptr<MessageLoopProxy> message_loop_proxy,
147 HeapProfilerStartFunction heap_profiler_start_function,
148 HeapProfilerStopFunction heap_profiler_stop_function,
149 GetHeapProfileFunction get_heap_profile_function)
150 : message_loop_proxy_(message_loop_proxy),
151 heap_profiler_start_function_(heap_profiler_start_function),
152 heap_profiler_stop_function_(heap_profiler_stop_function),
153 get_heap_profile_function_(get_heap_profile_function),
154 weak_factory_(this) {
155 // Force the "memory" category to show up in the trace viewer.
156 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("memory"), "init");
157 // Watch for the tracing system being enabled.
158 TraceLog::GetInstance()->AddEnabledStateObserver(this);
161 TraceMemoryController::~TraceMemoryController() {
162 if (dump_timer_.IsRunning())
163 StopProfiling();
164 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
167 // base::debug::TraceLog::EnabledStateChangedObserver overrides:
168 void TraceMemoryController::OnTraceLogEnabled() {
169 // Check to see if tracing is enabled for the memory category.
170 bool enabled;
171 TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("memory"),
172 &enabled);
173 if (!enabled)
174 return;
175 DVLOG(1) << "OnTraceLogEnabled";
176 message_loop_proxy_->PostTask(
177 FROM_HERE,
178 base::Bind(&TraceMemoryController::StartProfiling,
179 weak_factory_.GetWeakPtr()));
182 void TraceMemoryController::OnTraceLogDisabled() {
183 // The memory category is always disabled before OnTraceLogDisabled() is
184 // called, so we cannot tell if it was enabled before. Always try to turn
185 // off profiling.
186 DVLOG(1) << "OnTraceLogDisabled";
187 message_loop_proxy_->PostTask(
188 FROM_HERE,
189 base::Bind(&TraceMemoryController::StopProfiling,
190 weak_factory_.GetWeakPtr()));
193 void TraceMemoryController::StartProfiling() {
194 // Watch for the tracing framework sending enabling more than once.
195 if (dump_timer_.IsRunning())
196 return;
197 DVLOG(1) << "Starting trace memory";
198 if (!InitThreadLocalStorage())
199 return;
200 ScopedTraceMemory::set_enabled(true);
201 // Call ::HeapProfilerWithPseudoStackStart().
202 heap_profiler_start_function_(&GetPseudoStack);
203 const int kDumpIntervalSeconds = 5;
204 dump_timer_.Start(FROM_HERE,
205 TimeDelta::FromSeconds(kDumpIntervalSeconds),
206 base::Bind(&TraceMemoryController::DumpMemoryProfile,
207 weak_factory_.GetWeakPtr()));
210 void TraceMemoryController::DumpMemoryProfile() {
211 // Don't trace allocations here in the memory tracing system.
212 INTERNAL_TRACE_MEMORY(TRACE_DISABLED_BY_DEFAULT("memory"),
213 TRACE_MEMORY_IGNORE);
215 DVLOG(1) << "DumpMemoryProfile";
216 // MemoryDumpHolder takes ownership of this string. See GetHeapProfile() in
217 // tcmalloc for details.
218 char* dump = get_heap_profile_function_();
219 scoped_ptr<MemoryDumpHolder> dump_holder(new MemoryDumpHolder(dump));
220 const int kSnapshotId = 1;
221 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
222 TRACE_DISABLED_BY_DEFAULT("memory"),
223 "memory::Heap",
224 kSnapshotId,
225 dump_holder.PassAs<base::debug::ConvertableToTraceFormat>());
228 void TraceMemoryController::StopProfiling() {
229 // Watch for the tracing framework sending disabled more than once.
230 if (!dump_timer_.IsRunning())
231 return;
232 DVLOG(1) << "Stopping trace memory";
233 dump_timer_.Stop();
234 ScopedTraceMemory::set_enabled(false);
235 CleanupThreadLocalStorage();
236 // Call ::HeapProfilerStop().
237 heap_profiler_stop_function_();
240 bool TraceMemoryController::IsTimerRunningForTest() const {
241 return dump_timer_.IsRunning();
244 /////////////////////////////////////////////////////////////////////////////
246 // static
247 bool ScopedTraceMemory::enabled_ = false;
249 ScopedTraceMemory::ScopedTraceMemory(const char* category, const char* name) {
250 // Not enabled indicates that the trace system isn't running, so don't
251 // record anything.
252 if (!enabled_)
253 return;
254 // Get our thread's copy of the stack.
255 TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
256 const size_t index = trace_memory_stack->scope_depth;
257 // Don't record data for deeply nested scopes, but continue to increment
258 // |stack_depth| so we can match pushes and pops.
259 if (index < kMaxScopeDepth) {
260 ScopeData& event = trace_memory_stack->scope_data[index];
261 event.category = category;
262 event.name = name;
264 trace_memory_stack->scope_depth++;
267 ScopedTraceMemory::~ScopedTraceMemory() {
268 // Not enabled indicates that the trace system isn't running, so don't
269 // record anything.
270 if (!enabled_)
271 return;
272 // Get our thread's copy of the stack.
273 TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
274 // The tracing system can be turned on with ScopedTraceMemory objects
275 // allocated on the stack, so avoid potential underflow as they are destroyed.
276 if (trace_memory_stack->scope_depth > 0)
277 trace_memory_stack->scope_depth--;
280 // static
281 void ScopedTraceMemory::InitForTest() {
282 InitThreadLocalStorage();
283 enabled_ = true;
286 // static
287 void ScopedTraceMemory::CleanupForTest() {
288 enabled_ = false;
289 CleanupThreadLocalStorage();
292 // static
293 int ScopedTraceMemory::GetStackDepthForTest() {
294 TraceMemoryStack* stack = GetTraceMemoryStack();
295 return static_cast<int>(stack->scope_depth);
298 // static
299 ScopedTraceMemory::ScopeData ScopedTraceMemory::GetScopeDataForTest(
300 int stack_index) {
301 TraceMemoryStack* stack = GetTraceMemoryStack();
302 return stack->scope_data[stack_index];
305 /////////////////////////////////////////////////////////////////////////////
307 void AppendHeapProfileAsTraceFormat(const char* input, std::string* output) {
308 // Heap profile output has a header total line, then a list of stacks with
309 // memory totals, like this:
311 // heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile
312 // 95: 40940 [ 649: 114260] @ 0x7fa7f4b3be13
313 // 77: 32546 [ 742: 106234] @
314 // 68: 4195 [ 1087: 98009] @ 0x7fa7fa9b9ba0 0x7fa7f4b3be13
316 // MAPPED_LIBRARIES:
317 // 1be411fc1000-1be4139e4000 rw-p 00000000 00:00 0
318 // 1be4139e4000-1be4139e5000 ---p 00000000 00:00 0
319 // ...
321 // Skip input after MAPPED_LIBRARIES.
322 std::string input_string;
323 const char* mapped_libraries = strstr(input, "MAPPED_LIBRARIES");
324 if (mapped_libraries) {
325 input_string.assign(input, mapped_libraries - input);
326 } else {
327 input_string.assign(input);
330 std::vector<std::string> lines;
331 size_t line_count = Tokenize(input_string, "\n", &lines);
332 if (line_count == 0) {
333 DLOG(WARNING) << "No lines found";
334 return;
337 // Handle the initial summary line.
338 output->append("[");
339 AppendHeapProfileTotalsAsTraceFormat(lines[0], output);
341 // Handle the following stack trace lines.
342 for (size_t i = 1; i < line_count; ++i) {
343 const std::string& line = lines[i];
344 AppendHeapProfileLineAsTraceFormat(line, output);
346 output->append("]\n");
349 void AppendHeapProfileTotalsAsTraceFormat(const std::string& line,
350 std::string* output) {
351 // This is what a line looks like:
352 // heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile
354 // The numbers represent total allocations since profiling was enabled.
355 // From the example above:
356 // 357 = Outstanding allocations (mallocs - frees)
357 // 55227 = Outstanding bytes (malloc bytes - free bytes)
358 // 14653 = Total allocations (mallocs)
359 // 2624014 = Total bytes (malloc bytes)
360 std::vector<std::string> tokens;
361 Tokenize(line, " :[]@", &tokens);
362 if (tokens.size() < 4) {
363 DLOG(WARNING) << "Invalid totals line " << line;
364 return;
366 DCHECK_EQ(tokens[0], "heap");
367 DCHECK_EQ(tokens[1], "profile");
368 output->append("{\"current_allocs\": ");
369 output->append(tokens[2]);
370 output->append(", \"current_bytes\": ");
371 output->append(tokens[3]);
372 output->append(", \"trace\": \"\"}");
375 bool AppendHeapProfileLineAsTraceFormat(const std::string& line,
376 std::string* output) {
377 // This is what a line looks like:
378 // 68: 4195 [ 1087: 98009] @ 0x7fa7fa9b9ba0 0x7fa7f4b3be13
380 // The numbers represent allocations for a particular stack trace since
381 // profiling was enabled. From the example above:
382 // 68 = Outstanding allocations (mallocs - frees)
383 // 4195 = Outstanding bytes (malloc bytes - free bytes)
384 // 1087 = Total allocations (mallocs)
385 // 98009 = Total bytes (malloc bytes)
387 // 0x7fa7fa9b9ba0 0x7fa7f4b3be13 = Stack trace represented as pointers to
388 // static strings from trace event categories
389 // and names.
390 std::vector<std::string> tokens;
391 Tokenize(line, " :[]@", &tokens);
392 // It's valid to have no stack addresses, so only require 4 tokens.
393 if (tokens.size() < 4) {
394 DLOG(WARNING) << "Invalid line " << line;
395 return false;
397 // Don't bother with stacks that have no current allocations.
398 if (tokens[0] == "0")
399 return false;
400 output->append(",\n");
401 output->append("{\"current_allocs\": ");
402 output->append(tokens[0]);
403 output->append(", \"current_bytes\": ");
404 output->append(tokens[1]);
405 output->append(", \"trace\": \"");
407 // Convert pairs of "stack addresses" into category and name strings.
408 const std::string kSingleQuote = "'";
409 for (size_t t = 4; t < tokens.size(); t += 2) {
410 // Casting strings into pointers is ugly but otherwise tcmalloc would need
411 // to gain a special output serializer just for pseudo-stacks.
412 const char* trace_category = StringFromHexAddress(tokens[t]);
413 DCHECK_LT(t + 1, tokens.size());
414 const char* trace_name = StringFromHexAddress(tokens[t + 1]);
416 // TODO(jamescook): Report the trace category and name separately to the
417 // trace viewer and allow it to decide what decorations to apply. For now
418 // just hard-code a decoration for posted tasks.
419 std::string trace_string(trace_name);
420 if (!strcmp(trace_category, "task"))
421 trace_string.append("->PostTask");
423 // Some trace name strings have double quotes, convert them to single.
424 ReplaceChars(trace_string, "\"", kSingleQuote, &trace_string);
426 output->append(trace_string);
428 // Trace viewer expects a trailing space.
429 output->append(" ");
431 output->append("\"}");
432 return true;
435 const char* StringFromHexAddress(const std::string& hex_address) {
436 uint64 address = 0;
437 if (!base::HexStringToUInt64(hex_address, &address))
438 return "error";
439 if (!address)
440 return "null";
441 // Note that this cast handles 64-bit to 32-bit conversion if necessary.
442 return reinterpret_cast<const char*>(address);
445 } // namespace debug
446 } // namespace base