diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-24 09:54:13 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-24 09:54:13 +0000 |
commit | 7c927b60bffa117129ba9214fcb6174d753351d9 (patch) | |
tree | 785de404571d36e57c8c9b916426805fc43ee476 /chrome/browser/metrics | |
parent | af2ae52b8f5ec53e1ba1b4593368cd66f59c3a4d (diff) | |
download | chromium_src-7c927b60bffa117129ba9214fcb6174d753351d9.zip chromium_src-7c927b60bffa117129ba9214fcb6174d753351d9.tar.gz chromium_src-7c927b60bffa117129ba9214fcb6174d753351d9.tar.bz2 |
Further reduce the bad dependency of chrome/common on chrome/browser.
TEST=none
BUG=none
Review URL: http://codereview.chromium.org/656011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39877 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/metrics')
-rw-r--r-- | chrome/browser/metrics/histogram_synchronizer.cc | 278 | ||||
-rw-r--r-- | chrome/browser/metrics/histogram_synchronizer.h | 145 | ||||
-rw-r--r-- | chrome/browser/metrics/metrics_service.cc | 2 |
3 files changed, 424 insertions, 1 deletions
diff --git a/chrome/browser/metrics/histogram_synchronizer.cc b/chrome/browser/metrics/histogram_synchronizer.cc new file mode 100644 index 0000000..ff46199 --- /dev/null +++ b/chrome/browser/metrics/histogram_synchronizer.cc @@ -0,0 +1,278 @@ +// Copyright (c) 2009 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/histogram.h" +#include "base/logging.h" +#include "base/thread.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/render_messages.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +HistogramSynchronizer::HistogramSynchronizer() + : lock_(), + received_all_renderer_historgrams_(&lock_), + callback_task_(NULL), + callback_thread_(NULL), + io_message_loop_(NULL), + next_available_sequence_number_(0), + async_sequence_number_(0), + async_renderers_pending_(0), + async_callback_start_time_(TimeTicks::Now()), + synchronous_sequence_number_(0), + synchronous_renderers_pending_(0) { + DCHECK(histogram_synchronizer_ == NULL); + histogram_synchronizer_ = this; +} + +HistogramSynchronizer::~HistogramSynchronizer() { + // Clean up. + delete callback_task_; + callback_task_ = NULL; + callback_thread_ = NULL; + histogram_synchronizer_ = NULL; +} + +// static +HistogramSynchronizer* HistogramSynchronizer::CurrentSynchronizer() { + DCHECK(histogram_synchronizer_ != NULL); + return histogram_synchronizer_; +} + +void HistogramSynchronizer::FetchRendererHistogramsSynchronously( + TimeDelta wait_time) { + DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); + + int sequence_number = GetNextAvaibleSequenceNumber(SYNCHRONOUS_HISTOGRAMS); + for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); + !it.IsAtEnd(); it.Advance()) { + it.GetCurrentValue()->Send( + new ViewMsg_GetRendererHistograms(sequence_number)); + IncrementPendingRenderers(SYNCHRONOUS_HISTOGRAMS); + } + + TimeTicks start = TimeTicks::Now(); + TimeTicks end_time = start + wait_time; + int unresponsive_renderer_count; + { + AutoLock auto_lock(lock_); + while (synchronous_renderers_pending_ > 0 && + TimeTicks::Now() < end_time) { + wait_time = end_time - TimeTicks::Now(); + received_all_renderer_historgrams_.TimedWait(wait_time); + } + unresponsive_renderer_count = synchronous_renderers_pending_; + synchronous_renderers_pending_ = 0; + synchronous_sequence_number_ = 0; + } + 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(MessageLoop::current()->type() == MessageLoop::TYPE_UI); + DCHECK(callback_thread != NULL); + DCHECK(callback_task != NULL); + + HistogramSynchronizer* current_synchronizer = + HistogramSynchronizer::CurrentSynchronizer(); + + if (current_synchronizer == NULL) { + // System teardown is happening. + callback_thread->PostTask(FROM_HERE, callback_task); + return; + } + + // callback_task_ member can only be accessed on IO thread. + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + current_synchronizer, + &HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms, + callback_thread, + callback_task)); + + // Tell all renderer processes to send their histograms. + int sequence_number = + current_synchronizer->GetNextAvaibleSequenceNumber(ASYNC_HISTOGRAMS); + for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); + !it.IsAtEnd(); it.Advance()) { + it.GetCurrentValue()->Send( + new ViewMsg_GetRendererHistograms(sequence_number)); + current_synchronizer->IncrementPendingRenderers(ASYNC_HISTOGRAMS); + } + + // Post a task that would be called after waiting for wait_time. + ChromeThread::PostDelayedTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + current_synchronizer, + &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback, + sequence_number), + wait_time); +} + +// static +void HistogramSynchronizer::DeserializeHistogramList( + int sequence_number, + const std::vector<std::string>& histograms) { + HistogramSynchronizer* current_synchronizer = + HistogramSynchronizer::CurrentSynchronizer(); + if (current_synchronizer == NULL) + return; + + DCHECK(current_synchronizer->IsOnIoThread()); + + for (std::vector<std::string>::const_iterator it = histograms.begin(); + it < histograms.end(); + ++it) { + Histogram::DeserializeHistogramInfo(*it); + } + + // Record that we have received a histogram from renderer process. + current_synchronizer->RecordRendererHistogram(sequence_number); +} + +bool HistogramSynchronizer::RecordRendererHistogram(int sequence_number) { + DCHECK(IsOnIoThread()); + + if (sequence_number == async_sequence_number_) { + if ((async_renderers_pending_ == 0) || + (--async_renderers_pending_ > 0)) + return false; + DCHECK(callback_task_ != NULL); + CallCallbackTaskAndResetData(); + return true; + } + + { + AutoLock auto_lock(lock_); + if (sequence_number != synchronous_sequence_number_) { + // No need to do anything if the sequence_number does not match current + // synchronous_sequence_number_ or async_sequence_number_. + return true; + } + if ((synchronous_renderers_pending_ == 0) || + (--synchronous_renderers_pending_ > 0)) + return false; + DCHECK_EQ(synchronous_renderers_pending_, 0); + } + + // We could call Signal() without holding the lock. + received_all_renderer_historgrams_.Signal(); + return true; +} + +// This method is called on the IO thread. +void HistogramSynchronizer::SetCallbackTaskToCallAfterGettingHistograms( + MessageLoop* callback_thread, + Task* callback_task) { + DCHECK(IsOnIoThread()); + + // Test for the existence of a previous task, and post call to post it if it + // exists. We promised to post it after some timeout... and at this point, we + // should just force the posting. + if (callback_task_ != NULL) { + CallCallbackTaskAndResetData(); + } + + // Assert there was no callback_task_ already. + DCHECK(callback_task_ == NULL); + + // Save the thread and the callback_task. + DCHECK(callback_thread != NULL); + DCHECK(callback_task != NULL); + callback_task_ = callback_task; + callback_thread_ = callback_thread; + async_callback_start_time_ = TimeTicks::Now(); +} + +void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback( + int sequence_number) { + DCHECK(IsOnIoThread()); + + if (sequence_number == async_sequence_number_) { + CallCallbackTaskAndResetData(); + } +} + +// If wait time has elapsed or if we have received all the histograms from all +// the renderers, call the callback_task if a callback_task exists. This is +// called on IO Thread. +void HistogramSynchronizer::CallCallbackTaskAndResetData() { + DCHECK(IsOnIoThread()); + + // callback_task_ would be set to NULL, if we have heard from all renderers + // and we would have called the callback_task already. + if (callback_task_ == NULL) { + return; + } + + UMA_HISTOGRAM_COUNTS("Histogram.RendersNotRespondingAsynchronous", + async_renderers_pending_); + if (!async_renderers_pending_) + UMA_HISTOGRAM_TIMES("Histogram.FetchRendererHistogramsAsynchronously", + TimeTicks::Now() - async_callback_start_time_); + + DCHECK(callback_thread_ != NULL); + DCHECK(callback_task_ != NULL); + callback_thread_->PostTask(FROM_HERE, callback_task_); + async_renderers_pending_ = 0; + async_callback_start_time_ = TimeTicks::Now(); + callback_task_ = NULL; + callback_thread_ = NULL; +} + +int HistogramSynchronizer::GetNextAvaibleSequenceNumber( + RendererHistogramRequester requester) { + AutoLock auto_lock(lock_); + ++next_available_sequence_number_; + if (0 > next_available_sequence_number_) { + // We wrapped around. + next_available_sequence_number_ = + chrome::kHistogramSynchronizerReservedSequenceNumber + 1; + } + DCHECK_NE(next_available_sequence_number_, + chrome::kHistogramSynchronizerReservedSequenceNumber); + if (requester == ASYNC_HISTOGRAMS) { + async_sequence_number_ = next_available_sequence_number_; + async_renderers_pending_ = 0; + } else if (requester == SYNCHRONOUS_HISTOGRAMS) { + synchronous_sequence_number_ = next_available_sequence_number_; + synchronous_renderers_pending_ = 0; + } + return next_available_sequence_number_; +} + +void HistogramSynchronizer::IncrementPendingRenderers( + RendererHistogramRequester requester) { + if (requester == ASYNC_HISTOGRAMS) { + async_renderers_pending_++; + } else { + synchronous_renderers_pending_++; + } +} + +bool HistogramSynchronizer::IsOnIoThread() { + if (io_message_loop_ == NULL) { + io_message_loop_ = MessageLoop::current(); + } + return (MessageLoop::current() == io_message_loop_); +} + +// static +HistogramSynchronizer* HistogramSynchronizer::histogram_synchronizer_ = NULL; diff --git a/chrome/browser/metrics/histogram_synchronizer.h b/chrome/browser/metrics/histogram_synchronizer.h new file mode 100644 index 0000000..1a69afb --- /dev/null +++ b/chrome/browser/metrics/histogram_synchronizer.h @@ -0,0 +1,145 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_METRICS_HISTOGRAM_SYNCHRONIZER_H_ +#define CHROME_BROWSER_METRICS_HISTOGRAM_SYNCHRONIZER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/time.h" + +class MessageLoop; +class Task; + +class HistogramSynchronizer : public + base::RefCountedThreadSafe<HistogramSynchronizer> { + public: + + enum RendererHistogramRequester { + ASYNC_HISTOGRAMS, + SYNCHRONOUS_HISTOGRAMS + }; + + HistogramSynchronizer(); + + ~HistogramSynchronizer(); + + // Return pointer to the singleton instance, which is allocated and + // deallocated on the main UI thread (during system startup and teardown). + static HistogramSynchronizer* CurrentSynchronizer(); + + // Contact all renderers, and get them to upload to the browser any/all + // changes to histograms. Return when all changes have been acquired, or when + // the wait time expires (whichever is sooner). This method is called on the + // main UI thread from about:histograms. + void FetchRendererHistogramsSynchronously(base::TimeDelta wait_time); + + // Contact all renderers, and get them to upload to the browser any/all + // changes to histograms. When all changes have been acquired, or when the + // wait time expires (whichever is sooner), post the callback_task to the UI + // thread. Note the callback_task is posted exactly once. This method is + // called on the IO thread from UMA via PostMessage. + static void FetchRendererHistogramsAsynchronously( + MessageLoop* callback_thread, Task* callback_task, int wait_time); + + // This method is called on the IO thread. Desrializes the histograms and + // records that we have received histograms from a renderer process. + static void DeserializeHistogramList( + int sequence_number, const std::vector<std::string>& histograms); + + private: + // Records that we have received the histograms from a renderer for the given + // sequence number. If we have received a response from all histograms, either + // signal the waiting process or call the callback function. Returns true when + // we receive histograms from the last of N renderers that were contacted for + // an update. This is called on IO Thread. + bool RecordRendererHistogram(int sequence_number); + + void SetCallbackTaskToCallAfterGettingHistograms( + MessageLoop* callback_thread, Task* callback_task); + + void ForceHistogramSynchronizationDoneCallback(int sequence_number); + + // Calls the callback task, if there is a callback_task. + void CallCallbackTaskAndResetData(); + + // Gets a new sequence number to be sent to renderers from broswer process. + // This will also reset the current pending renderers for the given type. + int GetNextAvaibleSequenceNumber(RendererHistogramRequester requster); + + // Increments the count of the renderers we're waiting for for the request + // of the given type. + void IncrementPendingRenderers(RendererHistogramRequester requester); + + // For use ONLY in a DCHECK. This method initializes io_message_loop_ in its + // first call and then compares io_message_loop_ with MessageLoop::current() + // in subsequent calls. This method guarantees we're consistently on the + // singular IO thread and we don't need to worry about locks. + bool IsOnIoThread(); + + // This lock_ protects access to sequence number and + // synchronous_renderers_pending_. + Lock lock_; + + // This condition variable is used to block caller of the synchronous request + // to update histograms, and to signal that thread when updates are completed. + ConditionVariable received_all_renderer_historgrams_; + + // When a request is made to asynchronously update the histograms, we store + // the task and thread we use to post a completion notification in + // callback_task_ and callback_thread_. + Task* callback_task_; + MessageLoop* callback_thread_; + + // For use ONLY in a DCHECK and is used in IsOnIoThread(). io_message_loop_ is + // initialized during the first call to IsOnIoThread(), and then compares + // MessageLoop::current() against io_message_loop_ in subsequent calls for + // consistency. + MessageLoop* io_message_loop_; + + // We don't track the actual renderers that are contacted for an update, only + // the count of the number of renderers, and we can sometimes time-out and + // give up on a "slow to respond" renderer. We use a sequence_number to be + // sure a response from a renderer is associated with the current round of + // requests (and not merely a VERY belated prior response). + // next_available_sequence_number_ is the next available number (used to + // avoid reuse for a long time). + int next_available_sequence_number_; + + // The sequence number used by the most recent asynchronous update request to + // contact all renderers. + int async_sequence_number_; + + // The number of renderers that have not yet responded to requests (as part of + // an asynchronous update). + int async_renderers_pending_; + + // The time when we were told to start the fetch histograms asynchronously + // from renderers. + base::TimeTicks async_callback_start_time_; + + // The sequence number used by the most recent synchronous update request to + // contact all renderers. + int synchronous_sequence_number_; + + // The number of renderers that have not yet responded to requests (as part of + // a synchronous update). + int synchronous_renderers_pending_; + + // This singleton instance should be started during the single threaded + // portion of main(). It initializes globals to provide support for all future + // calls. This object is created on the UI thread, and it is destroyed after + // all the other threads have gone away. As a result, it is ok to call it + // from the UI thread (for UMA uploads), or for about:histograms. + static HistogramSynchronizer* histogram_synchronizer_; + + DISALLOW_COPY_AND_ASSIGN(HistogramSynchronizer); +}; + +#endif // CHROME_BROWSER_METRICS_HISTOGRAM_SYNCHRONIZER_H_ diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc index dbd48fa..6b4ebbb 100644 --- a/chrome/browser/metrics/metrics_service.cc +++ b/chrome/browser/metrics/metrics_service.cc @@ -174,13 +174,13 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/load_notification_details.h" #include "chrome/browser/memory_details.h" +#include "chrome/browser/metrics/histogram_synchronizer.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/child_process_logging.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/histogram_synchronizer.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" |