1 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef Telemetry_h__
7 #define Telemetry_h__
8 
9 #include "mozilla/GuardObjects.h"
10 #include "mozilla/TimeStamp.h"
11 #include "mozilla/StartupTimeline.h"
12 #include "nsTArray.h"
13 #include "nsStringGlue.h"
14 #include "nsXULAppAPI.h"
15 
16 #include "mozilla/TelemetryHistogramEnums.h"
17 #include "mozilla/TelemetryScalarEnums.h"
18 
19 /******************************************************************************
20  * This implements the Telemetry system.
21  * It allows recording into histograms as well some more specialized data
22  * points and gives access to the data.
23  *
24  * For documentation on how to add and use new Telemetry probes, see:
25  * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe
26  *
27  * For more general information on Telemetry see:
28  * https://wiki.mozilla.org/Telemetry
29  *****************************************************************************/
30 
31 namespace mozilla {
32 namespace HangMonitor {
33   class HangAnnotations;
34 } // namespace HangMonitor
35 namespace Telemetry {
36 
37 struct Accumulation;
38 struct KeyedAccumulation;
39 
40 enum TimerResolution {
41   Millisecond,
42   Microsecond
43 };
44 
45 /**
46  * Create and destroy the underlying base::StatisticsRecorder singleton.
47  * Creation has to be done very early in the startup sequence.
48  */
49 void CreateStatisticsRecorder();
50 void DestroyStatisticsRecorder();
51 
52 /**
53  * Initialize the Telemetry service on the main thread at startup.
54  */
55 void Init();
56 
57 /**
58  * Adds sample to a histogram defined in TelemetryHistogramEnums.h
59  *
60  * @param id - histogram id
61  * @param sample - value to record.
62  */
63 void Accumulate(ID id, uint32_t sample);
64 
65 /**
66  * Adds sample to a keyed histogram defined in TelemetryHistogramEnums.h
67  *
68  * @param id - keyed histogram id
69  * @param key - the string key
70  * @param sample - (optional) value to record, defaults to 1.
71  */
72 void Accumulate(ID id, const nsCString& key, uint32_t sample = 1);
73 
74 /**
75  * Adds a sample to a histogram defined in TelemetryHistogramEnums.h.
76  * This function is here to support telemetry measurements from Java,
77  * where we have only names and not numeric IDs.  You should almost
78  * certainly be using the by-enum-id version instead of this one.
79  *
80  * @param name - histogram name
81  * @param sample - value to record
82  */
83 void Accumulate(const char* name, uint32_t sample);
84 
85 /**
86  * Adds a sample to a histogram defined in TelemetryHistogramEnums.h.
87  * This function is here to support telemetry measurements from Java,
88  * where we have only names and not numeric IDs.  You should almost
89  * certainly be using the by-enum-id version instead of this one.
90  *
91  * @param name - histogram name
92  * @param key - the string key
93  * @param sample - sample - (optional) value to record, defaults to 1.
94  */
95 void Accumulate(const char *name, const nsCString& key, uint32_t sample = 1);
96 
97 /**
98  * Adds sample to a categorical histogram defined in TelemetryHistogramEnums.h
99  * This is the typesafe - and preferred - way to use the categorical histograms
100  * by passing values from the corresponding Telemetry::LABELS_* enum.
101  *
102  * @param enumValue - Label value from one of the Telemetry::LABELS_* enums.
103  */
104 template<class E>
105 void AccumulateCategorical(E enumValue) {
106   static_assert(IsCategoricalLabelEnum<E>::value,
107                 "Only categorical label enum types are supported.");
108   Accumulate(static_cast<ID>(CategoricalLabelId<E>::value),
109              static_cast<uint32_t>(enumValue));
110 };
111 
112 /**
113  * Adds sample to a categorical histogram defined in TelemetryHistogramEnums.h
114  * This string will be matched against the labels defined in Histograms.json.
115  * If the string does not match a label defined for the histogram, nothing will
116  * be recorded.
117  *
118  * @param id - The histogram id.
119  * @param label - A string label value that is defined in Histograms.json for this histogram.
120  */
121 void AccumulateCategorical(ID id, const nsCString& label);
122 
123 /**
124  * Adds time delta in milliseconds to a histogram defined in TelemetryHistogramEnums.h
125  *
126  * @param id - histogram id
127  * @param start - start time
128  * @param end - end time
129  */
130 void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
131 
132 /**
133  * Accumulate child process data into histograms for the given process type.
134  *
135  * @param aAccumulations - accumulation actions to perform
136  */
137 void AccumulateChild(GeckoProcessType aProcessType, const nsTArray<Accumulation>& aAccumulations);
138 
139 /**
140  * Accumulate child process data into keyed histograms for the given process type.
141  *
142  * @param aAccumulations - accumulation actions to perform
143  */
144 void AccumulateChildKeyed(GeckoProcessType aProcessType, const nsTArray<KeyedAccumulation>& aAccumulations);
145 
146 /**
147  * Enable/disable recording for this histogram at runtime.
148  * Recording is enabled by default, unless listed at kRecordingInitiallyDisabledIDs[].
149  * id must be a valid telemetry enum, otherwise an assertion is triggered.
150  *
151  * @param id - histogram id
152  * @param enabled - whether or not to enable recording from now on.
153  */
154 void SetHistogramRecordingEnabled(ID id, bool enabled);
155 
156 const char* GetHistogramName(ID id);
157 
158 /**
159  * Those wrappers are needed because the VS versions we use do not support free
160  * functions with default template arguments.
161  */
162 template<TimerResolution res>
163 struct AccumulateDelta_impl
164 {
165   static void compute(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
166   static void compute(ID id, const nsCString& key, TimeStamp start, TimeStamp end = TimeStamp::Now());
167 };
168 
169 template<>
170 struct AccumulateDelta_impl<Millisecond>
171 {
172   static void compute(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now()) {
173     Accumulate(id, static_cast<uint32_t>((end - start).ToMilliseconds()));
174   }
175   static void compute(ID id, const nsCString& key, TimeStamp start, TimeStamp end = TimeStamp::Now()) {
176     Accumulate(id, key, static_cast<uint32_t>((end - start).ToMilliseconds()));
177   }
178 };
179 
180 template<>
181 struct AccumulateDelta_impl<Microsecond>
182 {
183   static void compute(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now()) {
184     Accumulate(id, static_cast<uint32_t>((end - start).ToMicroseconds()));
185   }
186   static void compute(ID id, const nsCString& key, TimeStamp start, TimeStamp end = TimeStamp::Now()) {
187     Accumulate(id, key, static_cast<uint32_t>((end - start).ToMicroseconds()));
188   }
189 };
190 
191 
192 template<ID id, TimerResolution res = Millisecond>
193 class MOZ_RAII AutoTimer {
194 public:
195   explicit AutoTimer(TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
196      : start(aStart)
197   {
198     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
199   }
200 
201   explicit AutoTimer(const nsCString& aKey, TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
202     : start(aStart)
203     , key(aKey)
204   {
205     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
206   }
207 
208   ~AutoTimer() {
209     if (key.IsEmpty()) {
210       AccumulateDelta_impl<res>::compute(id, start);
211     } else {
212       AccumulateDelta_impl<res>::compute(id, key, start);
213     }
214   }
215 
216 private:
217   const TimeStamp start;
218   const nsCString key;
219   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
220 };
221 
222 template<ID id>
223 class MOZ_RAII AutoCounter {
224 public:
225   explicit AutoCounter(uint32_t counterStart = 0 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
226     : counter(counterStart)
227   {
228     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
229   }
230 
231   ~AutoCounter() {
232     Accumulate(id, counter);
233   }
234 
235   // Prefix increment only, to encourage good habits.
236   void operator++() {
237     ++counter;
238   }
239 
240   // Chaining doesn't make any sense, don't return anything.
241   void operator+=(int increment) {
242     counter += increment;
243   }
244 
245 private:
246   uint32_t counter;
247   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
248 };
249 
250 /**
251  * Indicates whether Telemetry base data recording is turned on. Added for future uses.
252  */
253 bool CanRecordBase();
254 
255 /**
256  * Indicates whether Telemetry extended data recording is turned on.  This is intended
257  * to guard calls to Accumulate when the statistic being recorded is expensive to compute.
258  */
259 bool CanRecordExtended();
260 
261 /**
262  * Records slow SQL statements for Telemetry reporting.
263  *
264  * @param statement - offending SQL statement to record
265  * @param dbName - DB filename
266  * @param delay - execution time in milliseconds
267  */
268 void RecordSlowSQLStatement(const nsACString &statement,
269                             const nsACString &dbName,
270                             uint32_t delay);
271 
272 /**
273  * Record Webrtc ICE candidate type combinations in a 17bit bitmask
274  *
275  * @param iceCandidateBitmask - the bitmask representing local and remote ICE
276  *                              candidate types present for the connection
277  * @param success - did the peer connection connected
278  */
279 void
280 RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask,
281                           const bool success);
282 /**
283  * Initialize I/O Reporting
284  * Initially this only records I/O for files in the binary directory.
285  *
286  * @param aXreDir - XRE directory
287  */
288 void InitIOReporting(nsIFile* aXreDir);
289 
290 /**
291  * Set the profile directory. Once called, files in the profile directory will
292  * be included in I/O reporting. We can't use the directory
293  * service to obtain this information because it isn't running yet.
294  */
295 void SetProfileDir(nsIFile* aProfD);
296 
297 /**
298  * Called to inform Telemetry that startup has completed.
299  */
300 void LeavingStartupStage();
301 
302 /**
303  * Called to inform Telemetry that shutdown is commencing.
304  */
305 void EnteringShutdownStage();
306 
307 /**
308  * Thresholds for a statement to be considered slow, in milliseconds
309  */
310 const uint32_t kSlowSQLThresholdForMainThread = 50;
311 const uint32_t kSlowSQLThresholdForHelperThreads = 100;
312 
313 class ProcessedStack;
314 
315 /**
316  * Record the main thread's call stack after it hangs.
317  *
318  * @param aDuration - Approximate duration of main thread hang, in seconds
319  * @param aStack - Array of PCs from the hung call stack
320  * @param aSystemUptime - System uptime at the time of the hang, in minutes
321  * @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes
322  * @param aAnnotations - Any annotations to be added to the report
323  */
324 #if defined(MOZ_ENABLE_PROFILER_SPS)
325 void RecordChromeHang(uint32_t aDuration,
326                       ProcessedStack &aStack,
327                       int32_t aSystemUptime,
328                       int32_t aFirefoxUptime,
329                       mozilla::UniquePtr<mozilla::HangMonitor::HangAnnotations>
330                               aAnnotations);
331 #endif
332 
333 class ThreadHangStats;
334 
335 /**
336  * Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries
337  * for active ThreadHangStats through BackgroundHangMonitor, but once a
338  * thread exits, the thread's copy of ThreadHangStats needs to be moved to
339  * inside Telemetry using this function.
340  *
341  * @param aStats ThreadHangStats to save; the data inside aStats
342  *               will be moved and aStats should be treated as
343  *               invalid after this function returns
344  */
345 void RecordThreadHangStats(ThreadHangStats& aStats);
346 
347 /**
348  * Record a failed attempt at locking the user's profile.
349  *
350  * @param aProfileDir The profile directory whose lock attempt failed
351  */
352 void WriteFailedProfileLock(nsIFile* aProfileDir);
353 
354 /**
355  * Adds the value to the given scalar.
356  *
357  * @param aId The scalar enum id.
358  * @param aValue The value to add to the scalar.
359  */
360 void ScalarAdd(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
361 
362 /**
363  * Sets the scalar to the given value.
364  *
365  * @param aId The scalar enum id.
366  * @param aValue The value to set the scalar to.
367  */
368 void ScalarSet(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
369 
370 /**
371  * Sets the scalar to the given value.
372  *
373  * @param aId The scalar enum id.
374  * @param aValue The value to set the scalar to.
375  */
376 void ScalarSet(mozilla::Telemetry::ScalarID aId, bool aValue);
377 
378 /**
379  * Sets the scalar to the given value.
380  *
381  * @param aId The scalar enum id.
382  * @param aValue The value to set the scalar to, truncated to
383  *        50 characters if exceeding that length.
384  */
385 void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aValue);
386 
387 /**
388  * Sets the scalar to the maximum of the current and the passed value.
389  *
390  * @param aId The scalar enum id.
391  * @param aValue The value the scalar is set to if its greater
392  *        than the current value.
393  */
394 void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
395 
396 /**
397  * Adds the value to the given scalar.
398  *
399  * @param aId The scalar enum id.
400  * @param aKey The scalar key.
401  * @param aValue The value to add to the scalar.
402  */
403 void ScalarAdd(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
404 
405 /**
406  * Sets the scalar to the given value.
407  *
408  * @param aId The scalar enum id.
409  * @param aKey The scalar key.
410  * @param aValue The value to set the scalar to.
411  */
412 void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
413 
414 /**
415  * Sets the scalar to the given value.
416  *
417  * @param aId The scalar enum id.
418  * @param aKey The scalar key.
419  * @param aValue The value to set the scalar to.
420  */
421 void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, bool aValue);
422 
423 /**
424  * Sets the scalar to the maximum of the current and the passed value.
425  *
426  * @param aId The scalar enum id.
427  * @param aKey The scalar key.
428  * @param aValue The value the scalar is set to if its greater
429  *        than the current value.
430  */
431 void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
432 
433 } // namespace Telemetry
434 } // namespace mozilla
435 
436 #endif // Telemetry_h__
437