123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/trace_event/memory_dump_manager.h"
- #include <inttypes.h>
- #include <stdio.h>
- #include <algorithm>
- #include <memory>
- #include <tuple>
- #include <utility>
- #include "base/allocator/buildflags.h"
- #include "base/base_switches.h"
- #include "base/command_line.h"
- #include "base/debug/alias.h"
- #include "base/debug/stack_trace.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/strings/string_util.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
- #include "base/threading/thread.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/trace_event/heap_profiler.h"
- #include "base/trace_event/heap_profiler_allocation_context_tracker.h"
- #include "base/trace_event/malloc_dump_provider.h"
- #include "base/trace_event/memory_dump_provider.h"
- #include "base/trace_event/memory_dump_scheduler.h"
- #include "base/trace_event/memory_infra_background_allowlist.h"
- #include "base/trace_event/process_memory_dump.h"
- #include "base/trace_event/trace_event.h"
- #include "base/trace_event/traced_value.h"
- #include "build/build_config.h"
- #if BUILDFLAG(IS_ANDROID)
- #include "base/trace_event/java_heap_dump_provider_android.h"
- #if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE)
- #include "base/trace_event/cfi_backtrace_android.h"
- #endif
- #endif // BUILDFLAG(IS_ANDROID)
- #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
- #include "base/trace_event/address_space_dump_provider.h"
- #endif
- namespace base {
- namespace trace_event {
- namespace {
- MemoryDumpManager* g_memory_dump_manager_for_testing = nullptr;
- // Temporary (until scheduler is moved outside of here)
- // trampoline function to match the |request_dump_function| passed to Initialize
- // to the callback expected by MemoryDumpScheduler.
- // TODO(primiano): remove this.
- void DoGlobalDumpWithoutCallback(
- MemoryDumpManager::RequestGlobalDumpFunction global_dump_fn,
- MemoryDumpType dump_type,
- MemoryDumpLevelOfDetail level_of_detail) {
- global_dump_fn.Run(dump_type, level_of_detail);
- }
- } // namespace
- // static
- constexpr const char* MemoryDumpManager::kTraceCategory;
- // static
- const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
- // static
- const uint64_t MemoryDumpManager::kInvalidTracingProcessId = 0;
- // static
- const char* const MemoryDumpManager::kSystemAllocatorPoolName =
- #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
- MallocDumpProvider::kAllocatedObjects;
- #else
- nullptr;
- #endif
- // static
- MemoryDumpManager* MemoryDumpManager::GetInstance() {
- if (g_memory_dump_manager_for_testing)
- return g_memory_dump_manager_for_testing;
- return Singleton<MemoryDumpManager,
- LeakySingletonTraits<MemoryDumpManager>>::get();
- }
- // static
- std::unique_ptr<MemoryDumpManager>
- MemoryDumpManager::CreateInstanceForTesting() {
- DCHECK(!g_memory_dump_manager_for_testing);
- std::unique_ptr<MemoryDumpManager> instance(new MemoryDumpManager());
- g_memory_dump_manager_for_testing = instance.get();
- return instance;
- }
- MemoryDumpManager::MemoryDumpManager() = default;
- MemoryDumpManager::~MemoryDumpManager() {
- Thread* dump_thread = nullptr;
- {
- AutoLock lock(lock_);
- if (dump_thread_) {
- dump_thread = dump_thread_.get();
- }
- }
- if (dump_thread) {
- dump_thread->Stop();
- }
- AutoLock lock(lock_);
- dump_thread_.reset();
- g_memory_dump_manager_for_testing = nullptr;
- }
- void MemoryDumpManager::Initialize(
- RequestGlobalDumpFunction request_dump_function,
- bool is_coordinator) {
- {
- AutoLock lock(lock_);
- DCHECK(!request_dump_function.is_null());
- DCHECK(!can_request_global_dumps());
- request_dump_function_ = request_dump_function;
- is_coordinator_ = is_coordinator;
- }
- // Enable the core dump providers.
- #if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
- RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
- #endif
- #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
- RegisterDumpProvider(AddressSpaceDumpProvider::GetInstance(),
- "PartitionAlloc.AddressSpace", nullptr);
- #endif
- #if BUILDFLAG(IS_ANDROID)
- RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
- nullptr);
- #endif
- }
- void MemoryDumpManager::RegisterDumpProvider(
- MemoryDumpProvider* mdp,
- const char* name,
- scoped_refptr<SingleThreadTaskRunner> task_runner,
- MemoryDumpProvider::Options options) {
- options.dumps_on_single_thread_task_runner = true;
- RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
- }
- void MemoryDumpManager::RegisterDumpProvider(
- MemoryDumpProvider* mdp,
- const char* name,
- scoped_refptr<SingleThreadTaskRunner> task_runner) {
- // Set |dumps_on_single_thread_task_runner| to true because all providers
- // without task runner are run on dump thread.
- MemoryDumpProvider::Options options;
- options.dumps_on_single_thread_task_runner = true;
- RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
- }
- void MemoryDumpManager::RegisterDumpProviderWithSequencedTaskRunner(
- MemoryDumpProvider* mdp,
- const char* name,
- scoped_refptr<SequencedTaskRunner> task_runner,
- MemoryDumpProvider::Options options) {
- DCHECK(task_runner);
- options.dumps_on_single_thread_task_runner = false;
- RegisterDumpProviderInternal(mdp, name, std::move(task_runner), options);
- }
- void MemoryDumpManager::RegisterDumpProviderInternal(
- MemoryDumpProvider* mdp,
- const char* name,
- scoped_refptr<SequencedTaskRunner> task_runner,
- const MemoryDumpProvider::Options& options) {
- if (dumper_registrations_ignored_for_testing_)
- return;
- // Only a handful of MDPs are required to compute the memory metrics. These
- // have small enough performance overhead that it is reasonable to run them
- // in the background while the user is doing other things. Those MDPs are
- // 'allowed in background mode'.
- bool allowed_in_background_mode = IsMemoryDumpProviderInAllowlist(name);
- scoped_refptr<MemoryDumpProviderInfo> mdpinfo = new MemoryDumpProviderInfo(
- mdp, name, std::move(task_runner), options, allowed_in_background_mode);
- {
- AutoLock lock(lock_);
- bool already_registered = !dump_providers_.insert(mdpinfo).second;
- // This actually happens in some tests which don't have a clean tear-down
- // path for RenderThreadImpl::Init().
- if (already_registered)
- return;
- }
- }
- void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
- UnregisterDumpProviderInternal(mdp, false /* delete_async */);
- }
- void MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon(
- std::unique_ptr<MemoryDumpProvider> mdp) {
- UnregisterDumpProviderInternal(mdp.release(), true /* delete_async */);
- }
- void MemoryDumpManager::UnregisterDumpProviderInternal(
- MemoryDumpProvider* mdp,
- bool take_mdp_ownership_and_delete_async) {
- std::unique_ptr<MemoryDumpProvider> owned_mdp;
- if (take_mdp_ownership_and_delete_async)
- owned_mdp.reset(mdp);
- AutoLock lock(lock_);
- auto mdp_iter = dump_providers_.begin();
- for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
- if ((*mdp_iter)->dump_provider == mdp)
- break;
- }
- if (mdp_iter == dump_providers_.end())
- return; // Not registered / already unregistered.
- if (take_mdp_ownership_and_delete_async) {
- // The MDP will be deleted whenever the MDPInfo struct will, that is either:
- // - At the end of this function, if no dump is in progress.
- // - In ContinueAsyncProcessDump() when MDPInfo is removed from
- // |pending_dump_providers|.
- DCHECK(!(*mdp_iter)->owned_dump_provider);
- (*mdp_iter)->owned_dump_provider = std::move(owned_mdp);
- } else {
- // If you hit this DCHECK, your dump provider has a bug.
- // Unregistration of a MemoryDumpProvider is safe only if:
- // - The MDP has specified a sequenced task runner affinity AND the
- // unregistration happens on the same task runner. So that the MDP cannot
- // unregister and be in the middle of a OnMemoryDump() at the same time.
- // - The MDP has NOT specified a task runner affinity and its ownership is
- // transferred via UnregisterAndDeleteDumpProviderSoon().
- // In all the other cases, it is not possible to guarantee that the
- // unregistration will not race with OnMemoryDump() calls.
- DCHECK((*mdp_iter)->task_runner &&
- (*mdp_iter)->task_runner->RunsTasksInCurrentSequence())
- << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to "
- << "unregister itself in a racy way. Please file a crbug.";
- }
- // The MDPInfo instance can still be referenced by the
- // |ProcessMemoryDumpAsyncState.pending_dump_providers|. For this reason
- // the MDPInfo is flagged as disabled. It will cause InvokeOnMemoryDump()
- // to just skip it, without actually invoking the |mdp|, which might be
- // destroyed by the caller soon after this method returns.
- (*mdp_iter)->disabled = true;
- dump_providers_.erase(mdp_iter);
- }
- bool MemoryDumpManager::IsDumpProviderRegisteredForTesting(
- MemoryDumpProvider* provider) {
- AutoLock lock(lock_);
- for (const auto& info : dump_providers_) {
- if (info->dump_provider == provider)
- return true;
- }
- return false;
- }
- scoped_refptr<SequencedTaskRunner>
- MemoryDumpManager::GetDumpThreadTaskRunner() {
- base::AutoLock lock(lock_);
- return GetOrCreateBgTaskRunnerLocked();
- }
- scoped_refptr<base::SequencedTaskRunner>
- MemoryDumpManager::GetOrCreateBgTaskRunnerLocked() {
- if (dump_thread_)
- return dump_thread_->task_runner();
- dump_thread_ = std::make_unique<Thread>("MemoryInfra");
- bool started = dump_thread_->Start();
- CHECK(started);
- return dump_thread_->task_runner();
- }
- void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
- ProcessMemoryDumpCallback callback) {
- char guid_str[20];
- snprintf(guid_str, std::size(guid_str), "0x%" PRIx64, args.dump_guid);
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump",
- TRACE_ID_LOCAL(args.dump_guid), "dump_guid",
- TRACE_STR_COPY(guid_str));
- // If argument filter is enabled then only background mode dumps should be
- // allowed. In case the trace config passed for background tracing session
- // missed the allowed modes argument, it crashes here instead of creating
- // unexpected dumps.
- if (TraceLog::GetInstance()
- ->GetCurrentTraceConfig()
- .IsArgumentFilterEnabled()) {
- CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail);
- }
- std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
- {
- AutoLock lock(lock_);
- pmd_async_state = std::make_unique<ProcessMemoryDumpAsyncState>(
- args, dump_providers_, std::move(callback),
- GetOrCreateBgTaskRunnerLocked());
- }
- // Start the process dump. This involves task runner hops as specified by the
- // MemoryDumpProvider(s) in RegisterDumpProvider()).
- ContinueAsyncProcessDump(pmd_async_state.release());
- }
- // Invokes OnMemoryDump() on all MDPs that are next in the pending list and run
- // on the current sequenced task runner. If the next MDP does not run in current
- // sequenced task runner, then switches to that task runner and continues. All
- // OnMemoryDump() invocations are linearized. |lock_| is used in these functions
- // purely to ensure consistency w.r.t. (un)registrations of |dump_providers_|.
- void MemoryDumpManager::ContinueAsyncProcessDump(
- ProcessMemoryDumpAsyncState* owned_pmd_async_state) {
- HEAP_PROFILER_SCOPED_IGNORE;
- // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
- // in the PostTask below don't end up registering their own dump providers
- // (for discounting trace memory overhead) while holding the |lock_|.
- TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
- // In theory |owned_pmd_async_state| should be a unique_ptr. The only reason
- // why it isn't is because of the corner case logic of |did_post_task|
- // above, which needs to take back the ownership of the |pmd_async_state| when
- // the PostTask() fails.
- // Unfortunately, PostTask() destroys the unique_ptr arguments upon failure
- // to prevent accidental leaks. Using a unique_ptr would prevent us to to
- // skip the hop and move on. Hence the manual naked -> unique ptr juggling.
- auto pmd_async_state = WrapUnique(owned_pmd_async_state);
- owned_pmd_async_state = nullptr;
- while (!pmd_async_state->pending_dump_providers.empty()) {
- // Read MemoryDumpProviderInfo thread safety considerations in
- // memory_dump_manager.h when accessing |mdpinfo| fields.
- MemoryDumpProviderInfo* mdpinfo =
- pmd_async_state->pending_dump_providers.back().get();
- // If we are in background mode, we should invoke only the allowed
- // providers. Ignore other providers and continue.
- if (pmd_async_state->req_args.level_of_detail ==
- MemoryDumpLevelOfDetail::BACKGROUND &&
- !mdpinfo->allowed_in_background_mode) {
- pmd_async_state->pending_dump_providers.pop_back();
- continue;
- }
- // If the dump provider did not specify a task runner affinity, dump on
- // |dump_thread_|.
- scoped_refptr<SequencedTaskRunner> task_runner = mdpinfo->task_runner;
- if (!task_runner) {
- DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner);
- task_runner = pmd_async_state->dump_thread_task_runner;
- DCHECK(task_runner);
- }
- // If |RunsTasksInCurrentSequence()| is true then no PostTask is
- // required since we are on the right SequencedTaskRunner.
- if (task_runner->RunsTasksInCurrentSequence()) {
- InvokeOnMemoryDump(mdpinfo, pmd_async_state->process_memory_dump.get());
- pmd_async_state->pending_dump_providers.pop_back();
- continue;
- }
- bool did_post_task = task_runner->PostTask(
- FROM_HERE,
- BindOnce(&MemoryDumpManager::ContinueAsyncProcessDump, Unretained(this),
- Unretained(pmd_async_state.get())));
- if (did_post_task) {
- // Ownership is transferred to the posted task.
- std::ignore = pmd_async_state.release();
- return;
- }
- // PostTask usually fails only if the process or thread is shut down. So,
- // the dump provider is disabled here. But, don't disable unbound dump
- // providers, since the |dump_thread_| is controlled by MDM.
- if (mdpinfo->task_runner) {
- // A locked access is required to R/W |disabled| (for the
- // UnregisterAndDeleteDumpProviderSoon() case).
- AutoLock lock(lock_);
- mdpinfo->disabled = true;
- }
- // PostTask failed. Ignore the dump provider and continue.
- pmd_async_state->pending_dump_providers.pop_back();
- }
- FinishAsyncProcessDump(std::move(pmd_async_state));
- }
- // This function is called on the right task runner for current MDP. It is
- // either the task runner specified by MDP or |dump_thread_task_runner| if the
- // MDP did not specify task runner. Invokes the dump provider's OnMemoryDump()
- // (unless disabled).
- void MemoryDumpManager::InvokeOnMemoryDump(MemoryDumpProviderInfo* mdpinfo,
- ProcessMemoryDump* pmd) {
- HEAP_PROFILER_SCOPED_IGNORE;
- DCHECK(!mdpinfo->task_runner ||
- mdpinfo->task_runner->RunsTasksInCurrentSequence());
- TRACE_EVENT1(kTraceCategory, "MemoryDumpManager::InvokeOnMemoryDump",
- "dump_provider.name", mdpinfo->name);
- // Do not add any other TRACE_EVENT macro (or function that might have them)
- // below this point. Under some rare circunstances, they can re-initialize
- // and invalide the current ThreadLocalEventBuffer MDP, making the
- // |should_dump| check below susceptible to TOCTTOU bugs
- // (https://crbug.com/763365).
- bool is_thread_bound;
- {
- // A locked access is required to R/W |disabled| (for the
- // UnregisterAndDeleteDumpProviderSoon() case).
- AutoLock lock(lock_);
- // Unregister the dump provider if it failed too many times consecutively.
- if (!mdpinfo->disabled &&
- mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) {
- mdpinfo->disabled = true;
- DLOG(ERROR) << "Disabling MemoryDumpProvider \"" << mdpinfo->name
- << "\". Dump failed multiple times consecutively.";
- }
- if (mdpinfo->disabled)
- return;
- is_thread_bound = mdpinfo->task_runner != nullptr;
- } // AutoLock lock(lock_);
- // Invoke the dump provider.
- // A stack allocated string with dump provider name is useful to debug
- // crashes while invoking dump after a |dump_provider| is not unregistered
- // in safe way.
- char provider_name_for_debugging[16];
- strncpy(provider_name_for_debugging, mdpinfo->name,
- sizeof(provider_name_for_debugging) - 1);
- provider_name_for_debugging[sizeof(provider_name_for_debugging) - 1] = '\0';
- base::debug::Alias(provider_name_for_debugging);
- ANNOTATE_BENIGN_RACE(&mdpinfo->disabled, "best-effort race detection");
- CHECK(!is_thread_bound ||
- !*(static_cast<volatile bool*>(&mdpinfo->disabled)));
- bool dump_successful =
- mdpinfo->dump_provider->OnMemoryDump(pmd->dump_args(), pmd);
- mdpinfo->consecutive_failures =
- dump_successful ? 0 : mdpinfo->consecutive_failures + 1;
- }
- void MemoryDumpManager::FinishAsyncProcessDump(
- std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
- HEAP_PROFILER_SCOPED_IGNORE;
- DCHECK(pmd_async_state->pending_dump_providers.empty());
- const uint64_t dump_guid = pmd_async_state->req_args.dump_guid;
- if (!pmd_async_state->callback_task_runner->BelongsToCurrentThread()) {
- scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
- pmd_async_state->callback_task_runner;
- callback_task_runner->PostTask(
- FROM_HERE, BindOnce(&MemoryDumpManager::FinishAsyncProcessDump,
- Unretained(this), std::move(pmd_async_state)));
- return;
- }
- TRACE_EVENT0(kTraceCategory, "MemoryDumpManager::FinishAsyncProcessDump");
- if (!pmd_async_state->callback.is_null()) {
- std::move(pmd_async_state->callback)
- .Run(true /* success */, dump_guid,
- std::move(pmd_async_state->process_memory_dump));
- }
- TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump",
- TRACE_ID_LOCAL(dump_guid));
- }
- void MemoryDumpManager::SetupForTracing(
- const TraceConfig::MemoryDumpConfig& memory_dump_config) {
- AutoLock lock(lock_);
- // At this point we must have the ability to request global dumps.
- DCHECK(can_request_global_dumps());
- MemoryDumpScheduler::Config periodic_config;
- for (const auto& trigger : memory_dump_config.triggers) {
- if (trigger.trigger_type == MemoryDumpType::PERIODIC_INTERVAL) {
- if (periodic_config.triggers.empty()) {
- periodic_config.callback =
- BindRepeating(&DoGlobalDumpWithoutCallback, request_dump_function_,
- MemoryDumpType::PERIODIC_INTERVAL);
- }
- periodic_config.triggers.push_back(
- {trigger.level_of_detail, trigger.min_time_between_dumps_ms});
- }
- }
- // Only coordinator process triggers periodic memory dumps.
- if (is_coordinator_ && !periodic_config.triggers.empty()) {
- MemoryDumpScheduler::GetInstance()->Start(periodic_config,
- GetOrCreateBgTaskRunnerLocked());
- }
- }
- void MemoryDumpManager::TeardownForTracing() {
- // There might be a memory dump in progress while this happens. Therefore,
- // ensure that the MDM state which depends on the tracing enabled / disabled
- // state is always accessed by the dumping methods holding the |lock_|.
- AutoLock lock(lock_);
- MemoryDumpScheduler::GetInstance()->Stop();
- }
- MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
- MemoryDumpRequestArgs req_args,
- const MemoryDumpProviderInfo::OrderedSet& dump_providers,
- ProcessMemoryDumpCallback callback,
- scoped_refptr<SequencedTaskRunner> dump_thread_task_runner)
- : req_args(req_args),
- callback(std::move(callback)),
- callback_task_runner(ThreadTaskRunnerHandle::Get()),
- dump_thread_task_runner(std::move(dump_thread_task_runner)) {
- pending_dump_providers.reserve(dump_providers.size());
- pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
- MemoryDumpArgs args = {req_args.level_of_detail, req_args.determinism,
- req_args.dump_guid};
- process_memory_dump = std::make_unique<ProcessMemoryDump>(args);
- }
- MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() =
- default;
- } // namespace trace_event
- } // namespace base
|