| 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
|
|
|