Chromium Code Reviews| 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", |
|
jar (doing other things)
2012/07/09 23:04:12
Be sure to update histograms.xml
ramant (doing other things)
2012/07/11 23:52:54
Will submit a separate CL for it.
Done.
|
| + 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_; |
| +}; |
| + |
| 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(); |
| if (current_synchronizer == NULL) { |
| - // System teardown is happening. |
| callback_thread->PostTask(FROM_HERE, callback); |
| return; |
|
jar (doing other things)
2012/07/09 23:04:12
nit: lines 214-217 are probably not needed here (u
ramant (doing other things)
2012/07/11 23:52:54
Done.
|
| } |
| - current_synchronizer->SetCallbackTaskAndThread(callback_thread, |
| - callback); |
| + current_synchronizer->SetCallbackTaskAndThread( |
| + callback_thread, callback); |
| - int sequence_number = |
| - current_synchronizer->NotifyAllRenderers(ASYNC_HISTOGRAMS); |
| + current_synchronizer->RegisterAndNotifyAllProcesses( |
| + HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time); |
| +} |
| +void HistogramSynchronizer::RegisterAndNotifyAllProcesses( |
| + ProcessHistogramRequester requester, |
| + base::TimeDelta wait_time) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + int sequence_number = GetNextAvailableSequenceNumber(requester); |
| + |
| + base::Closure callback = |
| + base::Bind( |
|
jar (doing other things)
2012/07/09 23:04:12
nit: move to previous line.
ramant (doing other things)
2012/07/11 23:52:54
Done.
|
| + &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", |
|
jar (doing other things)
2012/07/09 23:04:12
nit: consider obsoleting in the histograms.xml fil
ramant (doing other things)
2012/07/11 23:52:54
Submitting a separate CL for histograms.xml change
|
| - 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,19 @@ |
| // 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; |
| +base::LazyInstance |
| + <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky |
| + HistogramSynchronizer::RequestContext::outstanding_requests_ = |
| + LAZY_INSTANCE_INITIALIZER; |
|
jar (doing other things)
2012/07/09 23:04:12
nit: typedef to reduce length of line.
ramant (doing other things)
2012/07/11 23:52:54
Wasn't able to define a typedef. Will leave it int
|
| + |
| +} // namespace content |