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/debug/debugging_flags.h" 15#include "base/debug/stack_trace.h" 16#include "base/memory/ptr_util.h" 17#include "base/threading/thread.h" 18#include "base/threading/thread_task_runner_handle.h" 19#include "base/trace_event/heap_profiler.h" 20#include "base/trace_event/heap_profiler_allocation_context_tracker.h" 21#include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" 22#include "base/trace_event/heap_profiler_type_name_deduplicator.h" 23#include "base/trace_event/malloc_dump_provider.h" 24#include "base/trace_event/memory_dump_provider.h" 25#include "base/trace_event/memory_dump_session_state.h" 26#include "base/trace_event/memory_infra_background_whitelist.h" 27#include "base/trace_event/process_memory_dump.h" 28#include "base/trace_event/trace_event.h" 29#include "base/trace_event/trace_event_argument.h" 30#include "build/build_config.h" 31 32#if defined(OS_ANDROID) 33#include "base/trace_event/java_heap_dump_provider_android.h" 34#endif 35 36#if defined(OS_WIN) 37#include "base/trace_event/winheap_dump_provider_win.h" 38#endif 39 40namespace base { 41namespace trace_event { 42 43namespace { 44 45const int kTraceEventNumArgs = 1; 46const char* kTraceEventArgNames[] = {"dumps"}; 47const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; 48 49StaticAtomicSequenceNumber g_next_guid; 50MemoryDumpManager* g_instance_for_testing = nullptr; 51 52// Callback wrapper to hook upon the completion of RequestGlobalDump() and 53// inject trace markers. 54void OnGlobalDumpDone(MemoryDumpCallback wrapped_callback, 55 uint64_t dump_guid, 56 bool success) { 57 TRACE_EVENT_NESTABLE_ASYNC_END1( 58 MemoryDumpManager::kTraceCategory, "GlobalMemoryDump", 59 TRACE_ID_MANGLE(dump_guid), "success", success); 60 61 if (!wrapped_callback.is_null()) { 62 wrapped_callback.Run(dump_guid, success); 63 wrapped_callback.Reset(); 64 } 65} 66 67// Proxy class which wraps a ConvertableToTraceFormat owned by the 68// |session_state| into a proxy object that can be added to the trace event log. 69// This is to solve the problem that the MemoryDumpSessionState is refcounted 70// but the tracing subsystem wants a std::unique_ptr<ConvertableToTraceFormat>. 71template <typename T> 72struct SessionStateConvertableProxy : public ConvertableToTraceFormat { 73 using GetterFunctPtr = T* (MemoryDumpSessionState::*)() const; 74 75 SessionStateConvertableProxy( 76 scoped_refptr<MemoryDumpSessionState> session_state, 77 GetterFunctPtr getter_function) 78 : session_state(session_state), getter_function(getter_function) {} 79 80 void AppendAsTraceFormat(std::string* out) const override { 81 return (session_state.get()->*getter_function)()->AppendAsTraceFormat(out); 82 } 83 84 void EstimateTraceMemoryOverhead( 85 TraceEventMemoryOverhead* overhead) override { 86 return (session_state.get()->*getter_function)() 87 ->EstimateTraceMemoryOverhead(overhead); 88 } 89 90 scoped_refptr<MemoryDumpSessionState> session_state; 91 GetterFunctPtr const getter_function; 92}; 93 94} // namespace 95 96// static 97const char* const MemoryDumpManager::kTraceCategory = 98 TRACE_DISABLED_BY_DEFAULT("memory-infra"); 99 100// static 101const char* const MemoryDumpManager::kLogPrefix = "Memory-infra dump"; 102 103// static 104const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3; 105 106// static 107const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0; 108 109// static 110const char* const MemoryDumpManager::kSystemAllocatorPoolName = 111#if defined(MALLOC_MEMORY_TRACING_SUPPORTED) 112 MallocDumpProvider::kAllocatedObjects; 113#elif defined(OS_WIN) 114 WinHeapDumpProvider::kAllocatedObjects; 115#else 116 nullptr; 117#endif 118 119// static 120MemoryDumpManager* MemoryDumpManager::GetInstance() { 121 if (g_instance_for_testing) 122 return g_instance_for_testing; 123 124 return Singleton<MemoryDumpManager, 125 LeakySingletonTraits<MemoryDumpManager>>::get(); 126} 127 128// static 129void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { 130 g_instance_for_testing = instance; 131} 132 133MemoryDumpManager::MemoryDumpManager() 134 : delegate_(nullptr), 135 is_coordinator_(false), 136 memory_tracing_enabled_(0), 137 tracing_process_id_(kInvalidTracingProcessId), 138 dumper_registrations_ignored_for_testing_(false), 139 heap_profiling_enabled_(false) { 140 g_next_guid.GetNext(); // Make sure that first guid is not zero. 141 142 // At this point the command line may not be initialized but we try to 143 // enable the heap profiler to capture allocations as soon as possible. 144 EnableHeapProfilingIfNeeded(); 145} 146 147MemoryDumpManager::~MemoryDumpManager() { 148 TraceLog::GetInstance()->RemoveEnabledStateObserver(this); 149} 150 151void MemoryDumpManager::EnableHeapProfilingIfNeeded() { 152 if (heap_profiling_enabled_) 153 return; 154 155 if (!CommandLine::InitializedForCurrentProcess() || 156 !CommandLine::ForCurrentProcess()->HasSwitch( 157 switches::kEnableHeapProfiling)) 158 return; 159 160 std::string profiling_mode = CommandLine::ForCurrentProcess() 161 ->GetSwitchValueASCII(switches::kEnableHeapProfiling); 162 if (profiling_mode == "") { 163 AllocationContextTracker::SetCaptureMode( 164 AllocationContextTracker::CaptureMode::PSEUDO_STACK); 165 } 166 else if (profiling_mode == switches::kEnableHeapProfilingModeNative) { 167#if HAVE_TRACE_STACK_FRAME_POINTERS && \ 168 (BUILDFLAG(ENABLE_PROFILING) || !defined(NDEBUG)) 169 // We need frame pointers for native tracing to work, and they are 170 // enabled in profiling and debug builds. 171 AllocationContextTracker::SetCaptureMode( 172 AllocationContextTracker::CaptureMode::NATIVE_STACK); 173#else 174 CHECK(false) << "'" << profiling_mode << "' mode for " 175 << switches::kEnableHeapProfiling << " flag is not supported " 176 << "for this platform / build type."; 177#endif 178 } else { 179 CHECK(false) << "Invalid mode '" << profiling_mode << "' for " 180 << switches::kEnableHeapProfiling << " flag."; 181 } 182 183 for (auto mdp : dump_providers_) 184 mdp->dump_provider->OnHeapProfilingEnabled(true); 185 heap_profiling_enabled_ = true; 186} 187 188void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate, 189 bool is_coordinator) { 190 { 191 AutoLock lock(lock_); 192 DCHECK(delegate); 193 DCHECK(!delegate_); 194 delegate_ = delegate; 195 is_coordinator_ = is_coordinator; 196 EnableHeapProfilingIfNeeded(); 197 } 198 199// Enable the core dump providers. 200#if defined(MALLOC_MEMORY_TRACING_SUPPORTED) 201 RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr); 202#endif 203 204#if defined(OS_ANDROID) 205 RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap", 206 nullptr); 207#endif 208 209#if defined(OS_WIN) 210 RegisterDumpProvider(WinHeapDumpProvider::GetInstance(), "WinHeap", nullptr); 211#endif 212 213 // If tracing was enabled before initializing MemoryDumpManager, we missed the 214 // OnTraceLogEnabled() event. Synthetize it so we can late-join the party. 215 bool is_tracing_already_enabled = TraceLog::GetInstance()->IsEnabled(); 216 TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. 217 TraceLog::GetInstance()->AddEnabledStateObserver(this); 218 if (is_tracing_already_enabled) 219 OnTraceLogEnabled(); 220} 221 222void MemoryDumpManager::RegisterDumpProvider( 223 MemoryDumpProvider* mdp, 224 const char* name, 225 scoped_refptr<SingleThreadTaskRunner> task_runner, 226 MemoryDumpProvider::Options options) { 227 options.dumps_on_single_thread_task_runner = true; 228 RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); 229} 230 231void MemoryDumpManager::RegisterDumpProvider( 232 MemoryDumpProvider* mdp, 233 const char* name, 234 scoped_refptr<SingleThreadTaskRunner> task_runner) { 235 // Set |dumps_on_single_thread_task_runner| to true because all providers 236 // without task runner are run on dump thread. 237 MemoryDumpProvider::Options options; 238 options.dumps_on_single_thread_task_runner = true; 239 RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); 240} 241 242void MemoryDumpManager::RegisterDumpProviderWithSequencedTaskRunner( 243 MemoryDumpProvider* mdp, 244 const char* name, 245 scoped_refptr<SequencedTaskRunner> task_runner, 246 MemoryDumpProvider::Options options) { 247 DCHECK(task_runner); 248 options.dumps_on_single_thread_task_runner = false; 249 RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options); 250} 251 252void MemoryDumpManager::RegisterDumpProviderInternal( 253 MemoryDumpProvider* mdp, 254 const char* name, 255 scoped_refptr<SequencedTaskRunner> task_runner, 256 const MemoryDumpProvider::Options& options) { 257 if (dumper_registrations_ignored_for_testing_) 258 return; 259 260 bool whitelisted_for_background_mode = IsMemoryDumpProviderWhitelisted(name); 261 scoped_refptr<MemoryDumpProviderInfo> mdpinfo = 262 new MemoryDumpProviderInfo(mdp, name, std::move(task_runner), options, 263 whitelisted_for_background_mode); 264 265 { 266 AutoLock lock(lock_); 267 bool already_registered = !dump_providers_.insert(mdpinfo).second; 268 // This actually happens in some tests which don't have a clean tear-down 269 // path for RenderThreadImpl::Init(). 270 if (already_registered) 271 return; 272 } 273 274 if (heap_profiling_enabled_) 275 mdp->OnHeapProfilingEnabled(true); 276} 277 278void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { 279 UnregisterDumpProviderInternal(mdp, false /* delete_async */); 280} 281 282void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon( 283 std::unique_ptr<MemoryDumpProvider> mdp) { 284 UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */); 285} 286 287void MemoryDumpManager::UnregisterDumpProviderInternal( 288 MemoryDumpProvider* mdp, 289 bool take_mdp_ownership_and_delete_async) { 290 std::unique_ptr<MemoryDumpProvider> owned_mdp; 291 if (take_mdp_ownership_and_delete_async) 292 owned_mdp.reset(mdp); 293 294 AutoLock lock(lock_); 295 296 auto mdp_iter = dump_providers_.begin(); 297 for (; mdp_iter != dump_providers_.end(); ++mdp_iter) { 298 if ((*mdp_iter)->dump_provider == mdp) 299 break; 300 } 301 302 if (mdp_iter == dump_providers_.end()) 303 return; // Not registered / already unregistered. 304 305 if (take_mdp_ownership_and_delete_async) { 306 // The MDP will be deleted whenever the MDPInfo struct will, that is either: 307 // - At the end of this function, if no dump is in progress. 308 // - Either in SetupNextMemoryDump() or InvokeOnMemoryDump() when MDPInfo is 309 // removed from |pending_dump_providers|. 310 DCHECK(!(*mdp_iter)->owned_dump_provider); 311 (*mdp_iter)->owned_dump_provider = std::move(owned_mdp); 312 } else if (subtle::NoBarrier_Load(&memory_tracing_enabled_)) { 313 // If you hit this DCHECK, your dump provider has a bug. 314 // Unregistration of a MemoryDumpProvider is safe only if: 315 // - The MDP has specified a sequenced task runner affinity AND the 316 // unregistration happens on the same task runner. So that the MDP cannot 317 // unregister and be in the middle of a OnMemoryDump() at the same time. 318 // - The MDP has NOT specified a task runner affinity and its ownership is 319 // transferred via UnregisterAndDeleteDumpProviderSoon(). 320 // In all the other cases, it is not possible to guarantee that the 321 // unregistration will not race with OnMemoryDump() calls. 322 DCHECK((*mdp_iter)->task_runner && 323 (*mdp_iter)->task_runner->RunsTasksOnCurrentThread()) 324 << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to " 325 << "unregister itself in a racy way. Please file a crbug."; 326 } 327 328 // The MDPInfo instance can still be referenced by the 329 // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason 330 // the MDPInfo is flagged as disabled. It will cause InvokeOnMemoryDump() 331 // to just skip it, without actually invoking the |mdp|, which might be 332 // destroyed by the caller soon after this method returns. 333 (*mdp_iter)->disabled = true; 334 dump_providers_.erase(mdp_iter); 335} 336 337void MemoryDumpManager::RequestGlobalDump( 338 MemoryDumpType dump_type, 339 MemoryDumpLevelOfDetail level_of_detail, 340 const MemoryDumpCallback& callback) { 341 // Bail out immediately if tracing is not enabled at all or if the dump mode 342 // is not allowed. 343 if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)) || 344 !IsDumpModeAllowed(level_of_detail)) { 345 VLOG(1) << kLogPrefix << " failed because " << kTraceCategory 346 << " tracing category is not enabled or the requested dump mode is " 347 "not allowed by trace config."; 348 if (!callback.is_null()) 349 callback.Run(0u /* guid */, false /* success */); 350 return; 351 } 352 353 const uint64_t guid = 354 TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext()); 355 356 // Creates an async event to keep track of the global dump evolution. 357 // The |wrapped_callback| will generate the ASYNC_END event and then invoke 358 // the real |callback| provided by the caller. 359 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "GlobalMemoryDump", 360 TRACE_ID_MANGLE(guid)); 361 MemoryDumpCallback wrapped_callback = Bind(&OnGlobalDumpDone, callback); 362 363 // Technically there is no need to grab the |lock_| here as the delegate is 364 // long-lived and can only be set by Initialize(), which is locked and 365 // necessarily happens before memory_tracing_enabled_ == true. 366 // Not taking the |lock_|, though, is lakely make TSan barf and, at this point 367 // (memory-infra is enabled) we're not in the fast-path anymore. 368 MemoryDumpManagerDelegate* delegate; 369 { 370 AutoLock lock(lock_); 371 delegate = delegate_; 372 } 373 374 // The delegate will coordinate the IPC broadcast and at some point invoke 375 // CreateProcessDump() to get a dump for the current process. 376 MemoryDumpRequestArgs args = {guid, dump_type, level_of_detail}; 377 delegate->RequestGlobalMemoryDump(args, wrapped_callback); 378} 379 380void MemoryDumpManager::RequestGlobalDump( 381 MemoryDumpType dump_type, 382 MemoryDumpLevelOfDetail level_of_detail) { 383 RequestGlobalDump(dump_type, level_of_detail, MemoryDumpCallback()); 384} 385 386void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args, 387 const MemoryDumpCallback& callback) { 388 TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kTraceCategory, "ProcessMemoryDump", 389 TRACE_ID_MANGLE(args.dump_guid)); 390 391 // If argument filter is enabled then only background mode dumps should be 392 // allowed. In case the trace config passed for background tracing session 393 // missed the allowed modes argument, it crashes here instead of creating 394 // unexpected dumps. 395 if (TraceLog::GetInstance() 396 ->GetCurrentTraceConfig() 397 .IsArgumentFilterEnabled()) { 398 CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail); 399 } 400 401 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state; 402 { 403 AutoLock lock(lock_); 404 405 // |dump_thread_| can be nullptr is tracing was disabled before reaching 406 // here. SetupNextMemoryDump() is robust enough to tolerate it and will 407 // NACK the dump. 408 pmd_async_state.reset(new ProcessMemoryDumpAsyncState( 409 args, dump_providers_, session_state_, callback, 410 dump_thread_ ? dump_thread_->task_runner() : nullptr)); 411 412 // Safety check to prevent reaching here without calling RequestGlobalDump, 413 // with disallowed modes. If |session_state_| is null then tracing is 414 // disabled. 415 CHECK(!session_state_ || 416 session_state_->memory_dump_config().allowed_dump_modes.count( 417 args.level_of_detail)); 418 } 419 420 TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump", 421 TRACE_ID_MANGLE(args.dump_guid), 422 TRACE_EVENT_FLAG_FLOW_OUT); 423 424 // Start the process dump. This involves task runner hops as specified by the 425 // MemoryDumpProvider(s) in RegisterDumpProvider()). 426 SetupNextMemoryDump(std::move(pmd_async_state)); 427} 428 429// PostTask InvokeOnMemoryDump() to the dump provider's sequenced task runner. A 430// PostTask is always required for a generic SequencedTaskRunner to ensure that 431// no other task is running on it concurrently. SetupNextMemoryDump() and 432// InvokeOnMemoryDump() are called alternatively which linearizes the dump 433// provider's OnMemoryDump invocations. 434// At most one of either SetupNextMemoryDump() or InvokeOnMemoryDump() can be 435// active at any time for a given PMD, regardless of status of the |lock_|. 436// |lock_| is used in these functions purely to ensure consistency w.r.t. 437// (un)registrations of |dump_providers_|. 438void MemoryDumpManager::SetupNextMemoryDump( 439 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { 440 HEAP_PROFILER_SCOPED_IGNORE; 441 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs 442 // in the PostTask below don't end up registering their own dump providers 443 // (for discounting trace memory overhead) while holding the |lock_|. 444 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); 445 446 // |dump_thread_| might be destroyed before getting this point. 447 // It means that tracing was disabled right before starting this dump. 448 // Anyway either tracing is stopped or this was the last hop, create a trace 449 // event, add it to the trace and finalize process dump invoking the callback. 450 if (!pmd_async_state->dump_thread_task_runner.get()) { 451 if (pmd_async_state->pending_dump_providers.empty()) { 452 VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" 453 << " before finalizing the dump"; 454 } else { 455 VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" 456 << " before dumping " 457 << pmd_async_state->pending_dump_providers.back().get()->name; 458 } 459 pmd_async_state->dump_successful = false; 460 pmd_async_state->pending_dump_providers.clear(); 461 } 462 if (pmd_async_state->pending_dump_providers.empty()) 463 return FinalizeDumpAndAddToTrace(std::move(pmd_async_state)); 464 465 // Read MemoryDumpProviderInfo thread safety considerations in 466 // memory_dump_manager.h when accessing |mdpinfo| fields. 467 MemoryDumpProviderInfo* mdpinfo = 468 pmd_async_state->pending_dump_providers.back().get(); 469 470 // If we are in background tracing, we should invoke only the whitelisted 471 // providers. Ignore other providers and continue. 472 if (pmd_async_state->req_args.level_of_detail == 473 MemoryDumpLevelOfDetail::BACKGROUND && 474 !mdpinfo->whitelisted_for_background_mode) { 475 pmd_async_state->pending_dump_providers.pop_back(); 476 return SetupNextMemoryDump(std::move(pmd_async_state)); 477 } 478 479 // If the dump provider did not specify a task runner affinity, dump on 480 // |dump_thread_| which is already checked above for presence. 481 SequencedTaskRunner* task_runner = mdpinfo->task_runner.get(); 482 if (!task_runner) { 483 DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner); 484 task_runner = pmd_async_state->dump_thread_task_runner.get(); 485 DCHECK(task_runner); 486 } 487 488 if (mdpinfo->options.dumps_on_single_thread_task_runner && 489 task_runner->RunsTasksOnCurrentThread()) { 490 // If |dumps_on_single_thread_task_runner| is true then no PostTask is 491 // required if we are on the right thread. 492 return InvokeOnMemoryDump(pmd_async_state.release()); 493 } 494 495 bool did_post_task = task_runner->PostTask( 496 FROM_HERE, Bind(&MemoryDumpManager::InvokeOnMemoryDump, Unretained(this), 497 Unretained(pmd_async_state.get()))); 498 499 if (did_post_task) { 500 // Ownership is tranferred to InvokeOnMemoryDump(). 501 ignore_result(pmd_async_state.release()); 502 return; 503 } 504 505 // PostTask usually fails only if the process or thread is shut down. So, the 506 // dump provider is disabled here. But, don't disable unbound dump providers. 507 // The utility thread is normally shutdown when disabling the trace and 508 // getting here in this case is expected. 509 if (mdpinfo->task_runner) { 510 LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name 511 << "\". Failed to post task on the task runner provided."; 512 513 // A locked access is required to R/W |disabled| (for the 514 // UnregisterAndDeleteDumpProviderSoon() case). 515 AutoLock lock(lock_); 516 mdpinfo->disabled = true; 517 } 518 519 // PostTask failed. Ignore the dump provider and continue. 520 pmd_async_state->pending_dump_providers.pop_back(); 521 SetupNextMemoryDump(std::move(pmd_async_state)); 522} 523 524// This function is called on the right task runner for current MDP. It is 525// either the task runner specified by MDP or |dump_thread_task_runner| if the 526// MDP did not specify task runner. Invokes the dump provider's OnMemoryDump() 527// (unless disabled). 528void MemoryDumpManager::InvokeOnMemoryDump( 529 ProcessMemoryDumpAsyncState* owned_pmd_async_state) { 530 HEAP_PROFILER_SCOPED_IGNORE; 531 // In theory |owned_pmd_async_state| should be a scoped_ptr. The only reason 532 // why it isn't is because of the corner case logic of |did_post_task| 533 // above, which needs to take back the ownership of the |pmd_async_state| when 534 // the PostTask() fails. 535 // Unfortunately, PostTask() destroys the scoped_ptr arguments upon failure 536 // to prevent accidental leaks. Using a scoped_ptr would prevent us to to 537 // skip the hop and move on. Hence the manual naked -> scoped ptr juggling. 538 auto pmd_async_state = WrapUnique(owned_pmd_async_state); 539 owned_pmd_async_state = nullptr; 540 541 // Read MemoryDumpProviderInfo thread safety considerations in 542 // memory_dump_manager.h when accessing |mdpinfo| fields. 543 MemoryDumpProviderInfo* mdpinfo = 544 pmd_async_state->pending_dump_providers.back().get(); 545 546 DCHECK(!mdpinfo->task_runner || 547 mdpinfo->task_runner->RunsTasksOnCurrentThread()); 548 549 bool should_dump; 550 { 551 // A locked access is required to R/W |disabled| (for the 552 // UnregisterAndDeleteDumpProviderSoon() case). 553 AutoLock lock(lock_); 554 555 // Unregister the dump provider if it failed too many times consecutively. 556 if (!mdpinfo->disabled && 557 mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) { 558 mdpinfo->disabled = true; 559 LOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name 560 << "\". Dump failed multiple times consecutively."; 561 } 562 should_dump = !mdpinfo->disabled; 563 } // AutoLock lock(lock_); 564 565 if (should_dump) { 566 // Invoke the dump provider. 567 TRACE_EVENT_WITH_FLOW1(kTraceCategory, 568 "MemoryDumpManager::InvokeOnMemoryDump", 569 TRACE_ID_MANGLE(pmd_async_state->req_args.dump_guid), 570 TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, 571 "dump_provider.name", mdpinfo->name); 572 573 // Pid of the target process being dumped. Often kNullProcessId (= current 574 // process), non-zero when the coordinator process creates dumps on behalf 575 // of child processes (see crbug.com/461788). 576 ProcessId target_pid = mdpinfo->options.target_pid; 577 MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail}; 578 ProcessMemoryDump* pmd = 579 pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(target_pid, 580 args); 581 bool dump_successful = mdpinfo->dump_provider->OnMemoryDump(args, pmd); 582 mdpinfo->consecutive_failures = 583 dump_successful ? 0 : mdpinfo->consecutive_failures + 1; 584 } 585 586 pmd_async_state->pending_dump_providers.pop_back(); 587 SetupNextMemoryDump(std::move(pmd_async_state)); 588} 589 590// static 591void MemoryDumpManager::FinalizeDumpAndAddToTrace( 592 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { 593 HEAP_PROFILER_SCOPED_IGNORE; 594 DCHECK(pmd_async_state->pending_dump_providers.empty()); 595 const uint64_t dump_guid = pmd_async_state->req_args.dump_guid; 596 if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) { 597 scoped_refptr<SingleThreadTaskRunner> callback_task_runner = 598 pmd_async_state->callback_task_runner; 599 callback_task_runner->PostTask( 600 FROM_HERE, Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace, 601 Passed(&pmd_async_state))); 602 return; 603 } 604 605 TRACE_EVENT_WITH_FLOW0(kTraceCategory, 606 "MemoryDumpManager::FinalizeDumpAndAddToTrace", 607 TRACE_ID_MANGLE(dump_guid), TRACE_EVENT_FLAG_FLOW_IN); 608 609 for (const auto& kv : pmd_async_state->process_dumps) { 610 ProcessId pid = kv.first; // kNullProcessId for the current process. 611 ProcessMemoryDump* process_memory_dump = kv.second.get(); 612 std::unique_ptr<TracedValue> traced_value(new TracedValue); 613 process_memory_dump->AsValueInto(traced_value.get()); 614 traced_value->SetString("level_of_detail", 615 MemoryDumpLevelOfDetailToString( 616 pmd_async_state->req_args.level_of_detail)); 617 const char* const event_name = 618 MemoryDumpTypeToString(pmd_async_state->req_args.dump_type); 619 620 std::unique_ptr<ConvertableToTraceFormat> event_value( 621 std::move(traced_value)); 622 TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID( 623 TRACE_EVENT_PHASE_MEMORY_DUMP, 624 TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, 625 trace_event_internal::kGlobalScope, dump_guid, pid, 626 kTraceEventNumArgs, kTraceEventArgNames, 627 kTraceEventArgTypes, nullptr /* arg_values */, &event_value, 628 TRACE_EVENT_FLAG_HAS_ID); 629 } 630 631 bool tracing_still_enabled; 632 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &tracing_still_enabled); 633 if (!tracing_still_enabled) { 634 pmd_async_state->dump_successful = false; 635 VLOG(1) << kLogPrefix << " failed because tracing was disabled before" 636 << " the dump was completed"; 637 } 638 639 if (!pmd_async_state->callback.is_null()) { 640 pmd_async_state->callback.Run(dump_guid, pmd_async_state->dump_successful); 641 pmd_async_state->callback.Reset(); 642 } 643 644 TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", 645 TRACE_ID_MANGLE(dump_guid)); 646} 647 648void MemoryDumpManager::OnTraceLogEnabled() { 649 bool enabled; 650 TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); 651 if (!enabled) 652 return; 653 654 // Initialize the TraceLog for the current thread. This is to avoid that the 655 // TraceLog memory dump provider is registered lazily in the PostTask() below 656 // while the |lock_| is taken; 657 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); 658 659 // Spin-up the thread used to invoke unbound dump providers. 660 std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); 661 if (!dump_thread->Start()) { 662 LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; 663 return; 664 } 665 666 const TraceConfig trace_config = 667 TraceLog::GetInstance()->GetCurrentTraceConfig(); 668 scoped_refptr<MemoryDumpSessionState> session_state = 669 new MemoryDumpSessionState; 670 session_state->SetMemoryDumpConfig(trace_config.memory_dump_config()); 671 if (heap_profiling_enabled_) { 672 // If heap profiling is enabled, the stack frame deduplicator and type name 673 // deduplicator will be in use. Add a metadata events to write the frames 674 // and type IDs. 675 session_state->SetStackFrameDeduplicator( 676 WrapUnique(new StackFrameDeduplicator)); 677 678 session_state->SetTypeNameDeduplicator( 679 WrapUnique(new TypeNameDeduplicator)); 680 681 TRACE_EVENT_API_ADD_METADATA_EVENT( 682 TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames", 683 "stackFrames", 684 WrapUnique(new SessionStateConvertableProxy<StackFrameDeduplicator>( 685 session_state, &MemoryDumpSessionState::stack_frame_deduplicator))); 686 687 TRACE_EVENT_API_ADD_METADATA_EVENT( 688 TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames", 689 "typeNames", 690 WrapUnique(new SessionStateConvertableProxy<TypeNameDeduplicator>( 691 session_state, &MemoryDumpSessionState::type_name_deduplicator))); 692 } 693 694 { 695 AutoLock lock(lock_); 696 697 DCHECK(delegate_); // At this point we must have a delegate. 698 session_state_ = session_state; 699 700 DCHECK(!dump_thread_); 701 dump_thread_ = std::move(dump_thread); 702 703 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); 704 705 // TODO(primiano): This is a temporary hack to disable periodic memory dumps 706 // when running memory benchmarks until telemetry uses TraceConfig to 707 // enable/disable periodic dumps. See crbug.com/529184 . 708 if (!is_coordinator_ || 709 CommandLine::ForCurrentProcess()->HasSwitch( 710 "enable-memory-benchmarking")) { 711 return; 712 } 713 } 714 715 // Enable periodic dumps if necessary. 716 periodic_dump_timer_.Start(trace_config.memory_dump_config().triggers); 717} 718 719void MemoryDumpManager::OnTraceLogDisabled() { 720 // There might be a memory dump in progress while this happens. Therefore, 721 // ensure that the MDM state which depends on the tracing enabled / disabled 722 // state is always accessed by the dumping methods holding the |lock_|. 723 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); 724 std::unique_ptr<Thread> dump_thread; 725 { 726 AutoLock lock(lock_); 727 dump_thread = std::move(dump_thread_); 728 session_state_ = nullptr; 729 } 730 731 // Thread stops are blocking and must be performed outside of the |lock_| 732 // or will deadlock (e.g., if SetupNextMemoryDump() tries to acquire it). 733 periodic_dump_timer_.Stop(); 734 if (dump_thread) 735 dump_thread->Stop(); 736} 737 738bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) { 739 AutoLock lock(lock_); 740 if (!session_state_) 741 return false; 742 return session_state_->memory_dump_config().allowed_dump_modes.count( 743 dump_mode) != 0; 744} 745 746uint64_t MemoryDumpManager::GetTracingProcessId() const { 747 return delegate_->GetTracingProcessId(); 748} 749 750MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo( 751 MemoryDumpProvider* dump_provider, 752 const char* name, 753 scoped_refptr<SequencedTaskRunner> task_runner, 754 const MemoryDumpProvider::Options& options, 755 bool whitelisted_for_background_mode) 756 : dump_provider(dump_provider), 757 name(name), 758 task_runner(std::move(task_runner)), 759 options(options), 760 consecutive_failures(0), 761 disabled(false), 762 whitelisted_for_background_mode(whitelisted_for_background_mode) {} 763 764MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {} 765 766bool MemoryDumpManager::MemoryDumpProviderInfo::Comparator::operator()( 767 const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& a, 768 const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& b) const { 769 if (!a || !b) 770 return a.get() < b.get(); 771 // Ensure that unbound providers (task_runner == nullptr) always run last. 772 // Rationale: some unbound dump providers are known to be slow, keep them last 773 // to avoid skewing timings of the other dump providers. 774 return std::tie(a->task_runner, a->dump_provider) > 775 std::tie(b->task_runner, b->dump_provider); 776} 777 778MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( 779 MemoryDumpRequestArgs req_args, 780 const MemoryDumpProviderInfo::OrderedSet& dump_providers, 781 scoped_refptr<MemoryDumpSessionState> session_state, 782 MemoryDumpCallback callback, 783 scoped_refptr<SingleThreadTaskRunner> dump_thread_task_runner) 784 : req_args(req_args), 785 session_state(std::move(session_state)), 786 callback(callback), 787 dump_successful(true), 788 callback_task_runner(ThreadTaskRunnerHandle::Get()), 789 dump_thread_task_runner(std::move(dump_thread_task_runner)) { 790 pending_dump_providers.reserve(dump_providers.size()); 791 pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend()); 792} 793 794MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() { 795} 796 797ProcessMemoryDump* MemoryDumpManager::ProcessMemoryDumpAsyncState:: 798 GetOrCreateMemoryDumpContainerForProcess(ProcessId pid, 799 const MemoryDumpArgs& dump_args) { 800 auto iter = process_dumps.find(pid); 801 if (iter == process_dumps.end()) { 802 std::unique_ptr<ProcessMemoryDump> new_pmd( 803 new ProcessMemoryDump(session_state, dump_args)); 804 iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; 805 } 806 return iter->second.get(); 807} 808 809MemoryDumpManager::PeriodicGlobalDumpTimer::PeriodicGlobalDumpTimer() {} 810 811MemoryDumpManager::PeriodicGlobalDumpTimer::~PeriodicGlobalDumpTimer() { 812 Stop(); 813} 814 815void MemoryDumpManager::PeriodicGlobalDumpTimer::Start( 816 const std::vector<TraceConfig::MemoryDumpConfig::Trigger>& triggers_list) { 817 if (triggers_list.empty()) 818 return; 819 820 // At the moment the periodic support is limited to at most one periodic 821 // trigger per dump mode. All intervals should be an integer multiple of the 822 // smallest interval specified. 823 periodic_dumps_count_ = 0; 824 uint32_t min_timer_period_ms = std::numeric_limits<uint32_t>::max(); 825 uint32_t light_dump_period_ms = 0; 826 uint32_t heavy_dump_period_ms = 0; 827 DCHECK_LE(triggers_list.size(), 3u); 828 auto* mdm = MemoryDumpManager::GetInstance(); 829 for (const TraceConfig::MemoryDumpConfig::Trigger& config : triggers_list) { 830 DCHECK_NE(0u, config.periodic_interval_ms); 831 switch (config.level_of_detail) { 832 case MemoryDumpLevelOfDetail::BACKGROUND: 833 DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::BACKGROUND)); 834 break; 835 case MemoryDumpLevelOfDetail::LIGHT: 836 DCHECK_EQ(0u, light_dump_period_ms); 837 DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::LIGHT)); 838 light_dump_period_ms = config.periodic_interval_ms; 839 break; 840 case MemoryDumpLevelOfDetail::DETAILED: 841 DCHECK_EQ(0u, heavy_dump_period_ms); 842 DCHECK(mdm->IsDumpModeAllowed(MemoryDumpLevelOfDetail::DETAILED)); 843 heavy_dump_period_ms = config.periodic_interval_ms; 844 break; 845 } 846 min_timer_period_ms = 847 std::min(min_timer_period_ms, config.periodic_interval_ms); 848 } 849 850 DCHECK_EQ(0u, light_dump_period_ms % min_timer_period_ms); 851 light_dump_rate_ = light_dump_period_ms / min_timer_period_ms; 852 DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms); 853 heavy_dump_rate_ = heavy_dump_period_ms / min_timer_period_ms; 854 855 timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(min_timer_period_ms), 856 base::Bind(&PeriodicGlobalDumpTimer::RequestPeriodicGlobalDump, 857 base::Unretained(this))); 858} 859 860void MemoryDumpManager::PeriodicGlobalDumpTimer::Stop() { 861 if (IsRunning()) { 862 timer_.Stop(); 863 } 864} 865 866bool MemoryDumpManager::PeriodicGlobalDumpTimer::IsRunning() { 867 return timer_.IsRunning(); 868} 869 870void MemoryDumpManager::PeriodicGlobalDumpTimer::RequestPeriodicGlobalDump() { 871 MemoryDumpLevelOfDetail level_of_detail = MemoryDumpLevelOfDetail::BACKGROUND; 872 if (light_dump_rate_ > 0 && periodic_dumps_count_ % light_dump_rate_ == 0) 873 level_of_detail = MemoryDumpLevelOfDetail::LIGHT; 874 if (heavy_dump_rate_ > 0 && periodic_dumps_count_ % heavy_dump_rate_ == 0) 875 level_of_detail = MemoryDumpLevelOfDetail::DETAILED; 876 ++periodic_dumps_count_; 877 878 MemoryDumpManager::GetInstance()->RequestGlobalDump( 879 MemoryDumpType::PERIODIC_INTERVAL, level_of_detail); 880} 881 882} // namespace trace_event 883} // namespace base 884