summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorprimiano <primiano@chromium.org>2015-12-22 03:57:40 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-22 11:58:39 +0000
commit6dfc15bc20d12fdcd401011b00a521c4e557386e (patch)
tree70d57cb20382385338d75bc5fe36c983800553a3 /base
parent82e4040e3f48430c2e5bbfaa01109cb5b4411b0f (diff)
downloadchromium_src-6dfc15bc20d12fdcd401011b00a521c4e557386e.zip
chromium_src-6dfc15bc20d12fdcd401011b00a521c4e557386e.tar.gz
chromium_src-6dfc15bc20d12fdcd401011b00a521c4e557386e.tar.bz2
Reland of [tracing] Simplify logic of MemoryDumpManager
Reason for revert: Original CL: crrev.com/1536533004 Revert: crrev.com/1540283003 Reason for reland: The CL was reverted because caused a LayoutTest failure (pending-version-change-stuck-works-with-terminate.html). The failure was due to a race condition in an unrelated part of the codebase (see crbug.com/571432). The original CL managed to just unveil reliably the race. The race condition has been fixed in crrev.com/1544663002. I verified that the LayoutTest doesn't crash locally anymore. Original issue's description: > Revert of [tracing] Simplify logic of MemoryDumpManager (patchset #9 id:160001 of https://codereview.chromium.org/1536533004/ ) > > Reason for revert: > Caused crash in pending-version-change-stuck-works-with-terminate.html layout test. > > See: > https://build.chromium.org/p/chromium.webkit/builders/WebKit%20Mac10.10/builds/9506/steps/webkit_tests/logs/stdio > > Original issue's description: > > [tracing] Simplify logic of MemoryDumpManager > > > > This CL simplifies the unregistration logic of MemoryDumpManager. > > This is to make the overall code more readable and simplify upcoming > > changes. > > Prior to this CL, the MemoryDumpManager was keeping only one list to > > keep track of the registered dump providers. This caused the > > unregistration logic to be tricky because: > > - We couldn't remove the MDPInfo straight away, as that might > > cause invalidation of the |next_dump_provider| iterator. > > - We had to flag the MDPInfo as unregistered and postpone the deletion > > on the next dump. > > This has a major drawback: if we keep registering and unregistering > > dump providers when no tracing is happening at all, the dump_providers_ > > list grows without bounds. This is bad. > > > > This CL changes the logic as follows: > > - MDPInfo becomes refcounted. At any time it can be referenced by: > > - The MDM's |dump_providers_| list. > > - The |ProcessMemoryDumpAsyncState.pending_dump_providers| list. > > - Upon each dump, the dump provider list is snapshotted in the > > |ProcessMemoryDumpAsyncState.pending_dump_providers| > > - Upon unregistration of a dump provider we just remove the MDPInfo > > from the MDM's |dump_providers_|. If a dump is ongoing, the > > ProcessMemoryDumpAsyncState will keep it refcounted and delete it > > during the dump. > > > > This CL does not add or change any behavior of the MemoryDumpManager, > > with the exception of: > > - Fixing a corner case when dumping with no dump providers registered > > (See changes in the unittest). > > - Making the fail-safe logic more strict: if a dumper fails once, it > > will stay disabled forever. > > > > BUG=461788 > > > > Committed: https://crrev.com/9734733909e7cb41ef5c153f3c2d0927e8209133 > > Cr-Commit-Position: refs/heads/master@{#366374} > > TBR=ruuda@google.com,ssid@chromium.org > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > BUG=461788 > > Committed: https://crrev.com/6a7365c4c5ad3e3083ce3d9a269c3e1d8fdb35bc > Cr-Commit-Position: refs/heads/master@{#366386} TBR=ruuda@google.com,ssid@chromium.org BUG=461788 Review URL: https://codereview.chromium.org/1546583002 Cr-Commit-Position: refs/heads/master@{#366588}
Diffstat (limited to 'base')
-rw-r--r--base/trace_event/memory_dump_manager.cc254
-rw-r--r--base/trace_event/memory_dump_manager.h75
-rw-r--r--base/trace_event/memory_dump_manager_unittest.cc85
3 files changed, 226 insertions, 188 deletions
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index cbce210..f5c3c4f 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -193,17 +193,16 @@ void MemoryDumpManager::RegisterDumpProvider(
if (dumper_registrations_ignored_for_testing_)
return;
- MemoryDumpProviderInfo mdp_info(mdp, name, task_runner, options);
- AutoLock lock(lock_);
- auto iter_new = dump_providers_.insert(mdp_info);
-
- // If there was a previous entry, replace it with the new one. This is to deal
- // with the case where a dump provider unregisters itself and then re-
- // registers before a memory dump happens, so its entry was still in the
- // collection but flagged |unregistered|.
- if (!iter_new.second) {
- dump_providers_.erase(iter_new.first);
- dump_providers_.insert(mdp_info);
+ scoped_refptr<MemoryDumpProviderInfo> mdpinfo =
+ new MemoryDumpProviderInfo(mdp, name, task_runner, options);
+
+ {
+ AutoLock lock(lock_);
+ bool already_registered = !dump_providers_.insert(mdpinfo).second;
+ // This actually happen in some tests which don't have a clean tear-down
+ // path for RenderThreadImpl::Init().
+ if (already_registered)
+ return;
}
if (heap_profiling_enabled_)
@@ -222,7 +221,7 @@ void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
auto mdp_iter = dump_providers_.begin();
for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
- if (mdp_iter->dump_provider == mdp)
+ if ((*mdp_iter)->dump_provider == mdp)
break;
}
@@ -236,12 +235,18 @@ void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
// Otherwise, it is not possible to guarantee that its unregistration is
// race-free. If you hit this DCHECK, your MDP has a bug.
DCHECK(!subtle::NoBarrier_Load(&memory_tracing_enabled_) ||
- (mdp_iter->task_runner &&
- mdp_iter->task_runner->BelongsToCurrentThread()))
- << "MemoryDumpProvider \"" << mdp_iter->name << "\" attempted to "
+ ((*mdp_iter)->task_runner &&
+ (*mdp_iter)->task_runner->BelongsToCurrentThread()))
+ << "MemoryDumpProvider \"" << (*mdp_iter)->name << "\" attempted to "
<< "unregister itself in a racy way. Please file a crbug.";
- mdp_iter->unregistered = true;
+ // 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 ContinueAsyncProcessDump
+ // 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::RequestGlobalDump(
@@ -296,9 +301,9 @@ void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
{
AutoLock lock(lock_);
- pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
- args, dump_providers_.begin(), session_state_, callback,
- dump_thread_->task_runner()));
+ pmd_async_state.reset(
+ new ProcessMemoryDumpAsyncState(args, dump_providers_, session_state_,
+ callback, dump_thread_->task_runner()));
}
TRACE_EVENT_WITH_FLOW0(kTraceCategory, "MemoryDumpManager::CreateProcessDump",
@@ -308,7 +313,7 @@ void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
// Start the thread hop. |dump_providers_| are kept sorted by thread, so
// ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
// affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
- ContinueAsyncProcessDump(std::move(pmd_async_state));
+ ContinueAsyncProcessDump(pmd_async_state.release());
}
// At most one ContinueAsyncProcessDump() can be active at any time for a given
@@ -318,128 +323,103 @@ void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
// means of subsequent PostTask(s).
//
// 1) Prologue:
-// - Check if the dump provider is disabled, if so skip the dump.
+// - If this was the last hop, create a trace event, add it to the trace
+// and finalize (invoke callback).
// - Check if we are on the right thread. If not hop and continue there.
+// - Check if the dump provider is disabled, if so skip the dump.
// 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
// 3) Epilogue:
-// - Unregister the dump provider if it failed too many times consecutively.
-// - Advance the |next_dump_provider| iterator to the next dump provider.
-// - If this was the last hop, create a trace event, add it to the trace
-// and finalize (invoke callback).
-
+// - Unregister the dump provider if it failed too many times consecutively.
+// - Pop() the MDP from the |pending_dump_providers| list, eventually
+// destroying the MDPInfo if that was unregistered in the meantime.
void MemoryDumpManager::ContinueAsyncProcessDump(
- scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
+ ProcessMemoryDumpAsyncState* owned_pmd_async_state) {
// 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();
- const uint64_t dump_guid = pmd_async_state->req_args.dump_guid;
- const char* dump_provider_name = nullptr;
-
- // Pid of the target process being dumped. Often kNullProcessId (= current
- // process), non-zero when the coordinator process creates dumps on behalf
- // of child processes (see crbug.com/461788).
- ProcessId pid;
-
- // DO NOT put any LOG() statement in the locked sections, as in some contexts
- // (GPU process) LOG() ends up performing PostTask/IPCs.
- MemoryDumpProvider* mdp;
- bool skip_dump = false;
- {
- AutoLock lock(lock_);
+ // In theory |owned_pmd_async_state| should be a scoped_ptr. The only reason
+ // why it isn't is because of the corner case logic of |did_post_task| below,
+ // which needs to take back the ownership of the |pmd_async_state| when a
+ // thread goes away and consequently the PostTask() fails.
+ // Unfortunately, PostTask() destroys the scoped_ptr arguments upon failure
+ // to prevent accidental leaks. Using a scoped_ptr would prevent us to to
+ // skip the hop and move on. Hence the manual naked -> scoped ptr juggling.
+ auto pmd_async_state = make_scoped_ptr(owned_pmd_async_state);
+ owned_pmd_async_state = nullptr;
+
+ if (pmd_async_state->pending_dump_providers.empty())
+ return FinalizeDumpAndAddToTrace(std::move(pmd_async_state));
- auto mdp_info = pmd_async_state->next_dump_provider;
- mdp = mdp_info->dump_provider;
- dump_provider_name = mdp_info->name;
- pid = mdp_info->options.target_pid;
-
- // If the dump provider did not specify a thread affinity, dump on
- // |dump_thread_|.
- SingleThreadTaskRunner* task_runner = mdp_info->task_runner.get();
- if (!task_runner)
- task_runner = pmd_async_state->dump_thread_task_runner.get();
-
- // |dump_thread_| might have been Stop()-ed at this point (if tracing was
- // disabled in the meanwhile). In such case the PostTask() below will fail.
- // |task_runner|, however, should always be non-null.
- DCHECK(task_runner);
-
- if (mdp_info->disabled || mdp_info->unregistered) {
- skip_dump = true;
- } else if (!task_runner->BelongsToCurrentThread()) {
- // It's time to hop onto another thread.
-
- // Copy the callback + arguments just for the unlikley case in which
- // PostTask fails. In such case the Bind helper will destroy the
- // pmd_async_state and we must keep a copy of the fields to notify the
- // abort.
- MemoryDumpCallback callback = pmd_async_state->callback;
- scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
- pmd_async_state->callback_task_runner;
-
- const bool did_post_task = task_runner->PostTask(
- FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
- Unretained(this), Passed(&pmd_async_state)));
- if (did_post_task)
- return;
-
- // The thread is gone. At this point the best thing we can do is to
- // disable the dump provider and abort this dump.
- mdp_info->disabled = true;
- return AbortDumpLocked(callback, callback_task_runner, dump_guid);
+ // 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 the dump provider did not specify a thread affinity, dump on
+ // |dump_thread_|. Note that |dump_thread_| might have been Stop()-ed at this
+ // point (if tracing was disabled in the meanwhile). In such case the
+ // PostTask() below will fail, but |task_runner| should always be non-null.
+ SingleThreadTaskRunner* task_runner = mdpinfo->task_runner.get();
+ if (!task_runner)
+ task_runner = pmd_async_state->dump_thread_task_runner.get();
+
+ if (!task_runner->BelongsToCurrentThread()) {
+ // It's time to hop onto another thread.
+ const bool did_post_task = task_runner->PostTask(
+ FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
+ Unretained(this), Unretained(pmd_async_state.get())));
+ if (did_post_task) {
+ // Ownership is tranferred to the next ContinueAsyncProcessDump().
+ ignore_result(pmd_async_state.release());
+ return;
}
- } // AutoLock(lock_)
+ // The thread is gone. Skip the dump provider and keep going.
+ mdpinfo->disabled = true;
+ }
- // Invoke the dump provider without holding the |lock_|.
- bool finalize = false;
- bool dump_successful = false;
+ // At this point wither we are on the right thread (|mdpinfo.task_runner|)
+ // to access mdp fields, or the right thread is gone (and |disabled| == true).
- if (!skip_dump) {
+ if (!mdpinfo->disabled) {
+ // Invoke the dump provider.
TRACE_EVENT_WITH_FLOW1(kTraceCategory,
"MemoryDumpManager::ContinueAsyncProcessDump",
- TRACE_ID_MANGLE(dump_guid),
+ TRACE_ID_MANGLE(pmd_async_state->req_args.dump_guid),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
- "dump_provider.name", dump_provider_name);
+ "dump_provider.name", mdpinfo->name);
+
+ // Pid of the target process being dumped. Often kNullProcessId (= current
+ // process), non-zero when the coordinator process creates dumps on behalf
+ // of child processes (see crbug.com/461788).
+ ProcessId target_pid = mdpinfo->options.target_pid;
+ ProcessMemoryDump* pmd =
+ pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(target_pid);
MemoryDumpArgs args = {pmd_async_state->req_args.level_of_detail};
- ProcessMemoryDump* process_memory_dump =
- pmd_async_state->GetOrCreateMemoryDumpContainerForProcess(pid);
- dump_successful = mdp->OnMemoryDump(args, process_memory_dump);
- }
+ bool dump_successful = mdpinfo->dump_provider->OnMemoryDump(args, pmd);
- {
- AutoLock lock(lock_);
- auto mdp_info = pmd_async_state->next_dump_provider;
if (dump_successful) {
- mdp_info->consecutive_failures = 0;
- } else if (!skip_dump) {
- ++mdp_info->consecutive_failures;
- if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) {
- mdp_info->disabled = true;
+ mdpinfo->consecutive_failures = 0;
+ } else {
+ ++mdpinfo->consecutive_failures;
+ if (mdpinfo->consecutive_failures >= kMaxConsecutiveFailuresCount) {
+ mdpinfo->disabled = true;
+ LOG(ERROR) << "MemoryDumpProvider \"" << mdpinfo->name << "\" failed, "
+ << "possibly due to sandboxing (crbug.com/461788)."
+ << "Disabling dumper for current process. Try --no-sandbox.";
}
}
- ++pmd_async_state->next_dump_provider;
- finalize = pmd_async_state->next_dump_provider == dump_providers_.end();
-
- if (mdp_info->unregistered)
- dump_providers_.erase(mdp_info);
- }
-
- if (!skip_dump && !dump_successful) {
- LOG(ERROR) << "MemoryDumpProvider \"" << dump_provider_name << "\" failed, "
- << "possibly due to sandboxing (crbug.com/461788)."
- << "Disabling dumper for current process. Try --no-sandbox.";
- }
-
- if (finalize)
- return FinalizeDumpAndAddToTrace(std::move(pmd_async_state));
+ } // if (!mdpinfo->disabled)
- ContinueAsyncProcessDump(std::move(pmd_async_state));
+ pmd_async_state->pending_dump_providers.pop_back();
+ ContinueAsyncProcessDump(pmd_async_state.release());
}
// static
void MemoryDumpManager::FinalizeDumpAndAddToTrace(
scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
+ 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 =
@@ -483,20 +463,6 @@ void MemoryDumpManager::FinalizeDumpAndAddToTrace(
TRACE_ID_MANGLE(dump_guid));
}
-// static
-void MemoryDumpManager::AbortDumpLocked(
- MemoryDumpCallback callback,
- scoped_refptr<SingleThreadTaskRunner> task_runner,
- uint64_t dump_guid) {
- if (callback.is_null())
- return; // There is nothing to NACK.
-
- // Post the callback even if we are already on the right thread to avoid
- // invoking the callback while holding the lock_.
- task_runner->PostTask(FROM_HERE,
- Bind(callback, dump_guid, false /* success */));
-}
-
void MemoryDumpManager::OnTraceLogEnabled() {
bool enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
@@ -541,11 +507,6 @@ void MemoryDumpManager::OnTraceLogEnabled() {
session_state_ = new MemoryDumpSessionState(stack_frame_deduplicator,
type_name_deduplicator);
- for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
- it->disabled = false;
- it->consecutive_failures = 0;
- }
-
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
// TODO(primiano): This is a temporary hack to disable periodic memory dumps
@@ -617,31 +578,36 @@ MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
task_runner(task_runner),
options(options),
consecutive_failures(0),
- disabled(false),
- unregistered(false) {}
+ disabled(false) {}
MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {}
-bool MemoryDumpManager::MemoryDumpProviderInfo::operator<(
- const MemoryDumpProviderInfo& other) const {
- if (task_runner == other.task_runner)
- return dump_provider < other.dump_provider;
+bool MemoryDumpManager::MemoryDumpProviderInfo::Comparator::operator()(
+ const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& a,
+ const scoped_refptr<MemoryDumpManager::MemoryDumpProviderInfo>& b) const {
+ if (!a || !b)
+ return a.get() < b.get();
// Ensure that unbound providers (task_runner == nullptr) always run last.
- return !(task_runner < other.task_runner);
+ // Rationale: some unbound dump providers are known to be slow, keep them last
+ // to avoid skewing timings of the other dump providers.
+ return std::tie(a->task_runner, a->dump_provider) >
+ std::tie(b->task_runner, b->dump_provider);
}
MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
MemoryDumpRequestArgs req_args,
- MemoryDumpProviderInfoSet::iterator next_dump_provider,
+ const MemoryDumpProviderInfo::OrderedSet& dump_providers,
const scoped_refptr<MemoryDumpSessionState>& session_state,
MemoryDumpCallback callback,
const scoped_refptr<SingleThreadTaskRunner>& dump_thread_task_runner)
: req_args(req_args),
- next_dump_provider(next_dump_provider),
session_state(session_state),
callback(callback),
callback_task_runner(MessageLoop::current()->task_runner()),
- dump_thread_task_runner(dump_thread_task_runner) {}
+ dump_thread_task_runner(dump_thread_task_runner) {
+ pending_dump_providers.reserve(dump_providers.size());
+ pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
+}
MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
}
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index a5037cf..5391411 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <set>
+#include <vector>
#include "base/atomicops.h"
#include "base/containers/hash_tables.h"
@@ -130,42 +131,62 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
friend class MemoryDumpManagerDelegate;
friend class MemoryDumpManagerTest;
- // Descriptor struct used to hold information about registered MDPs. It is
- // deliberately copyable, in order to allow it to be used as std::set value.
- struct MemoryDumpProviderInfo {
+ // Descriptor used to hold information about registered MDPs.
+ // Some important considerations about lifetime of this object:
+ // - In nominal conditions, all the MemoryDumpProviderInfo instances live in
+ // the |dump_providers_| collection (% unregistration while dumping).
+ // - Upon each dump they (actually their scoped_refptr-s) are copied into
+ // the ProcessMemoryDumpAsyncState. This is to allow removal (see below).
+ // - When the MDP.OnMemoryDump() is invoked, the corresponding MDPInfo copy
+ // inside ProcessMemoryDumpAsyncState is removed.
+ // - In nominal conditions, the MDPInfo is destroyed in the
+ // UnregisterDumpProvider() call.
+ // - If UnregisterDumpProvider() is called while a dump is in progress, the
+ // MDPInfo is destroyed in the epilogue of ContinueAsyncProcessDump(), when
+ // the copy inside ProcessMemoryDumpAsyncState is erase()-d.
+ // - The non-const fields of MemoryDumpProviderInfo are safe to access only
+ // in the |task_runner| thread, unless the thread has been destroyed.
+ struct MemoryDumpProviderInfo
+ : public RefCountedThreadSafe<MemoryDumpProviderInfo> {
+ // Define a total order based on the thread (i.e. |task_runner|) affinity,
+ // so that all MDP belonging to the same thread are adjacent in the set.
+ struct Comparator {
+ bool operator()(const scoped_refptr<MemoryDumpProviderInfo>& a,
+ const scoped_refptr<MemoryDumpProviderInfo>& b) const;
+ };
+ using OrderedSet =
+ std::set<scoped_refptr<MemoryDumpProviderInfo>, Comparator>;
+
MemoryDumpProviderInfo(
MemoryDumpProvider* dump_provider,
const char* name,
const scoped_refptr<SingleThreadTaskRunner>& task_runner,
const MemoryDumpProvider::Options& options);
- ~MemoryDumpProviderInfo();
-
- // Define a total order based on the thread (i.e. |task_runner|) affinity,
- // so that all MDP belonging to the same thread are adjacent in the set.
- bool operator<(const MemoryDumpProviderInfo& other) const;
MemoryDumpProvider* const dump_provider;
+
+ // Human readable name, for debugging and testing. Not necessarily unique.
const char* const name;
// The task_runner affinity. Can be nullptr, in which case the dump provider
// will be invoked on |dump_thread_|.
- scoped_refptr<SingleThreadTaskRunner> task_runner;
+ const scoped_refptr<SingleThreadTaskRunner> task_runner;
// The |options| arg passed to RegisterDumpProvider().
const MemoryDumpProvider::Options options;
- // For fail-safe logic (auto-disable failing MDPs). These fields are mutable
- // as can be safely changed without impacting the order within the set.
- mutable int consecutive_failures;
- mutable bool disabled;
+ // For fail-safe logic (auto-disable failing MDPs).
+ int consecutive_failures;
- // When a dump provider unregisters, it is flagged as |unregistered| and it
- // is removed only upon the next memory dump. This is to avoid altering the
- // |dump_providers_| collection while a dump is in progress.
- mutable bool unregistered;
- };
+ // Flagged either by the auto-disable logic or during unregistration.
+ bool disabled;
- using MemoryDumpProviderInfoSet = std::set<MemoryDumpProviderInfo>;
+ private:
+ friend class base::RefCountedThreadSafe<MemoryDumpProviderInfo>;
+ ~MemoryDumpProviderInfo();
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryDumpProviderInfo);
+ };
// Holds the state of a process memory dump that needs to be carried over
// across threads in order to fulfil an asynchronous CreateProcessDump()
@@ -173,7 +194,7 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
struct ProcessMemoryDumpAsyncState {
ProcessMemoryDumpAsyncState(
MemoryDumpRequestArgs req_args,
- MemoryDumpProviderInfoSet::iterator next_dump_provider,
+ const MemoryDumpProviderInfo::OrderedSet& dump_providers,
const scoped_refptr<MemoryDumpSessionState>& session_state,
MemoryDumpCallback callback,
const scoped_refptr<SingleThreadTaskRunner>& dump_thread_task_runner);
@@ -191,9 +212,10 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// The arguments passed to the initial CreateProcessDump() request.
const MemoryDumpRequestArgs req_args;
- // The |dump_providers_| iterator to the next dump provider that should be
- // invoked (or dump_providers_.end() if at the end of the sequence).
- MemoryDumpProviderInfoSet::iterator next_dump_provider;
+ // An ordered sequence of dump providers that have to be invoked to complete
+ // the dump. This is a copy of |dump_providers_| at the beginning of a dump
+ // and becomes empty at the end, when all dump providers have been invoked.
+ std::vector<scoped_refptr<MemoryDumpProviderInfo>> pending_dump_providers;
// The trace-global session state.
scoped_refptr<MemoryDumpSessionState> session_state;
@@ -226,9 +248,6 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
static void SetInstanceForTesting(MemoryDumpManager* instance);
static void FinalizeDumpAndAddToTrace(
scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
- static void AbortDumpLocked(MemoryDumpCallback callback,
- scoped_refptr<SingleThreadTaskRunner> task_runner,
- uint64_t dump_guid);
// Internal, used only by MemoryDumpManagerDelegate.
// Creates a memory dump for the current process and appends it to the trace.
@@ -240,11 +259,11 @@ class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
// Continues the ProcessMemoryDump started by CreateProcessDump(), hopping
// across threads as needed as specified by MDPs in RegisterDumpProvider().
void ContinueAsyncProcessDump(
- scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
+ ProcessMemoryDumpAsyncState* owned_pmd_async_state);
// An ordererd set of registered MemoryDumpProviderInfo(s), sorted by thread
// affinity (MDPs belonging to the same thread are adjacent).
- MemoryDumpProviderInfoSet dump_providers_;
+ MemoryDumpProviderInfo::OrderedSet dump_providers_;
// Shared among all the PMDs to keep state scoped to the tracing session.
scoped_refptr<MemoryDumpSessionState> session_state_;
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 34c98b0..cf0f305 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -4,8 +4,10 @@
#include "base/trace_event/memory_dump_manager.h"
+#include <vector>
+
#include "base/bind_helpers.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
@@ -202,10 +204,13 @@ TEST_F(MemoryDumpManagerTest, SingleDumper) {
// Finally check the unregister logic: the delegate will be invoked but not
// the dump provider, as it has been unregistered.
EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
- EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
+ EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3);
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
- RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED);
+
+ for (int i = 0; i < 3; ++i) {
+ RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+ MemoryDumpLevelOfDetail::DETAILED);
+ }
DisableTracing();
}
@@ -531,13 +536,14 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
// dumping from a different thread than the dumping thread.
TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
InitializeMemoryDumpManager(false /* is_coordinator */);
- ScopedVector<TestIOThread> threads;
- ScopedVector<MockMemoryDumpProvider> mdps;
+ std::vector<scoped_ptr<TestIOThread>> threads;
+ std::vector<scoped_ptr<MockMemoryDumpProvider>> mdps;
for (int i = 0; i < 2; i++) {
- threads.push_back(new TestIOThread(TestIOThread::kAutoStart));
- mdps.push_back(new MockMemoryDumpProvider());
- RegisterDumpProvider(mdps.back(), threads.back()->task_runner(),
+ threads.push_back(
+ make_scoped_ptr(new TestIOThread(TestIOThread::kAutoStart)));
+ mdps.push_back(make_scoped_ptr(new MockMemoryDumpProvider()));
+ RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
kDefaultOptions);
}
@@ -545,10 +551,10 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
// When OnMemoryDump is called on either of the dump providers, it will
// unregister the other one.
- for (MockMemoryDumpProvider* mdp : mdps) {
+ for (const scoped_ptr<MockMemoryDumpProvider>& mdp : mdps) {
int other_idx = (mdps.front() == mdp);
- TestIOThread* other_thread = threads[other_idx];
- MockMemoryDumpProvider* other_mdp = mdps[other_idx];
+ TestIOThread* other_thread = threads[other_idx].get();
+ MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
auto on_dump = [this, other_thread, other_mdp, &on_memory_dump_call_count](
const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
other_thread->PostTaskAndWait(
@@ -571,7 +577,54 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
MemoryDumpLevelOfDetail::DETAILED);
ASSERT_EQ(1, on_memory_dump_call_count);
- ASSERT_EQ(true, last_callback_success_);
+ ASSERT_TRUE(last_callback_success_);
+
+ DisableTracing();
+}
+
+// If a thread (with a dump provider living on it) is torn down during a dump
+// its dump provider should be skipped but the dump itself should succeed.
+TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
+ InitializeMemoryDumpManager(false /* is_coordinator */);
+ std::vector<scoped_ptr<TestIOThread>> threads;
+ std::vector<scoped_ptr<MockMemoryDumpProvider>> mdps;
+
+ for (int i = 0; i < 2; i++) {
+ threads.push_back(
+ make_scoped_ptr(new TestIOThread(TestIOThread::kAutoStart)));
+ mdps.push_back(make_scoped_ptr(new MockMemoryDumpProvider()));
+ RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
+ kDefaultOptions);
+ }
+
+ int on_memory_dump_call_count = 0;
+
+ // When OnMemoryDump is called on either of the dump providers, it will
+ // tear down the thread of the other one.
+ for (const scoped_ptr<MockMemoryDumpProvider>& mdp : mdps) {
+ int other_idx = (mdps.front() == mdp);
+ TestIOThread* other_thread = threads[other_idx].get();
+ auto on_dump = [other_thread, &on_memory_dump_call_count](
+ const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+ other_thread->Stop();
+ on_memory_dump_call_count++;
+ return true;
+ };
+
+ // OnMemoryDump is called once for the provider that dumps first, and zero
+ // times for the other provider.
+ EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+ .Times(AtMost(1))
+ .WillOnce(Invoke(on_dump));
+ }
+
+ last_callback_success_ = false;
+ EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
+ EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
+ RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+ MemoryDumpLevelOfDetail::DETAILED);
+ ASSERT_EQ(1, on_memory_dump_call_count);
+ ASSERT_TRUE(last_callback_success_);
DisableTracing();
}
@@ -761,9 +814,9 @@ TEST_F(MemoryDumpManagerTest, DisableTracingWhileDumping) {
tracing_disabled_event.Signal();
run_loop.Run();
- // RequestGlobalMemoryDump() should be NACK-ed because one of the threads
- // threads died before we had a chance to PostTask onto them.
- EXPECT_FALSE(last_callback_success_);
+ // RequestGlobalMemoryDump() should still suceed even if some threads were
+ // torn down during the dump.
+ EXPECT_TRUE(last_callback_success_);
}
TEST_F(MemoryDumpManagerTest, DumpOnBehalfOfOtherProcess) {