Index: content/browser/histogram_synchronizer.cc |
=================================================================== |
--- content/browser/histogram_synchronizer.cc (working copy) |
+++ content/browser/histogram_synchronizer.cc (working copy) |
@@ -2,23 +2,25 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "chrome/browser/metrics/histogram_synchronizer.h" |
+#include "content/browser/histogram_synchronizer.h" |
#include "base/bind.h" |
+#include "base/lazy_instance.h" |
#include "base/logging.h" |
#include "base/metrics/histogram.h" |
#include "base/threading/thread.h" |
#include "base/threading/thread_restrictions.h" |
-#include "chrome/common/chrome_constants.h" |
-#include "chrome/common/render_messages.h" |
+#include "content/browser/histogram_controller.h" |
#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/histogram_fetcher.h" |
+#include "content/public/common/content_constants.h" |
using base::Time; |
using base::TimeDelta; |
using base::TimeTicks; |
-using content::BrowserThread; |
+namespace { |
+ |
// 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. |
@@ -26,153 +28,260 @@ |
// territory. |
static const int kNeverUsableSequenceNumber = -2; |
+} // anonymous namespace |
+ |
+namespace content { |
+ |
+// The "RequestContext" structure describes an individual request received from |
+// the UI. All methods are accessible on UI thread. |
+class HistogramSynchronizer::RequestContext { |
+ public: |
+ // A map from sequence_number_ to the actual RequestContexts. |
+ typedef std::map<int, RequestContext*> RequestContextMap; |
+ |
+ RequestContext(const base::Closure& callback, int sequence_number) |
+ : callback_(callback), |
+ sequence_number_(sequence_number), |
+ received_process_group_count_(0), |
+ processes_pending_(0) { |
+ } |
+ ~RequestContext() {} |
+ |
+ void SetReceivedProcessGroupCount(bool done) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ received_process_group_count_ = done; |
+ } |
+ |
+ // Methods for book keeping of 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 histogram 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| in |outstanding_requests_| map for the given |
+ // |sequence_number|. |
+ static void Register(const base::Closure& callback, int sequence_number) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ RequestContext* request = new RequestContext(callback, sequence_number); |
+ outstanding_requests_.Get()[sequence_number] = 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 = it->second; |
+ DCHECK_EQ(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_EQ(sequence_number, request->sequence_number_); |
+ bool received_process_group_count = request->received_process_group_count_; |
+ int unresponsive_processes = request->processes_pending_; |
+ |
+ request->callback_.Run(); |
+ |
+ delete request; |
+ outstanding_requests_.Get().erase(it); |
+ |
+ UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount", |
+ received_process_group_count); |
+ UMA_HISTOGRAM_COUNTS("Histogram.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_|. |
+ base::Closure callback_; |
+ |
+ // 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 (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>::Leaky outstanding_requests_; |
+}; |
+ |
+// static |
+base::LazyInstance |
+ <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky |
+ HistogramSynchronizer::RequestContext::outstanding_requests_ = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
HistogramSynchronizer::HistogramSynchronizer() |
- : lock_(), |
- received_all_renderer_histograms_(&lock_), |
- callback_thread_(NULL), |
- last_used_sequence_number_(kNeverUsableSequenceNumber), |
- async_sequence_number_(kNeverUsableSequenceNumber), |
- async_renderers_pending_(0), |
- synchronous_sequence_number_(kNeverUsableSequenceNumber), |
- synchronous_renderers_pending_(0) { |
- DCHECK(histogram_synchronizer_ == NULL); |
- histogram_synchronizer_ = this; |
+ : lock_(), |
+ callback_thread_(NULL), |
+ last_used_sequence_number_(kNeverUsableSequenceNumber), |
+ async_sequence_number_(kNeverUsableSequenceNumber) { |
+ content::HistogramController::GetInstance()->Register(this); |
} |
HistogramSynchronizer::~HistogramSynchronizer() { |
+ RequestContext::OnShutdown(); |
+ |
// Just in case we have any pending tasks, clear them out. |
SetCallbackTaskAndThread(NULL, base::Closure()); |
- histogram_synchronizer_ = NULL; |
} |
-// static |
-HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() { |
- DCHECK(histogram_synchronizer_ != NULL); |
- return histogram_synchronizer_; |
+HistogramSynchronizer* HistogramSynchronizer::GetInstance() { |
+ return Singleton<HistogramSynchronizer, |
+ LeakySingletonTraits<HistogramSynchronizer> >::get(); |
} |
-void HistogramSynchronizer::FetchRendererHistogramsSynchronously( |
- TimeDelta wait_time) { |
- NotifyAllRenderers(SYNCHRONOUS_HISTOGRAMS); |
+// static |
+void HistogramSynchronizer::FetchHistograms() { |
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, FROM_HERE, |
+ base::Bind(&HistogramSynchronizer::FetchHistograms)); |
+ return; |
+ } |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- TimeTicks start = TimeTicks::Now(); |
- TimeTicks end_time = start + wait_time; |
- int unresponsive_renderer_count; |
- { |
- base::AutoLock auto_lock(lock_); |
- while (synchronous_renderers_pending_ > 0 && TimeTicks::Now() < end_time) { |
- wait_time = end_time - TimeTicks::Now(); |
- base::ThreadRestrictions::ScopedAllowWait allow_wait; |
- received_all_renderer_histograms_.TimedWait(wait_time); |
- } |
- unresponsive_renderer_count = synchronous_renderers_pending_; |
- synchronous_renderers_pending_ = 0; |
- synchronous_sequence_number_ = kNeverUsableSequenceNumber; |
- } |
- UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingSynchronous", |
- unresponsive_renderer_count); |
- if (!unresponsive_renderer_count) |
- UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsSynchronously", |
- TimeTicks::Now() - start); |
+ HistogramSynchronizer* current_synchronizer = |
+ HistogramSynchronizer::GetInstance(); |
+ if (current_synchronizer == NULL) |
+ return; |
+ |
+ current_synchronizer->RegisterAndNotifyAllProcesses( |
+ HistogramSynchronizer::UNKNOWN, |
+ base::TimeDelta::FromMinutes(1)); |
} |
+void FetchHistogramsAsynchronously(MessageLoop* callback_thread, |
+ const base::Closure& callback, |
+ base::TimeDelta wait_time) { |
+ HistogramSynchronizer::FetchHistogramsAsynchronously( |
+ callback_thread, callback, wait_time); |
+} |
+ |
// static |
-void HistogramSynchronizer::FetchRendererHistogramsAsynchronously( |
+void HistogramSynchronizer::FetchHistogramsAsynchronously( |
MessageLoop* callback_thread, |
const base::Closure& callback, |
base::TimeDelta wait_time) { |
DCHECK(callback_thread != NULL); |
DCHECK(!callback.is_null()); |
- HistogramSynchronizer* current_synchronizer = CurrentSynchronizer(); |
+ HistogramSynchronizer* current_synchronizer = |
+ HistogramSynchronizer::GetInstance(); |
+ current_synchronizer->SetCallbackTaskAndThread( |
+ callback_thread, callback); |
- if (current_synchronizer == NULL) { |
- // System teardown is happening. |
- callback_thread->PostTask(FROM_HERE, callback); |
- return; |
- } |
+ current_synchronizer->RegisterAndNotifyAllProcesses( |
+ HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time); |
+} |
- current_synchronizer->SetCallbackTaskAndThread(callback_thread, |
- callback); |
+void HistogramSynchronizer::RegisterAndNotifyAllProcesses( |
+ ProcessHistogramRequester requester, |
+ base::TimeDelta wait_time) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- int sequence_number = |
- current_synchronizer->NotifyAllRenderers(ASYNC_HISTOGRAMS); |
+ int sequence_number = GetNextAvailableSequenceNumber(requester); |
+ base::Closure callback = base::Bind( |
+ &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, |
+ base::Unretained(this), |
+ sequence_number); |
+ |
+ RequestContext::Register(callback, sequence_number); |
+ |
+ // Get histogram data from renderer and browser child processes. |
+ content::HistogramController::GetInstance()->GetHistogramData( |
+ sequence_number); |
+ |
// Post a task that would be called after waiting for wait_time. This acts |
- // as a watchdog, to ensure that a non-responsive renderer won't block us from |
- // making the callback. |
+ // as a watchdog, to cancel the requests for non-responsive processes. |
BrowserThread::PostDelayedTask( |
BrowserThread::UI, FROM_HERE, |
- base::Bind( |
- &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, |
- current_synchronizer, |
- sequence_number), |
+ base::Bind(&RequestContext::Unregister, sequence_number), |
wait_time); |
} |
-// static |
-void HistogramSynchronizer::DeserializeHistogramList( |
- int sequence_number, |
- const std::vector<std::string>& histograms) { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
- for (std::vector<std::string>::const_iterator it = histograms.begin(); |
- it < histograms.end(); |
- ++it) { |
- base::Histogram::DeserializeHistogramInfo(*it); |
- } |
+void HistogramSynchronizer::OnPendingProcesses(int sequence_number, |
+ int pending_processes, |
+ bool end) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- HistogramSynchronizer* current_synchronizer = CurrentSynchronizer(); |
- if (current_synchronizer == NULL) |
+ RequestContext* request = RequestContext::GetRequestContext(sequence_number); |
+ if (!request) |
return; |
- |
- // Record that we have received a histogram from renderer process. |
- current_synchronizer->DecrementPendingRenderers(sequence_number); |
+ request->AddProcessesPending(pending_processes); |
+ request->SetReceivedProcessGroupCount(end); |
+ request->DeleteIfAllDone(); |
} |
-int HistogramSynchronizer::NotifyAllRenderers( |
- RendererHistogramRequester requester) { |
- // To iterate over RenderProcessHosts, or to send messages to the hosts, we |
- // need to be on the UI thread. |
+void HistogramSynchronizer::OnHistogramDataCollected( |
+ int sequence_number, |
+ const std::vector<std::string>& pickled_histograms) { |
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- int notification_count = 0; |
- for (content::RenderProcessHost::iterator it( |
- content::RenderProcessHost::AllHostsIterator()); |
- !it.IsAtEnd(); it.Advance()) |
- ++notification_count; |
+ RequestContext* request = RequestContext::GetRequestContext(sequence_number); |
- int sequence_number = GetNextAvailableSequenceNumber(requester, |
- notification_count); |
- for (content::RenderProcessHost::iterator it( |
- content::RenderProcessHost::AllHostsIterator()); |
- !it.IsAtEnd(); it.Advance()) { |
- if (!it.GetCurrentValue()->Send( |
- new ChromeViewMsg_GetRendererHistograms(sequence_number))) |
- DecrementPendingRenderers(sequence_number); |
+ for (std::vector<std::string>::const_iterator it = pickled_histograms.begin(); |
+ it < pickled_histograms.end(); |
+ ++it) { |
+ base::Histogram::DeserializeHistogramInfo(*it); |
} |
- return sequence_number; |
-} |
+ if (!request) |
+ return; |
-void HistogramSynchronizer::DecrementPendingRenderers(int sequence_number) { |
- bool synchronous_completed = false; |
- bool asynchronous_completed = false; |
- |
- { |
- base::AutoLock auto_lock(lock_); |
- if (sequence_number == async_sequence_number_) { |
- if (--async_renderers_pending_ <= 0) |
- asynchronous_completed = true; |
- } else if (sequence_number == synchronous_sequence_number_) { |
- if (--synchronous_renderers_pending_ <= 0) |
- synchronous_completed = true; |
- } |
- } |
- |
- if (asynchronous_completed) |
- ForceHistogramSynchronizationDoneCallback(sequence_number); |
- else if (synchronous_completed) |
- received_all_renderer_histograms_.Signal(); |
+ // Delete request if we have heard back from all child processes. |
+ request->DecrementProcessesPending(); |
+ request->DeleteIfAllDone(); |
} |
void HistogramSynchronizer::SetCallbackTaskAndThread( |
@@ -180,32 +289,23 @@ |
const base::Closure& callback) { |
base::Closure old_callback; |
MessageLoop* old_thread = NULL; |
- TimeTicks old_start_time; |
- int unresponsive_renderers; |
- const TimeTicks now = TimeTicks::Now(); |
{ |
base::AutoLock auto_lock(lock_); |
old_callback = callback_; |
callback_ = callback; |
old_thread = callback_thread_; |
callback_thread_ = callback_thread; |
- unresponsive_renderers = async_renderers_pending_; |
- old_start_time = async_callback_start_time_; |
- async_callback_start_time_ = now; |
// Prevent premature calling of our new callbacks. |
async_sequence_number_ = kNeverUsableSequenceNumber; |
} |
// Just in case there was a task pending.... |
- InternalPostTask(old_thread, old_callback, unresponsive_renderers, |
- old_start_time); |
+ InternalPostTask(old_thread, old_callback); |
} |
void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( |
int sequence_number) { |
base::Closure callback; |
MessageLoop* thread = NULL; |
- TimeTicks started; |
- int unresponsive_renderers; |
{ |
base::AutoLock lock(lock_); |
if (sequence_number != async_sequence_number_) |
@@ -214,31 +314,19 @@ |
thread = callback_thread_; |
callback_.Reset(); |
callback_thread_ = NULL; |
- started = async_callback_start_time_; |
- unresponsive_renderers = async_renderers_pending_; |
} |
- InternalPostTask(thread, callback, unresponsive_renderers, started); |
+ InternalPostTask(thread, callback); |
} |
void HistogramSynchronizer::InternalPostTask(MessageLoop* thread, |
- const base::Closure& callback, |
- int unresponsive_renderers, |
- const base::TimeTicks& started) { |
+ const base::Closure& callback) { |
if (callback.is_null() || !thread) |
return; |
- UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous", |
- unresponsive_renderers); |
- if (!unresponsive_renderers) { |
- UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously", |
- TimeTicks::Now() - started); |
- } |
- |
thread->PostTask(FROM_HERE, callback); |
} |
int HistogramSynchronizer::GetNextAvailableSequenceNumber( |
- RendererHistogramRequester requester, |
- int renderer_count) { |
+ ProcessHistogramRequester requester) { |
base::AutoLock auto_lock(lock_); |
++last_used_sequence_number_; |
// Watch out for wrapping to a negative number. |
@@ -246,19 +334,13 @@ |
// Bypass the reserved number, which is used when a renderer spontaneously |
// decides to send some histogram data. |
last_used_sequence_number_ = |
- chrome::kHistogramSynchronizerReservedSequenceNumber + 1; |
+ kHistogramSynchronizerReservedSequenceNumber + 1; |
} |
DCHECK_NE(last_used_sequence_number_, |
- chrome::kHistogramSynchronizerReservedSequenceNumber); |
- if (requester == ASYNC_HISTOGRAMS) { |
+ kHistogramSynchronizerReservedSequenceNumber); |
+ if (requester == ASYNC_HISTOGRAMS) |
async_sequence_number_ = last_used_sequence_number_; |
- async_renderers_pending_ = renderer_count; |
- } else if (requester == SYNCHRONOUS_HISTOGRAMS) { |
- synchronous_sequence_number_ = last_used_sequence_number_; |
- synchronous_renderers_pending_ = renderer_count; |
- } |
return last_used_sequence_number_; |
} |
-// static |
-HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL; |
+} // namespace content |