DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (d38398e5144e)

VCS Links

BackgroundHangMonitor

ThreadHangStatsIterator

ThreadType

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
/* -*- 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/HangAnnotations.h"
#include "mozilla/Monitor.h"
#include "mozilla/RefPtr.h"

#include "nsString.h"

#include <stdint.h>

namespace mozilla {

namespace Telemetry {
class ThreadHangStats;
} // namespace Telemetry

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
  };

  /**
   * ThreadHangStatsIterator is used to iterate through the ThreadHangStats
   * associated with each active monitored thread. Because of an internal
   * lock while this object is alive, a thread must use only one instance
   * of this class at a time and must iterate through the list as fast as
   * possible. The following example shows using the iterator:
   *
   * {
   *   // Scope the iter variable so it's destroyed as soon as we're done
   *   BackgroundHangMonitor::ThreadHangStatsIterator iter;
   *   for (ThreadHangStats* histogram = iter.GetNext();
   *        histogram; histogram = iter.GetNext()) {
   *     // Process histogram
   *   }
   * }
   */
  class ThreadHangStatsIterator : public MonitorAutoLock
  {
  private:
    BackgroundHangThread* mThread;

    ThreadHangStatsIterator(const ThreadHangStatsIterator&);
    ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);

  public:
    /**
     * Create an ThreadHangStatsIterator instance and take the internal lock.
     * Internal lock is released on destruction.
     */
    ThreadHangStatsIterator();

    /**
     * Get the next item in the list; the first call returns the first item.
     * Returns nullptr at the end of the list.
     */
    Telemetry::ThreadHangStats* GetNext();
  };

  /**
   * 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