Index: content/browser/tracing/tracing_controller_impl.cc |
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc |
index 54d1455aa3868711a0ca12e734f51cc992496b17..5fc480346a29332414bbd150039dfcabea0385ee 100644 |
--- a/content/browser/tracing/tracing_controller_impl.cc |
+++ b/content/browser/tracing/tracing_controller_impl.cc |
@@ -193,8 +193,13 @@ |
pending_trace_log_status_ack_count_(0), |
maximum_trace_buffer_usage_(0), |
approximate_event_count_(0), |
+ pending_memory_dump_ack_count_(0), |
+ failed_memory_dump_count_(0), |
pending_clock_sync_ack_count_(0), |
is_tracing_(false) { |
+ base::trace_event::MemoryDumpManager::GetInstance()->Initialize( |
+ this /* delegate */, true /* is_coordinator */); |
+ |
// Deliberately leaked, like this class. |
base::FileTracing::SetProvider(new FileTracingProviderImpl); |
} |
@@ -531,6 +536,20 @@ |
base::trace_event::TraceLogStatus())); |
} |
} |
+ if (pending_memory_dump_ack_count_ > 0) { |
+ DCHECK(!queued_memory_dump_requests_.empty()); |
+ TraceMessageFilterSet::const_iterator it = |
+ pending_memory_dump_filters_.find(trace_message_filter); |
+ if (it != pending_memory_dump_filters_.end()) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&TracingControllerImpl::OnProcessMemoryDumpResponse, |
+ base::Unretained(this), |
+ base::RetainedRef(trace_message_filter), |
+ queued_memory_dump_requests_.front().args.dump_guid, |
+ false /* success */)); |
+ } |
+ } |
trace_message_filters_.erase(trace_message_filter); |
} |
@@ -877,6 +896,87 @@ |
sink->AddMetadata(std::move(filtered_metadata)); |
} |
+void TracingControllerImpl::RequestGlobalMemoryDump( |
+ const base::trace_event::MemoryDumpRequestArgs& args, |
+ const base::trace_event::MemoryDumpCallback& callback) { |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&TracingControllerImpl::RequestGlobalMemoryDump, |
+ base::Unretained(this), args, callback)); |
+ return; |
+ } |
+ |
+ bool another_dump_already_in_progress = !queued_memory_dump_requests_.empty(); |
+ |
+ // If this is a periodic memory dump request and there already is another |
+ // request in the queue with the same level of detail, there's no point in |
+ // enqueuing this request. |
+ if (another_dump_already_in_progress && |
+ args.dump_type == base::trace_event::MemoryDumpType::PERIODIC_INTERVAL) { |
+ for (const auto& request : queued_memory_dump_requests_) { |
+ if (request.args.level_of_detail == args.level_of_detail) { |
+ VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix << " (" |
+ << base::trace_event::MemoryDumpTypeToString(args.dump_type) |
+ << ") skipped because another dump request with the same " |
+ "level of detail (" |
+ << base::trace_event::MemoryDumpLevelOfDetailToString( |
+ args.level_of_detail) |
+ << ") is already in the queue"; |
+ if (!callback.is_null()) |
+ callback.Run(args.dump_guid, false /* success */); |
+ return; |
+ } |
+ } |
+ } |
+ |
+ queued_memory_dump_requests_.emplace_back(args, callback); |
+ |
+ // If another dump is already in progress, this dump will automatically be |
+ // scheduled when the other dump finishes. |
+ if (another_dump_already_in_progress) |
+ return; |
+ |
+ PerformNextQueuedGlobalMemoryDump(); |
+} |
+ |
+void TracingControllerImpl::PerformNextQueuedGlobalMemoryDump() { |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ DCHECK(!queued_memory_dump_requests_.empty()); |
+ const base::trace_event::MemoryDumpRequestArgs& args = |
+ queued_memory_dump_requests_.front().args; |
+ |
+ // Count myself (local trace) in pending_memory_dump_ack_count_, acked by |
+ // OnBrowserProcessMemoryDumpDone(). |
+ pending_memory_dump_ack_count_ = trace_message_filters_.size() + 1; |
+ pending_memory_dump_filters_.clear(); |
+ failed_memory_dump_count_ = 0; |
+ |
+ MemoryDumpManagerDelegate::CreateProcessDump( |
+ args, base::Bind(&TracingControllerImpl::OnBrowserProcessMemoryDumpDone, |
+ base::Unretained(this))); |
+ |
+ // If there are no child processes we are just done. |
+ if (pending_memory_dump_ack_count_ == 1) |
+ return; |
+ |
+ pending_memory_dump_filters_ = trace_message_filters_; |
+ |
+ for (const scoped_refptr<TraceMessageFilter>& tmf : trace_message_filters_) |
+ tmf->SendProcessMemoryDumpRequest(args); |
+} |
+ |
+TracingControllerImpl::QueuedMemoryDumpRequest::QueuedMemoryDumpRequest( |
+ const base::trace_event::MemoryDumpRequestArgs& args, |
+ const base::trace_event::MemoryDumpCallback& callback) |
+ : args(args), callback(callback) {} |
+ |
+TracingControllerImpl::QueuedMemoryDumpRequest::~QueuedMemoryDumpRequest() {} |
+ |
+uint64_t TracingControllerImpl::GetTracingProcessId() const { |
+ return ChildProcessHost::kBrowserTracingProcessId; |
+} |
+ |
void TracingControllerImpl::AddTraceMessageFilterObserver( |
TraceMessageFilterObserver* observer) { |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
@@ -895,4 +995,76 @@ |
observer->OnTraceMessageFilterRemoved(filter.get()); |
} |
+void TracingControllerImpl::OnProcessMemoryDumpResponse( |
+ TraceMessageFilter* trace_message_filter, |
+ uint64_t dump_guid, |
+ bool success) { |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&TracingControllerImpl::OnProcessMemoryDumpResponse, |
+ base::Unretained(this), |
+ base::RetainedRef(trace_message_filter), dump_guid, |
+ success)); |
+ return; |
+ } |
+ |
+ TraceMessageFilterSet::iterator it = |
+ pending_memory_dump_filters_.find(trace_message_filter); |
+ |
+ DCHECK(!queued_memory_dump_requests_.empty()); |
+ if (queued_memory_dump_requests_.front().args.dump_guid != dump_guid || |
+ it == pending_memory_dump_filters_.end()) { |
+ DLOG(WARNING) << "Received unexpected memory dump response: " << dump_guid; |
+ return; |
+ } |
+ |
+ DCHECK_GT(pending_memory_dump_ack_count_, 0); |
+ --pending_memory_dump_ack_count_; |
+ pending_memory_dump_filters_.erase(it); |
+ if (!success) { |
+ ++failed_memory_dump_count_; |
+ VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix |
+ << " failed because of NACK from child " |
+ << trace_message_filter->peer_pid(); |
+ } |
+ FinalizeGlobalMemoryDumpIfAllProcessesReplied(); |
+} |
+ |
+void TracingControllerImpl::OnBrowserProcessMemoryDumpDone(uint64_t dump_guid, |
+ bool success) { |
+ DCHECK_GT(pending_memory_dump_ack_count_, 0); |
+ --pending_memory_dump_ack_count_; |
+ if (!success) { |
+ ++failed_memory_dump_count_; |
+ VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix |
+ << " aborted on the current process"; |
+ } |
+ FinalizeGlobalMemoryDumpIfAllProcessesReplied(); |
+} |
+ |
+void TracingControllerImpl::FinalizeGlobalMemoryDumpIfAllProcessesReplied() { |
+ if (pending_memory_dump_ack_count_ > 0) |
+ return; |
+ |
+ DCHECK(!queued_memory_dump_requests_.empty()); |
+ { |
+ const auto& callback = queued_memory_dump_requests_.front().callback; |
+ if (!callback.is_null()) { |
+ const bool global_success = failed_memory_dump_count_ == 0; |
+ callback.Run(queued_memory_dump_requests_.front().args.dump_guid, |
+ global_success); |
+ } |
+ } |
+ queued_memory_dump_requests_.pop_front(); |
+ |
+ // Schedule the next queued dump (if applicable). |
+ if (!queued_memory_dump_requests_.empty()) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&TracingControllerImpl::PerformNextQueuedGlobalMemoryDump, |
+ base::Unretained(this))); |
+ } |
+} |
+ |
} // namespace content |