| // Copyright (c) 2011 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. |
| |
| // This file defines a WatchDog thread that monitors the responsiveness of other |
| // browser threads like UI, IO, DB, FILE and CACHED threads. It also defines |
| // ThreadWatcher class which performs health check on threads that would like to |
| // be watched. This file also defines ThreadWatcherList class that has list of |
| // all active ThreadWatcher objects. |
| // |
| // ThreadWatcher class sends ping message to the watched thread and the watched |
| // thread responds back with a pong message. It uploads response time |
| // (difference between ping and pong times) as a histogram. |
| // |
| // TODO(raman): ThreadWatcher can detect hung threads. If a hung thread is |
| // detected, we should probably just crash, and allow the crash system to gather |
| // then stack trace. |
| // |
| // Example Usage: |
| // |
| // The following is an example for watching responsiveness of IO thread. |
| // sleep_time specifies how often ping messages have to be sent to IO thread. |
| // unresponsive_time is the wait time after ping message is sent, to check if |
| // we have received pong message or not. |
| // |
| // base::TimeDelta sleep_time = base::TimeDelta::FromSeconds(5); |
| // base::TimeDelta unresponsive_time = base::TimeDelta::FromSeconds(10); |
| // ThreadWatcher::StartWatching(BrowserThread::IO, "IO", sleep_time, |
| // unresponsive_time); |
| |
| #ifndef CHROME_BROWSER_METRICS_THREAD_WATCHER_H_ |
| #define CHROME_BROWSER_METRICS_THREAD_WATCHER_H_ |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/metrics/histogram.h" |
| #include "base/synchronization/lock.h" |
| #include "base/task.h" |
| #include "base/threading/thread.h" |
| #include "base/time.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/common/notification_observer.h" |
| #include "content/common/notification_registrar.h" |
| |
| class CustomThreadWatcher; |
| class ThreadWatcherList; |
| |
| // This class performs health check on threads that would like to be watched. |
| class ThreadWatcher { |
| public: |
| // This method starts performing health check on the given thread_id. It will |
| // create ThreadWatcher object for the given thread_id, thread_name, |
| // sleep_time and unresponsive_time. sleep_time_ is the wait time between ping |
| // messages. unresponsive_time_ is the wait time after ping message is sent, |
| // to check if we have received pong message or not. It will register that |
| // ThreadWatcher object and activate the thread watching of the given |
| // thread_id. |
| static void StartWatching(const BrowserThread::ID& thread_id, |
| const std::string& thread_name, |
| const base::TimeDelta& sleep_time, |
| const base::TimeDelta& unresponsive_time); |
| |
| // Return the thread_id of the thread being watched. |
| BrowserThread::ID thread_id() const { return thread_id_; } |
| |
| // Return the name of the thread being watched. |
| std::string thread_name() const { return thread_name_; } |
| |
| // Return the sleep time between ping messages to be sent to the thread. |
| base::TimeDelta sleep_time() const { return sleep_time_; } |
| |
| // Return the the wait time to check the responsiveness of the thread. |
| base::TimeDelta unresponsive_time() const { return unresponsive_time_; } |
| |
| // Returns true if we are montioring the thread. |
| bool active() const { return active_; } |
| |
| // Returns ping_time_ (used by unit tests). |
| base::TimeTicks ping_time() const { return ping_time_; } |
| |
| // Returns ping_sequence_number_ (used by unit tests). |
| uint64 ping_sequence_number() const { return ping_sequence_number_; } |
| |
| protected: |
| // Construct a ThreadWatcher for the given thread_id. sleep_time_ is the |
| // wait time between ping messages. unresponsive_time_ is the wait time after |
| // ping message is sent, to check if we have received pong message or not. |
| ThreadWatcher(const BrowserThread::ID& thread_id, |
| const std::string& thread_name, |
| const base::TimeDelta& sleep_time, |
| const base::TimeDelta& unresponsive_time); |
| virtual ~ThreadWatcher(); |
| |
| // This method activates the thread watching which starts ping/pong messaging. |
| virtual void ActivateThreadWatching(); |
| |
| // This method de-activates the thread watching and revokes all tasks. |
| virtual void DeActivateThreadWatching(); |
| |
| // This will ensure that the watching is actively taking place, and awaken |
| // (i.e., post a PostPingMessage) if the watcher has stopped pinging due to |
| // lack of user activity. It will also reset ping_count_ to kPingCount. |
| virtual void WakeUp(); |
| |
| // This method records when ping message was sent and it will Post a task |
| // (OnPingMessage) to the watched thread that does nothing but respond with |
| // OnPongMessage. It also posts a task (OnCheckResponsiveness) to check |
| // responsiveness of monitored thread that would be called after waiting |
| // unresponsive_time_. |
| // This method is accessible on WatchDogThread. |
| virtual void PostPingMessage(); |
| |
| // This method handles a Pong Message from watched thread. It will track the |
| // response time (pong time minus ping time) via histograms. It posts a |
| // PostPingMessage task that would be called after waiting sleep_time_. It |
| // increments ping_sequence_number_ by 1. |
| // This method is accessible on WatchDogThread. |
| virtual void OnPongMessage(uint64 ping_sequence_number); |
| |
| // This method will determine if the watched thread is responsive or not. If |
| // the latest ping_sequence_number_ is not same as the ping_sequence_number |
| // that is passed in, then we can assume that watched thread has responded |
| // with a pong message. |
| // This method is accessible on WatchDogThread. |
| virtual bool OnCheckResponsiveness(uint64 ping_sequence_number); |
| |
| private: |
| friend class ThreadWatcherList; |
| |
| // Allow tests to access our innards for testing purposes. |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, Registration); |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadResponding); |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, ThreadNotResponding); |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, MultipleThreadsResponding); |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, MultipleThreadsNotResponding); |
| |
| // Post constructor initialization. |
| void Initialize(); |
| |
| // Watched thread does nothing except post callback_task to the WATCHDOG |
| // Thread. This method is called on watched thread. |
| static void OnPingMessage(const BrowserThread::ID& thread_id, |
| Task* callback_task); |
| |
| // This is the number of ping messages to be sent when the user is idle. |
| // ping_count_ will be initialized to kPingCount whenever user becomes active. |
| static const int kPingCount; |
| |
| // The thread_id of the thread being watched. Only one instance can exist for |
| // the given thread_id of the thread being watched. |
| const BrowserThread::ID thread_id_; |
| |
| // The name of the thread being watched. |
| const std::string thread_name_; |
| |
| // It is the sleep time between between the receipt of a pong message back, |
| // and the sending of another ping message. |
| const base::TimeDelta sleep_time_; |
| |
| // It is the duration from sending a ping message, until we check status to be |
| // sure a pong message has been returned. |
| const base::TimeDelta unresponsive_time_; |
| |
| // This is the last time when ping message was sent. |
| base::TimeTicks ping_time_; |
| |
| // This is the sequence number of the next ping for which there is no pong. If |
| // the instance is sleeping, then it will be the sequence number for the next |
| // ping. |
| uint64 ping_sequence_number_; |
| |
| // This is set to true if thread watcher is watching. |
| bool active_; |
| |
| // The counter tracks least number of ping messages that will be sent to |
| // watched thread before the ping-pong mechanism will go into an extended |
| // sleep. If this value is zero, then the mechanism is in an extended sleep, |
| // and awaiting some observed user action before continuing. |
| int ping_count_; |
| |
| // Histogram that keeps track of response times for the watched thread. |
| base::Histogram* histogram_; |
| |
| // We use this factory to create callback tasks for ThreadWatcher object. We |
| // use this during ping-pong messaging between WatchDog thread and watched |
| // thread. |
| ScopedRunnableMethodFactory<ThreadWatcher> method_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadWatcher); |
| }; |
| |
| // Class with a list of all active thread watchers. A thread watcher is active |
| // if it has been registered, which includes determing the histogram name. This |
| // class provides utility functions to start and stop watching all browser |
| // threads. Only one instance of this class exists. |
| class ThreadWatcherList : public NotificationObserver { |
| public: |
| // A map from BrowserThread to the actual instances. |
| typedef std::map<BrowserThread::ID, ThreadWatcher*> RegistrationList; |
| |
| // This singleton holds the global list of registered ThreadWatchers. |
| ThreadWatcherList(); |
| // Destructor deletes all registered ThreadWatcher instances. |
| virtual ~ThreadWatcherList(); |
| |
| // Register() stores a pointer to the given ThreadWatcher in a global map. |
| static void Register(ThreadWatcher* watcher); |
| |
| // This method returns true if the ThreadWatcher object is registerd. |
| static bool IsRegistered(const BrowserThread::ID thread_id); |
| |
| // This method posts a task on WatchDogThread to start watching all browser |
| // threads. |
| // This method is accessible on UI thread. |
| static void StartWatchingAll(); |
| |
| // This method posts a task on WatchDogThread to RevokeAll tasks and to |
| // deactive thread watching of other threads and tell NotificationService to |
| // stop calling Observe. |
| // This method is accessible on UI thread. |
| static void StopWatchingAll(); |
| |
| // RemoveAll NotificationTypes that are being observed. |
| // This method is accessible on UI thread. |
| static void RemoveNotifications(); |
| |
| private: |
| // Allow tests to access our innards for testing purposes. |
| FRIEND_TEST_ALL_PREFIXES(ThreadWatcherTest, Registration); |
| |
| // Delete all thread watcher objects and remove them from global map. |
| // This method is accessible on WatchDogThread. |
| void DeleteAll(); |
| |
| // This will ensure that the watching is actively taking place. It will wakeup |
| // all thread watchers every 2 seconds. This is the implementation of |
| // NotificationObserver. When a matching notification is posted to the |
| // notification service, this method is called. |
| // This method is accessible on UI thread. |
| virtual void Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details); |
| |
| // This will ensure that the watching is actively taking place, and awaken |
| // all thread watchers that are registered. |
| // This method is accessible on WatchDogThread. |
| virtual void WakeUpAll(); |
| |
| // The Find() method can be used to test to see if a given ThreadWatcher was |
| // already registered, or to retrieve a pointer to it from the global map. |
| static ThreadWatcher* Find(const BrowserThread::ID& thread_id); |
| |
| // Helper function should be called only while holding lock_. |
| ThreadWatcher* PreLockedFind(const BrowserThread::ID& thread_id); |
| |
| static ThreadWatcherList* global_; // The singleton of this class. |
| |
| // Lock for access to registered_. |
| base::Lock lock_; |
| |
| // Map of all registered watched threads, from thread_id to ThreadWatcher. |
| RegistrationList registered_; |
| |
| // The registrar that holds NotificationTypes to be observed. |
| NotificationRegistrar registrar_; |
| |
| // This is the last time when woke all thread watchers up. |
| base::TimeTicks last_wakeup_time_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadWatcherList); |
| }; |
| |
| // Class for WatchDogThread and in its Init method, we start watching UI, IO, |
| // DB, FILE, CACHED threads. |
| class WatchDogThread : public base::Thread { |
| public: |
| // Constructor. |
| WatchDogThread(); |
| |
| // Destroys the thread and stops the thread. |
| virtual ~WatchDogThread(); |
| |
| // Callable on any thread. Returns whether you're currently on a |
| // watchdog_thread_. |
| static bool CurrentlyOnWatchDogThread(); |
| |
| // These are the same methods in message_loop.h, but are guaranteed to either |
| // get posted to the MessageLoop if it's still alive, or be deleted otherwise. |
| // They return true iff the watchdog thread existed and the task was posted. |
| // Note that even if the task is posted, there's no guarantee that it will |
| // run, since the target thread may already have a Quit message in its queue. |
| static bool PostTask(const tracked_objects::Location& from_here, Task* task); |
| static bool PostDelayedTask(const tracked_objects::Location& from_here, |
| Task* task, |
| int64 delay_ms); |
| |
| protected: |
| virtual void Init(); |
| virtual void CleanUp(); |
| virtual void CleanUpAfterMessageLoopDestruction(); |
| |
| private: |
| static bool PostTaskHelper( |
| const tracked_objects::Location& from_here, |
| Task* task, |
| int64 delay_ms); |
| |
| // This lock protects watchdog_thread_. |
| static base::Lock lock_; |
| |
| static WatchDogThread* watchdog_thread_; // The singleton of this class. |
| |
| DISALLOW_COPY_AND_ASSIGN(WatchDogThread); |
| }; |
| |
| // DISABLE_RUNNABLE_METHOD_REFCOUNT is a convenience macro for disabling |
| // refcounting of ThreadWatcher and ThreadWatcherList classes. |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcher); |
| DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcherList); |
| |
| #endif // CHROME_BROWSER_METRICS_THREAD_WATCHER_H_ |