Bug 870043 - Add a script-accessible statistics for various watchdog events. r=mrbkap

We need this even for testing wakeups, because we can't be certain that any
given operation callback was necessarily triggered from the watchdog thread
(since it's triggered from within the JS engine in various cases as well).
This commit is contained in:
Bobby Holley 2013-07-24 15:33:31 -07:00
parent 63f5620548
commit 93442ca1ad
4 changed files with 83 additions and 5 deletions

View File

@ -119,7 +119,7 @@ interface ScheduledGCCallback : nsISupports
/** /**
* interface of Components.utils * interface of Components.utils
*/ */
[scriptable, uuid(fdd32d38-9341-4067-9000-d781075a60c9)] [scriptable, uuid(35d5b0e5-9b29-48de-9f79-d2534b497435)]
interface nsIXPCComponents_Utils : nsISupports interface nsIXPCComponents_Utils : nsISupports
{ {
@ -426,6 +426,18 @@ interface nsIXPCComponents_Utils : nsISupports
* names are supported. * names are supported.
*/ */
nsIClassInfo getDOMClassInfo(in AString aClassName); nsIClassInfo getDOMClassInfo(in AString aClassName);
/**
* Retrieve the last time, in microseconds since epoch, that a given
* watchdog-related event occured.
*
* Valid categories:
* "RuntimeStateChange" - Runtime switching between active and inactive states
* "WatchdogWakeup" - Watchdog waking up from sleeping
* "WatchdogHibernateStart" - Watchdog begins hibernating
* "WatchdogHibernateStop" - Watchdog stops hibernating
*/
PRTime getWatchdogTimestamp(in AString aCategory);
}; };
/** /**

View File

@ -4541,6 +4541,24 @@ nsXPCComponents_Utils::GetDOMClassInfo(const nsAString& aClassName,
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
} }
NS_IMETHODIMP
nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime *aOut)
{
WatchdogTimestampCategory category;
if (aCategory.EqualsLiteral("RuntimeStateChange"))
category = TimestampRuntimeStateChange;
else if (aCategory.EqualsLiteral("WatchdogWakeup"))
category = TimestampWatchdogWakeup;
else if (aCategory.EqualsLiteral("WatchdogHibernateStart"))
category = TimestampWatchdogHibernateStart;
else if (aCategory.EqualsLiteral("WatchdogHibernateStop"))
category = TimestampWatchdogHibernateStop;
else
return NS_ERROR_INVALID_ARG;
*aOut = XPCJSRuntime::Get()->GetWatchdogTimestamp(category);
return NS_OK;
}
/***************************************************************************/ /***************************************************************************/
/***************************************************************************/ /***************************************************************************/
/***************************************************************************/ /***************************************************************************/

View File

@ -995,11 +995,15 @@ class Watchdog
class WatchdogManager : public nsIObserver class WatchdogManager : public nsIObserver
{ {
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime) WatchdogManager(XPCJSRuntime *aRuntime) : mRuntime(aRuntime)
, mRuntimeState(RUNTIME_INACTIVE) , mRuntimeState(RUNTIME_INACTIVE)
, mTimeAtLastRuntimeStateChange(PR_Now())
{ {
// All the timestamps start at zero except for runtime state change.
PodArrayZero(mTimestamps);
mTimestamps[TimestampRuntimeStateChange] = PR_Now();
// Enable the watchdog, if appropriate. // Enable the watchdog, if appropriate.
RefreshWatchdog(); RefreshWatchdog();
@ -1029,12 +1033,13 @@ class WatchdogManager : public nsIObserver
RecordRuntimeActivity(bool active) RecordRuntimeActivity(bool active)
{ {
// The watchdog reads this state, so acquire the lock before writing it. // The watchdog reads this state, so acquire the lock before writing it.
MOZ_ASSERT(NS_IsMainThread());
Maybe<AutoLockWatchdog> lock; Maybe<AutoLockWatchdog> lock;
if (mWatchdog) if (mWatchdog)
lock.construct(mWatchdog); lock.construct(mWatchdog);
// Write state. // Write state.
mTimeAtLastRuntimeStateChange = PR_Now(); mTimestamps[TimestampRuntimeStateChange] = PR_Now();
mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE; mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
// The watchdog may be hibernating, waiting for the runtime to go // The watchdog may be hibernating, waiting for the runtime to go
@ -1045,7 +1050,26 @@ class WatchdogManager : public nsIObserver
bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; } bool IsRuntimeActive() { return mRuntimeState == RUNTIME_ACTIVE; }
PRTime TimeSinceLastRuntimeStateChange() PRTime TimeSinceLastRuntimeStateChange()
{ {
return PR_Now() - mTimeAtLastRuntimeStateChange; return PR_Now() - GetTimestamp(TimestampRuntimeStateChange);
}
// Note - Because of the runtime activity timestamp, these are read and
// written from both threads.
void RecordTimestamp(WatchdogTimestampCategory aCategory)
{
// The watchdog thread always holds the lock when it runs.
Maybe<AutoLockWatchdog> maybeLock;
if (NS_IsMainThread() && mWatchdog)
maybeLock.construct(mWatchdog);
mTimestamps[aCategory] = PR_Now();
}
PRTime GetTimestamp(WatchdogTimestampCategory aCategory)
{
// The watchdog thread always holds the lock when it runs.
Maybe<AutoLockWatchdog> maybeLock;
if (NS_IsMainThread() && mWatchdog)
maybeLock.construct(mWatchdog);
return mTimestamps[aCategory];
} }
XPCJSRuntime* Runtime() { return mRuntime; } XPCJSRuntime* Runtime() { return mRuntime; }
@ -1081,7 +1105,7 @@ class WatchdogManager : public nsIObserver
nsAutoPtr<Watchdog> mWatchdog; nsAutoPtr<Watchdog> mWatchdog;
enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState; enum { RUNTIME_ACTIVE, RUNTIME_INACTIVE } mRuntimeState;
PRTime mTimeAtLastRuntimeStateChange; PRTime mTimestamps[TimestampCount];
}; };
NS_IMPL_ISUPPORTS1(WatchdogManager, nsIObserver) NS_IMPL_ISUPPORTS1(WatchdogManager, nsIObserver)
@ -1116,9 +1140,14 @@ WatchdogMain(void *arg)
{ {
self->Sleep(PR_TicksPerSecond()); self->Sleep(PR_TicksPerSecond());
} else { } else {
manager->RecordTimestamp(TimestampWatchdogHibernateStart);
self->Hibernate(); self->Hibernate();
manager->RecordTimestamp(TimestampWatchdogHibernateStop);
} }
// Rise and shine.
manager->RecordTimestamp(TimestampWatchdogWakeup);
// Don't trigger the operation callback if activity started less than one second ago. // Don't trigger the operation callback if activity started less than one second ago.
// The callback is only used for detecting long running scripts, and triggering the // The callback is only used for detecting long running scripts, and triggering the
// callback from off the main thread can be expensive. // callback from off the main thread can be expensive.
@ -1133,6 +1162,12 @@ WatchdogMain(void *arg)
self->Finished(); self->Finished();
} }
PRTime
XPCJSRuntime::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
{
return mWatchdogManager->GetTimestamp(aCategory);
}
//static //static
void void
XPCJSRuntime::ActivityCallback(void *arg, JSBool active) XPCJSRuntime::ActivityCallback(void *arg, JSBool active)

View File

@ -596,6 +596,16 @@ public:
class XPCJSContextStack; class XPCJSContextStack;
class WatchdogManager; class WatchdogManager;
enum WatchdogTimestampCategory
{
TimestampRuntimeStateChange = 0,
TimestampWatchdogWakeup,
TimestampWatchdogHibernateStart,
TimestampWatchdogHibernateStop,
TimestampCount
};
class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime
{ {
public: public:
@ -817,6 +827,9 @@ public:
JSObject* GetJunkScope(); JSObject* GetJunkScope();
void DeleteJunkScope(); void DeleteJunkScope();
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
private: private:
XPCJSRuntime(); // no implementation XPCJSRuntime(); // no implementation
XPCJSRuntime(nsXPConnect* aXPConnect); XPCJSRuntime(nsXPConnect* aXPConnect);