diff options
Diffstat (limited to 'content/browser/histogram_synchronizer.cc')
-rw-r--r-- | content/browser/histogram_synchronizer.cc | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/content/browser/histogram_synchronizer.cc b/content/browser/histogram_synchronizer.cc new file mode 100644 index 0000000..bb90b07 --- /dev/null +++ b/content/browser/histogram_synchronizer.cc @@ -0,0 +1,346 @@ +// Copyright (c) 2012 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 "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 "content/browser/histogram_controller.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/histogram_fetcher.h" +#include "content/public/common/content_constants.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +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. +// We have code that handles wrapping around on an overflow into negative +// 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_(), + 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()); +} + +HistogramSynchronizer* HistogramSynchronizer::GetInstance() { + return Singleton<HistogramSynchronizer, + LeakySingletonTraits<HistogramSynchronizer> >::get(); +} + +// 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)); + + 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::FetchHistogramsAsynchronously( + MessageLoop* callback_thread, + const base::Closure& callback, + base::TimeDelta wait_time) { + DCHECK(callback_thread != NULL); + DCHECK(!callback.is_null()); + + HistogramSynchronizer* current_synchronizer = + HistogramSynchronizer::GetInstance(); + current_synchronizer->SetCallbackTaskAndThread( + callback_thread, callback); + + 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( + &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 cancel the requests for non-responsive processes. + BrowserThread::PostDelayedTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&RequestContext::Unregister, sequence_number), + wait_time); +} + +void HistogramSynchronizer::OnPendingProcesses(int sequence_number, + int pending_processes, + bool end) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + RequestContext* request = RequestContext::GetRequestContext(sequence_number); + if (!request) + return; + request->AddProcessesPending(pending_processes); + request->SetReceivedProcessGroupCount(end); + request->DeleteIfAllDone(); +} + +void HistogramSynchronizer::OnHistogramDataCollected( + int sequence_number, + const std::vector<std::string>& pickled_histograms) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + RequestContext* request = RequestContext::GetRequestContext(sequence_number); + + for (std::vector<std::string>::const_iterator it = pickled_histograms.begin(); + it < pickled_histograms.end(); + ++it) { + base::Histogram::DeserializeHistogramInfo(*it); + } + + if (!request) + return; + + // Delete request if we have heard back from all child processes. + request->DecrementProcessesPending(); + request->DeleteIfAllDone(); +} + +void HistogramSynchronizer::SetCallbackTaskAndThread( + MessageLoop* callback_thread, + const base::Closure& callback) { + base::Closure old_callback; + MessageLoop* old_thread = NULL; + { + base::AutoLock auto_lock(lock_); + old_callback = callback_; + callback_ = callback; + old_thread = callback_thread_; + callback_thread_ = callback_thread; + // Prevent premature calling of our new callbacks. + async_sequence_number_ = kNeverUsableSequenceNumber; + } + // Just in case there was a task pending.... + InternalPostTask(old_thread, old_callback); +} + +void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( + int sequence_number) { + base::Closure callback; + MessageLoop* thread = NULL; + { + base::AutoLock lock(lock_); + if (sequence_number != async_sequence_number_) + return; + callback = callback_; + thread = callback_thread_; + callback_.Reset(); + callback_thread_ = NULL; + } + InternalPostTask(thread, callback); +} + +void HistogramSynchronizer::InternalPostTask(MessageLoop* thread, + const base::Closure& callback) { + if (callback.is_null() || !thread) + return; + thread->PostTask(FROM_HERE, callback); +} + +int HistogramSynchronizer::GetNextAvailableSequenceNumber( + ProcessHistogramRequester requester) { + 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_ = + kHistogramSynchronizerReservedSequenceNumber + 1; + } + DCHECK_NE(last_used_sequence_number_, + kHistogramSynchronizerReservedSequenceNumber); + if (requester == ASYNC_HISTOGRAMS) + async_sequence_number_ = last_used_sequence_number_; + return last_used_sequence_number_; +} + +} // namespace content |