Files
tubestation/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.h
Doug Thayer 67c03a21e3 Bug 1382440 - Watch CPU usage in BHR r=froydnj
We would like to be able to see if a given hang in BHR occurred
under high CPU load, as this is an indication that the hang is
of less use to us, since it's likely that the external CPU use
is more responsible for it.

The way this works is fairly simple. We get the system CPU usage
on a scale from 0 to 1, and we get the current process's CPU
usage, also on a scale from 0 to 1, and we subtract the latter
from the former. We then compare this value to a threshold, which
is 1 - (1 / p), where p is the number of (virtual) cores on the
machine. This threshold might need to be tuned, so that we
require an entire physical core in order to not annotate the hang,
but for now it seemed the most reasonable line in the sand.

I should note that this considers CPU usage in child or parent
processes as external. While we are responsible for that CPU usage,
it still indicates that the stack we receive from BHR is of little
value to us, since the source of the actual hang is external to
that stack.

MozReview-Commit-ID: JkG53zq1MdY
2017-07-24 13:46:09 -07:00

206 lines
6.8 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_BackgroundHangMonitor_h
#define mozilla_BackgroundHangMonitor_h
#include "mozilla/CPUUsageWatcher.h"
#include "mozilla/HangAnnotations.h"
#include "mozilla/Monitor.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"
#include <stdint.h>
namespace mozilla {
class BackgroundHangThread;
class BackgroundHangManager;
/**
* The background hang monitor is responsible for detecting and reporting
* hangs in main and background threads. A thread registers itself using
* the BackgroundHangMonitor object and periodically calls its methods to
* inform the hang monitor of the thread's activity. Each thread is given
* a thread name, a timeout, and a maximum timeout. If one of the thread's
* tasks runs for longer than the timeout duration but shorter than the
* maximum timeout, a (transient) hang is reported. On the other hand, if
* a task runs for longer than the maximum timeout duration or never
* finishes (e.g. in a deadlock), a permahang is reported.
*
* Tasks are defined arbitrarily, but are typically represented by events
* in an event loop -- processing one event is equivalent to running one
* task. To ensure responsiveness, tasks in a thread often have a target
* running time. This is a good starting point for determining the timeout
* and maximum timeout values. For example, the Compositor thread has a
* responsiveness goal of 60Hz or 17ms, so a starting timeout could be
* 100ms. Considering some platforms (e.g. Android) can terminate the app
* when a critical thread hangs for longer than a few seconds, a good
* starting maximum timeout is 4 or 5 seconds.
*
* A thread registers itself through the BackgroundHangMonitor constructor.
* Multiple BackgroundHangMonitor objects can be used in one thread. The
* constructor without arguments can be used when it is known that the thread
* already has a BackgroundHangMonitor registered. When all instances of
* BackgroundHangMonitor are destroyed, the thread is unregistered.
*
* The thread then uses two methods to inform BackgroundHangMonitor of the
* thread's activity:
*
* > BackgroundHangMonitor::NotifyActivity should be called *before*
* starting a task. The task run time is determined by the interval
* between this call and the next NotifyActivity call.
*
* > BackgroundHangMonitor::NotifyWait should be called *before* the
* thread enters a wait state (e.g. to wait for a new event). This
* prevents a waiting thread from being detected as hanging. The wait
* state is automatically cleared at the next NotifyActivity call.
*
* The following example shows hang monitoring in a simple event loop:
*
* void thread_main()
* {
* mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000);
* while (!exiting) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* }
*
* The following example shows reentrancy in nested event loops:
*
* void thread_main()
* {
* mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000);
* while (!exiting) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* }
*
* void process_next_event()
* {
* mozilla::BackgroundHangMonitor hangMonitor();
* if (is_sync_event) {
* while (!finished_event) {
* hangMonitor.NotifyActivity();
* process_next_event();
* hangMonitor.NotifyWait();
* wait_for_next_event();
* }
* } else {
* process_nonsync_event();
* }
* }
*/
class BackgroundHangMonitor
{
private:
friend BackgroundHangManager;
RefPtr<BackgroundHangThread> mThread;
static bool ShouldDisableOnBeta(const nsCString &);
static bool DisableOnBeta();
public:
static const uint32_t kNoTimeout = 0;
enum ThreadType {
// For a new BackgroundHangMonitor for thread T, only create a new
// monitoring thread for T if one doesn't already exist. If one does,
// share that pre-existing monitoring thread.
THREAD_SHARED,
// For a new BackgroundHangMonitor for thread T, create a new
// monitoring thread for T even if there are other, pre-existing
// monitoring threads for T.
THREAD_PRIVATE
};
/**
* Enable hang monitoring.
* Must return before using BackgroundHangMonitor.
*/
static void Startup();
/**
* Disable hang monitoring.
* Can be called without destroying all BackgroundHangMonitors first.
*/
static void Shutdown();
/**
* Returns true if BHR is disabled.
*/
static bool IsDisabled();
/**
* Start monitoring hangs for the current thread.
*
* @param aName Name to identify the thread with
* @param aTimeoutMs Amount of time in milliseconds without
* activity before registering a hang
* @param aMaxTimeoutMs Amount of time in milliseconds without
* activity before registering a permanent hang
* @param aThreadType
* The ThreadType type of monitoring thread that should be created
* for this monitor. See the documentation for ThreadType.
*/
BackgroundHangMonitor(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs,
ThreadType aThreadType = THREAD_SHARED);
/**
* Monitor hangs using an existing monitor
* associated with the current thread.
*/
BackgroundHangMonitor();
/**
* Destroys the hang monitor; hang monitoring for a thread stops
* when all monitors associated with the thread are destroyed.
*/
~BackgroundHangMonitor();
/**
* Notify the hang monitor of pending current thread activity.
* Call this method before starting an "activity" or after
* exiting from a wait state.
*/
void NotifyActivity();
/**
* Notify the hang monitor of current thread wait.
* Call this method before entering a wait state; call
* NotifyActivity when subsequently exiting the wait state.
*/
void NotifyWait();
/**
* Register an annotator with BHR for the current thread.
* @param aAnnotator annotator to register
* @return true if the annotator was registered, otherwise false.
*/
static bool RegisterAnnotator(HangMonitor::Annotator& aAnnotator);
/**
* Unregister an annotator that was previously registered via
* RegisterAnnotator.
* @param aAnnotator annotator to unregister
* @return true if there are still remaining annotators registered
*/
static bool UnregisterAnnotator(HangMonitor::Annotator& aAnnotator);
};
} // namespace mozilla
#endif // mozilla_BackgroundHangMonitor_h