Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / base / trace_event / memory_dump_manager.cc
blobea2760b118fb4043238df92416b14c7326523f72
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/hash.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/trace_event/memory_dump_provider.h"
15 #include "base/trace_event/memory_dump_session_state.h"
16 #include "base/trace_event/process_memory_dump.h"
17 #include "base/trace_event/trace_event_argument.h"
18 #include "build/build_config.h"
20 #if !defined(OS_NACL)
21 #include "base/trace_event/process_memory_totals_dump_provider.h"
22 #endif
24 #if defined(OS_LINUX) || defined(OS_ANDROID)
25 #include "base/trace_event/malloc_dump_provider.h"
26 #include "base/trace_event/process_memory_maps_dump_provider.h"
27 #endif
29 #if defined(OS_ANDROID)
30 #include "base/trace_event/java_heap_dump_provider_android.h"
31 #endif
33 #if defined(OS_WIN)
34 #include "base/trace_event/winheap_dump_provider_win.h"
35 #endif
37 namespace base {
38 namespace trace_event {
40 namespace {
42 // TODO(primiano): this should be smarter and should do something similar to
43 // trace event synthetic delays.
44 const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra");
46 // Throttle mmaps at a rate of once every kHeavyMmapsDumpsRate standard dumps.
47 const int kHeavyMmapsDumpsRate = 8; // 250 ms * 8 = 2000 ms.
48 const int kDumpIntervalMs = 250;
49 const int kTraceEventNumArgs = 1;
50 const char* kTraceEventArgNames[] = {"dumps"};
51 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
53 StaticAtomicSequenceNumber g_next_guid;
54 uint32 g_periodic_dumps_count = 0;
55 MemoryDumpManager* g_instance_for_testing = nullptr;
56 MemoryDumpProvider* g_mmaps_dump_provider = nullptr;
58 void RequestPeriodicGlobalDump() {
59 MemoryDumpType dump_type = g_periodic_dumps_count == 0
60 ? MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS
61 : MemoryDumpType::PERIODIC_INTERVAL;
62 if (++g_periodic_dumps_count == kHeavyMmapsDumpsRate)
63 g_periodic_dumps_count = 0;
65 MemoryDumpManager::GetInstance()->RequestGlobalDump(dump_type);
68 } // namespace
70 // static
71 const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
73 // static
74 const uint64 MemoryDumpManager::kInvalidTracingProcessId = 0;
76 // static
77 const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
79 // static
80 MemoryDumpManager* MemoryDumpManager::GetInstance() {
81 if (g_instance_for_testing)
82 return g_instance_for_testing;
84 return Singleton<MemoryDumpManager,
85 LeakySingletonTraits<MemoryDumpManager>>::get();
88 // static
89 void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
90 if (instance)
91 instance->skip_core_dumpers_auto_registration_for_testing_ = true;
92 g_instance_for_testing = instance;
95 MemoryDumpManager::MemoryDumpManager()
96 : did_unregister_dump_provider_(false),
97 delegate_(nullptr),
98 memory_tracing_enabled_(0),
99 tracing_process_id_(kInvalidTracingProcessId),
100 system_allocator_pool_name_(nullptr),
101 skip_core_dumpers_auto_registration_for_testing_(false) {
102 g_next_guid.GetNext(); // Make sure that first guid is not zero.
105 MemoryDumpManager::~MemoryDumpManager() {
106 base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
109 void MemoryDumpManager::Initialize() {
110 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
111 trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
113 if (skip_core_dumpers_auto_registration_for_testing_)
114 return;
116 // Enable the core dump providers.
117 #if !defined(OS_NACL)
118 RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance());
119 #endif
121 #if defined(OS_LINUX) || defined(OS_ANDROID)
122 g_mmaps_dump_provider = ProcessMemoryMapsDumpProvider::GetInstance();
123 RegisterDumpProvider(g_mmaps_dump_provider);
124 RegisterDumpProvider(MallocDumpProvider::GetInstance());
125 system_allocator_pool_name_ = MallocDumpProvider::kAllocatedObjects;
126 #endif
128 #if defined(OS_ANDROID)
129 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance());
130 #endif
132 #if defined(OS_WIN)
133 RegisterDumpProvider(WinHeapDumpProvider::GetInstance());
134 system_allocator_pool_name_ = WinHeapDumpProvider::kAllocatedObjects;
135 #endif
138 void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) {
139 AutoLock lock(lock_);
140 DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_);
141 delegate_ = delegate;
144 void MemoryDumpManager::RegisterDumpProvider(
145 MemoryDumpProvider* mdp,
146 const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
147 MemoryDumpProviderInfo mdp_info(mdp, task_runner);
148 AutoLock lock(lock_);
149 dump_providers_.insert(mdp_info);
152 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
153 RegisterDumpProvider(mdp, nullptr);
156 void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
157 AutoLock lock(lock_);
159 auto mdp_iter = dump_providers_.begin();
160 for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
161 if (mdp_iter->dump_provider == mdp)
162 break;
165 if (mdp_iter == dump_providers_.end())
166 return;
168 // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
169 // only if the MDP has specified a thread affinity (via task_runner()) AND
170 // the unregistration happens on the same thread (so the MDP cannot unregister
171 // and OnMemoryDump() at the same time).
172 // Otherwise, it is not possible to guarantee that its unregistration is
173 // race-free. If you hit this DCHECK, your MDP has a bug.
174 DCHECK_IMPLIES(
175 subtle::NoBarrier_Load(&memory_tracing_enabled_),
176 mdp_iter->task_runner && mdp_iter->task_runner->BelongsToCurrentThread())
177 << "The MemoryDumpProvider attempted to unregister itself in a racy way. "
178 << "Please file a crbug.";
180 dump_providers_.erase(mdp_iter);
181 did_unregister_dump_provider_ = true;
184 void MemoryDumpManager::RequestGlobalDump(
185 MemoryDumpType dump_type,
186 const MemoryDumpCallback& callback) {
187 // Bail out immediately if tracing is not enabled at all.
188 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) {
189 if (!callback.is_null())
190 callback.Run(0u /* guid */, false /* success */);
191 return;
194 const uint64 guid =
195 TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
197 // The delegate_ is supposed to be thread safe, immutable and long lived.
198 // No need to keep the lock after we ensure that a delegate has been set.
199 MemoryDumpManagerDelegate* delegate;
201 AutoLock lock(lock_);
202 delegate = delegate_;
205 if (delegate) {
206 // The delegate is in charge to coordinate the request among all the
207 // processes and call the CreateLocalDumpPoint on the local process.
208 MemoryDumpRequestArgs args = {guid, dump_type};
209 delegate->RequestGlobalMemoryDump(args, callback);
210 } else if (!callback.is_null()) {
211 callback.Run(guid, false /* success */);
215 void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) {
216 RequestGlobalDump(dump_type, MemoryDumpCallback());
219 void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
220 const MemoryDumpCallback& callback) {
221 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
223 AutoLock lock(lock_);
224 did_unregister_dump_provider_ = false;
225 pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
226 args, dump_providers_.begin(), session_state_, callback));
229 // Start the thread hop. |dump_providers_| are kept sorted by thread, so
230 // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
231 // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
232 ContinueAsyncProcessDump(pmd_async_state.Pass());
235 // At most one ContinueAsyncProcessDump() can be active at any time for a given
236 // PMD, regardless of status of the |lock_|. |lock_| is used here purely to
237 // ensure consistency w.r.t. (un)registrations of |dump_providers_|.
238 // The linearization of dump providers' OnMemoryDump invocations is achieved by
239 // means of subsequent PostTask(s).
241 // 1) Prologue:
242 // - Check if the dump provider is disabled, if so skip the dump.
243 // - Check if we are on the right thread. If not hop and continue there.
244 // 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
245 // 3) Epilogue:
246 // - Unregister the dump provider if it failed too many times consecutively.
247 // - Advance the |next_dump_provider| iterator to the next dump provider.
248 // - If this was the last hop, create a trace event, add it to the trace
249 // and finalize (invoke callback).
251 void MemoryDumpManager::ContinueAsyncProcessDump(
252 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
253 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
254 // in the PostTask below don't end up registering their own dump providers
255 // (for discounting trace memory overhead) while holding the |lock_|.
256 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
258 // DO NOT put any LOG() statement in the locked sections, as in some contexts
259 // (GPU process) LOG() ends up performing PostTask/IPCs.
260 MemoryDumpProvider* mdp;
261 bool skip_dump = false;
263 AutoLock lock(lock_);
264 // In the unlikely event that a dump provider was unregistered while
265 // dumping, abort the dump, as that would make |next_dump_provider| invalid.
266 // Registration, on the other hand, is safe as per std::set<> contract.
267 if (did_unregister_dump_provider_) {
268 return AbortDumpLocked(pmd_async_state->callback,
269 pmd_async_state->task_runner,
270 pmd_async_state->req_args.dump_guid);
273 auto* mdp_info = &*pmd_async_state->next_dump_provider;
274 mdp = mdp_info->dump_provider;
275 if (mdp_info->disabled) {
276 skip_dump = true;
277 } else if (mdp == g_mmaps_dump_provider &&
278 pmd_async_state->req_args.dump_type !=
279 MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS) {
280 // Mmaps dumping is very heavyweight and cannot be performed at the same
281 // rate of other dumps. TODO(primiano): this is a hack and should be
282 // cleaned up as part of crbug.com/499731.
283 skip_dump = true;
284 } else if (mdp_info->task_runner &&
285 !mdp_info->task_runner->BelongsToCurrentThread()) {
286 // It's time to hop onto another thread.
288 // Copy the callback + arguments just for the unlikley case in which
289 // PostTask fails. In such case the Bind helper will destroy the
290 // pmd_async_state and we must keep a copy of the fields to notify the
291 // abort.
292 MemoryDumpCallback callback = pmd_async_state->callback;
293 scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
294 pmd_async_state->task_runner;
295 const uint64 dump_guid = pmd_async_state->req_args.dump_guid;
297 const bool did_post_task = mdp_info->task_runner->PostTask(
298 FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
299 Unretained(this), Passed(pmd_async_state.Pass())));
300 if (did_post_task)
301 return;
303 // The thread is gone. At this point the best thing we can do is to
304 // disable the dump provider and abort this dump.
305 mdp_info->disabled = true;
306 return AbortDumpLocked(callback, callback_task_runner, dump_guid);
308 } // AutoLock(lock_)
310 // Invoke the dump provider without holding the |lock_|.
311 bool finalize = false;
312 bool dump_successful = false;
313 if (!skip_dump)
314 dump_successful = mdp->OnMemoryDump(&pmd_async_state->process_memory_dump);
317 AutoLock lock(lock_);
318 if (did_unregister_dump_provider_) {
319 return AbortDumpLocked(pmd_async_state->callback,
320 pmd_async_state->task_runner,
321 pmd_async_state->req_args.dump_guid);
323 auto* mdp_info = &*pmd_async_state->next_dump_provider;
324 if (dump_successful) {
325 mdp_info->consecutive_failures = 0;
326 } else if (!skip_dump) {
327 ++mdp_info->consecutive_failures;
328 if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) {
329 mdp_info->disabled = true;
332 ++pmd_async_state->next_dump_provider;
333 finalize = pmd_async_state->next_dump_provider == dump_providers_.end();
336 if (!skip_dump && !dump_successful) {
337 LOG(ERROR) << "A memory dumper failed, possibly due to sandboxing "
338 "(crbug.com/461788). Disabling dumper for current process. "
339 "Try restarting chrome with the --no-sandbox switch.";
342 if (finalize)
343 return FinalizeDumpAndAddToTrace(pmd_async_state.Pass());
345 ContinueAsyncProcessDump(pmd_async_state.Pass());
348 // static
349 void MemoryDumpManager::FinalizeDumpAndAddToTrace(
350 scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
351 if (!pmd_async_state->task_runner->BelongsToCurrentThread()) {
352 scoped_refptr<SingleThreadTaskRunner> task_runner =
353 pmd_async_state->task_runner;
354 task_runner->PostTask(FROM_HERE,
355 Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
356 Passed(pmd_async_state.Pass())));
357 return;
360 scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
361 pmd_async_state->process_memory_dump.AsValueInto(
362 static_cast<TracedValue*>(event_value.get()));
363 const char* const event_name =
364 MemoryDumpTypeToString(pmd_async_state->req_args.dump_type);
366 TRACE_EVENT_API_ADD_TRACE_EVENT(
367 TRACE_EVENT_PHASE_MEMORY_DUMP,
368 TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
369 pmd_async_state->req_args.dump_guid, kTraceEventNumArgs,
370 kTraceEventArgNames, kTraceEventArgTypes, nullptr /* arg_values */,
371 &event_value, TRACE_EVENT_FLAG_HAS_ID);
373 if (!pmd_async_state->callback.is_null()) {
374 pmd_async_state->callback.Run(pmd_async_state->req_args.dump_guid,
375 true /* success */);
376 pmd_async_state->callback.Reset();
380 // static
381 void MemoryDumpManager::AbortDumpLocked(
382 MemoryDumpCallback callback,
383 scoped_refptr<SingleThreadTaskRunner> task_runner,
384 uint64 dump_guid) {
385 if (callback.is_null())
386 return; // There is nothing to NACK.
388 // Post the callback even if we are already on the right thread to avoid
389 // invoking the callback while holding the lock_.
390 task_runner->PostTask(FROM_HERE,
391 Bind(callback, dump_guid, false /* success */));
394 void MemoryDumpManager::OnTraceLogEnabled() {
395 // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter
396 // to figure out (and cache) which dumpers should be enabled or not.
397 // For the moment piggy back everything on the generic "memory" category.
398 bool enabled;
399 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
401 // Initialize the TraceLog for the current thread. This is to avoid that the
402 // TraceLog memory dump provider is registered lazily in the PostTask() below
403 // while the |lock_| is taken;
404 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
406 AutoLock lock(lock_);
408 // There is no point starting the tracing without a delegate.
409 if (!enabled || !delegate_) {
410 // Disable all the providers.
411 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it)
412 it->disabled = true;
413 return;
416 session_state_ = new MemoryDumpSessionState();
417 for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
418 it->disabled = false;
419 it->consecutive_failures = 0;
422 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
424 // TODO(primiano): This is a temporary hack to disable periodic memory dumps
425 // when running memory benchmarks until they can be enabled/disabled in
426 // base::trace_event::TraceConfig. See https://goo.gl/5Hj3o0.
427 if (delegate_->IsCoordinatorProcess() &&
428 !CommandLine::ForCurrentProcess()->HasSwitch(
429 "enable-memory-benchmarking")) {
430 g_periodic_dumps_count = 0;
431 periodic_dump_timer_.Start(FROM_HERE,
432 TimeDelta::FromMilliseconds(kDumpIntervalMs),
433 base::Bind(&RequestPeriodicGlobalDump));
437 void MemoryDumpManager::OnTraceLogDisabled() {
438 AutoLock lock(lock_);
439 periodic_dump_timer_.Stop();
440 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
441 session_state_ = nullptr;
444 // static
445 uint64 MemoryDumpManager::ChildProcessIdToTracingProcessId(
446 int child_process_id) {
447 return static_cast<uint64>(
448 Hash(reinterpret_cast<const char*>(&child_process_id),
449 sizeof(child_process_id))) +
453 MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
454 MemoryDumpProvider* dump_provider,
455 const scoped_refptr<SingleThreadTaskRunner>& task_runner)
456 : dump_provider(dump_provider),
457 task_runner(task_runner),
458 consecutive_failures(0),
459 disabled(false) {
462 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {
465 bool MemoryDumpManager::MemoryDumpProviderInfo::operator<(
466 const MemoryDumpProviderInfo& other) const {
467 if (task_runner == other.task_runner)
468 return dump_provider < other.dump_provider;
469 return task_runner < other.task_runner;
472 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
473 MemoryDumpRequestArgs req_args,
474 MemoryDumpProviderInfoSet::iterator next_dump_provider,
475 const scoped_refptr<MemoryDumpSessionState>& session_state,
476 MemoryDumpCallback callback)
477 : process_memory_dump(session_state),
478 req_args(req_args),
479 next_dump_provider(next_dump_provider),
480 callback(callback),
481 task_runner(MessageLoop::current()->task_runner()) {
484 MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
487 } // namespace trace_event
488 } // namespace base