Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestThreadMetrics.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "gtest/gtest.h"
8
#include "gmock/gmock.h"
9
#include "mozilla/AbstractThread.h"
10
#include "mozilla/Preferences.h"
11
#include "mozilla/dom/DocGroup.h"
12
#include "mozilla/dom/DOMPrefs.h"
13
#include "mozilla/dom/TabGroup.h"
14
#include "mozilla/SchedulerGroup.h"
15
#include "mozilla/TaskCategory.h"
16
#include "mozilla/PerformanceCounter.h"
17
#include "nsThreadUtils.h"
18
#include "nsServiceManagerUtils.h"
19
#include "nsThread.h"
20
21
using namespace mozilla;
22
using mozilla::Runnable;
23
24
25
class MockSchedulerGroup: public SchedulerGroup
26
{
27
public:
28
  explicit MockSchedulerGroup(mozilla::dom::DocGroup* aDocGroup)
29
0
      : mDocGroup(aDocGroup) {}
30
  NS_INLINE_DECL_REFCOUNTING(MockSchedulerGroup);
31
32
  MOCK_METHOD1(SetValidatingAccess, void(ValidationType aType));
33
0
  mozilla::dom::DocGroup* DocGroup() {
34
0
    return mDocGroup;
35
0
  }
36
protected:
37
0
  virtual ~MockSchedulerGroup() = default;
38
private:
39
40
  mozilla::dom::DocGroup* mDocGroup;
41
};
42
43
44
typedef testing::NiceMock<MockSchedulerGroup> MSchedulerGroup;
45
46
/* Timed runnable which simulates some execution time
47
 * and can run a nested runnable.
48
 */
49
class TimedRunnable final : public Runnable
50
{
51
public:
52
  explicit TimedRunnable(uint32_t aExecutionTime1, uint32_t aExecutionTime2,
53
                         uint32_t aSubExecutionTime)
54
    : Runnable("TimedRunnable")
55
    , mExecutionTime1(aExecutionTime1)
56
    , mExecutionTime2(aExecutionTime2)
57
    , mSubExecutionTime(aSubExecutionTime)
58
0
  {
59
0
  }
60
  NS_IMETHODIMP Run()
61
0
  {
62
0
    PR_Sleep(PR_MillisecondsToInterval(mExecutionTime1 + 5));
63
0
    if (mSubExecutionTime > 0) {
64
0
      // Dispatch another runnable so nsThread::ProcessNextEvent is called recursively
65
0
      nsCOMPtr<nsIThread> thread = do_GetMainThread();
66
0
      nsCOMPtr<nsIRunnable> runnable = new TimedRunnable(mSubExecutionTime, 0, 0);
67
0
      thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
68
0
      (void)NS_ProcessNextEvent(thread, false);
69
0
    }
70
0
    PR_Sleep(PR_MillisecondsToInterval(mExecutionTime2 + 5));
71
0
    return NS_OK;
72
0
  }
73
private:
74
  uint32_t mExecutionTime1;
75
  uint32_t mExecutionTime2;
76
  uint32_t mSubExecutionTime;
77
};
78
79
80
/* test class used for all metrics tests
81
 *
82
 * - sets up the enable_scheduler_timing pref
83
 * - provides a function to dispatch runnables and spin the loop
84
 */
85
86
static const char prefKey[] = "dom.performance.enable_scheduler_timing";
87
88
class ThreadMetrics: public ::testing::Test
89
{
90
public:
91
0
  explicit ThreadMetrics() = default;
92
93
protected:
94
0
  virtual void SetUp() {
95
0
    mOldPref = Preferences::GetBool(prefKey);
96
0
    Preferences::SetBool(prefKey, true);
97
0
    // building the TabGroup/DocGroup structure
98
0
    nsCString key = NS_LITERAL_CSTRING("key");
99
0
    nsCOMPtr<nsIDocument> doc;
100
0
    RefPtr<mozilla::dom::TabGroup> tabGroup = new mozilla::dom::TabGroup(false);
101
0
    mDocGroup = tabGroup->AddDocument(key, doc);
102
0
    mSchedulerGroup = new MSchedulerGroup(mDocGroup);
103
0
    mCounter = mDocGroup->GetPerformanceCounter();
104
0
    mThreadMgr = do_GetService("@mozilla.org/thread-manager;1");
105
0
    mOther = DispatchCategory(TaskCategory::Other).GetValue();
106
0
    mDispatchCount = (uint32_t)TaskCategory::Other + 1;
107
0
  }
108
109
0
  virtual void TearDown() {
110
0
    // and remove the document from the doc group (actually, a nullptr)
111
0
    mDocGroup->RemoveDocument(nullptr);
112
0
    mDocGroup = nullptr;
113
0
    Preferences::SetBool(prefKey, mOldPref);
114
0
    ProcessAllEvents();
115
0
  }
116
117
  // this is used to get rid of transient events
118
0
  void initScheduler() {
119
0
    ProcessAllEvents();
120
0
  }
121
122
  nsresult Dispatch(uint32_t aExecutionTime1, uint32_t aExecutionTime2,
123
0
                    uint32_t aSubExecutionTime) {
124
0
    ProcessAllEvents();
125
0
    nsCOMPtr<nsIRunnable> runnable = new TimedRunnable(aExecutionTime1,
126
0
                                                       aExecutionTime2,
127
0
                                                       aSubExecutionTime);
128
0
    runnable = new SchedulerGroup::Runnable(runnable.forget(),
129
0
                                            mSchedulerGroup, mDocGroup);
130
0
    return mDocGroup->Dispatch(TaskCategory::Other, runnable.forget());
131
0
  }
132
133
0
  void ProcessAllEvents() {
134
0
    mThreadMgr->SpinEventLoopUntilEmpty();
135
0
  }
136
137
  uint32_t mOther;
138
  bool mOldPref;
139
  RefPtr<MSchedulerGroup> mSchedulerGroup;
140
  RefPtr<mozilla::dom::DocGroup> mDocGroup;
141
  RefPtr<mozilla::PerformanceCounter> mCounter;
142
  nsCOMPtr<nsIThreadManager> mThreadMgr;
143
  uint32_t mDispatchCount;
144
};
145
146
147
TEST_F(ThreadMetrics, CollectMetrics)
148
0
{
149
0
  nsresult rv;
150
0
  initScheduler();
151
0
152
0
  // Dispatching a runnable that will last for +50ms
153
0
  rv = Dispatch(25, 25, 0);
154
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
155
0
156
0
  // Flush the queue
157
0
  ProcessAllEvents();
158
0
159
0
  // Let's look at the task category "other" counter
160
0
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);
161
0
162
0
  // other counters should stay empty
163
0
  for (uint32_t i = 0; i < mDispatchCount; i++) {
164
0
    if (i != mOther) {
165
0
        ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
166
0
    }
167
0
  }
168
0
169
0
  // Did we get incremented in the docgroup ?
170
0
  uint64_t duration = mCounter->GetExecutionDuration();
171
0
  ASSERT_GE(duration, 50000u);
172
0
  ASSERT_LT(duration, 200000u);
173
0
}
174
175
176
TEST_F(ThreadMetrics, CollectRecursiveMetrics)
177
0
{
178
0
  nsresult rv;
179
0
180
0
  initScheduler();
181
0
182
0
  // Dispatching a runnable that will last for +50ms
183
0
  // and run another one recursively that lasts for 200ms
184
0
  rv = Dispatch(25, 25, 200);
185
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
186
0
187
0
  // Flush the queue
188
0
  ProcessAllEvents();
189
0
190
0
  // let's look at the counters
191
0
  ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);
192
0
193
0
  // other counters should stay empty
194
0
  for (uint32_t i = 0; i < mDispatchCount; i++) {
195
0
    if (i != mOther) {
196
0
        ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
197
0
    }
198
0
  }
199
0
200
0
  // did we get incremented in the docgroup ?
201
0
  uint64_t duration = mCounter->GetExecutionDuration();
202
0
  ASSERT_GE(duration, 50000u);
203
0
204
0
  // let's make sure we don't count the time spent in recursive calls
205
0
  ASSERT_LT(duration, 200000u);
206
0
}