Bug 1203427 (part 5) - Add logging of timer firings. r=froydnj.

This commit is contained in:
Nicholas Nethercote 2015-09-10 00:50:51 -07:00
parent c65f0f318a
commit 09b4a9e997
14 changed files with 354 additions and 62 deletions

View File

@ -53,6 +53,7 @@
#include "ScriptSettings.h"
#include "mozilla/Preferences.h"
#include "mozilla/Likely.h"
#include "mozilla/Snprintf.h"
#include "mozilla/unused.h"
// Other Classes
@ -545,8 +546,9 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release)
nsresult
nsTimeout::InitTimer(uint32_t aDelay)
{
return mTimer->InitWithFuncCallback(nsGlobalWindow::TimerCallback, this,
aDelay, nsITimer::TYPE_ONE_SHOT);
return mTimer->InitWithNameableFuncCallback(
nsGlobalWindow::TimerCallback, this, aDelay,
nsITimer::TYPE_ONE_SHOT, nsGlobalWindow::TimerNameCallback);
}
// Return true if this timeout has a refcount of 1. This is used to check
@ -13161,6 +13163,19 @@ nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
timeout->mWindow->RunTimeout(timeout);
}
// static
void
nsGlobalWindow::TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
size_t aLen)
{
nsRefPtr<nsTimeout> timeout = (nsTimeout*)aClosure;
const char* filename;
uint32_t lineNum, column;
timeout->mScriptHandler->GetLocation(&filename, &lineNum, &column);
snprintf(aBuf, aLen, "[content] %s:%u:%u", filename, lineNum, column);
}
//*****************************************************************************
// nsGlobalWindow: Helper Functions
//*****************************************************************************

View File

@ -1424,6 +1424,8 @@ public:
// fire after it, but no earlier than mTimeoutInsertionPoint, if any.
void InsertTimeoutIntoList(nsTimeout *aTimeout);
static void TimerCallback(nsITimer *aTimer, void *aClosure);
static void TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf,
size_t aLen);
// Helper Functions
already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();

View File

@ -1636,10 +1636,10 @@ nsJSContext::BeginCycleCollectionCallback()
// an incremental collection, and we want to be sure to finish it.
CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
if (sICCTimer) {
sICCTimer->InitWithFuncCallback(ICCTimerFired,
nullptr,
kICCIntersliceDelay,
nsITimer::TYPE_REPEATING_SLACK);
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
kICCIntersliceDelay,
nsITimer::TYPE_REPEATING_SLACK,
"ICCTimerFired");
}
}
@ -2036,14 +2036,15 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason, int aDelay)
static bool first = true;
sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
aDelay
? aDelay
: (first
? NS_FIRST_GC_DELAY
: NS_GC_DELAY),
nsITimer::TYPE_ONE_SHOT);
sGCTimer->InitWithNamedFuncCallback(GCTimerFired,
reinterpret_cast<void *>(aReason),
aDelay
? aDelay
: (first
? NS_FIRST_GC_DELAY
: NS_GC_DELAY),
nsITimer::TYPE_ONE_SHOT,
"GCTimerFired");
first = false;
}
@ -2062,9 +2063,11 @@ nsJSContext::PokeShrinkGCBuffers()
return;
}
sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nullptr,
NS_SHRINK_GC_BUFFERS_DELAY,
nsITimer::TYPE_ONE_SHOT);
sShrinkGCBuffersTimer->InitWithNamedFuncCallback(ShrinkGCBuffersTimerFired,
nullptr,
NS_SHRINK_GC_BUFFERS_DELAY,
nsITimer::TYPE_ONE_SHOT,
"ShrinkGCBuffersTimerFired");
}
// static
@ -2082,9 +2085,10 @@ nsJSContext::PokeShrinkingGC()
return;
}
sShrinkingGCTimer->InitWithFuncCallback(ShrinkingGCTimerFired, nullptr,
sCompactOnUserInactiveDelay,
nsITimer::TYPE_ONE_SHOT);
sShrinkingGCTimer->InitWithNamedFuncCallback(ShrinkingGCTimerFired, nullptr,
sCompactOnUserInactiveDelay,
nsITimer::TYPE_ONE_SHOT,
"ShrinkingGCTimerFired");
}
// static
@ -2104,9 +2108,10 @@ nsJSContext::MaybePokeCC()
// We can kill some objects before running forgetSkippable.
nsCycleCollector_dispatchDeferredDeletion();
sCCTimer->InitWithFuncCallback(CCTimerFired, nullptr,
NS_CC_SKIPPABLE_DELAY,
nsITimer::TYPE_REPEATING_SLACK);
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
NS_CC_SKIPPABLE_DELAY,
nsITimer::TYPE_REPEATING_SLACK,
"CCTimerFired");
}
}
@ -2263,10 +2268,11 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
if (aDesc.isCompartment_) {
if (!sFullGCTimer && !sShuttingDown) {
CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
nullptr,
NS_FULL_GC_DELAY,
nsITimer::TYPE_ONE_SHOT);
sFullGCTimer->InitWithNamedFuncCallback(FullGCTimerFired,
nullptr,
NS_FULL_GC_DELAY,
nsITimer::TYPE_ONE_SHOT,
"FullGCTimerFired");
}
} else {
nsJSContext::KillFullGCTimer();
@ -2295,10 +2301,11 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
nsJSContext::KillInterSliceGCTimer();
if (!sShuttingDown) {
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
sInterSliceGCTimer->InitWithFuncCallback(InterSliceGCTimerFired,
nullptr,
NS_INTERSLICE_GC_DELAY,
nsITimer::TYPE_ONE_SHOT);
sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired,
nullptr,
NS_INTERSLICE_GC_DELAY,
nsITimer::TYPE_ONE_SHOT,
"InterSliceGCTimerFired");
}
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {

View File

@ -3522,10 +3522,9 @@ void HTMLMediaElement::StartProgressTimer()
NS_ASSERTION(!mProgressTimer, "Already started progress timer.");
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
mProgressTimer->InitWithFuncCallback(ProgressTimerCallback,
this,
PROGRESS_MS,
nsITimer::TYPE_REPEATING_SLACK);
mProgressTimer->InitWithNamedFuncCallback(
ProgressTimerCallback, this, PROGRESS_MS, nsITimer::TYPE_REPEATING_SLACK,
"HTMLMediaElement::ProgressTimerCallback");
}
void HTMLMediaElement::StartProgress()

View File

@ -174,7 +174,9 @@ MediaTimer::ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow)
unsigned long delay = std::ceil((aTarget - aNow).ToMilliseconds());
TIMER_LOG("MediaTimer::ArmTimer delay=%lu", delay);
mCurrentTimerTarget = aTarget;
nsresult rv = mTimer->InitWithFuncCallback(&TimerCallback, this, delay, nsITimer::TYPE_ONE_SHOT);
nsresult rv = mTimer->InitWithNamedFuncCallback(&TimerCallback, this, delay,
nsITimer::TYPE_ONE_SHOT,
"MediaTimer::TimerCallback");
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
(void) rv;
}

View File

@ -1188,8 +1188,9 @@ public:
return false;
}
if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nullptr, aDelayMS,
nsITimer::TYPE_ONE_SHOT))) {
if (NS_FAILED(timer->InitWithNamedFuncCallback(
DummyCallback, nullptr, aDelayMS, nsITimer::TYPE_ONE_SHOT,
"dom::workers::DummyCallback(1)"))) {
JS_ReportError(aCx, "Failed to start timer!");
return false;
}
@ -4536,9 +4537,9 @@ WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->InitWithFuncCallback(DummyCallback,
nullptr, delay,
type)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
mGCTimer->InitWithNamedFuncCallback(DummyCallback, nullptr, delay, type,
"dom::workers::DummyCallback(2)")));
if (aMode == PeriodicTimer) {
LOG(("Worker %p scheduled periodic GC timer\n", this));
@ -5945,8 +5946,9 @@ WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
(mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, delay,
nsITimer::TYPE_ONE_SHOT);
nsresult rv = mTimer->InitWithNamedFuncCallback(
DummyCallback, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
"dom::workers::DummyCallback(3)");
if (NS_FAILED(rv)) {
JS_ReportError(aCx, "Failed to start timer!");
return false;

View File

@ -1748,9 +1748,9 @@ PresShell::Initialize(nscoord aWidth, nscoord aHeight)
Preferences::GetInt("nglayout.initialpaint.delay",
PAINTLOCK_EVENT_DELAY);
mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
this, delay,
nsITimer::TYPE_ONE_SHOT);
mPaintSuppressionTimer->InitWithNamedFuncCallback(
sPaintSuppressionCallback, this, delay, nsITimer::TYPE_ONE_SHOT,
"PresShell::sPaintSuppressionCallback");
}
}

View File

@ -426,9 +426,9 @@ ScrollbarActivity::StartFadeBeginTimer()
if (!mFadeBeginTimer) {
mFadeBeginTimer = do_CreateInstance("@mozilla.org/timer;1");
}
mFadeBeginTimer->InitWithFuncCallback(FadeBeginTimerFired, this,
mScrollbarFadeBeginDelay,
nsITimer::TYPE_ONE_SHOT);
mFadeBeginTimer->InitWithNamedFuncCallback(
FadeBeginTimerFired, this, mScrollbarFadeBeginDelay,
nsITimer::TYPE_ONE_SHOT, "ScrollbarActivity::FadeBeginTimerFired");
}
void

View File

@ -275,8 +275,9 @@ nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync()
// The timer value 50 should not hopefully slow down background pages too
// much, yet lets event loop to process enough between ticks.
// See bug 734015.
gFlushTimer->InitWithFuncCallback(FlushTimerCallback, nullptr,
50, nsITimer::TYPE_REPEATING_SLACK);
gFlushTimer->InitWithNamedFuncCallback(FlushTimerCallback, nullptr,
50, nsITimer::TYPE_REPEATING_SLACK,
"FlushTimerCallback");
}
}
}

View File

@ -355,8 +355,9 @@ nsBrowserStatusFilter::StartDelayTimer()
if (!mTimer)
return NS_ERROR_FAILURE;
return mTimer->InitWithFuncCallback(TimeoutHandler, this, 160,
nsITimer::TYPE_ONE_SHOT);
return mTimer->InitWithNamedFuncCallback(
TimeoutHandler, this, 160, nsITimer::TYPE_ONE_SHOT,
"nsBrowserStatusFilter::TimeoutHandler");
}
void

View File

@ -360,8 +360,8 @@ private:
if (!mTimer) {
return NS_ERROR_OUT_OF_MEMORY;
}
mTimer->InitWithFuncCallback(TimerCallback, this, mTimerPeriod,
nsITimer::TYPE_REPEATING_SLACK);
mTimer->InitWithNamedFuncCallback(TimerCallback, this, mTimerPeriod,
nsITimer::TYPE_REPEATING_SLACK, mName);
return NS_OK;
}
};

View File

@ -21,9 +21,24 @@ interface nsIEventTarget;
*/
class nsITimer;
typedef void (*nsTimerCallbackFunc) (nsITimer *aTimer, void *aClosure);
/**
* The signature of the timer name callback function passed to
* initWithNameableFuncCallback.
* This is the function that will get called when timer profiling is enabled
* via the "TimerFirings" log module.
*
* @param aTimer the timer which has expired
* @param aClosure opaque parameter passed to initWithFuncCallback
* @param aBuf a buffer in which to put the name
* @param aLen the length of the buffer
*/
typedef void (*nsTimerNameCallbackFunc) (nsITimer *aTimer, void *aClosure,
char *aBuf, size_t aLen);
%}
native nsTimerCallbackFunc(nsTimerCallbackFunc);
native nsTimerNameCallbackFunc(nsTimerNameCallbackFunc);
/**
* The callback interface for timers.
@ -57,7 +72,7 @@ interface nsITimerCallback : nsISupports
* target thread, or races may occur with bad results like timers firing after
* they've been canceled, and/or not firing after re-initiatization.
*/
[scriptable, uuid(c569e813-333f-4b78-8691-13ca5839e10a)]
[scriptable, uuid(3de4b105-363c-482c-a409-baac83a01bfc)]
interface nsITimer : nsISupports
{
/* Timer types */
@ -151,6 +166,40 @@ interface nsITimer : nsISupports
*/
void cancel();
/**
* Like initWithFuncCallback, but also takes a name for the timer; the name
* will be used when timer profiling is enabled via the "TimerFirings" log
* module.
*
* @param aFunc The function to invoke
* @param aClosure An opaque pointer to pass to that function
* @param aDelay The millisecond interval
* @param aType Timer type per TYPE* consts defined above
* @param aName The timer's name
*/
[noscript] void initWithNamedFuncCallback(in nsTimerCallbackFunc aCallback,
in voidPtr aClosure,
in unsigned long aDelay,
in unsigned long aType,
in string aName);
/**
* Like initWithNamedFuncCallback, but instead of a timer name it takes a
* callback that will provide a name when the timer fires.
*
* @param aFunc The function to invoke
* @param aClosure An opaque pointer to pass to that function
* @param aDelay The millisecond interval
* @param aType Timer type per TYPE* consts defined above
* @param aNameCallback The callback function
*/
[noscript] void initWithNameableFuncCallback(
in nsTimerCallbackFunc aCallback,
in voidPtr aClosure,
in unsigned long aDelay,
in unsigned long aType,
in nsTimerNameCallbackFunc aNameCallback);
/**
* The millisecond delay of the timeout.
*

View File

@ -21,6 +21,15 @@
using namespace mozilla::tasktracer;
#endif
#ifdef XP_WIN
#include <process.h>
#ifndef getpid
#define getpid _getpid
#endif
#else
#include <unistd.h>
#endif
using mozilla::Atomic;
using mozilla::LogLevel;
using mozilla::TimeDuration;
@ -29,6 +38,7 @@ using mozilla::TimeStamp;
static Atomic<int32_t> gGenerator;
static TimerThread* gThread = nullptr;
// This module prints info about the precision of timers.
PRLogModuleInfo*
GetTimerLog()
{
@ -39,6 +49,44 @@ GetTimerLog()
return sLog;
}
// This module prints info about which timers are firing, which is useful for
// wakeups for the purposes of power profiling. Set the following environment
// variable before starting the browser.
//
// NSPR_LOG_MODULES=TimerFirings:4
//
// Then a line will be printed for every timer that fires. The name used for a
// |CallbackType::Function| timer depends on the circumstances.
//
// - If it was explicitly named (e.g. it was initialized with
// InitWithNamedFuncCallback()) then that explicit name will be shown.
//
// - Otherwise, if we are on a platform that supports function name lookup
// (currently only Mac) then the looked-up name will be shown with a
// "[from dladdr]" annotation.
//
// - Otherwise, no name will be printed. If many timers hit this case then
// you'll need to re-run the workload on a Mac to find out which timers they
// are, and then give them explicit names.
//
// If you redirect this output to a file called "out", you can then
// post-process it with a command something like the following.
//
// cat out | grep timer | sort | uniq -c | sort -r -n
//
// This will show how often each unique line appears, with the most common ones
// first.
//
PRLogModuleInfo*
GetTimerFiringsLog()
{
static PRLogModuleInfo* sLog;
if (!sLog) {
sLog = PR_NewLogModule("TimerFirings");
}
return sLog;
}
#include <math.h>
double nsTimerImpl::sDeltaSumSquared = 0;
@ -128,6 +176,7 @@ nsTimerImpl::Release(void)
nsTimerImpl::nsTimerImpl() :
mClosure(nullptr),
mName(nsTimerImpl::Nothing),
mCallbackType(CallbackType::Unknown),
mFiring(false),
mArmed(false),
@ -216,11 +265,12 @@ nsTimerImpl::InitCommon(uint32_t aDelay, uint32_t aType)
return gThread->AddTimer(this);
}
NS_IMETHODIMP
nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType)
nsresult
nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType,
Name aName)
{
if (NS_WARN_IF(!aFunc)) {
return NS_ERROR_INVALID_ARG;
@ -230,10 +280,43 @@ nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
mCallbackType = CallbackType::Function;
mCallback.c = aFunc;
mClosure = aClosure;
mName = aName;
return InitCommon(aDelay, aType);
}
NS_IMETHODIMP
nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType)
{
Name name(nsTimerImpl::Nothing);
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
}
NS_IMETHODIMP
nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType,
const char* aNameString)
{
Name name(aNameString);
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
}
NS_IMETHODIMP
nsTimerImpl::InitWithNameableFuncCallback(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType,
nsTimerNameCallbackFunc aNameFunc)
{
Name name(aNameFunc);
return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
}
NS_IMETHODIMP
nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
uint32_t aDelay,
@ -432,6 +515,10 @@ nsTimerImpl::Fire()
}
ReleaseCallback();
if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
LogFiring(callbackType, callback);
}
switch (callbackType) {
case CallbackType::Function:
callback.c(this, mClosure);
@ -484,6 +571,108 @@ nsTimerImpl::Fire()
}
}
#if defined(XP_MACOSX)
#include <cxxabi.h>
#include <dlfcn.h>
#endif
// See the big comment above GetTimerFiringsLog() to understand this code.
void
nsTimerImpl::LogFiring(CallbackType aCallbackType, CallbackUnion aCallback)
{
const char* typeStr;
switch (mType) {
case TYPE_ONE_SHOT: typeStr = "ONE_SHOT"; break;
case TYPE_REPEATING_SLACK: typeStr = "SLACK "; break;
case TYPE_REPEATING_PRECISE: /* fall through */
case TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
default: MOZ_CRASH("bad type");
}
switch (aCallbackType) {
case CallbackType::Function: {
bool needToFreeName = false;
const char* annotation = "";
const char* name;
static const size_t buflen = 1024;
char buf[buflen];
if (mName.is<NameString>()) {
name = mName.as<NameString>();
} else if (mName.is<NameFunc>()) {
mName.as<NameFunc>()(this, mClosure, buf, buflen);
name = buf;
} else {
MOZ_ASSERT(mName.is<NameNothing>());
#if defined(XP_MACOSX)
annotation = "[from dladdr] ";
Dl_info info;
if (dladdr(reinterpret_cast<void*>(aCallback.c), &info) == 0) {
name = "???[dladdr: failed]";
} else if (!info.dli_sname) {
name = "???[dladdr: no matching symbol]";
} else {
int status;
name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
if (status == 0) {
// Success. Because we didn't pass in a buffer to __cxa_demangle it
// allocates its own one with malloc() which we must free() later.
MOZ_ASSERT(name);
needToFreeName = true;
} else if (status == -1) {
name = "???[__cxa_demangle: OOM]";
} else if (status == -2) {
name = "???[__cxa_demangle: invalid mangled name]";
} else if (status == -3) {
name = "???[__cxa_demangle: invalid argument]";
} else {
name = "???[__cxa_demangle: unexpected status value]";
}
}
#else
name = "???[dladdr: unavailable/doesn't work on this platform]";
#endif
}
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] fn timer (%s %5d ms): %s%s\n",
getpid(), typeStr, mDelay, annotation, name));
if (needToFreeName) {
free(const_cast<char*>(name));
}
break;
}
case CallbackType::Interface: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] iface timer (%s %5d ms): %p\n",
getpid(), typeStr, mDelay, aCallback.i));
break;
}
case CallbackType::Observer: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] obs timer (%s %5d ms): %p\n",
getpid(), typeStr, mDelay, aCallback.o));
break;
}
case CallbackType::Unknown:
default: {
MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
("[%d] ??? timer (%s, %5d ms)\n",
getpid(), typeStr, mDelay));
break;
}
}
}
void
nsTimerImpl::SetDelayInternal(uint32_t aDelay)
{
@ -511,6 +700,8 @@ nsTimerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
return aMallocSizeOf(this);
}
/* static */ const nsTimerImpl::NameNothing nsTimerImpl::Nothing = 0;
#ifdef MOZ_TASK_TRACER
void
nsTimerImpl::GetTLSTraceInfo()

View File

@ -13,9 +13,10 @@
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
#include "mozilla/Logging.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
#include "mozilla/Variant.h"
#ifdef MOZ_TASK_TRACER
#include "TracedTaskCommon.h"
@ -122,6 +123,28 @@ private:
nsIObserver* MOZ_OWNING_REF o;
} mCallback;
void LogFiring(CallbackType aCallbackType, CallbackUnion aCallbackUnion);
// |Name| is a tagged union type representing one of (a) nothing, (b) a
// string, or (c) a function. mozilla::Variant doesn't naturally handle the
// "nothing" case, so we define a dummy type and value (which is unused and
// so the exact value doesn't matter) for it.
typedef const int NameNothing;
typedef const char* NameString;
typedef nsTimerNameCallbackFunc NameFunc;
typedef mozilla::Variant<NameNothing, NameString, NameFunc> Name;
static const NameNothing Nothing;
nsresult InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
void* aClosure,
uint32_t aDelay,
uint32_t aType,
Name aName);
// This is set by Init. It records the name (if there is one) for the timer,
// for use when logging timer firings.
Name mName;
// Some callers expect to be able to access the callback while the
// timer is firing.
nsCOMPtr<nsITimerCallback> mTimerCallbackWhileFiring;