1 // Copyright 2015 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.
4
5 #include "base/trace_event/memory_dump_manager.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/atomic_sequence_num.h"
11 #include "base/base_switches.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/threading/thread.h"
16 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
17 #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h"
18 #include "base/trace_event/heap_profiler_type_name_deduplicator.h"
19 #include "base/trace_event/malloc_dump_provider.h"
20 #include "base/trace_event/memory_dump_provider.h"
21 #include "base/trace_event/memory_dump_session_state.h"
22 #include "base/trace_event/process_memory_dump.h"
23 #include "base/trace_event/trace_event_argument.h"
24 #include "build/build_config.h"
25
26 #if !defined(OS_NACL)
27 #include "base/trace_event/process_memory_totals_dump_provider.h"
28 #endif
29
30 #if defined(OS_LINUX) || defined(OS_ANDROID)
31 #include "base/trace_event/process_memory_maps_dump_provider.h"
32 #endif
33
34 #if defined(OS_ANDROID)
35 #include "base/trace_event/java_heap_dump_provider_android.h"
36 #endif
37
38 #if defined(OS_WIN)
39 #include "base/trace_event/winheap_dump_provider_win.h"
40 #endif
41
42 namespace base {
43 namespace trace_event {
44
45 namespace {
46
47 const int kTraceEventNumArgs = 1;
48 const char* kTraceEventArgNames[] = {"dumps"};
49 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
50
51 StaticAtomicSequenceNumber g_next_guid;
52 uint32_t g_periodic_dumps_count = 0;
53 uint32_t g_heavy_dumps_rate = 0;
54 MemoryDumpManager* g_instance_for_testing = nullptr;
55
RequestPeriodicGlobalDump()56 void RequestPeriodicGlobalDump() {
57 MemoryDumpLevelOfDetail level_of_detail;
58 if (g_heavy_dumps_rate == 0) {
59 level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
60 } else {
61 level_of_detail = g_periodic_dumps_count == 0
62 ? MemoryDumpLevelOfDetail::DETAILED
63 : MemoryDumpLevelOfDetail::LIGHT;
64
65 if (++g_periodic_dumps_count == g_heavy_dumps_rate)
66 g_periodic_dumps_count = 0;
67 }
68
69 MemoryDumpManager::GetInstance()->RequestGlobalDump(
70 MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
71 }
72
73 // Callback wrapper to hook upon the completion of RequestGlobalDump() and
74 // inject trace markers.
OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,uint64_t dump_guid,bool success)75 void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback,
76 uint64_t dump_guid,
77 bool success) {
78 TRACE_EVENT_NESTABLE_ASYNC_END1(
79 MemoryDumpManager::kTraceCategory, "GlobalMemoryDump",
80 TRACE_ID_MANGLE(dump_guid), "success", success);
81
82 if (!wrapped_callback.is_null()) {
83 wrapped_callback.Run(dump_guid, success);
84 wrapped_callback.Reset();
85 }
86 }
87
88 } // namespace
89
90 // static
91 const char* const MemoryDumpManager::kTraceCategory =
92 TRACE_DISABLED_BY_DEFAULT("memory-infra");
93
94 // static
95 const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
96
97 // static
98 const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0;
99
100 // static
101 const char* const MemoryDumpManager::kSystemAllocatorPoolName =
102 #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
103 MallocDumpProvider::kAllocatedObjects;
104 #elif defined(OS_WIN)
105 WinHeapDumpProvider::kAllocatedObjects;
106 #else
107 nullptr;
108 #endif
109
110 // static
GetInstance()111 MemoryDumpManager* MemoryDumpManager::GetInstance() {
112 if (g_instance_for_testing)
113 return g_instance_for_testing;
114
115 return Singleton<MemoryDumpManager,
116 LeakySingletonTraits<MemoryDumpManager>>::get();
117 }
118
119 // static
SetInstanceForTesting(MemoryDumpManager * instance)120 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
121 g_instance_for_testing = instance;
122 }
123
MemoryDumpManager()124 MemoryDumpManager::MemoryDumpManager()
125 : delegate_(nullptr),
126 is_coordinator_(false),
127 memory_tracing_enabled_(0),
128 tracing_process_id_(kInvalidTracingProcessId),
129 dumper_registrations_ignored_for_testing_(false) {
130 g_next_guid.GetNext(); // Make sure that first guid is not zero.
131
132 heap_profiling_enabled_ = CommandLine::InitializedForCurrentProcess()
133 ? CommandLine::ForCurrentProcess()->HasSwitch(
134 switches::kEnableHeapProfiling)
135 : false;
136
137 if (heap_profiling_enabled_)
138 AllocationContextTracker::SetCaptureEnabled(true);
139 }
140
~MemoryDumpManager()141 MemoryDumpManager::~MemoryDumpManager() {
142 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
143 }
144
Initialize(MemoryDumpManagerDelegate * delegate,bool is_coordinator)145 void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
146 bool is_coordinator) {
147 {
148 AutoLock lock(lock_);
149 DCHECK(delegate);
150 DCHECK(!delegate_);
151 delegate_ = delegate;
152 is_coordinator_ = is_coordinator;
153 }
154
155 // Enable the core dump providers.
156 #if !defined(OS_NACL)
157 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance(),
158 "ProcessMemoryTotals", nullptr);
159 #endif
160
161 #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
162 RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
163 #endif
164
165 #if defined(OS_LINUX) || defined(OS_ANDROID)
166 RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance(),
167 "ProcessMemoryMaps", nullptr);
168 #endif
169
170 #if defined(OS_ANDROID)
171 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
172 nullptr);
173 #endif
174
175 #if defined(OS_WIN)
176 RegisterDumpProvider(WinHeapDumpProvider::GetInstance(), "WinHeap", nullptr);
177 #endif
178
179 // If tracing was enabled before initializing MemoryDumpManager, we missed the
180 // OnTraceLogEnabled() event. Synthetize it so we can late-join the party.
181 bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled();
182 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
183 TraceLog::GetInstance()->AddEnabledStateObserver(this);
184 if (is_tracing_already_enabled)
185 OnTraceLogEnabled();
186 }
187
RegisterDumpProvider(MemoryDumpProvider * mdp,const char * name,const scoped_refptr<SingleThreadTaskRunner> & task_runner,const MemoryDumpProvider::Options & options)188 void MemoryDumpManager::RegisterDumpProvider(
189 MemoryDumpProvider* mdp,
190 const char* name,
191 const scoped_refptr<SingleThreadTaskRunner>& task_runner,
192 const MemoryDumpProvider::Options& options) {
193 if (dumper_registrations_ignored_for_testing_)
194 return;
195
196 scoped_refptr<MemoryDumpProviderInfo> mdpinfo =
197 new MemoryDumpProviderInfo(mdp, name, task_runner, options);
198
199 {
200 AutoLock lock(lock_);
201 bool already_registered = !dump_providers_.insert(mdpinfo).second;
202 // This actually happens in some tests which don't have a clean tear-down
203 // path for RenderThreadImpl::Init().
204 if (already_registered)
205 return;
206 }
207
208 if (heap_profiling_enabled_)
209 mdp->OnHeapProfilingEnabled(true);
210 }
211
RegisterDumpProvider(MemoryDumpProvider * mdp,const char * name,const scoped_refptr<SingleThreadTaskRunner> & task_runner)212 void MemoryDumpManager::RegisterDumpProvider(
213 MemoryDumpProvider* mdp,
214 const char* name,
215 const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
216 RegisterDumpProvider(mdp, name, task_runner, MemoryDumpProvider::Options());
217 }
218
UnregisterDumpProvider(MemoryDumpProvider * mdp)219 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
220 UnregisterDumpProviderInternal(mdp, false /* delete_async */);
221 }
222
UnregisterAndDeleteDumpProviderSoon(scoped_ptr<MemoryDumpProvider> mdp)223 void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon(
224 scoped_ptr<MemoryDumpProvider> mdp) {
225 UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */);
226 }
227
UnregisterDumpProviderInternal(MemoryDumpProvider * mdp,bool take_mdp_ownership_and_delete_async)228 void MemoryDumpManager::UnregisterDumpProviderInternal(
229 MemoryDumpProvider* mdp,
230 bool take_mdp_ownership_and_delete_async) {
231 scoped_ptr<MemoryDumpProvider> owned_mdp;
232 if (take_mdp_ownership_and_delete_async)
233 owned_mdp.reset(mdp);
234
235 AutoLock lock(lock_);
236
237 auto mdp_iter = dump_providers_.begin();
238 for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
239 if ((*mdp_iter)->dump_provider == mdp)
240 break;
241 }
242
243 if (mdp_iter == dump_providers_.end())
244 return; // Not registered / already unregistered.
245
246 if (take_mdp_ownership_and_delete_async) {
247 // The MDP will be deleted whenever the MDPInfo struct will, that is either:
248 // - At the end of this function, if no dump is in progress.
249 // - In the prologue of the ContinueAsyncProcessDump().
250 DCHECK(!(*mdp_iter)->owned_dump_provider);
251 (*mdp_iter)->owned_dump_provider = std::move(owned_mdp);
252 } else if (subtle::NoBarrier_Load(&memory_tracing_enabled_)) {
253 // If you hit this DCHECK, your dump provider has a bug.
254 // Unregistration of a MemoryDumpProvider is safe only if:
255 // - The MDP has specified a thread affinity (via task_runner()) AND
256 // the unregistration happens on the same thread (so the MDP cannot
257 // unregister and be in the middle of a OnMemoryDump() at the same time.
258 // - The MDP has NOT specified a thread affinity and its ownership is
259 // transferred via UnregisterAndDeleteDumpProviderSoon().
260 // In all the other cases, it is not possible to guarantee that the
261 // unregistration will not race with OnMemoryDump() calls.
262 DCHECK((*mdp_iter)->task_runner &&
263 (*mdp_iter)->task_runner->BelongsToCurrentThread())
264 << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to "
265 << "unregister itself in a racy way. Please file a crbug.";
266 }
267
268 // The MDPInfo instance can still be referenced by the
269 // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason
270 // the MDPInfo is flagged as disabled. It will cause ContinueAsyncProcessDump
271 // to just skip it, without actually invoking the |mdp|, which might be
272 // destroyed by the caller soon after this method returns.
273 (*mdp_iter)->disabled = true;
274 dump_providers_.erase(mdp_iter);
275 }
276
RequestGlobalDump(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail,const MemoryDumpCallback & callback)277 void MemoryDumpManager::RequestGlobalDump(
278 MemoryDumpType dump_type,
279 MemoryDumpLevelOfDetail level_of_detail,
280 const MemoryDumpCallback& callback) {
281 // Bail out immediately if tracing is not enabled at all.
282 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) {
283 if (!callback.is_null())
284 callback.Run(0u /* guid */, false /* success */);
285 return;
286 }
287
288 const uint64_t guid =
289 TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
290
291 // Creates an async event to keep track of the global dump evolution.
292 // The |wrapped_callback| will generate the ASYNC_END event and then invoke
293 // the real |callback| provided by the caller.
294 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "GlobalMemoryDump",
295 TRACE_ID_MANGLE(guid));
296 MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback);
297
298 // Technically there is no need to grab the |lock_| here as the delegate is
299 // long-lived and can only be set by Initialize(), which is locked and
300 // necessarily happens before memory_tracing_enabled_ == true.
301 // Not taking the |lock_|, though, is lakely make TSan barf and, at this point
302 // (memory-infra is enabled) we're not in the fast-path anymore.
303 MemoryDumpManagerDelegate* delegate;
304 {
305 AutoLock lock(lock_);
306 delegate = delegate_;
307 }
308
309 // The delegate will coordinate the IPC broadcast and at some point invoke
310 // CreateProcessDump() to get a dump for the current process.
311 MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail};
312 delegate->RequestGlobalMemoryDump(args, wrapped_callback);
313 }
314
RequestGlobalDump(MemoryDumpType dump_type,MemoryDumpLevelOfDetail level_of_detail)315 void MemoryDumpManager::RequestGlobalDump(
316 MemoryDumpType dump_type,
317 MemoryDumpLevelOfDetail level_of_detail) {
318 RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback());
319 }
320
CreateProcessDump(const MemoryDumpRequestArgs & args,const MemoryDumpCallback & callback)321 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
322 const MemoryDumpCallback& callback) {
323 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "ProcessMemoryDump",
324 TRACE_ID_MANGLE(args.dump_guid));
325
326 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
327 {
328 AutoLock lock(lock_);
329 pmd_async_state.reset(
330 new ProcessMemoryDumpAsyncState(args, dump_providers_, session_state_,
331 callback, dump_thread_->task_runner()));
332 }
333
334 TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump",
335 TRACE_ID_MANGLE(args.dump_guid),
336 TRACE_EVENT_FLAG_FLOW_OUT);
337
338 // Start the thread hop. |dump_providers_| are kept sorted by thread, so
339 // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
340 // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
341 ContinueAsyncProcessDump(pmd_async_state.release());
342 }
343
344 // At most one ContinueAsyncProcessDump() can be active at any time for a given
345 // PMD, regardless of status of the |lock_|. |lock_| is used here purely to
346 // ensure consistency w.r.t. (un)registrations of |dump_providers_|.
347 // The linearization of dump providers' OnMemoryDump invocations is achieved by
348 // means of subsequent PostTask(s).
349 //
350 // 1) Prologue:
351 // - If this was the last hop, create a trace event, add it to the trace
352 // and finalize (invoke callback).
353 // - Check if we are on the right thread. If not hop and continue there.
354 // - Check if the dump provider is disabled, if so skip the dump.
355 // 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
356 // 3) Epilogue:
357 // - Unregister the dump provider if it failed too many times consecutively.
358 // - Pop() the MDP from the |pending_dump_providers| list, eventually
359 // destroying the MDPInfo if that was unregistered in the meantime.
ContinueAsyncProcessDump(ProcessMemoryDumpAsyncState * owned_pmd_async_state)360 void MemoryDumpManager::ContinueAsyncProcessDump(
361 ProcessMemoryDumpAsyncState* owned_pmd_async_state) {
362 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
363 // in the PostTask below don't end up registering their own dump providers
364 // (for discounting trace memory overhead) while holding the |lock_|.
365 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
366
367 // In theory |owned_pmd_async_state| should be a scoped_ptr. The only reason
368 // why it isn't is because of the corner case logic of |did_post_task| below,
369 // which needs to take back the ownership of the |pmd_async_state| when a
370 // thread goes away and consequently the PostTask() fails.
371 // Unfortunately, PostTask() destroys the scoped_ptr arguments upon failure
372 // to prevent accidental leaks. Using a scoped_ptr would prevent us to to
373 // skip the hop and move on. Hence the manual naked -> scoped ptr juggling.
374 auto pmd_async_state = make_scoped_ptr(owned_pmd_async_state);
375 owned_pmd_async_state = nullptr;
376
377 if (pmd_async_state->pending_dump_providers.empty())
378 return FinalizeDumpAndAddToTrace(std::move(pmd_async_state));
379
380 // Read MemoryDumpProviderInfo thread safety considerations in
381 // memory_dump_manager.h when accessing |mdpinfo| fields.
382 MemoryDumpProviderInfo* mdpinfo =
383 pmd_async_state->pending_dump_providers.back().get();
384
385 // If the dump provider did not specify a thread affinity, dump on
386 // |dump_thread_|. Note that |dump_thread_| might have been Stop()-ed at this
387 // point (if tracing was disabled in the meanwhile). In such case the
388 // PostTask() below will fail, but |task_runner| should always be non-null.
389 SingleThreadTaskRunner* task_runner = mdpinfo->task_runner.get();
390 if (!task_runner)
391 task_runner = pmd_async_state->dump_thread_task_runner.get();
392
393 bool post_task_failed = false;
394 if (!task_runner->BelongsToCurrentThread()) {
395 // It's time to hop onto another thread.
396 post_task_failed = !task_runner->PostTask(
397 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
398 Unretained(this), Unretained(pmd_async_state.get())));
399 if (!post_task_failed) {
400 // Ownership is tranferred to the next ContinueAsyncProcessDump().
401 ignore_result(pmd_async_state.release());
402 return;
403 }
404 }
405
406 // At this point either:
407 // - The MDP has a task runner affinity and we are on the right thread.
408 // - The MDP has a task runner affinity but the underlying thread is gone,
409 // hence the above |post_task_failed| == true.
410 // - The MDP does NOT have a task runner affinity. A locked access is required
411 // to R/W |disabled| (for the UnregisterAndDeleteDumpProviderSoon() case).
412 bool should_dump;
413 const char* disabled_reason = nullptr;
414 {
415 AutoLock lock(lock_);
416 if (!mdpinfo->disabled) {
417 if (mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) {
418 mdpinfo->disabled = true;
419 disabled_reason =
420 "Dump failure, possibly related with sandboxing (crbug.com/461788)."
421 " Try --no-sandbox.";
422 } else if (post_task_failed) {
423 disabled_reason = "The thread it was meant to dump onto is gone.";
424 mdpinfo->disabled = true;
425 }
426 }
427 should_dump = !mdpinfo->disabled;
428 }
429
430 if (disabled_reason) {
431 LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name << "\". "
432 << disabled_reason;
433 }
434
435 if (should_dump) {
436 // Invoke the dump provider.
437 TRACE_EVENT_WITH_FLOW1(kTraceCategory,
438 "MemoryDumpManager::ContinueAsyncProcessDump",
439 TRACE_ID_MANGLE(pmd_async_state->req_args.dump_guid),
440 TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
441 "dump_provider.name", mdpinfo->name);
442
443 // Pid of the target process being dumped. Often kNullProcessId (= current
444 // process), non-zero when the coordinator process creates dumps on behalf
445 // of child processes (see crbug.com/461788).
446 ProcessId target_pid = mdpinfo->options.target_pid;
447 ProcessMemoryDump* pmd =
448 pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(target_pid);
449 MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail};
450 bool dump_successful = mdpinfo->dump_provider->OnMemoryDump(args, pmd);
451 mdpinfo->consecutive_failures =
452 dump_successful ? 0 : mdpinfo->consecutive_failures + 1;
453 } // if (!mdpinfo->disabled)
454
455 pmd_async_state->pending_dump_providers.pop_back();
456 ContinueAsyncProcessDump(pmd_async_state.release());
457 }
458
459 // static
FinalizeDumpAndAddToTrace(scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state)460 void MemoryDumpManager::FinalizeDumpAndAddToTrace(
461 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
462 DCHECK(pmd_async_state->pending_dump_providers.empty());
463 const uint64_t dump_guid = pmd_async_state->req_args.dump_guid;
464 if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) {
465 scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
466 pmd_async_state->callback_task_runner;
467 callback_task_runner->PostTask(
468 FROM_HERE, Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
469 Passed(&pmd_async_state)));
470 return;
471 }
472
473 TRACE_EVENT_WITH_FLOW0(kTraceCategory,
474 "MemoryDumpManager::FinalizeDumpAndAddToTrace",
475 TRACE_ID_MANGLE(dump_guid), TRACE_EVENT_FLAG_FLOW_IN);
476
477 for (const auto& kv : pmd_async_state->process_dumps) {
478 ProcessId pid = kv.first; // kNullProcessId for the current process.
479 ProcessMemoryDump* process_memory_dump = kv.second.get();
480 TracedValue* traced_value = new TracedValue();
481 scoped_refptr<ConvertableToTraceFormat> event_value(traced_value);
482 process_memory_dump->AsValueInto(traced_value);
483 traced_value->SetString("level_of_detail",
484 MemoryDumpLevelOfDetailToString(
485 pmd_async_state->req_args.level_of_detail));
486 const char* const event_name =
487 MemoryDumpTypeToString(pmd_async_state->req_args.dump_type);
488
489 TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID(
490 TRACE_EVENT_PHASE_MEMORY_DUMP,
491 TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
492 dump_guid, pid, kTraceEventNumArgs, kTraceEventArgNames,
493 kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
494 TRACE_EVENT_FLAG_HAS_ID);
495 }
496
497 if (!pmd_async_state->callback.is_null()) {
498 pmd_async_state->callback.Run(dump_guid, true /* success */);
499 pmd_async_state->callback.Reset();
500 }
501
502 TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump",
503 TRACE_ID_MANGLE(dump_guid));
504 }
505
OnTraceLogEnabled()506 void MemoryDumpManager::OnTraceLogEnabled() {
507 bool enabled;
508 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
509 if (!enabled)
510 return;
511
512 // Initialize the TraceLog for the current thread. This is to avoid that the
513 // TraceLog memory dump provider is registered lazily in the PostTask() below
514 // while the |lock_| is taken;
515 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
516
517 // Spin-up the thread used to invoke unbound dump providers.
518 scoped_ptr<Thread> dump_thread(new Thread("MemoryInfra"));
519 if (!dump_thread->Start()) {
520 LOG(ERROR) << "Failed to start the memory-infra thread for tracing";
521 return;
522 }
523
524 AutoLock lock(lock_);
525
526 DCHECK(delegate_); // At this point we must have a delegate.
527
528 scoped_refptr<StackFrameDeduplicator> stack_frame_deduplicator = nullptr;
529 scoped_refptr<TypeNameDeduplicator> type_name_deduplicator = nullptr;
530
531 if (heap_profiling_enabled_) {
532 // If heap profiling is enabled, the stack frame deduplicator and type name
533 // deduplicator will be in use. Add a metadata events to write the frames
534 // and type IDs.
535 stack_frame_deduplicator = new StackFrameDeduplicator;
536 type_name_deduplicator = new TypeNameDeduplicator;
537 TRACE_EVENT_API_ADD_METADATA_EVENT(
538 "stackFrames", "stackFrames",
539 scoped_refptr<ConvertableToTraceFormat>(stack_frame_deduplicator));
540 TRACE_EVENT_API_ADD_METADATA_EVENT(
541 "typeNames", "typeNames",
542 scoped_refptr<ConvertableToTraceFormat>(type_name_deduplicator));
543 }
544
545 DCHECK(!dump_thread_);
546 dump_thread_ = std::move(dump_thread);
547 session_state_ = new MemoryDumpSessionState(stack_frame_deduplicator,
548 type_name_deduplicator);
549
550 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
551
552 // TODO(primiano): This is a temporary hack to disable periodic memory dumps
553 // when running memory benchmarks until telemetry uses TraceConfig to
554 // enable/disable periodic dumps. See crbug.com/529184 .
555 if (!is_coordinator_ ||
556 CommandLine::ForCurrentProcess()->HasSwitch(
557 "enable-memory-benchmarking")) {
558 return;
559 }
560
561 // Enable periodic dumps. At the moment the periodic support is limited to at
562 // most one low-detail periodic dump and at most one high-detail periodic
563 // dump. If both are specified the high-detail period must be an integer
564 // multiple of the low-level one.
565 g_periodic_dumps_count = 0;
566 const TraceConfig trace_config =
567 TraceLog::GetInstance()->GetCurrentTraceConfig();
568 const TraceConfig::MemoryDumpConfig& config_list =
569 trace_config.memory_dump_config();
570 if (config_list.empty())
571 return;
572
573 uint32_t min_timer_period_ms = std::numeric_limits<uint32_t>::max();
574 uint32_t heavy_dump_period_ms = 0;
575 DCHECK_LE(config_list.size(), 2u);
576 for (const TraceConfig::MemoryDumpTriggerConfig& config : config_list) {
577 DCHECK(config.periodic_interval_ms);
578 if (config.level_of_detail == MemoryDumpLevelOfDetail::DETAILED)
579 heavy_dump_period_ms = config.periodic_interval_ms;
580 min_timer_period_ms =
581 std::min(min_timer_period_ms, config.periodic_interval_ms);
582 }
583 DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms);
584 g_heavy_dumps_rate = heavy_dump_period_ms / min_timer_period_ms;
585
586 periodic_dump_timer_.Start(FROM_HERE,
587 TimeDelta::FromMilliseconds(min_timer_period_ms),
588 base::Bind(&RequestPeriodicGlobalDump));
589 }
590
OnTraceLogDisabled()591 void MemoryDumpManager::OnTraceLogDisabled() {
592 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
593 scoped_ptr<Thread> dump_thread;
594 {
595 AutoLock lock(lock_);
596 dump_thread = std::move(dump_thread_);
597 session_state_ = nullptr;
598 }
599
600 // Thread stops are blocking and must be performed outside of the |lock_|
601 // or will deadlock (e.g., if ContinueAsyncProcessDump() tries to acquire it).
602 periodic_dump_timer_.Stop();
603 if (dump_thread)
604 dump_thread->Stop();
605 }
606
GetTracingProcessId() const607 uint64_t MemoryDumpManager::GetTracingProcessId() const {
608 return delegate_->GetTracingProcessId();
609 }
610
MemoryDumpProviderInfo(MemoryDumpProvider * dump_provider,const char * name,const scoped_refptr<SingleThreadTaskRunner> & task_runner,const MemoryDumpProvider::Options & options)611 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
612 MemoryDumpProvider* dump_provider,
613 const char* name,
614 const scoped_refptr<SingleThreadTaskRunner>& task_runner,
615 const MemoryDumpProvider::Options& options)
616 : dump_provider(dump_provider),
617 name(name),
618 task_runner(task_runner),
619 options(options),
620 consecutive_failures(0),
621 disabled(false) {}
622
~MemoryDumpProviderInfo()623 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {}
624
operator ()(const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo> & a,const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo> & b) const625 bool MemoryDumpManager::MemoryDumpProviderInfo::Comparator::operator()(
626 const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& a,
627 const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& b) const {
628 if (!a || !b)
629 return a.get() < b.get();
630 // Ensure that unbound providers (task_runner == nullptr) always run last.
631 // Rationale: some unbound dump providers are known to be slow, keep them last
632 // to avoid skewing timings of the other dump providers.
633 return std::tie(a->task_runner, a->dump_provider) >
634 std::tie(b->task_runner, b->dump_provider);
635 }
636
ProcessMemoryDumpAsyncState(MemoryDumpRequestArgs req_args,const MemoryDumpProviderInfo::OrderedSet & dump_providers,const scoped_refptr<MemoryDumpSessionState> & session_state,MemoryDumpCallback callback,const scoped_refptr<SingleThreadTaskRunner> & dump_thread_task_runner)637 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
638 MemoryDumpRequestArgs req_args,
639 const MemoryDumpProviderInfo::OrderedSet& dump_providers,
640 const scoped_refptr<MemoryDumpSessionState>& session_state,
641 MemoryDumpCallback callback,
642 const scoped_refptr<SingleThreadTaskRunner>& dump_thread_task_runner)
643 : req_args(req_args),
644 session_state(session_state),
645 callback(callback),
646 callback_task_runner(MessageLoop::current()->task_runner()),
647 dump_thread_task_runner(dump_thread_task_runner) {
648 pending_dump_providers.reserve(dump_providers.size());
649 pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
650 }
651
~ProcessMemoryDumpAsyncState()652 MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
653 }
654
655 ProcessMemoryDump* MemoryDumpManager::ProcessMemoryDumpAsyncState::
GetOrCreateMemoryDumpContainerForProcess(ProcessId pid)656 GetOrCreateMemoryDumpContainerForProcess(ProcessId pid) {
657 auto iter = process_dumps.find(pid);
658 if (iter == process_dumps.end()) {
659 scoped_ptr<ProcessMemoryDump> new_pmd(new ProcessMemoryDump(session_state));
660 iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first;
661 }
662 return iter->second.get();
663 }
664
665 } // namespace trace_event
666 } // namespace base
667