Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/perfmonitoring/nsPerformanceStats.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; 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 nsPerformanceStats_h
7
#define nsPerformanceStats_h
8
9
#include "jsapi.h"
10
11
#include "nsHashKeys.h"
12
#include "nsTHashtable.h"
13
14
#include "nsIObserver.h"
15
#include "nsPIDOMWindow.h"
16
17
#include "nsIPerformanceStats.h"
18
19
class nsPerformanceGroup;
20
class nsPerformanceGroupDetails;
21
22
typedef mozilla::Vector<RefPtr<nsPerformanceGroup>, 8> GroupVector;
23
24
/**
25
 * A data structure for registering observers interested in
26
 * performance alerts.
27
 *
28
 * Each performance group owns a single instance of this class.
29
 * Additionally, the service owns instances designed to observe the
30
 * performance alerts in all webpages.
31
 */
32
class nsPerformanceObservationTarget final: public nsIPerformanceObservable {
33
public:
34
  NS_DECL_ISUPPORTS
35
  NS_DECL_NSIPERFORMANCEOBSERVABLE
36
37
  /**
38
   * `true` if this target has at least once performance observer
39
   * registered, `false` otherwise.
40
   */
41
  bool HasObservers() const;
42
43
  /**
44
   * Notify all the observers that jank has happened.
45
   */
46
  void NotifyJankObservers(nsIPerformanceGroupDetails* source, nsIPerformanceAlert* gravity);
47
48
  /**
49
   * Set the details on the group being observed.
50
   */
51
  void SetTarget(nsPerformanceGroupDetails* details);
52
53
private:
54
0
  ~nsPerformanceObservationTarget() {}
55
56
  // The observers for this target. We hold them as a vector, despite
57
  // the linear removal cost, as we expect that the typical number of
58
  // observers will be lower than 3, and that (un)registrations will
59
  // be fairly infrequent.
60
  mozilla::Vector<nsCOMPtr<nsIPerformanceObserver>> mObservers;
61
62
  // Details on the group being observed. May be `nullptr`.
63
  RefPtr<nsPerformanceGroupDetails> mDetails;
64
};
65
66
/**
67
 * The base class for entries of maps from window id to
68
 * performance group.
69
 *
70
 * Performance observers may be registered before their group is
71
 * created (e.g., one may register an observer for a webpage before all
72
 * its iframes are loaded). This class serves to hold the observation
73
 * target until the performance group may be created, and then to
74
 * associate the observation target and the performance group.
75
 */
76
class nsGroupHolder {
77
public:
78
  nsGroupHolder()
79
    : mGroup(nullptr)
80
    , mPendingObservationTarget(nullptr)
81
0
  { }
82
83
  /**
84
   * Get the observation target, creating it if necessary.
85
   */
86
  nsPerformanceObservationTarget* ObservationTarget();
87
88
  /**
89
   * Get the group, if it has been created.
90
   *
91
   * May return `null` if the group hasn't been created yet.
92
   */
93
  class nsPerformanceGroup* GetGroup();
94
95
  /**
96
   * Set the group.
97
   *
98
   * Once this method has been called, calling
99
   * `this->ObservationTarget()` and `group->ObservationTarget()` is equivalent.
100
   *
101
   * Must only be called once.
102
   */
103
  void SetGroup(class nsPerformanceGroup*);
104
private:
105
  // The group. Initially `nullptr`, until we have called `SetGroup`.
106
  class nsPerformanceGroup* mGroup;
107
108
  // The observation target. Instantiated by the first call to
109
  // `ObservationTarget()`.
110
  RefPtr<nsPerformanceObservationTarget> mPendingObservationTarget;
111
};
112
113
/**
114
 * An implementation of the nsIPerformanceStatsService.
115
 *
116
 * Note that this implementation is not thread-safe.
117
 */
118
class nsPerformanceStatsService final : public nsIPerformanceStatsService,
119
                                        public nsIObserver
120
{
121
public:
122
  NS_DECL_ISUPPORTS
123
  NS_DECL_NSIPERFORMANCESTATSSERVICE
124
  NS_DECL_NSIOBSERVER
125
126
  nsPerformanceStatsService();
127
  nsresult Init();
128
129
private:
130
  nsresult InitInternal();
131
  void Dispose();
132
  ~nsPerformanceStatsService();
133
134
protected:
135
  friend nsPerformanceGroup;
136
137
  /**
138
   * `false` until `Init()` and after `Dispose()`, `true` inbetween.
139
   */
140
  bool mIsAvailable;
141
142
  /**
143
   * `true` once we have called `Dispose()`.
144
   */
145
  bool mDisposed;
146
147
  /**
148
   * A unique identifier for the process.
149
   *
150
   * Process HANDLE under Windows, pid under Unix.
151
   */
152
  const uint64_t mProcessId;
153
154
  /**
155
   * Generate unique identifiers.
156
   */
157
  uint64_t GetNextId();
158
  uint64_t mUIdCounter;
159
160
161
162
  /**
163
   * Extract a snapshot of performance statistics from a performance group.
164
   */
165
  static nsIPerformanceStats* GetStatsForGroup(const js::PerformanceGroup* group);
166
  static nsIPerformanceStats* GetStatsForGroup(const nsPerformanceGroup* group);
167
168
169
170
  /**
171
   * Get the performance groups associated to a given JS compartment.
172
   *
173
   * A compartment is typically associated to the following groups:
174
   * - the top group, shared by the entire process;
175
   * - the window group, if the code is executed in a window, shared
176
   *     by all compartments for that window (typically, all frames);
177
   * - the compartment's own group.
178
   *
179
   * Pre-condition: the VM must have entered the JS compartment.
180
   *
181
   * The caller is expected to cache the results of this method, as
182
   * calling it more than once may not return the same instances of
183
   * performance groups.
184
   */
185
  bool GetPerformanceGroups(JSContext* cx, js::PerformanceGroupVector&);
186
  static bool GetPerformanceGroupsCallback(JSContext* cx, js::PerformanceGroupVector&, void* closure);
187
188
189
190
  /**********************************************************
191
   *
192
   * Sets of all performance groups, indexed by several keys.
193
   *
194
   * These sets do not keep the performance groups alive. Rather, a
195
   * performance group is inserted in the relevant sets upon
196
   * construction and removed from the sets upon destruction or when
197
   * we Dispose() of the service.
198
   *
199
   * A `nsPerformanceGroup` is typically kept alive (as a
200
   * `js::PerformanceGroup`) by the JS::Compartment to which it is
201
   * associated. It may also temporarily be kept alive by the JS
202
   * stack, in particular in case of nested event loops.
203
   */
204
205
  /**
206
   * Set of performance groups associated to windows, indexed by outer
207
   * window id. Each item is shared by all the compartments that
208
   * belong to the window.
209
   */
210
  struct WindowIdToGroup: public nsUint64HashKey,
211
                          public nsGroupHolder {
212
    explicit WindowIdToGroup(const uint64_t* key)
213
      : nsUint64HashKey(key)
214
0
    {}
215
  };
216
  nsTHashtable<WindowIdToGroup> mWindowIdToGroup;
217
218
  /**
219
   * Set of all performance groups.
220
   */
221
  struct Groups: public nsPtrHashKey<nsPerformanceGroup> {
222
    explicit Groups(const nsPerformanceGroup* key)
223
      : nsPtrHashKey<nsPerformanceGroup>(key)
224
0
    {}
225
  };
226
  nsTHashtable<Groups> mGroups;
227
228
  /**
229
   * The performance group representing the runtime itself.  All
230
   * compartments are associated to this group.
231
   */
232
  RefPtr<nsPerformanceGroup> mTopGroup;
233
234
  /**********************************************************
235
   *
236
   * Measuring and recording the CPU use of the system.
237
   *
238
   */
239
240
  /**
241
   * Get the OS-reported time spent in userland/systemland, in
242
   * microseconds. On most platforms, this data is per-thread,
243
   * but on some platforms we need to fall back to per-process.
244
   *
245
   * Data is not guaranteed to be monotonic.
246
   */
247
  nsresult GetResources(uint64_t* userTime, uint64_t* systemTime) const;
248
249
  /**
250
   * Amount of user/system CPU time used by the thread (or process,
251
   * for platforms that don't support per-thread measure) since start.
252
   * Updated by `StopwatchStart` at most once per event.
253
   *
254
   * Unit: microseconds.
255
   */
256
  uint64_t mUserTimeStart;
257
  uint64_t mSystemTimeStart;
258
259
  bool mIsHandlingUserInput;
260
261
  /**
262
   * The number of user inputs since the start of the process. Used to
263
   * determine whether the current iteration has triggered a
264
   * (JS-implemented) user input.
265
   */
266
  uint64_t mUserInputCount;
267
268
  /**********************************************************
269
   *
270
   * Callbacks triggered by the JS VM when execution of JavaScript
271
   * code starts/completes.
272
   *
273
   * As measures of user CPU time/system CPU time have low resolution
274
   * (and are somewhat slow), we measure both only during the calls to
275
   * `StopwatchStart`/`StopwatchCommit` and we make the assumption
276
   * that each group's user/system CPU time is proportional to the
277
   * number of clock cycles spent executing code in the group between
278
   * `StopwatchStart`/`StopwatchCommit`.
279
   *
280
   * The results may be skewed by the thread being rescheduled to a
281
   * different CPU during the measure, but we expect that on average,
282
   * the skew will have limited effects, and will generally tend to
283
   * make already-slow executions appear slower.
284
   */
285
286
  /**
287
   * Execution of JavaScript code has started. This may happen several
288
   * times in succession if the JavaScript code contains nested event
289
   * loops, in which case only the innermost call will receive
290
   * `StopwatchCommitCallback`.
291
   *
292
   * @param iteration The number of times we have started executing
293
   * JavaScript code.
294
   */
295
  static bool StopwatchStartCallback(uint64_t iteration, void* closure);
296
  bool StopwatchStart(uint64_t iteration);
297
298
  /**
299
   * Execution of JavaScript code has reached completion (including
300
   * enqueued microtasks). In cse of tested event loops, any ongoing
301
   * measurement on outer loops is silently cancelled without any call
302
   * to this method.
303
   *
304
   * @param iteration The number of times we have started executing
305
   * JavaScript code.
306
   * @param recentGroups The groups that have seen activity during this
307
   * event.
308
   */
309
  static bool StopwatchCommitCallback(uint64_t iteration,
310
                                      js::PerformanceGroupVector& recentGroups,
311
                                      void* closure);
312
  bool StopwatchCommit(uint64_t iteration, js::PerformanceGroupVector& recentGroups);
313
314
  /**
315
   * The number of times we have started executing JavaScript code.
316
   */
317
  uint64_t mIteration;
318
319
  /**
320
   * Commit performance measures of a single group.
321
   *
322
   * Data is transfered from `group->recent*` to `group->data`.
323
   *
324
   *
325
   * @param iteration The current iteration.
326
   * @param userTime The total user CPU time for this thread (or
327
   *   process, if per-thread data is not available) between the
328
   *   calls to `StopwatchStart` and `StopwatchCommit`.
329
   * @param systemTime The total system CPU time for this thread (or
330
   *   process, if per-thread data is not available) between the
331
   *   calls to `StopwatchStart` and `StopwatchCommit`.
332
   * @param cycles The total number of cycles for this thread
333
   *   between the calls to `StopwatchStart` and `StopwatchCommit`.
334
   * @param isJankVisible If `true`, expect that the user will notice
335
   *   any slowdown.
336
   * @param group The group containing the data to commit.
337
   */
338
  void CommitGroup(uint64_t iteration,
339
                   uint64_t userTime, uint64_t systemTime,  uint64_t cycles,
340
                   bool isJankVisible,
341
                   nsPerformanceGroup* group);
342
343
344
345
346
  /**********************************************************
347
   *
348
   * To check whether our algorithm makes sense, we keep count of the
349
   * number of times the process has been rescheduled to another CPU
350
   * while we were monitoring the performance of a group and we upload
351
   * this data through Telemetry.
352
   */
353
  nsresult UpdateTelemetry();
354
355
  uint64_t mProcessStayed;
356
  uint64_t mProcessMoved;
357
  uint32_t mProcessUpdateCounter;
358
359
  /**********************************************************
360
   *
361
   * Options controlling measurements.
362
   */
363
364
  /**
365
   * Determine if we are measuring the performance of every individual
366
   * compartment (in particular, every individual module, frame,
367
   * sandbox). Note that this makes measurements noticeably slower.
368
   */
369
  bool mIsMonitoringPerCompartment;
370
371
372
  /**********************************************************
373
   *
374
   * Determining whether jank is user-visible.
375
   */
376
377
  /**
378
   * `true` if we believe that any slowdown can cause a noticeable
379
   * delay in handling user-input.
380
   *
381
   * In the current implementation, we return `true` if the latest
382
   * user input was less than MAX_DURATION_OF_INTERACTION_MS ago. This
383
   * includes all inputs (mouse, keyboard, other devices), with the
384
   * exception of mousemove.
385
   */
386
  bool IsHandlingUserInput();
387
388
389
public:
390
  /**********************************************************
391
   *
392
   * Letting observers register themselves to watch for performance
393
   * alerts.
394
   *
395
   * To avoid saturating clients with alerts (or even creating loops
396
   * of alerts), each alert is buffered. At the end of each iteration
397
   * of the event loop, groups that have caused performance alerts
398
   * are registered in a set of pending alerts, and the collection
399
   * timer hasn't been started yet, it is started. Once the timer
400
   * firers, we gather all the pending alerts, empty the set and
401
   * dispatch to observers.
402
   */
403
404
  /**
405
   * Clear the set of pending alerts and dispatch the pending alerts
406
   * to observers.
407
   */
408
  void NotifyJankObservers(const mozilla::Vector<uint64_t>& previousJankLevels);
409
410
private:
411
  /**
412
   * The set of groups for which we know that an alert should be
413
   * raised. This set is cleared once `mPendingAlertsCollector`
414
   * fires.
415
   *
416
   * Invariant: no group may appear twice in this vector.
417
   */
418
  GroupVector mPendingAlerts;
419
420
  /**
421
   * A timer callback in charge of collecting the groups in
422
   * `mPendingAlerts` and triggering `NotifyJankObservers` to dispatch
423
   * performance alerts.
424
   */
425
  RefPtr<class PendingAlertsCollector> mPendingAlertsCollector;
426
427
428
  /**
429
   * Observation targets that are not attached to a specific group.
430
   */
431
  struct UniversalTargets {
432
    UniversalTargets();
433
    /**
434
     * A target for observers interested in watching all windows.
435
     */
436
    RefPtr<nsPerformanceObservationTarget> mWindows;
437
  };
438
  UniversalTargets mUniversalTargets;
439
440
  /**
441
   * The threshold, in microseconds, above which a performance group is
442
   * considered "slow" and should raise performance alerts.
443
   */
444
  uint64_t mJankAlertThreshold;
445
446
  /**
447
   * A buffering delay, in milliseconds, used by the service to
448
   * regroup performance alerts, before observers are actually
449
   * noticed. Higher delays let the system avoid redundant
450
   * notifications for the same group, and are generally better for
451
   * performance.
452
   */
453
  uint32_t mJankAlertBufferingDelay;
454
455
  /**
456
   * The threshold above which jank, as reported by the refresh drivers,
457
   * is considered user-visible.
458
   *
459
   * A value of n means that any jank above 2^n ms will be considered
460
   * user visible.
461
   */
462
  short mJankLevelVisibilityThreshold;
463
464
  /**
465
   * The number of microseconds during which we assume that a
466
   * user-interaction can keep the code jank-critical. Any user
467
   * interaction that lasts longer than this duration is expected to
468
   * either have already caused jank or have caused a nested event
469
   * loop.
470
   *
471
   * In either case, we consider that monitoring
472
   * jank-during-interaction after this duration is useless.
473
   */
474
  uint64_t mMaxExpectedDurationOfInteractionUS;
475
};
476
477
478
479
/**
480
 * Container for performance data.
481
 *
482
 * All values are monotonic.
483
 *
484
 * All values are updated after running to completion.
485
 */
486
struct PerformanceData {
487
  /**
488
   * Number of times we have spent at least 2^n consecutive
489
   * milliseconds executing code in this group.
490
   * durations[0] is increased whenever we spend at least 1 ms
491
   * executing code in this group
492
   * durations[1] whenever we spend 2ms+
493
   * ...
494
   * durations[i] whenever we spend 2^ims+
495
   */
496
  uint64_t mDurations[10];
497
498
  /**
499
   * Total amount of time spent executing code in this group, in
500
   * microseconds.
501
   */
502
  uint64_t mTotalUserTime;
503
  uint64_t mTotalSystemTime;
504
  uint64_t mTotalCPOWTime;
505
506
  /**
507
   * Total number of times code execution entered this group, since
508
   * process launch. This may be greater than the number of times we
509
   * have entered the event loop.
510
   */
511
  uint64_t mTicks;
512
513
  PerformanceData();
514
  PerformanceData(const PerformanceData& from) = default;
515
  PerformanceData& operator=(const PerformanceData& from) = default;
516
};
517
518
519
520
/**
521
 * Identification information for an item that can hold performance
522
 * data.
523
 */
524
class nsPerformanceGroupDetails final: public nsIPerformanceGroupDetails {
525
public:
526
  NS_DECL_ISUPPORTS
527
  NS_DECL_NSIPERFORMANCEGROUPDETAILS
528
529
  nsPerformanceGroupDetails(const nsAString& aName,
530
                            const nsAString& aGroupId,
531
                            const uint64_t aWindowId,
532
                            const uint64_t aProcessId,
533
                            const bool aIsSystem)
534
    : mName(aName)
535
    , mGroupId(aGroupId)
536
    , mWindowId(aWindowId)
537
    , mProcessId(aProcessId)
538
    , mIsSystem(aIsSystem)
539
0
  { }
540
public:
541
  const nsAString& Name() const;
542
  const nsAString& GroupId() const;
543
  uint64_t WindowId() const;
544
  uint64_t ProcessId() const;
545
  bool IsWindow() const;
546
  bool IsSystem() const;
547
  bool IsContentProcess() const;
548
private:
549
0
  ~nsPerformanceGroupDetails() {}
550
551
  const nsString mName;
552
  const nsString mGroupId;
553
  const uint64_t mWindowId;
554
  const uint64_t mProcessId;
555
  const bool mIsSystem;
556
};
557
558
/**
559
 * The kind of compartments represented by this group.
560
 */
561
enum class PerformanceGroupScope {
562
  /**
563
   * This group represents the entire runtime (i.e. the thread).
564
   */
565
  RUNTIME,
566
567
  /**
568
   * This group represents all the compartments executed in a window.
569
   */
570
  WINDOW,
571
572
  /**
573
   * This group represents a single compartment.
574
   */
575
  COMPARTMENT,
576
};
577
578
/**
579
 * A concrete implementation of `js::PerformanceGroup`, also holding
580
 * performance data. Instances may represent individual compartments,
581
 * windows or the entire runtime.
582
 *
583
 * This class is intended to be the sole implementation of
584
 * `js::PerformanceGroup`.
585
 */
586
class nsPerformanceGroup final: public js::PerformanceGroup {
587
public:
588
589
  // Ideally, we would define the enum class in nsPerformanceGroup,
590
  // but this seems to choke some versions of gcc.
591
  typedef PerformanceGroupScope GroupScope;
592
593
  /**
594
   * Construct a performance group.
595
   *
596
   * @param cx The container context. Used to generate a unique identifier.
597
   * @param service The performance service. Used during destruction to
598
   *   cleanup the hash tables.
599
   * @param name A name for the group, designed mostly for debugging purposes,
600
   *   so it should be at least somewhat human-readable.
601
   * @param windowId The identifier of the window. Should be 0 when the
602
   *   group is not part of a window.
603
   * @param processId A unique identifier for the process.
604
   * @param isSystem `true` if the code of the group is executed with
605
   *   system credentials, `false` otherwise.
606
   * @param scope the scope of this group.
607
   */
608
  static nsPerformanceGroup*
609
    Make(nsPerformanceStatsService* service,
610
         const nsAString& name,
611
         uint64_t windowId,
612
         uint64_t processId,
613
         bool isSystem,
614
         GroupScope scope);
615
616
  /**
617
   * Utility: type-safer conversion from js::PerformanceGroup to nsPerformanceGroup.
618
   */
619
0
  static inline nsPerformanceGroup* Get(js::PerformanceGroup* self) {
620
0
    return static_cast<nsPerformanceGroup*>(self);
621
0
  }
622
0
  static inline const nsPerformanceGroup* Get(const js::PerformanceGroup* self) {
623
0
    return static_cast<const nsPerformanceGroup*>(self);
624
0
  }
625
626
  /**
627
   * The performance data committed to this group.
628
   */
629
  PerformanceData data;
630
631
  /**
632
   * The scope of this group. Used to determine whether the group
633
   * should be (de)activated.
634
   */
635
  GroupScope Scope() const;
636
637
  /**
638
   * Identification details for this group.
639
   */
640
  nsPerformanceGroupDetails* Details() const;
641
642
  /**
643
   * Cleanup any references.
644
   */
645
  void Dispose();
646
647
  /**
648
   * Set the observation target for this group.
649
   *
650
   * This method must be called exactly once, when the performance
651
   * group is attached to its `nsGroupHolder`.
652
   */
653
  void SetObservationTarget(nsPerformanceObservationTarget*);
654
655
656
  /**
657
   * `true` if we have already noticed that a performance alert should
658
   * be raised for this group but we have not dispatched it yet,
659
   * `false` otherwise.
660
   */
661
  bool HasPendingAlert() const;
662
  void SetHasPendingAlert(bool value);
663
664
protected:
665
  nsPerformanceGroup(nsPerformanceStatsService* service,
666
                     const nsAString& name,
667
                     const nsAString& groupId,
668
                     uint64_t windowId,
669
                     uint64_t processId,
670
                     bool isSystem,
671
                     GroupScope scope);
672
673
674
  /**
675
   * Virtual implementation of `delete`, to make sure that objects are
676
   * destoyed with an implementation of `delete` compatible with the
677
   * implementation of `new` used to allocate them.
678
   *
679
   * Called by SpiderMonkey.
680
   */
681
0
  virtual void Delete() override {
682
0
    delete this;
683
0
  }
684
  ~nsPerformanceGroup();
685
686
private:
687
  /**
688
   * Identification details for this group.
689
   */
690
  RefPtr<nsPerformanceGroupDetails> mDetails;
691
692
  /**
693
   * The stats service. Used to perform cleanup during destruction.
694
   */
695
  RefPtr<nsPerformanceStatsService> mService;
696
697
  /**
698
   * The scope of this group. Used to determine whether the group
699
   * should be (de)activated.
700
   */
701
  const GroupScope mScope;
702
703
// Observing performance alerts.
704
705
public:
706
  /**
707
   * The observation target, used to register observers.
708
   */
709
  nsPerformanceObservationTarget* ObservationTarget() const;
710
711
  /**
712
   * Record a jank duration.
713
   *
714
   * Update the highest recent jank if necessary.
715
   */
716
  void RecordJank(uint64_t jank);
717
  uint64_t HighestRecentJank();
718
719
  /**
720
   * Record a CPOW duration.
721
   *
722
   * Update the highest recent CPOW if necessary.
723
   */
724
  void RecordCPOW(uint64_t cpow);
725
  uint64_t HighestRecentCPOW();
726
727
  /**
728
   * Record that this group has recently been involved in handling
729
   * user input. Note that heuristics are involved here, so the
730
   * result is not 100% accurate.
731
   */
732
  void RecordUserInput();
733
  bool HasRecentUserInput();
734
735
  /**
736
   * Reset recent values (recent highest CPOW and jank, involvement in
737
   * user input).
738
   */
739
  void ResetRecent();
740
private:
741
  /**
742
   * The target used by observers to register for watching slow
743
   * performance alerts caused by this group.
744
   *
745
   * May be nullptr for groups that cannot be watched (the top group).
746
   */
747
  RefPtr<class nsPerformanceObservationTarget> mObservationTarget;
748
749
  /**
750
   * The highest jank encountered since jank observers for this group
751
   * were last called, in microseconds.
752
   */
753
  uint64_t mHighestJank;
754
755
  /**
756
   * The highest CPOW encountered since jank observers for this group
757
   * were last called, in microseconds.
758
   */
759
  uint64_t mHighestCPOW;
760
761
  /**
762
   * `true` if this group has been involved in handling user input,
763
   * `false` otherwise.
764
   *
765
   * Note that we use heuristics to determine whether a group is
766
   * involved in handling user input, so this value is not 100%
767
   * accurate.
768
   */
769
  bool mHasRecentUserInput;
770
771
  /**
772
   * `true` if this group has caused a performance alert and this alert
773
   * hasn't been dispatched yet.
774
   *
775
   * We use this as part of the buffering of performance alerts. If
776
   * the group generates several alerts several times during the
777
   * buffering delay, we only wish to add the group once to the list
778
   * of alerts.
779
   */
780
  bool mHasPendingAlert;
781
};
782
783
#endif