Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_BackgroundHangMonitor_h
8 : #define mozilla_BackgroundHangMonitor_h
9 :
10 : #include "mozilla/HangAnnotations.h"
11 : #include "mozilla/Monitor.h"
12 : #include "mozilla/RefPtr.h"
13 :
14 : #include "nsString.h"
15 :
16 : #include <stdint.h>
17 :
18 : namespace mozilla {
19 :
20 : namespace Telemetry {
21 : class ThreadHangStats;
22 : } // namespace Telemetry
23 :
24 : class BackgroundHangThread;
25 : class BackgroundHangManager;
26 :
27 : /**
28 : * The background hang monitor is responsible for detecting and reporting
29 : * hangs in main and background threads. A thread registers itself using
30 : * the BackgroundHangMonitor object and periodically calls its methods to
31 : * inform the hang monitor of the thread's activity. Each thread is given
32 : * a thread name, a timeout, and a maximum timeout. If one of the thread's
33 : * tasks runs for longer than the timeout duration but shorter than the
34 : * maximum timeout, a (transient) hang is reported. On the other hand, if
35 : * a task runs for longer than the maximum timeout duration or never
36 : * finishes (e.g. in a deadlock), a permahang is reported.
37 : *
38 : * Tasks are defined arbitrarily, but are typically represented by events
39 : * in an event loop -- processing one event is equivalent to running one
40 : * task. To ensure responsiveness, tasks in a thread often have a target
41 : * running time. This is a good starting point for determining the timeout
42 : * and maximum timeout values. For example, the Compositor thread has a
43 : * responsiveness goal of 60Hz or 17ms, so a starting timeout could be
44 : * 100ms. Considering some platforms (e.g. Android) can terminate the app
45 : * when a critical thread hangs for longer than a few seconds, a good
46 : * starting maximum timeout is 4 or 5 seconds.
47 : *
48 : * A thread registers itself through the BackgroundHangMonitor constructor.
49 : * Multiple BackgroundHangMonitor objects can be used in one thread. The
50 : * constructor without arguments can be used when it is known that the thread
51 : * already has a BackgroundHangMonitor registered. When all instances of
52 : * BackgroundHangMonitor are destroyed, the thread is unregistered.
53 : *
54 : * The thread then uses two methods to inform BackgroundHangMonitor of the
55 : * thread's activity:
56 : *
57 : * > BackgroundHangMonitor::NotifyActivity should be called *before*
58 : * starting a task. The task run time is determined by the interval
59 : * between this call and the next NotifyActivity call.
60 : *
61 : * > BackgroundHangMonitor::NotifyWait should be called *before* the
62 : * thread enters a wait state (e.g. to wait for a new event). This
63 : * prevents a waiting thread from being detected as hanging. The wait
64 : * state is automatically cleared at the next NotifyActivity call.
65 : *
66 : * The following example shows hang monitoring in a simple event loop:
67 : *
68 : * void thread_main()
69 : * {
70 : * mozilla::BackgroundHangMonitor hangMonitor("example1", 100, 1000);
71 : * while (!exiting) {
72 : * hangMonitor.NotifyActivity();
73 : * process_next_event();
74 : * hangMonitor.NotifyWait();
75 : * wait_for_next_event();
76 : * }
77 : * }
78 : *
79 : * The following example shows reentrancy in nested event loops:
80 : *
81 : * void thread_main()
82 : * {
83 : * mozilla::BackgroundHangMonitor hangMonitor("example2", 100, 1000);
84 : * while (!exiting) {
85 : * hangMonitor.NotifyActivity();
86 : * process_next_event();
87 : * hangMonitor.NotifyWait();
88 : * wait_for_next_event();
89 : * }
90 : * }
91 : *
92 : * void process_next_event()
93 : * {
94 : * mozilla::BackgroundHangMonitor hangMonitor();
95 : * if (is_sync_event) {
96 : * while (!finished_event) {
97 : * hangMonitor.NotifyActivity();
98 : * process_next_event();
99 : * hangMonitor.NotifyWait();
100 : * wait_for_next_event();
101 : * }
102 : * } else {
103 : * process_nonsync_event();
104 : * }
105 : * }
106 : */
107 : class BackgroundHangMonitor
108 : {
109 : private:
110 : friend BackgroundHangManager;
111 :
112 : RefPtr<BackgroundHangThread> mThread;
113 :
114 : static bool ShouldDisableOnBeta(const nsCString &);
115 : static bool DisableOnBeta();
116 :
117 : public:
118 : static const uint32_t kNoTimeout = 0;
119 : enum ThreadType {
120 : // For a new BackgroundHangMonitor for thread T, only create a new
121 : // monitoring thread for T if one doesn't already exist. If one does,
122 : // share that pre-existing monitoring thread.
123 : THREAD_SHARED,
124 : // For a new BackgroundHangMonitor for thread T, create a new
125 : // monitoring thread for T even if there are other, pre-existing
126 : // monitoring threads for T.
127 : THREAD_PRIVATE
128 : };
129 :
130 : /**
131 : * ThreadHangStatsIterator is used to iterate through the ThreadHangStats
132 : * associated with each active monitored thread. Because of an internal
133 : * lock while this object is alive, a thread must use only one instance
134 : * of this class at a time and must iterate through the list as fast as
135 : * possible. The following example shows using the iterator:
136 : *
137 : * {
138 : * // Scope the iter variable so it's destroyed as soon as we're done
139 : * BackgroundHangMonitor::ThreadHangStatsIterator iter;
140 : * for (ThreadHangStats* histogram = iter.GetNext();
141 : * histogram; histogram = iter.GetNext()) {
142 : * // Process histogram
143 : * }
144 : * }
145 : */
146 0 : class ThreadHangStatsIterator : public MonitorAutoLock
147 : {
148 : private:
149 : BackgroundHangThread* mThread;
150 :
151 : ThreadHangStatsIterator(const ThreadHangStatsIterator&);
152 : ThreadHangStatsIterator& operator=(const ThreadHangStatsIterator&);
153 :
154 : public:
155 : /**
156 : * Create an ThreadHangStatsIterator instance and take the internal lock.
157 : * Internal lock is released on destruction.
158 : */
159 : ThreadHangStatsIterator();
160 :
161 : /**
162 : * Get the next item in the list; the first call returns the first item.
163 : * Returns nullptr at the end of the list.
164 : */
165 : Telemetry::ThreadHangStats* GetNext();
166 : };
167 :
168 : /**
169 : * Enable hang monitoring.
170 : * Must return before using BackgroundHangMonitor.
171 : */
172 : static void Startup();
173 :
174 : /**
175 : * Disable hang monitoring.
176 : * Can be called without destroying all BackgroundHangMonitors first.
177 : */
178 : static void Shutdown();
179 :
180 : /**
181 : * Returns true if BHR is disabled.
182 : */
183 : static bool IsDisabled();
184 :
185 : /**
186 : * Start monitoring hangs for the current thread.
187 : *
188 : * @param aName Name to identify the thread with
189 : * @param aTimeoutMs Amount of time in milliseconds without
190 : * activity before registering a hang
191 : * @param aMaxTimeoutMs Amount of time in milliseconds without
192 : * activity before registering a permanent hang
193 : * @param aThreadType
194 : * The ThreadType type of monitoring thread that should be created
195 : * for this monitor. See the documentation for ThreadType.
196 : */
197 : BackgroundHangMonitor(const char* aName,
198 : uint32_t aTimeoutMs,
199 : uint32_t aMaxTimeoutMs,
200 : ThreadType aThreadType = THREAD_SHARED);
201 :
202 : /**
203 : * Monitor hangs using an existing monitor
204 : * associated with the current thread.
205 : */
206 : BackgroundHangMonitor();
207 :
208 : /**
209 : * Destroys the hang monitor; hang monitoring for a thread stops
210 : * when all monitors associated with the thread are destroyed.
211 : */
212 : ~BackgroundHangMonitor();
213 :
214 : /**
215 : * Notify the hang monitor of pending current thread activity.
216 : * Call this method before starting an "activity" or after
217 : * exiting from a wait state.
218 : */
219 : void NotifyActivity();
220 :
221 : /**
222 : * Notify the hang monitor of current thread wait.
223 : * Call this method before entering a wait state; call
224 : * NotifyActivity when subsequently exiting the wait state.
225 : */
226 : void NotifyWait();
227 :
228 : /**
229 : * Register an annotator with BHR for the current thread.
230 : * @param aAnnotator annotator to register
231 : * @return true if the annotator was registered, otherwise false.
232 : */
233 : static bool RegisterAnnotator(HangMonitor::Annotator& aAnnotator);
234 :
235 : /**
236 : * Unregister an annotator that was previously registered via
237 : * RegisterAnnotator.
238 : * @param aAnnotator annotator to unregister
239 : * @return true if there are still remaining annotators registered
240 : */
241 : static bool UnregisterAnnotator(HangMonitor::Annotator& aAnnotator);
242 : };
243 :
244 : } // namespace mozilla
245 :
246 : #endif // mozilla_BackgroundHangMonitor_h
|