summaryrefslogtreecommitdiffstats
path: root/content/browser/histogram_synchronizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/histogram_synchronizer.cc')
-rw-r--r--content/browser/histogram_synchronizer.cc346
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