зеркало из https://github.com/mozilla/gecko-dev.git
184 строки
5.6 KiB
C++
184 строки
5.6 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"
|
|
#include "nsThread.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,
|
|
uint32_t aSubExecutionTime)
|
|
: Runnable("TimedRunnable"),
|
|
mExecutionTime1(aExecutionTime1),
|
|
mExecutionTime2(aExecutionTime2),
|
|
mSubExecutionTime(aSubExecutionTime) {}
|
|
NS_IMETHODIMP Run() {
|
|
PR_Sleep(PR_MillisecondsToInterval(mExecutionTime1 + 5));
|
|
if (mSubExecutionTime > 0) {
|
|
// Dispatch another runnable so nsThread::ProcessNextEvent is called
|
|
// recursively
|
|
nsCOMPtr<nsIThread> thread = do_GetMainThread();
|
|
nsCOMPtr<nsIRunnable> runnable =
|
|
new TimedRunnable(mSubExecutionTime, 0, 0);
|
|
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;
|
|
uint32_t mSubExecutionTime;
|
|
};
|
|
|
|
/* test class used for all metrics tests
|
|
*
|
|
* - sets up the enable_scheduler_timing pref
|
|
* - provides a function to dispatch runnables and spin the loop
|
|
*/
|
|
|
|
class ThreadMetrics : public ::testing::Test {
|
|
public:
|
|
explicit ThreadMetrics() = default;
|
|
|
|
protected:
|
|
virtual void SetUp() {
|
|
// building the TabGroup/DocGroup structure
|
|
nsCString key = NS_LITERAL_CSTRING("key");
|
|
RefPtr<dom::Document> doc;
|
|
RefPtr<dom::TabGroup> tabGroup = new 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() {
|
|
// and remove the document from the doc group (actually, a nullptr)
|
|
mDocGroup->RemoveDocument(nullptr);
|
|
mDocGroup = nullptr;
|
|
ProcessAllEvents();
|
|
}
|
|
|
|
// this is used to get rid of transient events
|
|
void initScheduler() { ProcessAllEvents(); }
|
|
|
|
nsresult Dispatch(uint32_t aExecutionTime1, uint32_t aExecutionTime2,
|
|
uint32_t aSubExecutionTime) {
|
|
ProcessAllEvents();
|
|
nsCOMPtr<nsIRunnable> runnable =
|
|
new TimedRunnable(aExecutionTime1, aExecutionTime2, aSubExecutionTime);
|
|
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;
|
|
initScheduler();
|
|
|
|
// Dispatching a runnable that will last for +50ms
|
|
rv = Dispatch(25, 25, 0);
|
|
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, 50000u);
|
|
}
|
|
|
|
TEST_F(ThreadMetrics, CollectRecursiveMetrics) {
|
|
nsresult rv;
|
|
|
|
initScheduler();
|
|
|
|
// Dispatching a runnable that will last for +50ms
|
|
// and run another one recursively that lasts for 400ms
|
|
rv = Dispatch(25, 25, 400);
|
|
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, 50000u);
|
|
|
|
// let's make sure we don't count the time spent in recursive calls
|
|
ASSERT_LT(duration, 300000u);
|
|
}
|