Index: chrome/browser/metrics/tracking_synchronizer.cc |
=================================================================== |
--- chrome/browser/metrics/tracking_synchronizer.cc (revision 112492) |
+++ chrome/browser/metrics/tracking_synchronizer.cc (working copy) |
@@ -5,17 +5,13 @@ |
#include "chrome/browser/metrics/tracking_synchronizer.h" |
#include "base/bind.h" |
-#include "base/json/json_reader.h" |
-#include "base/json/json_writer.h" |
-#include "base/logging.h" |
#include "base/metrics/histogram.h" |
+#include "base/process_util.h" |
#include "base/threading/thread.h" |
#include "base/tracked_objects.h" |
-#include "chrome/browser/ui/webui/tracing_ui.h" |
-#include "chrome/common/chrome_constants.h" |
-#include "chrome/common/render_messages.h" |
+#include "content/common/child_process_info.h" |
#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/profiler_controller.h" |
#include "content/public/common/process_type.h" |
using base::TimeTicks; |
@@ -23,6 +19,142 @@ |
namespace chrome_browser_metrics { |
+// The "RequestContext" structure describes an individual request received |
+// from the UI. All methods are accessible on UI thread. |
+class RequestContext { |
+ public: |
+ // A map from sequence_number_ to the actual RequestContexts. |
+ typedef std::map<int, RequestContext*> RequestContextMap; |
+ |
+ ~RequestContext() {} |
+ |
+ RequestContext(const base::WeakPtr<ProfilerUI>& callback_object, |
+ int sequence_number) |
+ : callback_object_(callback_object), |
+ sequence_number_(sequence_number), |
+ received_process_group_count_(0), |
+ processes_pending_(0) { |
+ } |
+ |
+ void SetReceivedProcessGroupCount(bool done) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ received_process_group_count_ = done; |
+ } |
+ |
+ // Methods for book keeping of processes_pending_. |
+ void IncrementProcessesPending() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ ++processes_pending_; |
+ } |
+ |
+ void AddProcessesPending(int processes_pending) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ processes_pending_ += processes_pending; |
+ } |
+ |
+ void DecrementProcessesPending() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ --processes_pending_; |
+ } |
+ |
+ // Records that we are waiting for one less tracking data from a process for |
+ // the given sequence number. If |received_process_group_count_| and |
+ // |processes_pending_| are zero, then delete the current object by calling |
+ // Unregister. |
+ void DeleteIfAllDone() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (processes_pending_ <= 0 && received_process_group_count_) { |
+ RequestContext::Unregister(sequence_number_); |
+ } |
+ } |
+ |
+ |
+ // Register |callback_object| in |outstanding_requests_| map for the given |
+ // |sequence_number|. |
+ static RequestContext* Register( |
+ int sequence_number, |
+ const base::WeakPtr<ProfilerUI>& callback_object) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ RequestContext* request = new RequestContext( |
+ callback_object, sequence_number); |
+ outstanding_requests_.Get()[sequence_number] = request; |
+ |
+ return request; |
+ } |
+ |
+ // Find the |RequestContext| in |outstanding_requests_| map for the given |
+ // |sequence_number|. |
+ static RequestContext* GetRequestContext(int sequence_number) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ RequestContextMap::iterator it = |
+ outstanding_requests_.Get().find(sequence_number); |
+ if (it == outstanding_requests_.Get().end()) |
+ return NULL; |
+ |
+ RequestContext* request = NULL; |
+ request = it->second; |
+ DCHECK(sequence_number == request->sequence_number_); |
+ return request; |
+ } |
+ |
+ // Delete the entry for the given sequence_number| from |
+ // |outstanding_requests_| map. This method is called when all changes have |
+ // been acquired, or when the wait time expires (whichever is sooner). |
+ static void Unregister(int sequence_number) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ RequestContextMap::iterator it = |
+ outstanding_requests_.Get().find(sequence_number); |
+ if (it == outstanding_requests_.Get().end()) |
+ return; |
+ |
+ RequestContext* request = it->second; |
+ DCHECK(sequence_number == request->sequence_number_); |
+ bool received_process_group_count = request->received_process_group_count_; |
+ int unresponsive_processes = request->processes_pending_; |
+ |
+ delete it->second; |
+ outstanding_requests_.Get().erase(it); |
+ |
+ UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount", |
+ received_process_group_count); |
+ UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding", |
+ unresponsive_processes); |
+ } |
+ |
+ |
+ // Delete all the entries in |outstanding_requests_| map. |
+ static void OnShutdown() { |
+ // Just in case we have any pending tasks, clear them out. |
+ while (!outstanding_requests_.Get().empty()) { |
+ RequestContextMap::iterator it = outstanding_requests_.Get().begin(); |
+ delete it->second; |
+ outstanding_requests_.Get().erase(it); |
+ } |
+ } |
+ |
+ // Requests are made to asynchronously send data to the |callback_object_|. |
+ base::WeakPtr<ProfilerUI> callback_object_; |
+ |
+ // The sequence number used by the most recent update request to contact all |
+ // processes. |
+ int sequence_number_; |
+ |
+ // Indicates if we have received all pending processes count. |
+ bool received_process_group_count_; |
+ |
+ // The number of pending processes (browser, all renderer processes and |
+ // browser child processes) that have not yet responded to requests. |
+ int processes_pending_; |
+ |
+ // Map of all outstanding RequestContexts, from sequence_number_ to |
+ // RequestContext. |
+ static base::LazyInstance<RequestContextMap> outstanding_requests_; |
+}; |
+ |
// Negative numbers are never used as sequence numbers. We explicitly pick a |
// negative number that is "so negative" that even when we add one (as is done |
// when we generated the next sequence number) that it will still be negative. |
@@ -30,32 +162,29 @@ |
// territory. |
static const int kNeverUsableSequenceNumber = -2; |
+// TrackingSynchronizer methods and members. |
+// |
+// static |
+TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; |
+ |
TrackingSynchronizer::TrackingSynchronizer() |
: last_used_sequence_number_(kNeverUsableSequenceNumber) { |
DCHECK(tracking_synchronizer_ == NULL); |
tracking_synchronizer_ = this; |
+ content::ProfilerController::GetInstance()->Register(this); |
} |
TrackingSynchronizer::~TrackingSynchronizer() { |
+ content::ProfilerController::GetInstance()->Unregister(this); |
+ |
// Just in case we have any pending tasks, clear them out. |
- while (!outstanding_requests_.empty()) { |
- RequestContextMap::iterator it = outstanding_requests_.begin(); |
- delete it->second; |
- outstanding_requests_.erase(it); |
- } |
+ RequestContext::OnShutdown(); |
tracking_synchronizer_ = NULL; |
} |
// static |
-TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(tracking_synchronizer_ != NULL); |
- return tracking_synchronizer_; |
-} |
- |
-// static |
-void TrackingSynchronizer::FetchTrackingDataAsynchronously( |
+void TrackingSynchronizer::FetchProfilerDataAsynchronously( |
const base::WeakPtr<ProfilerUI>& callback_object) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
@@ -72,133 +201,56 @@ |
// as a watchdog, to cancel the requests for non-responsive processes. |
BrowserThread::PostDelayedTask( |
BrowserThread::UI, FROM_HERE, |
- NewRunnableMethod( |
- current_synchronizer, |
- &TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback, |
- sequence_number), |
+ base::Bind(&RequestContext::Unregister, sequence_number), |
60000); |
} |
-// static |
-void TrackingSynchronizer::SetTrackingStatus(bool enable) { |
- // To iterate over all processes, or to send messages to the hosts, we need |
- // to be on the UI thread. |
+void TrackingSynchronizer::OnPendingProcesses(int sequence_number, |
+ int pending_processes, |
+ bool end) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- for (content::RenderProcessHost::iterator it( |
- content::RenderProcessHost::AllHostsIterator()); |
- !it.IsAtEnd(); it.Advance()) { |
- content::RenderProcessHost* render_process_host = it.GetCurrentValue(); |
- DCHECK(render_process_host); |
- // Ignore processes that don't have a connection, such as crashed tabs. |
- if (!render_process_host->HasConnection()) |
- continue; |
- |
- render_process_host->Send(new ChromeViewMsg_SetTrackingStatus(enable)); |
- } |
-} |
- |
-// static |
-void TrackingSynchronizer::IsTrackingEnabled(int process_id) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
- // To find the process, or to send messages to the hosts, we need to be on the |
- // UI thread. |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind( |
- &TrackingSynchronizer::SetTrackingStatusInProcess, process_id)); |
-} |
- |
-// static |
-void TrackingSynchronizer::SetTrackingStatusInProcess(int process_id) { |
- // To find the process, or to send messages to the hosts, we need to be on the |
- // UI thread. |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- |
- bool enable = tracked_objects::ThreadData::tracking_status(); |
- |
- content::RenderProcessHost* process = |
- content::RenderProcessHost::FromID(process_id); |
- // Ignore processes that don't have a connection, such as crashed tabs. |
- if (!process || !process->HasConnection()) |
+ RequestContext* request = RequestContext::GetRequestContext(sequence_number); |
+ if (!request) |
return; |
- process->Send(new ChromeViewMsg_SetTrackingStatus(enable)); |
+ request->AddProcessesPending(pending_processes); |
+ request->SetReceivedProcessGroupCount(end); |
+ request->DeleteIfAllDone(); |
} |
-// static |
-void TrackingSynchronizer::DeserializeTrackingList( |
+void TrackingSynchronizer::OnProfilerDataCollected( |
int sequence_number, |
- const std::string& tracking_data, |
- content::ProcessType process_type) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind( |
- &TrackingSynchronizer::DeserializeTrackingListOnUI, |
- sequence_number, tracking_data, process_type)); |
-} |
- |
-// static |
-void TrackingSynchronizer::DeserializeTrackingListOnUI( |
- int sequence_number, |
- const std::string& tracking_data, |
- content::ProcessType process_type) { |
+ base::DictionaryValue* profiler_data) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); |
- if (current_synchronizer == NULL) |
+ RequestContext* request = RequestContext::GetRequestContext(sequence_number); |
+ if (!request) |
return; |
- base::Value* value = |
- base::JSONReader().JsonToValue(tracking_data, false, true); |
- DCHECK(value->GetType() == base::Value::TYPE_DICTIONARY); |
- base::DictionaryValue* dictionary_value = |
- static_cast<DictionaryValue*>(value); |
- dictionary_value->SetString( |
- "process_type", content::GetProcessTypeNameInEnglish(process_type)); |
- |
- current_synchronizer->DecrementPendingProcessesAndSendData( |
- sequence_number, dictionary_value); |
+ DecrementPendingProcessesAndSendData(sequence_number, profiler_data); |
} |
int TrackingSynchronizer::RegisterAndNotifyAllProcesses( |
const base::WeakPtr<ProfilerUI>& callback_object) { |
- // To iterate over all processes, or to send messages to the hosts, we need |
- // to be on the UI thread. |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
int sequence_number = GetNextAvailableSequenceNumber(); |
- // Initialize processes_pending with one because we are going to send |
- // browser's ThreadData. |
- RequestContext* request = new RequestContext( |
- callback_object, sequence_number, 1, TimeTicks::Now()); |
- outstanding_requests_[sequence_number] = request; |
+ RequestContext* request = |
+ RequestContext::Register(sequence_number, callback_object); |
- DCHECK_GT(request->processes_pending_, 0); |
- for (content::RenderProcessHost::iterator it( |
- content::RenderProcessHost::AllHostsIterator()); |
- !it.IsAtEnd(); it.Advance()) { |
- content::RenderProcessHost* render_process_host = it.GetCurrentValue(); |
- DCHECK(render_process_host); |
- // Ignore processes that don't have a connection, such as crashed tabs. |
- if (!render_process_host->HasConnection()) |
- continue; |
+ // Increment pending process count for sending browser's profiler data. |
+ request->IncrementProcessesPending(); |
- ++request->processes_pending_; |
- if (!render_process_host->Send( |
- new ChromeViewMsg_GetRendererTrackedData(sequence_number))) { |
- DecrementPendingProcesses(sequence_number); |
- } |
- } |
+ // Get profiler data from renderer and browser child processes. |
+ content::ProfilerController::GetInstance()->GetProfilerData(sequence_number); |
- // Get the ThreadData for the browser process and send it back. |
+ // Send profiler_data from browser process. |
base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); |
const std::string process_type = |
content::GetProcessTypeNameInEnglish(content::PROCESS_TYPE_BROWSER); |
value->SetString("process_type", process_type); |
value->SetInteger("process_id", base::GetCurrentProcId()); |
- DCHECK_GT(request->processes_pending_, 0); |
DecrementPendingProcessesAndSendData(sequence_number, value); |
return sequence_number; |
@@ -209,17 +261,12 @@ |
base::DictionaryValue* value) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- RequestContextMap::iterator it = |
- outstanding_requests_.find(sequence_number); |
- if (it == outstanding_requests_.end()) { |
+ RequestContext* request = RequestContext::GetRequestContext(sequence_number); |
+ if (!request) { |
delete value; |
return; |
} |
- RequestContext* request = NULL; |
- request = it->second; |
- DCHECK(sequence_number == request->sequence_number_); |
- |
if (value && request->callback_object_) { |
// Transfers ownership of |value| to |callback_object_|. |
request->callback_object_->ReceivedData(value); |
@@ -227,37 +274,11 @@ |
delete value; |
} |
- if (--request->processes_pending_ <= 0) |
- ForceTrackingSynchronizationDoneCallback(sequence_number); |
+ // Delete request if we have heard back from all child processes. |
+ request->DecrementProcessesPending(); |
+ request->DeleteIfAllDone(); |
} |
-void TrackingSynchronizer::DecrementPendingProcesses(int sequence_number) { |
- DecrementPendingProcessesAndSendData(sequence_number, NULL); |
-} |
- |
-void TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback( |
- int sequence_number) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- |
- int unresponsive_processes; |
- RequestContextMap::iterator it = |
- outstanding_requests_.find(sequence_number); |
- if (it == outstanding_requests_.end()) |
- return; |
- |
- RequestContext* request = it->second; |
- |
- DCHECK(sequence_number == request->sequence_number_); |
- |
- unresponsive_processes = request->processes_pending_; |
- |
- delete it->second; |
- outstanding_requests_.erase(it); |
- |
- UMA_HISTOGRAM_COUNTS("Tracking.ProcessNotRespondingAsynchronous", |
- unresponsive_processes); |
-} |
- |
int TrackingSynchronizer::GetNextAvailableSequenceNumber() { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
@@ -270,6 +291,14 @@ |
} |
// static |
-TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; |
+TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ DCHECK(tracking_synchronizer_ != NULL); |
+ return tracking_synchronizer_; |
+} |
+// static |
+base::LazyInstance<RequestContext::RequestContextMap> |
+ RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER; |
+ |
} // namespace chrome_browser_metrics |