Index: chrome/browser/metrics/tracking_synchronizer.cc |
=================================================================== |
--- chrome/browser/metrics/tracking_synchronizer.cc (revision 111826) |
+++ chrome/browser/metrics/tracking_synchronizer.cc (working copy) |
@@ -5,23 +5,92 @@ |
#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" |
using base::TimeTicks; |
using content::BrowserThread; |
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(); |
jam
2011/11/30 00:22:25
nit: up to you, but since this class is in the cc
ramant (doing other things)
2011/11/30 19:37:21
Done.
|
+ |
+ private: |
+ friend class TrackingSynchronizer; |
jam
2011/11/30 00:22:25
nit: you can get rid fo the private constructor an
ramant (doing other things)
2011/11/30 19:37:21
Done.
|
+ |
+ RequestContext(const base::WeakPtr<ProfilerUI>& callback_object, |
+ int sequence_number); |
+ |
+ // Methods for book keeping of process_group_count_. |
+ void AddProcessGroupCount(int process_group_count); |
+ void DecrementProcessGroupCount(); |
+ |
+ // Methods for book keeping of processes_pending_. |
+ void IncrementProcessesPending(); |
+ void AddProcessesPending(int processes_pending); |
+ void DecrementProcessesPending(); |
+ |
+ // Records that we are waiting for one less tracking data from a process for |
+ // the given sequence number. If |process_group_count_| and |
+ // |processes_pending_| are zero, then delete the current object by calling |
+ // Unregister. |
+ void DeleteIfAllDone(); |
+ |
+ // Register |callback_object| in |outstanding_requests_| map for the given |
+ // |sequence_number|. |
+ static RequestContext* Register( |
+ int sequence_number, |
+ const base::WeakPtr<ProfilerUI>& callback_object); |
+ |
+ // Find the |RequestContext| in |outstanding_requests_| map for the given |
+ // |sequence_number|. |
+ static RequestContext* GetRequestContext(int sequence_number); |
+ |
+ // 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); |
+ |
+ // Delete all the entries in |outstanding_requests_| map. |
+ static void OnShutdown(); |
+ |
+ // 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_; |
+ |
+ // The number of times we need to hear from |content| about number of |
+ // pending processes. |content| code calls OnPendingProcesses once for |
+ // RenderProcessHosts and another time for BrowserChildProcessHosts after |
+ // sending GetChildProfilerData message to all respective child processes. |
+ // RegisterAndNotifyAllProcesses initializes it by the value returned by the |
+ // content::GetProfilerData and OnPendingProcesses decrements it when it gets |
+ // processes pending count. |
+ int 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. |
@@ -29,32 +98,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)); |
@@ -71,133 +137,57 @@ |
// 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) { |
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->DecrementProcessGroupCount(); |
+ request->DeleteIfAllDone(); |
} |
-// static |
-void TrackingSynchronizer::DeserializeTrackingList( |
+void TrackingSynchronizer::OnProfilerDataCollected( |
int sequence_number, |
- const std::string& tracking_data, |
- ChildProcessInfo::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, |
- ChildProcessInfo::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", ChildProcessInfo::GetTypeNameInEnglish(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. |
+ int process_group_count = content::GetProfilerData(sequence_number); |
- // Get the ThreadData for the browser process and send it back. |
+ request->AddProcessGroupCount(process_group_count); |
+ |
+ // Send profiler_data from browser process. |
base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); |
const std::string process_type = |
ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS); |
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; |
@@ -208,17 +198,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); |
@@ -226,49 +211,139 @@ |
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); |
+int TrackingSynchronizer::GetNextAvailableSequenceNumber() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ ++last_used_sequence_number_; |
+ |
+ // Watch out for wrapping to a negative number. |
+ if (last_used_sequence_number_ < 0) |
+ last_used_sequence_number_ = 1; |
+ return last_used_sequence_number_; |
} |
-void TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback( |
+// static |
+TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ DCHECK(tracking_synchronizer_ != NULL); |
+ return tracking_synchronizer_; |
+} |
+ |
+// RequestContext methods and members. |
+// |
+// static |
+base::LazyInstance<RequestContext::RequestContextMap> |
+ RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER; |
+ |
+RequestContext::RequestContext(const base::WeakPtr<ProfilerUI>& callback_object, |
+ int sequence_number) |
+ : callback_object_(callback_object), |
+ sequence_number_(sequence_number), |
+ process_group_count_(0), |
+ processes_pending_(0) { |
+} |
+ |
+RequestContext::~RequestContext() { |
+} |
+ |
+void RequestContext::AddProcessGroupCount(int process_group_count) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ process_group_count_ += process_group_count; |
+} |
+ |
+void RequestContext::DecrementProcessGroupCount() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ --process_group_count_; |
+} |
+ |
+void RequestContext::IncrementProcessesPending() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ ++processes_pending_; |
+} |
+ |
+void RequestContext::AddProcessesPending(int processes_pending) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ processes_pending_ += processes_pending; |
+} |
+ |
+void RequestContext::DecrementProcessesPending() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ --processes_pending_; |
+} |
+ |
+void RequestContext::DeleteIfAllDone() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (processes_pending_ <= 0 && process_group_count_ <= 0) { |
+ RequestContext::Unregister(sequence_number_); |
+ } |
+} |
+ |
+// static |
+RequestContext* 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; |
+} |
+ |
+// static |
+RequestContext* RequestContext::GetRequestContext( |
int sequence_number) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- int unresponsive_processes; |
RequestContextMap::iterator it = |
- outstanding_requests_.find(sequence_number); |
- if (it == outstanding_requests_.end()) |
+ 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; |
+} |
+ |
+// static |
+void RequestContext::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_); |
+ int process_group_count = request->process_group_count_; |
+ int unresponsive_processes = request->processes_pending_; |
- unresponsive_processes = request->processes_pending_; |
- |
delete it->second; |
- outstanding_requests_.erase(it); |
+ outstanding_requests_.Get().erase(it); |
- UMA_HISTOGRAM_COUNTS("Tracking.ProcessNotRespondingAsynchronous", |
+ UMA_HISTOGRAM_COUNTS("Profiling.ProcessGroupCount", |
+ process_group_count); |
+ UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding", |
unresponsive_processes); |
} |
-int TrackingSynchronizer::GetNextAvailableSequenceNumber() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- |
- ++last_used_sequence_number_; |
- |
- // Watch out for wrapping to a negative number. |
- if (last_used_sequence_number_ < 0) |
- last_used_sequence_number_ = 1; |
- return last_used_sequence_number_; |
+// static |
+void RequestContext::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); |
+ } |
} |
-// static |
-TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; |
- |
} // namespace chrome_browser_metrics |