mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 12:50:09 +00:00

Adds a PeformanceCounter class that is used in DocGroup and WorkerPrivate to track runnables execution and dispatch counts. MozReview-Commit-ID: 51DLj6ORD2O --HG-- extra : rebase_source : b481c9aa3b735569722bb7472872ec2d22adcb89
201 lines
5.9 KiB
C++
201 lines
5.9 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "gmock/gmock.h"
|
|
#include "mozilla/AbstractThread.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/dom/DocGroup.h"
|
|
#include "mozilla/dom/TabGroup.h"
|
|
#include "mozilla/SchedulerGroup.h"
|
|
#include "mozilla/TaskCategory.h"
|
|
#include "mozilla/PerformanceCounter.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
using namespace mozilla;
|
|
using mozilla::Runnable;
|
|
|
|
|
|
class MockSchedulerGroup: public SchedulerGroup
|
|
{
|
|
public:
|
|
explicit MockSchedulerGroup(mozilla::dom::DocGroup* aDocGroup)
|
|
: mDocGroup(aDocGroup) {}
|
|
NS_INLINE_DECL_REFCOUNTING(MockSchedulerGroup);
|
|
|
|
MOCK_METHOD1(SetValidatingAccess, void(ValidationType aType));
|
|
mozilla::dom::DocGroup* DocGroup() {
|
|
return mDocGroup;
|
|
}
|
|
protected:
|
|
virtual ~MockSchedulerGroup() = default;
|
|
private:
|
|
|
|
mozilla::dom::DocGroup* mDocGroup;
|
|
};
|
|
|
|
|
|
typedef testing::NiceMock<MockSchedulerGroup> MSchedulerGroup;
|
|
|
|
/* Timed runnable which simulates some execution time
|
|
* and can run a nested runnable.
|
|
*/
|
|
class TimedRunnable final : public Runnable
|
|
{
|
|
public:
|
|
explicit TimedRunnable(uint32_t aExecutionTime1, uint32_t aExecutionTime2,
|
|
bool aRecursive)
|
|
: Runnable("TimedRunnable")
|
|
, mExecutionTime1(aExecutionTime1)
|
|
, mExecutionTime2(aExecutionTime2)
|
|
, mRecursive(aRecursive)
|
|
{
|
|
}
|
|
NS_IMETHODIMP Run()
|
|
{
|
|
PR_Sleep(PR_MillisecondsToInterval(mExecutionTime1 + 5));
|
|
if (mRecursive) {
|
|
// Dispatch another runnable so nsThread::ProcessNextEvent is called recursively
|
|
nsCOMPtr<nsIThread> thread = do_GetMainThread();
|
|
nsCOMPtr<nsIRunnable> runnable = new TimedRunnable(155, 0, false);
|
|
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
|
(void)NS_ProcessNextEvent(thread, false);
|
|
}
|
|
PR_Sleep(PR_MillisecondsToInterval(mExecutionTime2 + 5));
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
uint32_t mExecutionTime1;
|
|
uint32_t mExecutionTime2;
|
|
bool mRecursive;
|
|
};
|
|
|
|
|
|
/* test class used for all metrics tests
|
|
*
|
|
* - sets up the enable_scheduler_timing pref
|
|
* - provides a function to dispatch runnables and spin the loop
|
|
*/
|
|
|
|
static const char prefKey[] = "dom.performance.enable_scheduler_timing";
|
|
|
|
class ThreadMetrics: public ::testing::Test
|
|
{
|
|
public:
|
|
explicit ThreadMetrics() = default;
|
|
|
|
protected:
|
|
virtual void SetUp() {
|
|
mOldPref = Preferences::GetBool(prefKey);
|
|
Preferences::SetBool(prefKey, true);
|
|
// building the TabGroup/DocGroup structure
|
|
nsCString key = NS_LITERAL_CSTRING("key");
|
|
nsCOMPtr<nsIDocument> doc;
|
|
RefPtr<mozilla::dom::TabGroup> tabGroup = new mozilla::dom::TabGroup(false);
|
|
mDocGroup = tabGroup->AddDocument(key, doc);
|
|
mSchedulerGroup = new MSchedulerGroup(mDocGroup);
|
|
mCounter = mDocGroup->GetPerformanceCounter();
|
|
mThreadMgr = do_GetService("@mozilla.org/thread-manager;1");
|
|
mOther = DispatchCategory(TaskCategory::Other).GetValue();
|
|
mDispatchCount = (uint32_t)TaskCategory::Other + 1;
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
// let's reset the counters
|
|
mCounter->ResetPerformanceCounters();
|
|
for (uint32_t i = 0; i < mDispatchCount; i++) {
|
|
ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
|
|
}
|
|
ASSERT_EQ(mCounter->GetExecutionDuration(), 0u);
|
|
// and remove the document from the doc group (actually, a nullptr)
|
|
mDocGroup->RemoveDocument(nullptr);
|
|
mDocGroup = nullptr;
|
|
Preferences::SetBool(prefKey, mOldPref);
|
|
}
|
|
|
|
nsresult Dispatch(uint32_t aExecutionTime1, uint32_t aExecutionTime2,
|
|
bool aRecursive) {
|
|
nsCOMPtr<nsIRunnable> runnable = new TimedRunnable(aExecutionTime1,
|
|
aExecutionTime2,
|
|
aRecursive);
|
|
runnable = new SchedulerGroup::Runnable(runnable.forget(),
|
|
mSchedulerGroup, mDocGroup);
|
|
return mDocGroup->Dispatch(TaskCategory::Other, runnable.forget());
|
|
}
|
|
|
|
void ProcessAllEvents() {
|
|
mThreadMgr->SpinEventLoopUntilEmpty();
|
|
}
|
|
|
|
uint32_t mOther;
|
|
bool mOldPref;
|
|
RefPtr<MSchedulerGroup> mSchedulerGroup;
|
|
RefPtr<mozilla::dom::DocGroup> mDocGroup;
|
|
RefPtr<mozilla::PerformanceCounter> mCounter;
|
|
nsCOMPtr<nsIThreadManager> mThreadMgr;
|
|
uint32_t mDispatchCount;
|
|
};
|
|
|
|
|
|
TEST_F(ThreadMetrics, CollectMetrics)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Dispatching a runnable that will last for +100ms
|
|
rv = Dispatch(100, 0, false);
|
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
|
|
|
// Flush the queue
|
|
ProcessAllEvents();
|
|
|
|
// Let's look at the task category "other" counter
|
|
ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);
|
|
|
|
// other counters should stay empty
|
|
for (uint32_t i = 0; i < mDispatchCount; i++) {
|
|
if (i != mOther) {
|
|
ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
|
|
}
|
|
}
|
|
|
|
// Did we get incremented in the docgroup ?
|
|
uint64_t duration = mCounter->GetExecutionDuration();
|
|
ASSERT_GE(duration, 100000u);
|
|
ASSERT_LT(duration, 150000u);
|
|
}
|
|
|
|
|
|
TEST_F(ThreadMetrics, CollectRecursiveMetrics)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Dispatching a runnable that will last for +100ms
|
|
// and run another one recursively
|
|
rv = Dispatch(50, 50, true);
|
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
|
|
|
// Flush the queue
|
|
ProcessAllEvents();
|
|
|
|
// let's look at the counters
|
|
ASSERT_EQ(mCounter->GetDispatchCounter()[mOther], 1u);
|
|
|
|
// other counters should stay empty
|
|
for (uint32_t i = 0; i < mDispatchCount; i++) {
|
|
if (i != mOther) {
|
|
ASSERT_EQ(mCounter->GetDispatchCounter()[i], 0u);
|
|
}
|
|
}
|
|
|
|
// did we get incremented in the docgroup ?
|
|
uint64_t duration = mCounter->GetExecutionDuration();
|
|
ASSERT_GE(duration, 100000u);
|
|
|
|
// let's make sure we don't count the time spent in recursive calls
|
|
ASSERT_LT(duration, 150000u);
|
|
}
|