Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/tools/profiler/gecko/ThreadResponsiveness.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "ThreadResponsiveness.h"
7
8
#include "mozilla/Atomics.h"
9
#include "mozilla/SystemGroup.h"
10
11
#include "nsITimer.h"
12
#include "platform.h"
13
14
using namespace mozilla;
15
16
class CheckResponsivenessTask : public CancelableRunnable,
17
                                public nsITimerCallback {
18
public:
19
  explicit CheckResponsivenessTask(nsIEventTarget* aThread, bool aIsMainThread)
20
    : CancelableRunnable("CheckResponsivenessTask")
21
    , mStartToPrevTracer_us(uint64_t(profiler_time() * 1000.0))
22
    , mStop(false)
23
    , mHasEverBeenSuccessfullyDispatched(false)
24
    , mThread(aThread)
25
    , mIsMainThread(aIsMainThread)
26
0
  {
27
0
  }
28
29
protected:
30
  ~CheckResponsivenessTask()
31
0
  {
32
0
  }
33
34
public:
35
36
  // Must be called from the same thread every time. Call that the update
37
  // thread, because it's the thread that ThreadResponsiveness::Update() is
38
  // called on. In reality it's the profiler's sampler thread.
39
  bool DoFirstDispatchIfNeeded()
40
0
  {
41
0
    if (mHasEverBeenSuccessfullyDispatched) {
42
0
      return true;
43
0
    }
44
0
45
0
    // The profiler for the main thread is set up before the thread manager is,
46
0
    // meaning we can't get the nsIThread when the CheckResponsivenessTask is
47
0
    // constructed. We _do_ know whether it is the main thread at that time,
48
0
    // however, so here's the workaround. We can still hit this code before the
49
0
    // thread manager is initted, in which case we won't try to record
50
0
    // responsiveness, which is fine because there's no event queue to check
51
0
    // responsiveness on anyway.
52
0
    if (mIsMainThread) {
53
0
      if (!mThread) {
54
0
        nsCOMPtr<nsIThread> temp;
55
0
        NS_GetMainThread(getter_AddRefs(temp));
56
0
        mThread = temp.forget();
57
0
      }
58
0
59
0
      if (mThread) {
60
0
        nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, do_AddRef(this));
61
0
        if (NS_SUCCEEDED(rv)) {
62
0
          mHasEverBeenSuccessfullyDispatched = true;
63
0
        }
64
0
      }
65
0
    } else if (mThread) {
66
0
      nsresult rv = mThread->Dispatch(this, nsIThread::NS_DISPATCH_NORMAL);
67
0
      if (NS_SUCCEEDED(rv)) {
68
0
        mHasEverBeenSuccessfullyDispatched = true;
69
0
      }
70
0
    }
71
0
72
0
    return mHasEverBeenSuccessfullyDispatched;
73
0
  }
74
75
  nsresult Cancel() override
76
0
  {
77
0
    // No special work needed.
78
0
    return NS_OK;
79
0
  }
80
81
  // Only runs on the thread being profiled. Always called via a thread
82
  // dispatch, so inherently functions as a responsiveness statistic.
83
  NS_IMETHOD Run() override
84
0
  {
85
0
    // This approach means that the 16ms delay in the timer below, _plus_ any
86
0
    // additional delays in the TimerThread itself, become part of the
87
0
    // responsiveness statistic for this thread. What we should probably be
88
0
    // doing is recording responsiveness only when we have dispatched (but not
89
0
    // executed) a call to this function, either because of a call to
90
0
    // DoFirstDispatchIfNeeded, or a call to Notify.
91
0
    mStartToPrevTracer_us = uint64_t(profiler_time() * 1000.0);
92
0
93
0
    if (!mStop) {
94
0
      if (!mTimer) {
95
0
        if (mIsMainThread) {
96
0
          mTimer = NS_NewTimer(
97
0
            SystemGroup::EventTargetFor(TaskCategory::Other));
98
0
        } else {
99
0
          mTimer = NS_NewTimer();
100
0
        }
101
0
      }
102
0
      mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
103
0
    }
104
0
105
0
    return NS_OK;
106
0
  }
107
108
  // Should always fire on the thread being profiled
109
  NS_IMETHOD Notify(nsITimer* aTimer) final
110
0
  {
111
0
    Run();
112
0
    return NS_OK;
113
0
  }
114
115
  // Can be called on any thread.
116
0
  void Terminate() {
117
0
    mStop = true;
118
0
  }
119
120
  // Can be called on any thread.
121
0
  double GetStartToPrevTracer_ms() const {
122
0
    return mStartToPrevTracer_us / 1000.0;
123
0
  }
124
125
  NS_DECL_ISUPPORTS_INHERITED
126
127
private:
128
  // The timer that's responsible for redispatching this event to the thread we
129
  // are profiling (ie; mThread). Only touched on mThread.
130
  nsCOMPtr<nsITimer> mTimer;
131
132
  // The time (in integer microseconds since process startup) at which this
133
  // event was last processed (Run() was last called).
134
  // This field is written on mThread and read on the update thread.
135
  // This is stored as integer microseconds instead of double milliseconds
136
  // because Atomic<double> is not available.
137
  Atomic<uint64_t> mStartToPrevTracer_us;
138
139
  // Whether we should stop redispatching this event once the timer fires the
140
  // next time. Set to true by any thread when the profiler is stopped; read on
141
  // mThread.
142
  Atomic<bool> mStop;
143
144
  // Only accessed on the update thread.
145
  bool mHasEverBeenSuccessfullyDispatched;
146
147
  // The thread that we're profiling. Use nsIEventTarget to allow for checking
148
  // responsiveness on non-nsIThreads someday.
149
  nsCOMPtr<nsIEventTarget> mThread;
150
  bool mIsMainThread;
151
};
152
153
NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, CancelableRunnable,
154
                            nsITimerCallback)
155
156
ThreadResponsiveness::ThreadResponsiveness(nsIEventTarget* aThread,
157
                                           bool aIsMainThread)
158
  : mActiveTracerEvent(new CheckResponsivenessTask(aThread, aIsMainThread))
159
0
{
160
0
  MOZ_COUNT_CTOR(ThreadResponsiveness);
161
0
}
162
163
ThreadResponsiveness::~ThreadResponsiveness()
164
0
{
165
0
  MOZ_COUNT_DTOR(ThreadResponsiveness);
166
0
  mActiveTracerEvent->Terminate();
167
0
}
168
169
void
170
ThreadResponsiveness::Update()
171
0
{
172
0
  if (!mActiveTracerEvent->DoFirstDispatchIfNeeded()) {
173
0
    return;
174
0
  }
175
0
  mStartToPrevTracer_ms = Some(mActiveTracerEvent->GetStartToPrevTracer_ms());
176
0
}
177