diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/jankometer.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/jankometer.cc')
-rw-r--r-- | chrome/browser/jankometer.cc | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/chrome/browser/jankometer.cc b/chrome/browser/jankometer.cc new file mode 100644 index 0000000..aa256d3 --- /dev/null +++ b/chrome/browser/jankometer.cc @@ -0,0 +1,248 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <limits> + +#include "chrome/browser/jankometer.h" + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/histogram.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/stats_counters.h" +#include "base/thread.h" +#include "base/time.h" +#include "base/watchdog.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/chrome_switches.h" + +namespace { + +// The maximum threshold of delay of the message before considering it a delay. +// For a debug build, you may want to set IO delay around 500ms. +// For a release build, setting it around 350ms is sensible. +// Visit about:histograms to see what the distribution is on your system, with +// your build. Be sure to do some work to get interesting stats. +// The numbers below came from a warm start (you'll get about 5-10 alarms with +// a cold start), and running the page-cycler for 5 rounds. +#ifdef NDEBUG +const int kMaxUIMessageDelayMs = 350; +const int kMaxIOMessageDelayMs = 200; +#else +const int kMaxUIMessageDelayMs = 500; +const int kMaxIOMessageDelayMs = 400; +#endif + +// Maximum processing time (excluding queueing delay) for a message before +// considering it delayed. +const int kMaxMessageProcessingMs = 100; + +// TODO(brettw) Consider making this a pref. +const bool kPlaySounds = false; + +//------------------------------------------------------------------------------ +// Provide a special watchdog to make it easy to set the breakpoint on this +// class only. +class JankWatchdog : public Watchdog { + public: + JankWatchdog(const TimeDelta& duration, + const std::wstring& thread_watched_name, + bool enabled) + : Watchdog(duration, thread_watched_name, enabled), + thread_name_watched_(thread_watched_name), + alarm_count_(0) { + } + + virtual ~JankWatchdog() {} + + virtual void Alarm() { + // Put break point here if you want to stop threads and look at what caused + // the jankiness. + alarm_count_++; + Watchdog::Alarm(); + } + + private: + std::wstring thread_name_watched_; + int alarm_count_; + + DISALLOW_EVIL_CONSTRUCTORS(JankWatchdog); +}; + +//------------------------------------------------------------------------------ +class JankObserver : public base::RefCountedThreadSafe<JankObserver>, + public MessageLoop::Observer { + public: + JankObserver(const wchar_t* thread_name, + const TimeDelta& excessive_duration, + bool watchdog_enable) + : MaxMessageDelay_(excessive_duration), + slow_processing_counter_(std::wstring(L"Chrome.SlowMsg") + thread_name), + queueing_delay_counter_(std::wstring(L"Chrome.DelayMsg") + thread_name), + process_times_((std::wstring(L"Chrome.ProcMsgL ") + + thread_name).c_str(), 1, 3600000, 50), + total_times_((std::wstring(L"Chrome.TotalMsgL ") + + thread_name).c_str(), 1, 3600000, 50), + total_time_watchdog_(excessive_duration, std::wstring(thread_name), + watchdog_enable) { + process_times_.SetFlags(kUmaTargetedHistogramFlag); + total_times_.SetFlags(kUmaTargetedHistogramFlag); + } + + // Attaches the observer to the current thread's message loop. You can only + // attach to the current thread, so this function can be invoked on another + // thread to attach it. + void AttachToCurrentThread() { + MessageLoop::current()->AddObserver(this); + } + + // Detaches the observer to the current thread's message loop. + void DetachFromCurrentThread() { + MessageLoop::current()->RemoveObserver(this); + } + + void WillProcessMessage(const MSG& msg) { + begin_process_message_ = TimeTicks::Now(); + + // GetMessageTime returns a LONG (signed 32-bit) and GetTickCount returns + // a DWORD (unsigned 32-bit). They both wrap around when the time is longer + // than they can hold. I'm not sure if GetMessageTime wraps around to 0, + // or if the original time comes from GetTickCount, it might wrap around + // to -1. + // + // Therefore, I cast to DWORD so if it wraps to -1 we will correct it. If + // it doesn't, then our time delta will be negative if a message happens + // to straddle the wraparound point, it will still be OK. + DWORD cur_message_issue_time = static_cast<DWORD>(msg.time); + DWORD cur_time = GetTickCount(); + queueing_time_ = TimeDelta::FromMilliseconds(cur_time + - cur_message_issue_time); + // Simulate arming when the message entered the queue. + total_time_watchdog_.ArmSomeTimeDeltaAgo(queueing_time_); + if (queueing_time_ > MaxMessageDelay_) { + // Message is too delayed. + queueing_delay_counter_.Increment(); + if (kPlaySounds) + MessageBeep(MB_ICONASTERISK); + } + } + + void DidProcessMessage(const MSG& msg) { + total_time_watchdog_.Disarm(); + TimeTicks now = TimeTicks::Now(); + if (begin_process_message_ != TimeTicks()) { + TimeDelta processing_time = now - begin_process_message_; + process_times_.AddTime(processing_time); + total_times_.AddTime(queueing_time_ + processing_time); + } + if (now - begin_process_message_ > + TimeDelta::FromMilliseconds(kMaxMessageProcessingMs)) { + // Message took too long to process. + slow_processing_counter_.Increment(); + if (kPlaySounds) + MessageBeep(MB_ICONHAND); + } + } + + private: + const TimeDelta MaxMessageDelay_; + TimeTicks begin_process_message_; + TimeDelta queueing_time_; + + // Counters for the two types of jank we measure. + StatsCounter slow_processing_counter_; // Messages with long processing time. + StatsCounter queueing_delay_counter_; // Messages with long queueing delay. + Histogram process_times_; // Time spent processing task. + Histogram total_times_; // Total of queueing plus processing time. + JankWatchdog total_time_watchdog_; // Watching for excessive total_time. + + DISALLOW_EVIL_CONSTRUCTORS(JankObserver); +}; + +// These objects are created by InstallJankometer and leaked. +JankObserver* ui_observer = NULL; +JankObserver* io_observer = NULL; + +} // namespace + +void InstallJankometer(const CommandLine &parsed_command_line) { + if (ui_observer || io_observer) { + NOTREACHED() << "Initializing jank-o-meter twice"; + return; + } + + bool ui_watchdog_enabled = false; + bool io_watchdog_enabled = false; + if (parsed_command_line.HasSwitch(switches::kEnableWatchdog)) { + std::wstring list = + parsed_command_line.GetSwitchValue(switches::kEnableWatchdog); + if (list.npos != list.find(L"ui")) + ui_watchdog_enabled = true; + if (list.npos != list.find(L"io")) + io_watchdog_enabled = true; + } + + // Install on the UI thread. + ui_observer = new JankObserver( + L"UI", + TimeDelta::FromMilliseconds(kMaxUIMessageDelayMs), + ui_watchdog_enabled); + ui_observer->AddRef(); + ui_observer->AttachToCurrentThread(); + + // Now install on the I/O thread. Hiccups on that thread will block + // interaction with web pages. We must proxy to that thread before we can + // add our observer. + io_observer = new JankObserver( + L"IO", + TimeDelta::FromMilliseconds(kMaxIOMessageDelayMs), + io_watchdog_enabled); + io_observer->AddRef(); + Thread* io_thread = g_browser_process->io_thread(); + if (io_thread) { + io_thread->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(io_observer, + &JankObserver::AttachToCurrentThread)); + } +} + +void UninstallJankometer() { + if (ui_observer) { + ui_observer->DetachFromCurrentThread(); + ui_observer->Release(); + ui_observer = NULL; + } + if (io_observer) { + // IO thread can't be running when we remove observers. + DCHECK((!g_browser_process) || !(g_browser_process->io_thread())); + io_observer->Release(); + io_observer = NULL; + } +} |