Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / base / trace_event / memory_dump_manager.cc
bloba2fa95396360caad11ccf78d58c19457f9f42098
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.
5 #include "base/trace_event/memory_dump_manager.h"
7 #include <algorithm>
9 #include "base/atomic_sequence_num.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/trace_event/memory_dump_provider.h"
14 #include "base/trace_event/memory_dump_session_state.h"
15 #include "base/trace_event/process_memory_dump.h"
16 #include "base/trace_event/trace_event_argument.h"
17 #include "build/build_config.h"
19 #if !defined(OS_NACL)
20 #include "base/trace_event/process_memory_totals_dump_provider.h"
21 #endif
23 #if defined(OS_LINUX) || defined(OS_ANDROID)
24 #include "base/trace_event/malloc_dump_provider.h"
25 #include "base/trace_event/process_memory_maps_dump_provider.h"
26 #endif
28 #if defined(OS_ANDROID)
29 #include "base/trace_event/java_heap_dump_provider_android.h"
30 #endif
32 #if defined(OS_WIN)
33 #include "base/trace_event/winheap_dump_provider_win.h"
34 #endif
36 namespace base {
37 namespace trace_event {
39 namespace {
41 const int kTraceEventNumArgs = 1;
42 const char* kTraceEventArgNames[] = {"dumps"};
43 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
45 StaticAtomicSequenceNumber g_next_guid;
46 uint32 g_periodic_dumps_count = 0;
47 uint32 g_heavy_dumps_rate = 0;
48 MemoryDumpManager* g_instance_for_testing = nullptr;
50 void RequestPeriodicGlobalDump() {
51 MemoryDumpLevelOfDetail level_of_detail;
52 if (g_heavy_dumps_rate == 0) {
53 level_of_detail = MemoryDumpLevelOfDetail::LIGHT;
54 } else {
55 level_of_detail = g_periodic_dumps_count == 0
56 ? MemoryDumpLevelOfDetail::DETAILED
57 : MemoryDumpLevelOfDetail::LIGHT;
59 if (++g_periodic_dumps_count == g_heavy_dumps_rate)
60 g_periodic_dumps_count = 0;
63 MemoryDumpManager::GetInstance()->RequestGlobalDump(
64 MemoryDumpType::PERIODIC_INTERVAL, level_of_detail);
67 } // namespace
69 // static
70 const char* const MemoryDumpManager::kTraceCategory =
71 TRACE_DISABLED_BY_DEFAULT("memory-infra");
73 // static
74 const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
76 // static
77 const uint64 MemoryDumpManager::kInvalidTracingProcessId = 0;
79 // static
80 const char* const MemoryDumpManager::kSystemAllocatorPoolName =
81 #if defined(OS_LINUX) || defined(OS_ANDROID)
82 MallocDumpProvider::kAllocatedObjects;
83 #elif defined(OS_WIN)
84 WinHeapDumpProvider::kAllocatedObjects;
85 #else
86 nullptr;
87 #endif
90 // static
91 MemoryDumpManager* MemoryDumpManager::GetInstance() {
92 if (g_instance_for_testing)
93 return g_instance_for_testing;
95 return Singleton<MemoryDumpManager,
96 LeakySingletonTraits<MemoryDumpManager>>::get();
99 // static
100 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
101 if (instance)
102 instance->skip_core_dumpers_auto_registration_for_testing_ = true;
103 g_instance_for_testing = instance;
106 MemoryDumpManager::MemoryDumpManager()
107 : delegate_(nullptr),
108 is_coordinator_(false),
109 memory_tracing_enabled_(0),
110 tracing_process_id_(kInvalidTracingProcessId),
111 skip_core_dumpers_auto_registration_for_testing_(false) {
112 g_next_guid.GetNext(); // Make sure that first guid is not zero.
115 MemoryDumpManager::~MemoryDumpManager() {
116 TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
119 void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
120 bool is_coordinator) {
122 AutoLock lock(lock_);
123 DCHECK(delegate);
124 DCHECK(!delegate_);
125 delegate_ = delegate;
126 is_coordinator_ = is_coordinator;
129 // Enable the core dump providers.
130 if (!skip_core_dumpers_auto_registration_for_testing_) {
131 #if !defined(OS_NACL)
132 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance());
133 #endif
135 #if defined(OS_LINUX) || defined(OS_ANDROID)
136 RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
137 RegisterDumpProvider(MallocDumpProvider::GetInstance());
138 #endif
140 #if defined(OS_ANDROID)
141 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance());
142 #endif
144 #if defined(OS_WIN)
145 RegisterDumpProvider(WinHeapDumpProvider::GetInstance());
146 #endif
147 } // !skip_core_dumpers_auto_registration_for_testing_
149 // If tracing was enabled before initializing MemoryDumpManager, we missed the
150 // OnTraceLogEnabled() event. Synthetize it so we can late-join the party.
151 bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled();
152 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
153 TraceLog::GetInstance()->AddEnabledStateObserver(this);
154 if (is_tracing_already_enabled)
155 OnTraceLogEnabled();
158 void MemoryDumpManager::RegisterDumpProvider(
159 MemoryDumpProvider* mdp,
160 const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
161 MemoryDumpProviderInfo mdp_info(mdp, task_runner);
162 AutoLock lock(lock_);
163 auto iter_new = dump_providers_.insert(mdp_info);
165 // If there was a previous entry, replace it with the new one. This is to deal
166 // with the case where a dump provider unregisters itself and then re-
167 // registers before a memory dump happens, so its entry was still in the
168 // collection but flagged |unregistered|.
169 if (!iter_new.second) {
170 dump_providers_.erase(iter_new.first);
171 dump_providers_.insert(mdp_info);
175 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
176 RegisterDumpProvider(mdp, nullptr);
179 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
180 AutoLock lock(lock_);
182 auto mdp_iter = dump_providers_.begin();
183 for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
184 if (mdp_iter->dump_provider == mdp)
185 break;
188 if (mdp_iter == dump_providers_.end())
189 return;
191 // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
192 // only if the MDP has specified a thread affinity (via task_runner()) AND
193 // the unregistration happens on the same thread (so the MDP cannot unregister
194 // and OnMemoryDump() at the same time).
195 // Otherwise, it is not possible to guarantee that its unregistration is
196 // race-free. If you hit this DCHECK, your MDP has a bug.
197 DCHECK_IMPLIES(
198 subtle::NoBarrier_Load(&memory_tracing_enabled_),
199 mdp_iter->task_runner && mdp_iter->task_runner->BelongsToCurrentThread())
200 << "The MemoryDumpProvider attempted to unregister itself in a racy way. "
201 << "Please file a crbug.";
203 mdp_iter->unregistered = true;
206 void MemoryDumpManager::RequestGlobalDump(
207 MemoryDumpType dump_type,
208 MemoryDumpLevelOfDetail level_of_detail,
209 const MemoryDumpCallback& callback) {
210 // Bail out immediately if tracing is not enabled at all.
211 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) {
212 if (!callback.is_null())
213 callback.Run(0u /* guid */, false /* success */);
214 return;
217 const uint64 guid =
218 TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
220 // Technically there is no need to grab the |lock_| here as the delegate is
221 // long-lived and can only be set by Initialize(), which is locked and
222 // necessarily happens before memory_tracing_enabled_ == true.
223 // Not taking the |lock_|, though, is lakely make TSan barf and, at this point
224 // (memory-infra is enabled) we're not in the fast-path anymore.
225 MemoryDumpManagerDelegate* delegate;
227 AutoLock lock(lock_);
228 delegate = delegate_;
231 // The delegate will coordinate the IPC broadcast and at some point invoke
232 // CreateProcessDump() to get a dump for the current process.
233 MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail};
234 delegate->RequestGlobalMemoryDump(args, callback);
237 void MemoryDumpManager::RequestGlobalDump(
238 MemoryDumpType dump_type,
239 MemoryDumpLevelOfDetail level_of_detail) {
240 RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback());
243 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
244 const MemoryDumpCallback& callback) {
245 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
247 AutoLock lock(lock_);
248 pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
249 args, dump_providers_.begin(), session_state_, callback));
252 // Start the thread hop. |dump_providers_| are kept sorted by thread, so
253 // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
254 // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
255 ContinueAsyncProcessDump(pmd_async_state.Pass());
258 // At most one ContinueAsyncProcessDump() can be active at any time for a given
259 // PMD, regardless of status of the |lock_|. |lock_| is used here purely to
260 // ensure consistency w.r.t. (un)registrations of |dump_providers_|.
261 // The linearization of dump providers' OnMemoryDump invocations is achieved by
262 // means of subsequent PostTask(s).
264 // 1) Prologue:
265 // - Check if the dump provider is disabled, if so skip the dump.
266 // - Check if we are on the right thread. If not hop and continue there.
267 // 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
268 // 3) Epilogue:
269 // - Unregister the dump provider if it failed too many times consecutively.
270 // - Advance the |next_dump_provider| iterator to the next dump provider.
271 // - If this was the last hop, create a trace event, add it to the trace
272 // and finalize (invoke callback).
274 void MemoryDumpManager::ContinueAsyncProcessDump(
275 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
276 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
277 // in the PostTask below don't end up registering their own dump providers
278 // (for discounting trace memory overhead) while holding the |lock_|.
279 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
281 // DO NOT put any LOG() statement in the locked sections, as in some contexts
282 // (GPU process) LOG() ends up performing PostTask/IPCs.
283 MemoryDumpProvider* mdp;
284 bool skip_dump = false;
286 AutoLock lock(lock_);
288 auto mdp_info = pmd_async_state->next_dump_provider;
289 mdp = mdp_info->dump_provider;
290 if (mdp_info->disabled || mdp_info->unregistered) {
291 skip_dump = true;
292 } else if (mdp_info->task_runner &&
293 !mdp_info->task_runner->BelongsToCurrentThread()) {
294 // It's time to hop onto another thread.
296 // Copy the callback + arguments just for the unlikley case in which
297 // PostTask fails. In such case the Bind helper will destroy the
298 // pmd_async_state and we must keep a copy of the fields to notify the
299 // abort.
300 MemoryDumpCallback callback = pmd_async_state->callback;
301 scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
302 pmd_async_state->task_runner;
303 const uint64 dump_guid = pmd_async_state->req_args.dump_guid;
305 const bool did_post_task = mdp_info->task_runner->PostTask(
306 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
307 Unretained(this), Passed(pmd_async_state.Pass())));
308 if (did_post_task)
309 return;
311 // The thread is gone. At this point the best thing we can do is to
312 // disable the dump provider and abort this dump.
313 mdp_info->disabled = true;
314 return AbortDumpLocked(callback, callback_task_runner, dump_guid);
316 } // AutoLock(lock_)
318 // Invoke the dump provider without holding the |lock_|.
319 bool finalize = false;
320 bool dump_successful = false;
322 if (!skip_dump) {
323 MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail};
324 dump_successful =
325 mdp->OnMemoryDump(args, &pmd_async_state->process_memory_dump);
329 AutoLock lock(lock_);
330 auto mdp_info = pmd_async_state->next_dump_provider;
331 if (dump_successful) {
332 mdp_info->consecutive_failures = 0;
333 } else if (!skip_dump) {
334 ++mdp_info->consecutive_failures;
335 if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) {
336 mdp_info->disabled = true;
339 ++pmd_async_state->next_dump_provider;
340 finalize = pmd_async_state->next_dump_provider == dump_providers_.end();
342 if (mdp_info->unregistered)
343 dump_providers_.erase(mdp_info);
346 if (!skip_dump && !dump_successful) {
347 LOG(ERROR) << "A memory dumper failed, possibly due to sandboxing "
348 "(crbug.com/461788). Disabling dumper for current process. "
349 "Try restarting chrome with the --no-sandbox switch.";
352 if (finalize)
353 return FinalizeDumpAndAddToTrace(pmd_async_state.Pass());
355 ContinueAsyncProcessDump(pmd_async_state.Pass());
358 // static
359 void MemoryDumpManager::FinalizeDumpAndAddToTrace(
360 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
361 if (!pmd_async_state->task_runner->BelongsToCurrentThread()) {
362 scoped_refptr<SingleThreadTaskRunner> task_runner =
363 pmd_async_state->task_runner;
364 task_runner->PostTask(FROM_HERE,
365 Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
366 Passed(pmd_async_state.Pass())));
367 return;
370 TracedValue* traced_value = new TracedValue();
371 scoped_refptr<ConvertableToTraceFormat> event_value(traced_value);
372 pmd_async_state->process_memory_dump.AsValueInto(traced_value);
373 traced_value->SetString("level_of_detail",
374 MemoryDumpLevelOfDetailToString(
375 pmd_async_state->req_args.level_of_detail));
376 const char* const event_name =
377 MemoryDumpTypeToString(pmd_async_state->req_args.dump_type);
379 TRACE_EVENT_API_ADD_TRACE_EVENT(
380 TRACE_EVENT_PHASE_MEMORY_DUMP,
381 TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
382 pmd_async_state->req_args.dump_guid, kTraceEventNumArgs,
383 kTraceEventArgNames, kTraceEventArgTypes, nullptr /* arg_values */,
384 &event_value, TRACE_EVENT_FLAG_HAS_ID);
386 if (!pmd_async_state->callback.is_null()) {
387 pmd_async_state->callback.Run(pmd_async_state->req_args.dump_guid,
388 true /* success */);
389 pmd_async_state->callback.Reset();
393 // static
394 void MemoryDumpManager::AbortDumpLocked(
395 MemoryDumpCallback callback,
396 scoped_refptr<SingleThreadTaskRunner> task_runner,
397 uint64 dump_guid) {
398 if (callback.is_null())
399 return; // There is nothing to NACK.
401 // Post the callback even if we are already on the right thread to avoid
402 // invoking the callback while holding the lock_.
403 task_runner->PostTask(FROM_HERE,
404 Bind(callback, dump_guid, false /* success */));
407 void MemoryDumpManager::OnTraceLogEnabled() {
408 bool enabled;
409 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
410 if (!enabled)
411 return;
413 // Initialize the TraceLog for the current thread. This is to avoid that the
414 // TraceLog memory dump provider is registered lazily in the PostTask() below
415 // while the |lock_| is taken;
416 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
418 AutoLock lock(lock_);
420 DCHECK(delegate_); // At this point we must have a delegate.
422 session_state_ = new MemoryDumpSessionState();
423 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
424 it->disabled = false;
425 it->consecutive_failures = 0;
428 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
430 // TODO(primiano): This is a temporary hack to disable periodic memory dumps
431 // when running memory benchmarks until telemetry uses TraceConfig to
432 // enable/disable periodic dumps. See crbug.com/529184 .
433 if (!is_coordinator_ ||
434 CommandLine::ForCurrentProcess()->HasSwitch(
435 "enable-memory-benchmarking")) {
436 return;
439 // Enable periodic dumps. At the moment the periodic support is limited to at
440 // most one low-detail periodic dump and at most one high-detail periodic
441 // dump. If both are specified the high-detail period must be an integer
442 // multiple of the low-level one.
443 g_periodic_dumps_count = 0;
444 const TraceConfig trace_config =
445 TraceLog::GetInstance()->GetCurrentTraceConfig();
446 const TraceConfig::MemoryDumpConfig& config_list =
447 trace_config.memory_dump_config();
448 if (config_list.empty())
449 return;
451 uint32 min_timer_period_ms = std::numeric_limits<uint32>::max();
452 uint32 heavy_dump_period_ms = 0;
453 DCHECK_LE(config_list.size(), 2u);
454 for (const TraceConfig::MemoryDumpTriggerConfig& config : config_list) {
455 DCHECK(config.periodic_interval_ms);
456 if (config.level_of_detail == MemoryDumpLevelOfDetail::DETAILED)
457 heavy_dump_period_ms = config.periodic_interval_ms;
458 min_timer_period_ms =
459 std::min(min_timer_period_ms, config.periodic_interval_ms);
461 DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms);
462 g_heavy_dumps_rate = heavy_dump_period_ms / min_timer_period_ms;
464 periodic_dump_timer_.Start(FROM_HERE,
465 TimeDelta::FromMilliseconds(min_timer_period_ms),
466 base::Bind(&RequestPeriodicGlobalDump));
469 void MemoryDumpManager::OnTraceLogDisabled() {
470 AutoLock lock(lock_);
471 periodic_dump_timer_.Stop();
472 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
473 session_state_ = nullptr;
476 uint64 MemoryDumpManager::GetTracingProcessId() const {
477 return delegate_->GetTracingProcessId();
480 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
481 MemoryDumpProvider* dump_provider,
482 const scoped_refptr<SingleThreadTaskRunner>& task_runner)
483 : dump_provider(dump_provider),
484 task_runner(task_runner),
485 consecutive_failures(0),
486 disabled(false),
487 unregistered(false) {}
489 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {
492 bool MemoryDumpManager::MemoryDumpProviderInfo::operator<(
493 const MemoryDumpProviderInfo& other) const {
494 if (task_runner == other.task_runner)
495 return dump_provider < other.dump_provider;
496 return task_runner < other.task_runner;
499 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
500 MemoryDumpRequestArgs req_args,
501 MemoryDumpProviderInfoSet::iterator next_dump_provider,
502 const scoped_refptr<MemoryDumpSessionState>& session_state,
503 MemoryDumpCallback callback)
504 : process_memory_dump(session_state),
505 req_args(req_args),
506 next_dump_provider(next_dump_provider),
507 callback(callback),
508 task_runner(MessageLoop::current()->task_runner()) {
511 MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
514 } // namespace trace_event
515 } // namespace base