Line data Source code
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 "nsTimerImpl.h"
8 : #include "TimerThread.h"
9 : #include "nsAutoPtr.h"
10 : #include "nsThreadManager.h"
11 : #include "nsThreadUtils.h"
12 : #include "pratom.h"
13 : #include "GeckoProfiler.h"
14 : #include "mozilla/Atomics.h"
15 : #include "mozilla/IntegerPrintfMacros.h"
16 : #include "mozilla/Logging.h"
17 : #ifdef MOZ_TASK_TRACER
18 : #include "GeckoTaskTracerImpl.h"
19 : using namespace mozilla::tasktracer;
20 : #endif
21 :
22 : #ifdef XP_WIN
23 : #include <process.h>
24 : #ifndef getpid
25 : #define getpid _getpid
26 : #endif
27 : #else
28 : #include <unistd.h>
29 : #endif
30 :
31 : using mozilla::Atomic;
32 : using mozilla::LogLevel;
33 : using mozilla::TimeDuration;
34 : using mozilla::TimeStamp;
35 :
36 : static TimerThread* gThread = nullptr;
37 :
38 : // This module prints info about the precision of timers.
39 : static mozilla::LazyLogModule sTimerLog("nsTimerImpl");
40 :
41 : mozilla::LogModule*
42 1015 : GetTimerLog()
43 : {
44 1015 : return sTimerLog;
45 : }
46 :
47 : TimeStamp
48 159 : NS_GetTimerDeadlineHintOnCurrentThread(TimeStamp aDefault, uint32_t aSearchBound)
49 : {
50 : return gThread
51 159 : ? gThread->FindNextFireTimeForCurrentThread(aDefault, aSearchBound)
52 318 : : TimeStamp();
53 : }
54 :
55 : // This module prints info about which timers are firing, which is useful for
56 : // wakeups for the purposes of power profiling. Set the following environment
57 : // variable before starting the browser.
58 : //
59 : // MOZ_LOG=TimerFirings:4
60 : //
61 : // Then a line will be printed for every timer that fires. The name used for a
62 : // |Callback::Type::Function| timer depends on the circumstances.
63 : //
64 : // - If it was explicitly named (e.g. it was initialized with
65 : // InitWithNamedFuncCallback()) then that explicit name will be shown.
66 : //
67 : // - Otherwise, if we are on a platform that supports function name lookup
68 : // (Mac or Linux) then the looked-up name will be shown with a
69 : // "[from dladdr]" annotation. On Mac the looked-up name will be immediately
70 : // useful. On Linux it'll need post-processing with
71 : // tools/rb/fix_linux_stack.py.
72 : //
73 : // - Otherwise, no name will be printed. If many timers hit this case then
74 : // you'll need to re-run the workload on a Mac to find out which timers they
75 : // are, and then give them explicit names.
76 : //
77 : // If you redirect this output to a file called "out", you can then
78 : // post-process it with a command something like the following.
79 : //
80 : // cat out | grep timer | sort | uniq -c | sort -r -n
81 : //
82 : // This will show how often each unique line appears, with the most common ones
83 : // first.
84 : //
85 : // More detailed docs are here:
86 : // https://developer.mozilla.org/en-US/docs/Mozilla/Performance/TimerFirings_logging
87 : //
88 : static mozilla::LazyLogModule sTimerFiringsLog("TimerFirings");
89 :
90 : mozilla::LogModule*
91 73 : GetTimerFiringsLog()
92 : {
93 73 : return sTimerFiringsLog;
94 : }
95 :
96 : #include <math.h>
97 :
98 : double nsTimerImpl::sDeltaSumSquared = 0;
99 : double nsTimerImpl::sDeltaSum = 0;
100 : double nsTimerImpl::sDeltaNum = 0;
101 :
102 : static void
103 0 : myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
104 : double* meanResult, double* stdDevResult)
105 : {
106 0 : double mean = 0.0, var = 0.0, stdDev = 0.0;
107 0 : if (n > 0.0 && sumOfValues >= 0) {
108 0 : mean = sumOfValues / n;
109 0 : double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
110 0 : if (temp < 0.0 || n <= 1) {
111 0 : var = 0.0;
112 : } else {
113 0 : var = temp / (n * (n - 1));
114 : }
115 : // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
116 0 : stdDev = var != 0.0 ? sqrt(var) : 0.0;
117 : }
118 0 : *meanResult = mean;
119 0 : *stdDevResult = stdDev;
120 0 : }
121 :
122 654 : NS_IMPL_QUERY_INTERFACE(nsTimer, nsITimer)
123 1060 : NS_IMPL_ADDREF(nsTimer)
124 :
125 : NS_IMETHODIMP_(MozExternalRefCountType)
126 771 : nsTimer::Release(void)
127 : {
128 771 : nsrefcnt count = --mRefCnt;
129 771 : NS_LOG_RELEASE(this, count, "nsTimer");
130 :
131 771 : if (count == 1) {
132 83 : if (!mImpl->CancelCheckIfFiring()) {
133 : // Last ref, in nsTimerImpl::mITimer. Make sure the cycle is broken.
134 : // (when Cancel fails, nsTimerImpl::Fire is in progress, which has grabbed
135 : // another ref to the nsITimer since we checked the value of mRefCnt
136 : // above)
137 : // If there is a nsTimerEvent in a queue for this timer, the nsTimer will
138 : // live until that event pops, otherwise the nsTimerImpl will go away and
139 : // the nsTimer along with it.
140 83 : mImpl = nullptr;
141 : }
142 688 : } else if (count == 0) {
143 83 : delete this;
144 : }
145 :
146 771 : return count;
147 : }
148 :
149 218 : nsTimerImpl::nsTimerImpl(nsITimer* aTimer) :
150 : mHolder(nullptr),
151 : mGeneration(0),
152 : mITimer(aTimer),
153 218 : mMutex("nsTimerImpl::mMutex")
154 : {
155 : // XXXbsmedberg: shouldn't this be in Init()?
156 218 : mEventTarget = GetCurrentThreadEventTarget();
157 218 : }
158 :
159 : //static
160 : nsresult
161 3 : nsTimerImpl::Startup()
162 : {
163 : nsresult rv;
164 :
165 3 : gThread = new TimerThread();
166 :
167 3 : NS_ADDREF(gThread);
168 3 : rv = gThread->InitLocks();
169 :
170 3 : if (NS_FAILED(rv)) {
171 0 : NS_RELEASE(gThread);
172 : }
173 :
174 3 : return rv;
175 : }
176 :
177 : void
178 0 : nsTimerImpl::Shutdown()
179 : {
180 0 : if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
181 0 : double mean = 0, stddev = 0;
182 0 : myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev);
183 :
184 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
185 : ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n",
186 : sDeltaNum, sDeltaSum, sDeltaSumSquared));
187 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
188 : ("mean: %fms, stddev: %fms\n", mean, stddev));
189 : }
190 :
191 0 : if (!gThread) {
192 0 : return;
193 : }
194 :
195 0 : gThread->Shutdown();
196 0 : NS_RELEASE(gThread);
197 : }
198 :
199 : nsresult
200 208 : nsTimerImpl::InitCommon(uint32_t aDelayMS, uint32_t aType,
201 : Callback&& aNewCallback)
202 : {
203 416 : return InitCommon(TimeDuration::FromMilliseconds(aDelayMS),
204 624 : aType, Move(aNewCallback));
205 : }
206 :
207 :
208 : nsresult
209 288 : nsTimerImpl::InitCommon(const TimeDuration& aDelay, uint32_t aType,
210 : Callback&& newCallback)
211 : {
212 288 : mMutex.AssertCurrentThreadOwns();
213 :
214 288 : if (NS_WARN_IF(!gThread)) {
215 0 : return NS_ERROR_NOT_INITIALIZED;
216 : }
217 :
218 288 : if (!mEventTarget) {
219 0 : NS_ERROR("mEventTarget is NULL");
220 0 : return NS_ERROR_NOT_INITIALIZED;
221 : }
222 :
223 288 : gThread->RemoveTimer(this);
224 288 : mCallback.swap(newCallback);
225 288 : ++mGeneration;
226 :
227 288 : mType = (uint8_t)aType;
228 288 : mDelay = aDelay;
229 288 : mTimeout = TimeStamp::Now() + mDelay;
230 :
231 288 : return gThread->AddTimer(this);
232 : }
233 :
234 : nsresult
235 199 : nsTimerImpl::InitWithFuncCallbackCommon(nsTimerCallbackFunc aFunc,
236 : void* aClosure,
237 : uint32_t aDelay,
238 : uint32_t aType,
239 : const Callback::Name& aName)
240 : {
241 199 : if (NS_WARN_IF(!aFunc)) {
242 0 : return NS_ERROR_INVALID_ARG;
243 : }
244 :
245 398 : Callback cb; // Goes out of scope after the unlock, prevents deadlock
246 199 : cb.mType = Callback::Type::Function;
247 199 : cb.mCallback.c = aFunc;
248 199 : cb.mClosure = aClosure;
249 199 : cb.mName = aName;
250 :
251 398 : MutexAutoLock lock(mMutex);
252 199 : return InitCommon(aDelay, aType, mozilla::Move(cb));
253 : }
254 :
255 : nsresult
256 199 : nsTimerImpl::InitWithNamedFuncCallback(nsTimerCallbackFunc aFunc,
257 : void* aClosure,
258 : uint32_t aDelay,
259 : uint32_t aType,
260 : const char* aNameString)
261 : {
262 398 : Callback::Name name(aNameString);
263 398 : return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
264 : }
265 :
266 : nsresult
267 0 : nsTimerImpl::InitWithNameableFuncCallback(nsTimerCallbackFunc aFunc,
268 : void* aClosure,
269 : uint32_t aDelay,
270 : uint32_t aType,
271 : nsTimerNameCallbackFunc aNameFunc)
272 : {
273 0 : Callback::Name name(aNameFunc);
274 0 : return InitWithFuncCallbackCommon(aFunc, aClosure, aDelay, aType, name);
275 : }
276 :
277 : nsresult
278 70 : nsTimerImpl::InitWithCallback(nsITimerCallback* aCallback,
279 : uint32_t aDelay,
280 : uint32_t aType)
281 : {
282 : return InitHighResolutionWithCallback(aCallback,
283 140 : TimeDuration::FromMilliseconds(aDelay),
284 140 : aType);
285 : }
286 :
287 : nsresult
288 80 : nsTimerImpl::InitHighResolutionWithCallback(nsITimerCallback* aCallback,
289 : const TimeDuration& aDelay,
290 : uint32_t aType)
291 : {
292 80 : if (NS_WARN_IF(!aCallback)) {
293 0 : return NS_ERROR_INVALID_ARG;
294 : }
295 :
296 160 : Callback cb; // Goes out of scope after the unlock, prevents deadlock
297 80 : cb.mType = Callback::Type::Interface;
298 80 : cb.mCallback.i = aCallback;
299 80 : NS_ADDREF(cb.mCallback.i);
300 :
301 160 : MutexAutoLock lock(mMutex);
302 80 : return InitCommon(aDelay, aType, mozilla::Move(cb));
303 : }
304 :
305 : nsresult
306 9 : nsTimerImpl::Init(nsIObserver* aObserver, uint32_t aDelay, uint32_t aType)
307 : {
308 9 : if (NS_WARN_IF(!aObserver)) {
309 0 : return NS_ERROR_INVALID_ARG;
310 : }
311 :
312 18 : Callback cb; // Goes out of scope after the unlock, prevents deadlock
313 9 : cb.mType = Callback::Type::Observer;
314 9 : cb.mCallback.o = aObserver;
315 9 : NS_ADDREF(cb.mCallback.o);
316 :
317 18 : MutexAutoLock lock(mMutex);
318 9 : return InitCommon(aDelay, aType, mozilla::Move(cb));
319 : }
320 :
321 : bool
322 1426 : nsTimerImpl::CancelCheckIfFiring()
323 : {
324 2852 : Callback cb;
325 :
326 2852 : MutexAutoLock lock(mMutex);
327 :
328 1426 : if (gThread) {
329 1426 : gThread->RemoveTimer(this);
330 : }
331 :
332 1426 : cb.swap(mCallback);
333 1426 : ++mGeneration;
334 :
335 1426 : if (mCallbackDuringFire.mType != Callback::Type::Unknown) {
336 45 : return true;
337 : }
338 1381 : return false;
339 : }
340 :
341 : nsresult
342 1343 : nsTimerImpl::Cancel()
343 : {
344 1343 : (void)CancelCheckIfFiring();
345 1343 : return NS_OK;
346 : }
347 :
348 : nsresult
349 0 : nsTimerImpl::SetDelay(uint32_t aDelay)
350 : {
351 0 : MutexAutoLock lock(mMutex);
352 0 : if (GetCallback().mType == Callback::Type::Unknown && !IsRepeating()) {
353 : // This may happen if someone tries to re-use a one-shot timer
354 : // by re-setting delay instead of reinitializing the timer.
355 0 : NS_ERROR("nsITimer->SetDelay() called when the "
356 : "one-shot timer is not set up.");
357 0 : return NS_ERROR_NOT_INITIALIZED;
358 : }
359 :
360 0 : bool reAdd = false;
361 0 : if (gThread) {
362 0 : reAdd = NS_SUCCEEDED(gThread->RemoveTimer(this));
363 : }
364 :
365 0 : mDelay = TimeDuration::FromMilliseconds(aDelay);
366 0 : mTimeout = TimeStamp::Now() + mDelay;
367 :
368 0 : if (reAdd) {
369 0 : gThread->AddTimer(this);
370 : }
371 :
372 0 : return NS_OK;
373 : }
374 :
375 : nsresult
376 2 : nsTimerImpl::GetDelay(uint32_t* aDelay)
377 : {
378 4 : MutexAutoLock lock(mMutex);
379 2 : *aDelay = mDelay.ToMilliseconds();
380 4 : return NS_OK;
381 : }
382 :
383 : nsresult
384 0 : nsTimerImpl::SetType(uint32_t aType)
385 : {
386 0 : MutexAutoLock lock(mMutex);
387 0 : mType = (uint8_t)aType;
388 : // XXX if this is called, we should change the actual type.. this could effect
389 : // repeating timers. we need to ensure in Fire() that if mType has changed
390 : // during the callback that we don't end up with the timer in the queue twice.
391 0 : return NS_OK;
392 : }
393 :
394 : nsresult
395 0 : nsTimerImpl::GetType(uint32_t* aType)
396 : {
397 0 : MutexAutoLock lock(mMutex);
398 0 : *aType = mType;
399 0 : return NS_OK;
400 : }
401 :
402 :
403 : nsresult
404 0 : nsTimerImpl::GetClosure(void** aClosure)
405 : {
406 0 : MutexAutoLock lock(mMutex);
407 0 : *aClosure = GetCallback().mClosure;
408 0 : return NS_OK;
409 : }
410 :
411 :
412 : nsresult
413 0 : nsTimerImpl::GetCallback(nsITimerCallback** aCallback)
414 : {
415 0 : MutexAutoLock lock(mMutex);
416 0 : if (GetCallback().mType == Callback::Type::Interface) {
417 0 : NS_IF_ADDREF(*aCallback = GetCallback().mCallback.i);
418 : } else {
419 0 : *aCallback = nullptr;
420 : }
421 :
422 0 : return NS_OK;
423 : }
424 :
425 :
426 : nsresult
427 0 : nsTimerImpl::GetTarget(nsIEventTarget** aTarget)
428 : {
429 0 : MutexAutoLock lock(mMutex);
430 0 : NS_IF_ADDREF(*aTarget = mEventTarget);
431 0 : return NS_OK;
432 : }
433 :
434 :
435 : nsresult
436 195 : nsTimerImpl::SetTarget(nsIEventTarget* aTarget)
437 : {
438 390 : MutexAutoLock lock(mMutex);
439 195 : if (NS_WARN_IF(mCallback.mType != Callback::Type::Unknown)) {
440 0 : return NS_ERROR_ALREADY_INITIALIZED;
441 : }
442 :
443 195 : if (aTarget) {
444 192 : mEventTarget = aTarget;
445 : } else {
446 3 : mEventTarget = GetCurrentThreadEventTarget();
447 : }
448 195 : return NS_OK;
449 : }
450 :
451 : nsresult
452 2 : nsTimerImpl::GetAllowedEarlyFiringMicroseconds(uint32_t* aValueOut)
453 : {
454 2 : *aValueOut = gThread ? gThread->AllowedEarlyFiringMicroseconds() : 0;
455 2 : return NS_OK;
456 : }
457 :
458 : void
459 78 : nsTimerImpl::Fire(int32_t aGeneration)
460 : {
461 : uint8_t oldType;
462 : uint32_t oldDelay;
463 78 : TimeStamp oldTimeout;
464 151 : nsCOMPtr<nsITimer> kungFuDeathGrip;
465 :
466 : {
467 : // Don't fire callbacks or fiddle with refcounts when the mutex is locked.
468 : // If some other thread Cancels/Inits after this, they're just too late.
469 151 : MutexAutoLock lock(mMutex);
470 78 : if (aGeneration != mGeneration) {
471 5 : return;
472 : }
473 :
474 73 : mCallbackDuringFire.swap(mCallback);
475 73 : oldType = mType;
476 73 : oldDelay = mDelay.ToMilliseconds();
477 73 : oldTimeout = mTimeout;
478 : // Ensure that the nsITimer does not unhook from the nsTimerImpl during
479 : // Fire; this will cause null pointer crashes if the user of the timer drops
480 : // its reference, and then uses the nsITimer* passed in the callback.
481 73 : kungFuDeathGrip = mITimer;
482 : }
483 :
484 146 : AUTO_PROFILER_LABEL("nsTimerImpl::Fire", OTHER);
485 :
486 73 : TimeStamp now = TimeStamp::Now();
487 73 : if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
488 0 : TimeDuration delta = now - oldTimeout;
489 0 : int32_t d = delta.ToMilliseconds(); // delta in ms
490 0 : sDeltaSum += abs(d);
491 0 : sDeltaSumSquared += double(d) * double(d);
492 0 : sDeltaNum++;
493 :
494 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
495 : ("[this=%p] expected delay time %4ums\n", this, oldDelay));
496 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
497 : ("[this=%p] actual delay time %4dms\n", this, oldDelay + d));
498 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
499 : ("[this=%p] (mType is %d) -------\n", this, oldType));
500 0 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
501 : ("[this=%p] delta %4dms\n", this, d));
502 : }
503 :
504 73 : if (MOZ_LOG_TEST(GetTimerFiringsLog(), LogLevel::Debug)) {
505 0 : LogFiring(mCallbackDuringFire, oldType, oldDelay);
506 : }
507 :
508 73 : switch (mCallbackDuringFire.mType) {
509 : case Callback::Type::Function:
510 58 : mCallbackDuringFire.mCallback.c(mITimer, mCallbackDuringFire.mClosure);
511 58 : break;
512 : case Callback::Type::Interface:
513 15 : mCallbackDuringFire.mCallback.i->Notify(mITimer);
514 15 : break;
515 : case Callback::Type::Observer:
516 0 : mCallbackDuringFire.mCallback.o->Observe(mITimer, NS_TIMER_CALLBACK_TOPIC,
517 0 : nullptr);
518 0 : break;
519 : default:
520 : ;
521 : }
522 :
523 146 : Callback trash; // Swap into here to dispose of callback after the unlock
524 146 : MutexAutoLock lock(mMutex);
525 73 : if (aGeneration == mGeneration && IsRepeating()) {
526 : // Repeating timer has not been re-init or canceled; reschedule
527 18 : mCallbackDuringFire.swap(mCallback);
528 18 : if (IsSlack()) {
529 18 : mTimeout = TimeStamp::Now() + mDelay;
530 : } else {
531 0 : mTimeout = mTimeout + mDelay;
532 : }
533 18 : if (gThread) {
534 18 : gThread->AddTimer(this);
535 : }
536 : }
537 :
538 73 : mCallbackDuringFire.swap(trash);
539 :
540 73 : MOZ_LOG(GetTimerLog(), LogLevel::Debug,
541 : ("[this=%p] Took %fms to fire timer callback\n",
542 : this, (TimeStamp::Now() - now).ToMilliseconds()));
543 : }
544 :
545 : #if defined(HAVE_DLADDR) && defined(HAVE___CXA_DEMANGLE)
546 : #define USE_DLADDR 1
547 : #endif
548 :
549 : #ifdef USE_DLADDR
550 : #include <cxxabi.h>
551 : #include <dlfcn.h>
552 : #endif
553 :
554 : // See the big comment above GetTimerFiringsLog() to understand this code.
555 : void
556 0 : nsTimerImpl::LogFiring(const Callback& aCallback, uint8_t aType, uint32_t aDelay)
557 : {
558 : const char* typeStr;
559 0 : switch (aType) {
560 0 : case nsITimer::TYPE_ONE_SHOT: typeStr = "ONE_SHOT "; break;
561 0 : case nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY: typeStr = "ONE_LOW "; break;
562 0 : case nsITimer::TYPE_REPEATING_SLACK: typeStr = "SLACK "; break;
563 0 : case nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY: typeStr = "SLACK_LOW "; break;
564 : case nsITimer::TYPE_REPEATING_PRECISE: /* fall through */
565 0 : case nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP: typeStr = "PRECISE "; break;
566 0 : default: MOZ_CRASH("bad type");
567 : }
568 :
569 0 : switch (aCallback.mType) {
570 : case Callback::Type::Function: {
571 0 : bool needToFreeName = false;
572 0 : const char* annotation = "";
573 : const char* name;
574 : static const size_t buflen = 1024;
575 : char buf[buflen];
576 :
577 0 : if (aCallback.mName.is<Callback::NameString>()) {
578 0 : name = aCallback.mName.as<Callback::NameString>();
579 :
580 0 : } else if (aCallback.mName.is<Callback::NameFunc>()) {
581 0 : aCallback.mName.as<Callback::NameFunc>()(
582 0 : mITimer, /* aAnonymize = */ false, aCallback.mClosure, buf, buflen);
583 0 : name = buf;
584 :
585 : } else {
586 0 : MOZ_ASSERT(aCallback.mName.is<Callback::NameNothing>());
587 : #ifdef USE_DLADDR
588 0 : annotation = "[from dladdr] ";
589 :
590 : Dl_info info;
591 0 : void* addr = reinterpret_cast<void*>(aCallback.mCallback.c);
592 0 : if (dladdr(addr, &info) == 0) {
593 0 : name = "???[dladdr: failed]";
594 :
595 0 : } else if (info.dli_sname) {
596 : int status;
597 0 : name = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
598 0 : if (status == 0) {
599 : // Success. Because we didn't pass in a buffer to __cxa_demangle it
600 : // allocates its own one with malloc() which we must free() later.
601 0 : MOZ_ASSERT(name);
602 0 : needToFreeName = true;
603 0 : } else if (status == -1) {
604 0 : name = "???[__cxa_demangle: OOM]";
605 0 : } else if (status == -2) {
606 0 : name = "???[__cxa_demangle: invalid mangled name]";
607 0 : } else if (status == -3) {
608 0 : name = "???[__cxa_demangle: invalid argument]";
609 : } else {
610 0 : name = "???[__cxa_demangle: unexpected status value]";
611 : }
612 :
613 0 : } else if (info.dli_fname) {
614 : // The "#0: " prefix is necessary for fix_linux_stack.py to interpret
615 : // this string as something to convert.
616 0 : snprintf(buf, buflen, "#0: ???[%s +0x%" PRIxPTR "]\n",
617 0 : info.dli_fname, uintptr_t(addr) - uintptr_t(info.dli_fbase));
618 0 : name = buf;
619 :
620 : } else {
621 0 : name = "???[dladdr: no symbol or shared object obtained]";
622 : }
623 : #else
624 : name = "???[dladdr is unimplemented or doesn't work well on this OS]";
625 : #endif
626 : }
627 :
628 0 : MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
629 : ("[%d] fn timer (%s %5d ms): %s%s\n",
630 : getpid(), typeStr, aDelay, annotation, name));
631 :
632 0 : if (needToFreeName) {
633 0 : free(const_cast<char*>(name));
634 : }
635 :
636 0 : break;
637 : }
638 :
639 : case Callback::Type::Interface: {
640 0 : MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
641 : ("[%d] iface timer (%s %5d ms): %p\n",
642 : getpid(), typeStr, aDelay, aCallback.mCallback.i));
643 0 : break;
644 : }
645 :
646 : case Callback::Type::Observer: {
647 0 : MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
648 : ("[%d] obs timer (%s %5d ms): %p\n",
649 : getpid(), typeStr, aDelay, aCallback.mCallback.o));
650 0 : break;
651 : }
652 :
653 : case Callback::Type::Unknown:
654 : default: {
655 0 : MOZ_LOG(GetTimerFiringsLog(), LogLevel::Debug,
656 : ("[%d] ??? timer (%s, %5d ms)\n",
657 : getpid(), typeStr, aDelay));
658 0 : break;
659 : }
660 : }
661 0 : }
662 :
663 : void
664 85 : nsTimerImpl::GetName(nsACString& aName)
665 : {
666 170 : MutexAutoLock lock(mMutex);
667 85 : Callback& cb(GetCallback());
668 85 : switch (cb.mType) {
669 : case Callback::Type::Function:
670 63 : if (cb.mName.is<Callback::NameString>()) {
671 63 : aName.Assign(cb.mName.as<Callback::NameString>());
672 0 : } else if (cb.mName.is<Callback::NameFunc>()) {
673 : static const size_t buflen = 1024;
674 : char buf[buflen];
675 0 : cb.mName.as<Callback::NameFunc>()(
676 0 : mITimer, /* aAnonymize = */ true, cb.mClosure, buf, buflen);
677 0 : aName.Assign(buf);
678 : } else {
679 0 : MOZ_ASSERT(cb.mName.is<Callback::NameNothing>());
680 0 : aName.AssignLiteral("Anonymous_callback_timer");
681 : }
682 63 : break;
683 :
684 : case Callback::Type::Interface:
685 34 : if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.i)) {
686 8 : named->GetName(aName);
687 : } else {
688 9 : aName.AssignLiteral("Anonymous_interface_timer");
689 : }
690 17 : break;
691 :
692 : case Callback::Type::Observer:
693 0 : if (nsCOMPtr<nsINamed> named = do_QueryInterface(cb.mCallback.o)) {
694 0 : named->GetName(aName);
695 : } else {
696 0 : aName.AssignLiteral("Anonymous_observer_timer");
697 : }
698 0 : break;
699 :
700 : case Callback::Type::Unknown:
701 5 : aName.AssignLiteral("Canceled_timer");
702 5 : break;
703 : }
704 85 : }
705 :
706 : void
707 570 : nsTimerImpl::SetHolder(nsTimerImplHolder* aHolder)
708 : {
709 570 : mHolder = aHolder;
710 570 : }
711 :
712 166 : nsTimer::~nsTimer()
713 : {
714 249 : }
715 :
716 : size_t
717 0 : nsTimer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
718 : {
719 0 : return aMallocSizeOf(this);
720 : }
721 :
722 : /* static */
723 9 : const nsTimerImpl::Callback::NameNothing nsTimerImpl::Callback::Nothing = 0;
724 :
725 : #ifdef MOZ_TASK_TRACER
726 : void
727 : nsTimerImpl::GetTLSTraceInfo()
728 : {
729 : mTracedTask.GetTLSTraceInfo();
730 : }
731 :
732 : TracedTaskCommon
733 : nsTimerImpl::GetTracedTask()
734 : {
735 : return mTracedTask;
736 : }
737 :
738 : #endif
|