| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // 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 "base/metrics/histogram.h" |
| #include "base/logging.h" |
| #include "base/threading/thread.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/render_messages.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| |
| using base::Time; |
| using base::TimeDelta; |
| using base::TimeTicks; |
| |
| // 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. |
| // We have code that handles wrapping around on an overflow into negative |
| // territory. |
| static const int kNeverUsableSequenceNumber = -2; |
| |
| HistogramSynchronizer::HistogramSynchronizer() |
| : lock_(), |
| received_all_renderer_histograms_(&lock_), |
| callback_task_(NULL), |
| 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; |
| } |
| |
| HistogramSynchronizer::~HistogramSynchronizer() { |
| // Just in case we have any pending tasks, clear them out. |
| SetCallbackTaskAndThread(NULL, NULL); |
| histogram_synchronizer_ = NULL; |
| } |
| |
| // static |
| HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() { |
| DCHECK(histogram_synchronizer_ != NULL); |
| return histogram_synchronizer_; |
| } |
| |
| void HistogramSynchronizer::FetchRendererHistogramsSynchronously( |
| TimeDelta wait_time) { |
| NotifyAllRenderers(SYNCHRONOUS_HISTOGRAMS); |
| |
| 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(); |
| 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); |
| } |
| |
| // static |
| void HistogramSynchronizer::FetchRendererHistogramsAsynchronously( |
| MessageLoop* callback_thread, |
| Task* callback_task, |
| int wait_time) { |
| DCHECK(callback_thread != NULL); |
| DCHECK(callback_task != NULL); |
| |
| HistogramSynchronizer* current_synchronizer = CurrentSynchronizer(); |
| |
| if (current_synchronizer == NULL) { |
| // System teardown is happening. |
| callback_thread->PostTask(FROM_HERE, callback_task); |
| return; |
| } |
| |
| current_synchronizer->SetCallbackTaskAndThread(callback_thread, |
| callback_task); |
| |
| int sequence_number = |
| current_synchronizer->NotifyAllRenderers(ASYNC_HISTOGRAMS); |
| |
| // 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. |
| BrowserThread::PostDelayedTask( |
| BrowserThread::UI, FROM_HERE, |
| NewRunnableMethod( |
| current_synchronizer, |
| &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, |
| 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); |
| } |
| |
| HistogramSynchronizer* current_synchronizer = CurrentSynchronizer(); |
| if (current_synchronizer == NULL) |
| return; |
| |
| // Record that we have received a histogram from renderer process. |
| current_synchronizer->DecrementPendingRenderers(sequence_number); |
| } |
| |
| int HistogramSynchronizer::NotifyAllRenderers( |
| RendererHistogramRequester requester) { |
| // To iterate over RenderProcessHosts, or to send messages to the hosts, we |
| // need to be on the UI thread. |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| int notification_count = 0; |
| for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); |
| !it.IsAtEnd(); it.Advance()) |
| ++notification_count; |
| |
| int sequence_number = GetNextAvailableSequenceNumber(requester, |
| notification_count); |
| for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); |
| !it.IsAtEnd(); it.Advance()) { |
| if (!it.GetCurrentValue()->Send( |
| new ViewMsg_GetRendererHistograms(sequence_number))) |
| DecrementPendingRenderers(sequence_number); |
| } |
| |
| return sequence_number; |
| } |
| |
| 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(); |
| } |
| |
| void HistogramSynchronizer::SetCallbackTaskAndThread( |
| MessageLoop* callback_thread, |
| Task* callback_task) { |
| Task* old_task = NULL; |
| MessageLoop* old_thread = NULL; |
| TimeTicks old_start_time; |
| int unresponsive_renderers; |
| const TimeTicks now = TimeTicks::Now(); |
| { |
| base::AutoLock auto_lock(lock_); |
| old_task = callback_task_; |
| callback_task_ = callback_task; |
| 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_task, unresponsive_renderers, |
| old_start_time); |
| } |
| |
| void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( |
| int sequence_number) { |
| Task* task = NULL; |
| MessageLoop* thread = NULL; |
| TimeTicks started; |
| int unresponsive_renderers; |
| { |
| base::AutoLock lock(lock_); |
| if (sequence_number != async_sequence_number_) |
| return; |
| task = callback_task_; |
| thread = callback_thread_; |
| callback_task_ = NULL; |
| callback_thread_ = NULL; |
| started = async_callback_start_time_; |
| unresponsive_renderers = async_renderers_pending_; |
| } |
| InternalPostTask(thread, task, unresponsive_renderers, started); |
| } |
| |
| void HistogramSynchronizer::InternalPostTask(MessageLoop* thread, Task* task, |
| int unresponsive_renderers, |
| const base::TimeTicks& started) { |
| if (!task || !thread) |
| return; |
| UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous", |
| unresponsive_renderers); |
| if (!unresponsive_renderers) { |
| UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously", |
| TimeTicks::Now() - started); |
| } |
| |
| thread->PostTask(FROM_HERE, task); |
| } |
| |
| int HistogramSynchronizer::GetNextAvailableSequenceNumber( |
| RendererHistogramRequester requester, |
| int renderer_count) { |
| base::AutoLock auto_lock(lock_); |
| ++last_used_sequence_number_; |
| // Watch out for wrapping to a negative number. |
| if (last_used_sequence_number_ < 0) { |
| // Bypass the reserved number, which is used when a renderer spontaneously |
| // decides to send some histogram data. |
| last_used_sequence_number_ = |
| chrome::kHistogramSynchronizerReservedSequenceNumber + 1; |
| } |
| DCHECK_NE(last_used_sequence_number_, |
| chrome::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; |