1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsTimelineService.h"
52 static PRFileDesc
*timelineFD
= PR_STDERR
;
53 static PRBool gTimelineDisabled
= PR_TRUE
;
55 // Notes about threading:
56 // We avoid locks as we always use thread-local-storage.
57 // This means every other thread has its own private copy of
58 // data, and this thread can't re-enter (as our implemenation
59 // doesn't call back out anywhere). Thus, we can avoid locks!
61 static const PRUintn BAD_TLS_INDEX
= (PRUintn
) -1;
62 static PRUintn gTLSIndex
= BAD_TLS_INDEX
;
64 class TimelineThreadData
{
66 TimelineThreadData() : initTime(0), indent(0),
67 disabled(PR_TRUE
), timers(nsnull
) {}
68 ~TimelineThreadData() {if (timers
) PL_HashTableDestroy(timers
);}
75 /* Implementation file */
76 NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimelineService
, nsITimelineService
)
79 * Timer structure stored in a hash table to keep track of named
82 class nsTimelineServiceTimer
{
84 nsTimelineServiceTimer();
85 ~nsTimelineServiceTimer();
89 * Caller passes in "now" rather than having us calculate it so
90 * that we can avoid including timer overhead in the time being
93 void stop(PRTime now
);
96 PRTime
getAccum(PRTime now
);
102 PRThread
*mOwnerThread
; // only used for asserts - could be #if MOZ_DEBUG
105 #define TIMER_CHECK_OWNER() \
106 NS_ABORT_IF_FALSE(PR_GetCurrentThread() == mOwnerThread, \
107 "Timer used by non-owning thread")
110 nsTimelineServiceTimer::nsTimelineServiceTimer()
111 : mAccum(LL_ZERO
), mStart(LL_ZERO
), mRunning(0),
112 mOwnerThread(PR_GetCurrentThread())
116 nsTimelineServiceTimer::~nsTimelineServiceTimer()
120 void nsTimelineServiceTimer::start()
129 void nsTimelineServiceTimer::stop(PRTime now
)
135 LL_SUB(delta
, now
, mStart
);
136 LL_ADD(accum
, mAccum
, delta
);
141 void nsTimelineServiceTimer::reset()
148 PRTime
nsTimelineServiceTimer::getAccum()
157 LL_SUB(delta
, PR_Now(), mStart
);
158 LL_ADD(accum
, mAccum
, delta
);
163 PRTime
nsTimelineServiceTimer::getAccum(PRTime now
)
172 LL_SUB(delta
, now
, mStart
);
173 LL_ADD(accum
, mAccum
, delta
);
178 static TimelineThreadData
*GetThisThreadData()
180 NS_ABORT_IF_FALSE(gTLSIndex
!=BAD_TLS_INDEX
, "Our TLS not initialized");
181 TimelineThreadData
*new_data
= nsnull
;
182 TimelineThreadData
*data
= (TimelineThreadData
*)PR_GetThreadPrivate(gTLSIndex
);
183 if (data
== nsnull
) {
184 // First request for this thread - allocate it.
185 new_data
= new TimelineThreadData();
190 new_data
->timers
= PL_NewHashTable(100, PL_HashString
, PL_CompareStrings
,
191 PL_CompareValues
, NULL
, NULL
);
192 if (new_data
->timers
==NULL
)
194 new_data
->initTime
= PR_Now();
195 NS_ASSERTION(!gTimelineDisabled
,
196 "Why are we creating new state when disabled?");
197 new_data
->disabled
= PR_FALSE
;
200 PR_SetThreadPrivate(gTLSIndex
, data
);
203 if (new_data
) // eeek - error during creation!
205 NS_ASSERTION(data
, "TimelineService could not get thread-local data");
210 static void ThreadDestruct (void *data
);
211 static PRStatus
TimelineInit(void);
214 void ThreadDestruct( void *data
)
217 delete (TimelineThreadData
*)data
;
221 * PRCallOnceFN that initializes stuff for the timing service.
223 static PRCallOnceType initonce
;
225 PRStatus
TimelineInit(void)
229 const char *timelineEnable
;
234 PRStatus status
= PR_NewThreadPrivateIndex( &gTLSIndex
, ThreadDestruct
);
235 NS_ASSERTION(status
==0, "TimelineService could not allocate TLS storage.");
237 timeStr
= PR_GetEnv("NS_TIMELINE_INIT_TIME");
238 // NS_TIMELINE_INIT_TIME only makes sense for the main thread, so if it
239 // exists, set it there. If not, let normal thread management code take
240 // care of setting the init time.
241 if (timeStr
&& *timeStr
&& (2 == PR_sscanf(timeStr
, "%d.%d", &secs
, &msecs
))) {
242 PRTime
&initTime
= GetThisThreadData()->initTime
;
243 LL_MUL(tmp1
, (PRInt64
)secs
, 1000000);
244 LL_MUL(tmp2
, (PRInt64
)msecs
, 1000);
245 LL_ADD(initTime
, tmp1
, tmp2
);
249 fileName
= PR_GetEnv("NS_TIMELINE_LOG_FILE");
250 if (fileName
&& *fileName
251 && (fd
= PR_Open(fileName
, PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
,
255 "NOTE: due to asynchrony, the indentation that you see does"
256 " not necessarily correspond to nesting in the code.\n\n");
259 // Runtime disable of timeline
260 timelineEnable
= PR_GetEnv("NS_TIMELINE_ENABLE");
261 if (timelineEnable
&& *timelineEnable
)
262 gTimelineDisabled
= PR_FALSE
;
266 static void ParseTime(PRTime tm
, PRInt32
& secs
, PRInt32
& msecs
)
268 PRTime llsecs
, llmsecs
, tmp
;
270 LL_DIV(llsecs
, tm
, 1000000);
271 LL_MOD(tmp
, tm
, 1000000);
272 LL_DIV(llmsecs
, tmp
, 1000);
274 LL_L2I(secs
, llsecs
);
275 LL_L2I(msecs
, llmsecs
);
278 static char *Indent(char *buf
)
280 int &indent
= GetThisThreadData()->indent
;
282 if (amount
> MAXINDENT
) {
288 PR_Write(timelineFD
, "indent underflow!\n", 18);
296 static void PrintTime(PRTime tm
, const char *text
, va_list args
)
299 char pbuf
[550], *pc
, tbuf
[550];
301 ParseTime(tm
, secs
, msecs
);
303 // snprintf/write rather than fprintf because we don't want
304 // messages from multiple threads to garble one another.
306 PR_vsnprintf(pc
, sizeof pbuf
- (pc
- pbuf
), text
, args
);
307 PR_snprintf(tbuf
, sizeof tbuf
, "%05d.%03d (%08p): %s\n",
308 secs
, msecs
, PR_GetCurrentThread(), pbuf
);
309 PR_Write(timelineFD
, tbuf
, strlen(tbuf
));
313 * Make this public if we need it.
315 static nsresult
NS_TimelineMarkV(const char *text
, va_list args
)
319 PR_CallOnce(&initonce
, TimelineInit
);
321 TimelineThreadData
*thread
= GetThisThreadData();
324 LL_SUB(elapsed
, tmp
, thread
->initTime
);
326 PrintTime(elapsed
, text
, args
);
331 PR_IMPLEMENT(nsresult
) NS_TimelineForceMark(const char *text
, ...)
334 va_start(args
, text
);
335 NS_TimelineMarkV(text
, args
);
340 PR_IMPLEMENT(nsresult
) NS_TimelineMark(const char *text
, ...)
343 va_start(args
, text
);
345 PR_CallOnce(&initonce
, TimelineInit
);
347 if (gTimelineDisabled
)
348 return NS_ERROR_NOT_AVAILABLE
;
350 TimelineThreadData
*thread
= GetThisThreadData();
352 if (thread
->disabled
)
353 return NS_ERROR_NOT_AVAILABLE
;
355 NS_TimelineMarkV(text
, args
);
360 PR_IMPLEMENT(nsresult
) NS_TimelineStartTimer(const char *timerName
)
362 PR_CallOnce(&initonce
, TimelineInit
);
364 if (gTimelineDisabled
)
365 return NS_ERROR_NOT_AVAILABLE
;
367 TimelineThreadData
*thread
= GetThisThreadData();
369 if (thread
->timers
== NULL
)
370 return NS_ERROR_FAILURE
;
371 if (thread
->disabled
)
372 return NS_ERROR_NOT_AVAILABLE
;
374 nsTimelineServiceTimer
*timer
375 = (nsTimelineServiceTimer
*)PL_HashTableLookup(thread
->timers
, timerName
);
377 timer
= new nsTimelineServiceTimer
;
379 return NS_ERROR_OUT_OF_MEMORY
;
381 PL_HashTableAdd(thread
->timers
, timerName
, timer
);
387 PR_IMPLEMENT(nsresult
) NS_TimelineStopTimer(const char *timerName
)
389 if (gTimelineDisabled
)
390 return NS_ERROR_NOT_AVAILABLE
;
392 * Strange-looking now/timer->stop() interaction is to avoid
393 * including time spent in TLS and PL_HashTableLookup in the
396 PRTime now
= PR_Now();
398 TimelineThreadData
*thread
= GetThisThreadData();
399 if (thread
->timers
== NULL
)
400 return NS_ERROR_FAILURE
;
401 if (thread
->disabled
)
402 return NS_ERROR_NOT_AVAILABLE
;
403 nsTimelineServiceTimer
*timer
404 = (nsTimelineServiceTimer
*)PL_HashTableLookup(thread
->timers
, timerName
);
406 return NS_ERROR_FAILURE
;
414 PR_IMPLEMENT(nsresult
) NS_TimelineMarkTimer(const char *timerName
, const char *str
)
416 PR_CallOnce(&initonce
, TimelineInit
);
418 if (gTimelineDisabled
)
419 return NS_ERROR_NOT_AVAILABLE
;
421 TimelineThreadData
*thread
= GetThisThreadData();
422 if (thread
->timers
== NULL
)
423 return NS_ERROR_FAILURE
;
424 if (thread
->disabled
)
425 return NS_ERROR_NOT_AVAILABLE
;
426 nsTimelineServiceTimer
*timer
427 = (nsTimelineServiceTimer
*)PL_HashTableLookup(thread
->timers
, timerName
);
429 return NS_ERROR_FAILURE
;
431 PRTime accum
= timer
->getAccum();
435 ParseTime(accum
, sec
, msec
);
437 PR_snprintf(buf
, sizeof buf
, "%s total: %d.%03d",
438 timerName
, sec
, msec
);
440 PR_snprintf(buf
, sizeof buf
, "%s total: %d.%03d (%s)",
441 timerName
, sec
, msec
, str
);
442 NS_TimelineMark(buf
);
447 PR_IMPLEMENT(nsresult
) NS_TimelineResetTimer(const char *timerName
)
449 if (gTimelineDisabled
)
450 return NS_ERROR_NOT_AVAILABLE
;
452 TimelineThreadData
*thread
= GetThisThreadData();
453 if (thread
->timers
== NULL
)
454 return NS_ERROR_FAILURE
;
455 if (thread
->disabled
)
456 return NS_ERROR_NOT_AVAILABLE
;
457 nsTimelineServiceTimer
*timer
458 = (nsTimelineServiceTimer
*)PL_HashTableLookup(thread
->timers
, timerName
);
460 return NS_ERROR_FAILURE
;
467 PR_IMPLEMENT(nsresult
) NS_TimelineIndent()
469 if (gTimelineDisabled
)
470 return NS_ERROR_NOT_AVAILABLE
;
472 TimelineThreadData
*thread
= GetThisThreadData();
473 if (thread
->disabled
)
474 return NS_ERROR_NOT_AVAILABLE
;
479 PR_IMPLEMENT(nsresult
) NS_TimelineOutdent()
481 if (gTimelineDisabled
)
482 return NS_ERROR_NOT_AVAILABLE
;
484 TimelineThreadData
*thread
= GetThisThreadData();
485 if (thread
->disabled
)
486 return NS_ERROR_NOT_AVAILABLE
;
491 PR_IMPLEMENT(nsresult
) NS_TimelineEnter(const char *text
)
493 nsresult rv
= NS_TimelineMark("%s...", text
);
497 return NS_TimelineIndent();
500 PR_IMPLEMENT(nsresult
) NS_TimelineLeave(const char *text
)
502 nsresult rv
= NS_TimelineOutdent();
506 return NS_TimelineMark("...%s", text
);
509 nsTimelineService::nsTimelineService()
511 /* member initializers and constructor code */
514 /* void mark (in string text); */
515 NS_IMETHODIMP
nsTimelineService::Mark(const char *text
)
517 return NS_TimelineMark(text
);
520 /* void startTimer (in string timerName); */
521 NS_IMETHODIMP
nsTimelineService::StartTimer(const char *timerName
)
523 return NS_TimelineStartTimer(timerName
);
526 /* void stopTimer (in string timerName); */
527 NS_IMETHODIMP
nsTimelineService::StopTimer(const char *timerName
)
529 return NS_TimelineStopTimer(timerName
);
532 /* void markTimer (in string timerName); */
533 NS_IMETHODIMP
nsTimelineService::MarkTimer(const char *timerName
)
535 return NS_TimelineMarkTimer(timerName
);
538 /* void markTimerWithComment(in string timerName, in string comment); */
539 NS_IMETHODIMP
nsTimelineService::MarkTimerWithComment(const char *timerName
, const char *comment
)
541 return NS_TimelineMarkTimer(timerName
, comment
);
544 /* void resetTimer (in string timerName); */
545 NS_IMETHODIMP
nsTimelineService::ResetTimer(const char *timerName
)
547 return NS_TimelineResetTimer(timerName
);
550 /* void indent (); */
551 NS_IMETHODIMP
nsTimelineService::Indent()
553 return NS_TimelineIndent();
556 /* void outdent (); */
557 NS_IMETHODIMP
nsTimelineService::Outdent()
559 return NS_TimelineOutdent();
562 /* void enter (in string text); */
563 NS_IMETHODIMP
nsTimelineService::Enter(const char *text
)
565 return NS_TimelineEnter(text
);
568 /* void leave (in string text); */
569 NS_IMETHODIMP
nsTimelineService::Leave(const char *text
)
571 return NS_TimelineLeave(text
);
574 #endif /* MOZ_TIMELINE */