Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2828)

Unified Diff: base/trace_event/memory_dump_manager.cc

Issue 1124763003: Update from https://crrev.com/327068 (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: update nacl, buildtools, fix display_change_notifier_unittest Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/trace_event/memory_dump_manager.cc
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 0ec5d1983374538598af6422d2084385c47dcbaa..859e8e02dc75dfca8f5e5726c7db7f131d9dc34d 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -9,41 +9,123 @@
#include "base/atomic_sequence_num.h"
#include "base/compiler_specific.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event_argument.h"
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include "base/trace_event/malloc_dump_provider.h"
+#include "base/trace_event/process_memory_maps_dump_provider.h"
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+#elif defined(OS_WIN)
+#include "base/trace_event/winheap_dump_provider_win.h"
+#endif
+
namespace base {
namespace trace_event {
namespace {
+// TODO(primiano): this should be smarter and should do something similar to
+// trace event synthetic delays.
+const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra");
+
MemoryDumpManager* g_instance_for_testing = nullptr;
+const int kDumpIntervalSeconds = 2;
const int kTraceEventNumArgs = 1;
const char* kTraceEventArgNames[] = {"dumps"};
const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
StaticAtomicSequenceNumber g_next_guid;
-const char* DumpPointTypeToString(const DumpPointType& dump_point_type) {
- switch (dump_point_type) {
- case DumpPointType::TASK_BEGIN:
+const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
+ switch (dump_type) {
+ case MemoryDumpType::TASK_BEGIN:
return "TASK_BEGIN";
- case DumpPointType::TASK_END:
+ case MemoryDumpType::TASK_END:
return "TASK_END";
- case DumpPointType::PERIODIC_INTERVAL:
+ case MemoryDumpType::PERIODIC_INTERVAL:
return "PERIODIC_INTERVAL";
- case DumpPointType::EXPLICITLY_TRIGGERED:
+ case MemoryDumpType::EXPLICITLY_TRIGGERED:
return "EXPLICITLY_TRIGGERED";
}
NOTREACHED();
return "UNKNOWN";
}
+// Internal class used to hold details about ProcessMemoryDump requests for the
+// current process.
+// TODO(primiano): In the upcoming CLs, ProcessMemoryDump will become async.
+// and this class will be used to convey more details across PostTask()s.
+class ProcessMemoryDumpHolder
+ : public RefCountedThreadSafe<ProcessMemoryDumpHolder> {
+ public:
+ ProcessMemoryDumpHolder(
+ MemoryDumpRequestArgs req_args,
+ const scoped_refptr<MemoryDumpSessionState>& session_state,
+ MemoryDumpCallback callback)
+ : process_memory_dump(session_state),
+ req_args(req_args),
+ callback(callback),
+ task_runner(MessageLoop::current()->task_runner()),
+ num_pending_async_requests(0) {}
+
+ ProcessMemoryDump process_memory_dump;
+ const MemoryDumpRequestArgs req_args;
+
+ // Callback passed to the initial call to CreateProcessDump().
+ MemoryDumpCallback callback;
+
+ // Thread on which FinalizeDumpAndAddToTrace() should be called, which is the
+ // same that invoked the initial CreateProcessDump().
+ const scoped_refptr<SingleThreadTaskRunner> task_runner;
+
+ // Number of pending ContinueAsyncProcessDump() calls.
+ int num_pending_async_requests;
+
+ private:
+ friend class RefCountedThreadSafe<ProcessMemoryDumpHolder>;
+ virtual ~ProcessMemoryDumpHolder() {}
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpHolder);
+};
+
+void FinalizeDumpAndAddToTrace(
+ const scoped_refptr<ProcessMemoryDumpHolder>& pmd_holder) {
+ DCHECK_EQ(0, pmd_holder->num_pending_async_requests);
+
+ if (!pmd_holder->task_runner->BelongsToCurrentThread()) {
+ pmd_holder->task_runner->PostTask(
+ FROM_HERE, Bind(&FinalizeDumpAndAddToTrace, pmd_holder));
+ return;
+ }
+
+ scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
+ pmd_holder->process_memory_dump.AsValueInto(
+ static_cast<TracedValue*>(event_value.get()));
+ const char* const event_name =
+ MemoryDumpTypeToString(pmd_holder->req_args.dump_type);
+
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_MEMORY_DUMP,
+ TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
+ pmd_holder->req_args.dump_guid, kTraceEventNumArgs, kTraceEventArgNames,
+ kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
+ TRACE_EVENT_FLAG_HAS_ID);
+
+ if (!pmd_holder->callback.is_null()) {
+ pmd_holder->callback.Run(pmd_holder->req_args.dump_guid, true);
+ pmd_holder->callback.Reset();
+ }
+}
+
+void RequestPeriodicGlobalDump() {
+ MemoryDumpManager::GetInstance()->RequestGlobalDump(
+ MemoryDumpType::PERIODIC_INTERVAL);
+}
+
} // namespace
-// TODO(primiano): this should be smarter and should do something similar to
-// trace event synthetic delays.
-const char MemoryDumpManager::kTraceCategory[] =
- TRACE_DISABLED_BY_DEFAULT("memory-dumps");
+// static
+const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
// static
MemoryDumpManager* MemoryDumpManager::GetInstance() {
@@ -56,11 +138,17 @@ MemoryDumpManager* MemoryDumpManager::GetInstance() {
// static
void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
+ if (instance)
+ instance->skip_core_dumpers_auto_registration_for_testing_ = true;
g_instance_for_testing = instance;
}
MemoryDumpManager::MemoryDumpManager()
- : dump_provider_currently_active_(nullptr), memory_tracing_enabled_(0) {
+ : dump_provider_currently_active_(nullptr),
+ delegate_(nullptr),
+ memory_tracing_enabled_(0),
+ skip_core_dumpers_auto_registration_for_testing_(false) {
+ g_next_guid.GetNext(); // Make sure that first guid is not zero.
}
MemoryDumpManager::~MemoryDumpManager() {
@@ -70,94 +158,176 @@ MemoryDumpManager::~MemoryDumpManager() {
void MemoryDumpManager::Initialize() {
TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
+
+ if (skip_core_dumpers_auto_registration_for_testing_)
+ return;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ // Enable the core dump providers.
+ RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance());
+ RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
+ RegisterDumpProvider(MallocDumpProvider::GetInstance());
+#elif defined(OS_WIN)
+ RegisterDumpProvider(WinHeapDumpProvider::GetInstance());
+#endif
+}
+
+void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) {
+ AutoLock lock(lock_);
+ DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_);
+ delegate_ = delegate;
}
void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
AutoLock lock(lock_);
- if (std::find(dump_providers_registered_.begin(),
- dump_providers_registered_.end(),
- mdp) != dump_providers_registered_.end()) {
- return;
- }
- dump_providers_registered_.push_back(mdp);
+ dump_providers_registered_.insert(mdp);
}
void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
AutoLock lock(lock_);
- // Remove from the registered providers list.
- auto it = std::find(dump_providers_registered_.begin(),
- dump_providers_registered_.end(), mdp);
- if (it != dump_providers_registered_.end())
- dump_providers_registered_.erase(it);
+ // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
+ // only if the MDP has specified a thread affinity (via task_runner()) AND
+ // the unregistration happens on the same thread (so the MDP cannot unregister
+ // and DumpInto() at the same time).
+ // Otherwise, it is not possible to guarantee that its unregistration is
+ // race-free. If you hit this DCHECK, your MDP has a bug.
+ DCHECK_IMPLIES(
+ subtle::NoBarrier_Load(&memory_tracing_enabled_),
+ mdp->task_runner() && mdp->task_runner()->BelongsToCurrentThread())
+ << "The MemoryDumpProvider " << mdp->GetFriendlyName() << " attempted to "
+ << "unregister itself in a racy way. Please file a crbug.";
// Remove from the enabled providers list. This is to deal with the case that
// UnregisterDumpProvider is called while the trace is enabled.
- it = std::find(dump_providers_enabled_.begin(), dump_providers_enabled_.end(),
- mdp);
- if (it != dump_providers_enabled_.end())
- dump_providers_enabled_.erase(it);
+ dump_providers_enabled_.erase(mdp);
+ dump_providers_registered_.erase(mdp);
}
-void MemoryDumpManager::RequestDumpPoint(DumpPointType dump_point_type) {
- // TODO(primiano): this will have more logic to coordinate dump points across
- // multiple processes via IPC. See crbug.com/462930.
-
+void MemoryDumpManager::RequestGlobalDump(
+ MemoryDumpType dump_type,
+ const MemoryDumpCallback& callback) {
// Bail out immediately if tracing is not enabled at all.
if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)))
return;
- // TODO(primiano): Make guid actually unique (cross-process) by hashing it
- // with the PID. See crbug.com/462931 for details.
- const uint64 guid = g_next_guid.GetNext();
- CreateLocalDumpPoint(dump_point_type, guid);
+ const uint64 guid =
+ TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
+
+ // The delegate_ is supposed to be thread safe, immutable and long lived.
+ // No need to keep the lock after we ensure that a delegate has been set.
+ MemoryDumpManagerDelegate* delegate;
+ {
+ AutoLock lock(lock_);
+ delegate = delegate_;
+ }
+
+ if (delegate) {
+ // The delegate is in charge to coordinate the request among all the
+ // processes and call the CreateLocalDumpPoint on the local process.
+ MemoryDumpRequestArgs args = {guid, dump_type};
+ delegate->RequestGlobalMemoryDump(args, callback);
+ } else if (!callback.is_null()) {
+ callback.Run(guid, false /* success */);
+ }
}
-void MemoryDumpManager::BroadcastDumpRequest() {
- NOTREACHED(); // TODO(primiano): implement IPC synchronization.
+void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) {
+ RequestGlobalDump(dump_type, MemoryDumpCallback());
}
-// Creates a dump point for the current process and appends it to the trace.
-void MemoryDumpManager::CreateLocalDumpPoint(DumpPointType dump_point_type,
- uint64 guid) {
+// Creates a memory dump for the current process and appends it to the trace.
+void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) {
+ scoped_refptr<ProcessMemoryDumpHolder> pmd_holder(
+ new ProcessMemoryDumpHolder(args, session_state_, callback));
+ ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
bool did_any_provider_dump = false;
- scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
- // Serialize dump point generation so that memory dump providers don't have to
- // deal with thread safety.
+ // Iterate over the active dump providers and invoke DumpInto(pmd).
+ // The MDM guarantees linearity (at most one MDP is active within one
+ // process) and thread-safety (MDM enforces the right locking when entering /
+ // leaving the MDP.DumpInto() call). This is to simplify the clients' design
+ // and not let the MDPs worry about locking.
+ // As regards thread affinity, depending on the MDP configuration (see
+ // memory_dump_provider.h), the DumpInto() invocation can happen:
+ // - Synchronousy on the MDM thread, when MDP.task_runner() is not set.
+ // - Posted on MDP.task_runner(), when MDP.task_runner() is set.
{
AutoLock lock(lock_);
- for (auto it = dump_providers_enabled_.begin();
- it != dump_providers_enabled_.end();) {
- dump_provider_currently_active_ = *it;
- if (dump_provider_currently_active_->DumpInto(pmd.get())) {
- did_any_provider_dump = true;
- ++it;
+ for (auto dump_provider_iter = dump_providers_enabled_.begin();
+ dump_provider_iter != dump_providers_enabled_.end();) {
+ // InvokeDumpProviderLocked will remove the MDP from the set if it fails.
+ MemoryDumpProvider* mdp = *dump_provider_iter;
+ ++dump_provider_iter;
+ if (mdp->task_runner()) {
+ // The DumpInto() call must be posted.
+ bool did_post_async_task = mdp->task_runner()->PostTask(
+ FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
+ Unretained(this), Unretained(mdp), pmd_holder));
+ // The thread underlying the TaskRunner might have gone away.
+ if (did_post_async_task)
+ ++pmd_holder->num_pending_async_requests;
} else {
- LOG(ERROR) << "The memory dumper "
- << dump_provider_currently_active_->GetFriendlyName()
- << " failed, possibly due to sandboxing (crbug.com/461788), "
- "disabling it for current process. Try restarting chrome "
- "with the --no-sandbox switch.";
- it = dump_providers_enabled_.erase(it);
+ // Invoke the dump provider synchronously.
+ did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd);
}
- dump_provider_currently_active_ = nullptr;
}
- }
+ } // AutoLock
- // Don't create a dump point if all the dumpers failed.
- if (!did_any_provider_dump)
- return;
+ // If at least one synchronous provider did dump and there are no pending
+ // asynchronous requests, add the dump to the trace and invoke the callback
+ // straight away (FinalizeDumpAndAddToTrace() takes care of the callback).
+ if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0)
+ FinalizeDumpAndAddToTrace(pmd_holder);
+}
- scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
- pmd->AsValueInto(static_cast<TracedValue*>(event_value.get()));
- const char* const event_name = DumpPointTypeToString(dump_point_type);
+// Invokes the MemoryDumpProvider.DumpInto(), taking care of the failsafe logic
+// which disables the dumper when failing (crbug.com/461788).
+bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
+ ProcessMemoryDump* pmd) {
+ lock_.AssertAcquired();
+ dump_provider_currently_active_ = mdp;
+ bool dump_successful = mdp->DumpInto(pmd);
+ dump_provider_currently_active_ = nullptr;
+ if (!dump_successful) {
+ LOG(ERROR) << "The memory dumper " << mdp->GetFriendlyName()
+ << " failed, possibly due to sandboxing (crbug.com/461788), "
+ "disabling it for current process. Try restarting chrome "
+ "with the --no-sandbox switch.";
+ dump_providers_enabled_.erase(mdp);
+ }
+ return dump_successful;
+}
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_MEMORY_DUMP,
- TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, guid,
- kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
- NULL /* arg_values */, &event_value, TRACE_EVENT_FLAG_HAS_ID);
+// This is posted to arbitrary threads as a continuation of CreateProcessDump(),
+// when one or more MemoryDumpProvider(s) require the DumpInto() call to happen
+// on a different thread.
+void MemoryDumpManager::ContinueAsyncProcessDump(
+ MemoryDumpProvider* mdp,
+ scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) {
+ bool should_finalize_dump = false;
+ {
+ // The lock here is to guarantee that different asynchronous dumps on
+ // different threads are still serialized, so that the MemoryDumpProvider
+ // has a consistent view of the |pmd| argument passed.
+ AutoLock lock(lock_);
+ ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
+
+ // Check if the MemoryDumpProvider is still there. It might have been
+ // destroyed and unregistered while hopping threads.
+ if (dump_providers_enabled_.count(mdp))
+ InvokeDumpProviderLocked(mdp, pmd);
+
+ // Finalize the dump appending it to the trace if this was the last
+ // asynchronous request pending.
+ --pmd_holder->num_pending_async_requests;
+ if (pmd_holder->num_pending_async_requests == 0)
+ should_finalize_dump = true;
+ } // AutoLock(lock_)
+
+ if (should_finalize_dump)
+ FinalizeDumpAndAddToTrace(pmd_holder);
}
void MemoryDumpManager::OnTraceLogEnabled() {
@@ -168,19 +338,36 @@ void MemoryDumpManager::OnTraceLogEnabled() {
TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
AutoLock lock(lock_);
- if (enabled) {
- dump_providers_enabled_.assign(dump_providers_registered_.begin(),
- dump_providers_registered_.end());
- } else {
+
+ // There is no point starting the tracing without a delegate.
+ if (!enabled || !delegate_) {
dump_providers_enabled_.clear();
+ return;
+ }
+
+ // Merge the dictionary of allocator attributes from all dump providers
+ // into the session state.
+ session_state_ = new MemoryDumpSessionState();
+ for (const MemoryDumpProvider* mdp : dump_providers_registered_) {
+ session_state_->allocators_attributes_type_info.Update(
+ mdp->allocator_attributes_type_info());
}
+ dump_providers_enabled_ = dump_providers_registered_;
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
+
+ if (delegate_->IsCoordinatorProcess()) {
+ periodic_dump_timer_.Start(FROM_HERE,
+ TimeDelta::FromSeconds(kDumpIntervalSeconds),
+ base::Bind(&RequestPeriodicGlobalDump));
+ }
}
void MemoryDumpManager::OnTraceLogDisabled() {
AutoLock lock(lock_);
+ periodic_dump_timer_.Stop();
dump_providers_enabled_.clear();
subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
+ session_state_ = nullptr;
}
} // namespace trace_event

Powered by Google App Engine
This is Rietveld 408576698