|  | // 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 <utility> | 
|  |  | 
|  | #include "base/base_switches.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/debug/alias.h" | 
|  | #include "base/debug/stack_trace.h" | 
|  | #include "base/debug/thread_heap_usage_tracker.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/sequenced_task_runner.h" | 
|  | #include "base/strings/string_util.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/heap_profiler_event_filter.h" | 
|  | #include "base/trace_event/heap_profiler_serialization_state.h" | 
|  | #include "base/trace_event/heap_profiler_stack_frame_deduplicator.h" | 
|  | #include "base/trace_event/heap_profiler_type_name_deduplicator.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_whitelist.h" | 
|  | #include "base/trace_event/memory_peak_detector.h" | 
|  | #include "base/trace_event/process_memory_dump.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "base/trace_event/trace_event_argument.h" | 
|  | #include "build/build_config.h" | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | #include "base/trace_event/java_heap_dump_provider_android.h" | 
|  |  | 
|  | #endif  // defined(OS_ANDROID) | 
|  |  | 
|  | namespace base { | 
|  | namespace trace_event { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char* const kTraceEventArgNames[] = {"dumps"}; | 
|  | const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE}; | 
|  |  | 
|  | MemoryDumpManager* g_memory_dump_manager_for_testing = nullptr; | 
|  |  | 
|  | // Temporary (until peak detector and scheduler are moved outside of here) | 
|  | // trampoline function to match the |request_dump_function| passed to Initialize | 
|  | // to the callback expected by MemoryPeakDetector and 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); | 
|  | } | 
|  |  | 
|  | // Proxy class which wraps a ConvertableToTraceFormat owned by the | 
|  | // |heap_profiler_serialization_state| into a proxy object that can be added to | 
|  | // the trace event log. This is to solve the problem that the | 
|  | // HeapProfilerSerializationState is refcounted but the tracing subsystem wants | 
|  | // a std::unique_ptr<ConvertableToTraceFormat>. | 
|  | template <typename T> | 
|  | struct SessionStateConvertableProxy : public ConvertableToTraceFormat { | 
|  | using GetterFunctPtr = T* (HeapProfilerSerializationState::*)() const; | 
|  |  | 
|  | SessionStateConvertableProxy(scoped_refptr<HeapProfilerSerializationState> | 
|  | heap_profiler_serialization_state, | 
|  | GetterFunctPtr getter_function) | 
|  | : heap_profiler_serialization_state(heap_profiler_serialization_state), | 
|  | getter_function(getter_function) {} | 
|  |  | 
|  | void AppendAsTraceFormat(std::string* out) const override { | 
|  | return (heap_profiler_serialization_state.get()->*getter_function)() | 
|  | ->AppendAsTraceFormat(out); | 
|  | } | 
|  |  | 
|  | void EstimateTraceMemoryOverhead( | 
|  | TraceEventMemoryOverhead* overhead) override { | 
|  | return (heap_profiler_serialization_state.get()->*getter_function)() | 
|  | ->EstimateTraceMemoryOverhead(overhead); | 
|  | } | 
|  |  | 
|  | scoped_refptr<HeapProfilerSerializationState> | 
|  | heap_profiler_serialization_state; | 
|  | GetterFunctPtr const getter_function; | 
|  | }; | 
|  |  | 
|  | void NotifyHeapProfilingEnabledOnMDPThread( | 
|  | scoped_refptr<MemoryDumpProviderInfo> mdpinfo, | 
|  | bool profiling_enabled) { | 
|  | mdpinfo->dump_provider->OnHeapProfilingEnabled(profiling_enabled); | 
|  | } | 
|  |  | 
|  | inline bool ShouldEnableMDPAllocatorHooks(HeapProfilingMode mode) { | 
|  | return (mode == kHeapProfilingModePseudo) || | 
|  | (mode == kHeapProfilingModeNative) || | 
|  | (mode == kHeapProfilingModeBackground); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | const char* const MemoryDumpManager::kTraceCategory = | 
|  | TRACE_DISABLED_BY_DEFAULT("memory-infra"); | 
|  |  | 
|  | // 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() | 
|  | : is_coordinator_(false), | 
|  | tracing_process_id_(kInvalidTracingProcessId), | 
|  | dumper_registrations_ignored_for_testing_(false), | 
|  | heap_profiling_mode_(kHeapProfilingModeDisabled) {} | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | bool MemoryDumpManager::EnableHeapProfiling(HeapProfilingMode profiling_mode) { | 
|  | AutoLock lock(lock_); | 
|  | heap_profiling_mode_ = kHeapProfilingModeInvalid; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | HeapProfilingMode MemoryDumpManager::GetHeapProfilingMode() { | 
|  | AutoLock lock(lock_); | 
|  | return heap_profiling_mode_; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | base::trace_event::MemoryDumpProvider::Options options; | 
|  | options.supports_heap_profiling = true; | 
|  | RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr, | 
|  | options); | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_ANDROID) | 
|  | RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap", | 
|  | nullptr); | 
|  | #endif | 
|  |  | 
|  | TRACE_EVENT_WARMUP_CATEGORY(kTraceCategory); | 
|  | } | 
|  |  | 
|  | 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 resonable to run them | 
|  | // in the background while the user is doing other things. Those MDPs are | 
|  | // 'whitelisted for background mode'. | 
|  | bool whitelisted_for_background_mode = IsMemoryDumpProviderWhitelisted(name); | 
|  |  | 
|  | scoped_refptr<MemoryDumpProviderInfo> mdpinfo = | 
|  | new MemoryDumpProviderInfo(mdp, name, std::move(task_runner), options, | 
|  | whitelisted_for_background_mode); | 
|  |  | 
|  | if (options.is_fast_polling_supported) { | 
|  | DCHECK(!mdpinfo->task_runner) << "MemoryDumpProviders capable of fast " | 
|  | "polling must NOT be thread bound."; | 
|  | } | 
|  |  | 
|  | { | 
|  | 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; | 
|  |  | 
|  | if (options.is_fast_polling_supported) | 
|  | MemoryPeakDetector::GetInstance()->NotifyMemoryDumpProvidersChanged(); | 
|  |  | 
|  | if (ShouldEnableMDPAllocatorHooks(heap_profiling_mode_)) | 
|  | NotifyHeapProfilingEnabledLocked(mdpinfo, true); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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|. | 
|  | // - When the provider is removed from other clients (MemoryPeakDetector). | 
|  | 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."; | 
|  | } | 
|  |  | 
|  | if ((*mdp_iter)->options.is_fast_polling_supported) { | 
|  | DCHECK(take_mdp_ownership_and_delete_async); | 
|  | MemoryPeakDetector::GetInstance()->NotifyMemoryDumpProvidersChanged(); | 
|  | } | 
|  |  | 
|  | // 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); | 
|  | } | 
|  |  | 
|  | void MemoryDumpManager::GetDumpProvidersForPolling( | 
|  | std::vector<scoped_refptr<MemoryDumpProviderInfo>>* providers) { | 
|  | DCHECK(providers->empty()); | 
|  | AutoLock lock(lock_); | 
|  | for (const scoped_refptr<MemoryDumpProviderInfo>& mdp : dump_providers_) { | 
|  | if (mdp->options.is_fast_polling_supported) | 
|  | providers->push_back(mdp); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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<base::SequencedTaskRunner> | 
|  | MemoryDumpManager::GetOrCreateBgTaskRunnerLocked() { | 
|  | lock_.AssertAcquired(); | 
|  |  | 
|  | 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, | 
|  | const ProcessMemoryDumpCallback& callback) { | 
|  | char guid_str[20]; | 
|  | sprintf(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_); | 
|  |  | 
|  | // MDM could have been disabled by this point destroying | 
|  | // |heap_profiler_serialization_state|. If heap profiling is enabled we | 
|  | // require session state so if heap profiling is on and session state is | 
|  | // absent we fail the dump immediately. If heap profiler is enabled during | 
|  | // the dump, then the dump succeeds since the dump was requested before, and | 
|  | // the future process dumps will contain heap dumps. | 
|  | if (args.dump_type != MemoryDumpType::SUMMARY_ONLY && | 
|  | ShouldEnableMDPAllocatorHooks(heap_profiling_mode_) && | 
|  | !heap_profiler_serialization_state_) { | 
|  | callback.Run(false /* success */, args.dump_guid, nullptr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | pmd_async_state.reset(new ProcessMemoryDumpAsyncState( | 
|  | args, dump_providers_, heap_profiler_serialization_state_, callback, | 
|  | GetOrCreateBgTaskRunnerLocked())); | 
|  |  | 
|  | // If enabled, holds back the peak detector resetting its estimation window. | 
|  | MemoryPeakDetector::GetInstance()->Throttle(); | 
|  | } | 
|  |  | 
|  | // 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 whitelisted | 
|  | // providers. Ignore other providers and continue. | 
|  | if (pmd_async_state->req_args.level_of_detail == | 
|  | MemoryDumpLevelOfDetail::BACKGROUND && | 
|  | !mdpinfo->whitelisted_for_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 tranferred to the posted task. | 
|  | ignore_result(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"); | 
|  |  | 
|  | // In the general case (allocators and edges) the serialization into the trace | 
|  | // buffer is handled by the memory-infra service (see tracing_observer.cc). | 
|  | // This special case below deals only with serialization of the heap profiler | 
|  | // and is temporary given the upcoming work on the out-of-process heap | 
|  | // profiler. | 
|  | const auto& args = pmd_async_state->req_args; | 
|  | if (!pmd_async_state->process_memory_dump->heap_dumps().empty()) { | 
|  | std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>(); | 
|  | pmd_async_state->process_memory_dump->SerializeHeapProfilerDumpsInto( | 
|  | traced_value.get()); | 
|  |  | 
|  | traced_value->SetString("level_of_detail", | 
|  | base::trace_event::MemoryDumpLevelOfDetailToString( | 
|  | args.level_of_detail)); | 
|  | std::unique_ptr<base::trace_event::ConvertableToTraceFormat> event_value( | 
|  | std::move(traced_value)); | 
|  | TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID( | 
|  | TRACE_EVENT_PHASE_MEMORY_DUMP, | 
|  | base::trace_event::TraceLog::GetCategoryGroupEnabled( | 
|  | base::trace_event::MemoryDumpManager::kTraceCategory), | 
|  | base::trace_event::MemoryDumpTypeToString(args.dump_type), | 
|  | trace_event_internal::kGlobalScope, args.dump_guid, | 
|  | base::kNullProcessId, 1 /* num_args */, kTraceEventArgNames, | 
|  | kTraceEventArgTypes, nullptr /* arg_values */, &event_value, | 
|  | TRACE_EVENT_FLAG_HAS_ID); | 
|  | } | 
|  |  | 
|  | if (!pmd_async_state->callback.is_null()) { | 
|  | pmd_async_state->callback.Run( | 
|  | true /* success */, dump_guid, | 
|  | std::move(pmd_async_state->process_memory_dump)); | 
|  | pmd_async_state->callback.Reset(); | 
|  | } | 
|  |  | 
|  | TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", | 
|  | TRACE_ID_LOCAL(dump_guid)); | 
|  | } | 
|  |  | 
|  | void MemoryDumpManager::SetupForTracing( | 
|  | const TraceConfig::MemoryDumpConfig& memory_dump_config) { | 
|  | AutoLock lock(lock_); | 
|  | heap_profiler_serialization_state_ = new HeapProfilerSerializationState(); | 
|  | heap_profiler_serialization_state_ | 
|  | ->set_heap_profiler_breakdown_threshold_bytes( | 
|  | memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); | 
|  | InitializeHeapProfilerStateIfNeededLocked(); | 
|  |  | 
|  | // At this point we must have the ability to request global dumps. | 
|  | DCHECK(can_request_global_dumps()); | 
|  |  | 
|  | MemoryDumpScheduler::Config periodic_config; | 
|  | bool peak_detector_configured = false; | 
|  | 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}); | 
|  | } else if (trigger.trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { | 
|  | // At most one peak trigger is allowed. | 
|  | CHECK(!peak_detector_configured); | 
|  | peak_detector_configured = true; | 
|  | MemoryPeakDetector::GetInstance()->Setup( | 
|  | BindRepeating(&MemoryDumpManager::GetDumpProvidersForPolling, | 
|  | Unretained(this)), | 
|  | GetOrCreateBgTaskRunnerLocked(), | 
|  | BindRepeating(&DoGlobalDumpWithoutCallback, request_dump_function_, | 
|  | MemoryDumpType::PEAK_MEMORY_USAGE, | 
|  | trigger.level_of_detail)); | 
|  |  | 
|  | MemoryPeakDetector::Config peak_config; | 
|  | peak_config.polling_interval_ms = 10; | 
|  | peak_config.min_time_between_peaks_ms = trigger.min_time_between_dumps_ms; | 
|  | peak_config.enable_verbose_poll_tracing = | 
|  | trigger.level_of_detail == MemoryDumpLevelOfDetail::DETAILED; | 
|  | MemoryPeakDetector::GetInstance()->Start(peak_config); | 
|  |  | 
|  | // When peak detection is enabled, trigger a dump straight away as it | 
|  | // gives a good reference point for analyzing the trace. | 
|  | if (is_coordinator_) { | 
|  | GetOrCreateBgTaskRunnerLocked()->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce(&DoGlobalDumpWithoutCallback, request_dump_function_, | 
|  | MemoryDumpType::PEAK_MEMORY_USAGE, | 
|  | trigger.level_of_detail)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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(); | 
|  | MemoryPeakDetector::GetInstance()->TearDown(); | 
|  | heap_profiler_serialization_state_ = nullptr; | 
|  | } | 
|  |  | 
|  | void MemoryDumpManager::InitializeHeapProfilerStateIfNeededLocked() { | 
|  | lock_.AssertAcquired(); | 
|  | if (!ShouldEnableMDPAllocatorHooks(heap_profiling_mode_) || | 
|  | !heap_profiler_serialization_state_ || | 
|  | heap_profiler_serialization_state_->is_initialized()) { | 
|  | return; | 
|  | } | 
|  | // If heap profiling is enabled, the stack frame deduplicator and type name | 
|  | // deduplicator will be in use. Add a metadata events to write the frames | 
|  | // and type IDs. | 
|  | heap_profiler_serialization_state_->SetStackFrameDeduplicator( | 
|  | WrapUnique(new StackFrameDeduplicator)); | 
|  | heap_profiler_serialization_state_->SetTypeNameDeduplicator( | 
|  | WrapUnique(new TypeNameDeduplicator)); | 
|  |  | 
|  | TRACE_EVENT_API_ADD_METADATA_EVENT( | 
|  | TraceLog::GetCategoryGroupEnabled("__metadata"), "stackFrames", | 
|  | "stackFrames", | 
|  | std::make_unique<SessionStateConvertableProxy<StackFrameDeduplicator>>( | 
|  | heap_profiler_serialization_state_, | 
|  | &HeapProfilerSerializationState::stack_frame_deduplicator)); | 
|  |  | 
|  | TRACE_EVENT_API_ADD_METADATA_EVENT( | 
|  | TraceLog::GetCategoryGroupEnabled("__metadata"), "typeNames", "typeNames", | 
|  | std::make_unique<SessionStateConvertableProxy<TypeNameDeduplicator>>( | 
|  | heap_profiler_serialization_state_, | 
|  | &HeapProfilerSerializationState::type_name_deduplicator)); | 
|  | } | 
|  |  | 
|  | void MemoryDumpManager::NotifyHeapProfilingEnabledLocked( | 
|  | scoped_refptr<MemoryDumpProviderInfo> mdpinfo, | 
|  | bool enabled) { | 
|  | lock_.AssertAcquired(); | 
|  | if (!mdpinfo->options.supports_heap_profiling) | 
|  | return; | 
|  |  | 
|  | const auto& task_runner = mdpinfo->task_runner | 
|  | ? mdpinfo->task_runner | 
|  | : GetOrCreateBgTaskRunnerLocked(); | 
|  | // TODO(ssid): Post tasks only for MDPs that support heap profiling. | 
|  | task_runner->PostTask( | 
|  | FROM_HERE, | 
|  | BindOnce(&NotifyHeapProfilingEnabledOnMDPThread, mdpinfo, enabled)); | 
|  | } | 
|  |  | 
|  | MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( | 
|  | MemoryDumpRequestArgs req_args, | 
|  | const MemoryDumpProviderInfo::OrderedSet& dump_providers, | 
|  | scoped_refptr<HeapProfilerSerializationState> | 
|  | heap_profiler_serialization_state_in, | 
|  | ProcessMemoryDumpCallback callback, | 
|  | scoped_refptr<SequencedTaskRunner> dump_thread_task_runner) | 
|  | : req_args(req_args), | 
|  | heap_profiler_serialization_state( | 
|  | std::move(heap_profiler_serialization_state_in)), | 
|  | callback(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.dump_guid}; | 
|  | process_memory_dump = std::make_unique<ProcessMemoryDump>( | 
|  | heap_profiler_serialization_state, args); | 
|  | } | 
|  |  | 
|  | MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() = | 
|  | default; | 
|  |  | 
|  | }  // namespace trace_event | 
|  | }  // namespace base |