VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp@ 4837

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
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
4 *
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/
9 *
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
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
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.
21 *
22 * Contributor(s):
23 *
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.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#include "nsTimelineService.h"
39#include "prlong.h"
40#include "prprf.h"
41#include "prenv.h"
42#include "plhash.h"
43#include "prlock.h"
44#include "prinit.h"
45#include "prinrval.h"
46#include "prthread.h"
47
48#ifdef MOZ_TIMELINE
49
50#define MAXINDENT 20
51
52#ifdef XP_MAC
53static PRIntervalTime initInterval = 0;
54#endif
55
56static PRFileDesc *timelineFD = PR_STDERR;
57static PRBool gTimelineDisabled = PR_TRUE;
58
59// Notes about threading:
60// We avoid locks as we always use thread-local-storage.
61// This means every other thread has its own private copy of
62// data, and this thread can't re-enter (as our implemenation
63// doesn't call back out anywhere). Thus, we can avoid locks!
64// TLS index
65static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
66static PRUintn gTLSIndex = BAD_TLS_INDEX;
67
68class TimelineThreadData {
69public:
70 TimelineThreadData() : initTime(0), indent(0),
71 disabled(PR_TRUE), timers(nsnull) {}
72 ~TimelineThreadData() {if (timers) PL_HashTableDestroy(timers);}
73 PRTime initTime;
74 PRHashTable *timers;
75 int indent;
76 PRBool disabled;
77};
78
79/* Implementation file */
80NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimelineService, nsITimelineService)
81
82static PRTime Now(void);
83
84/*
85 * Timer structure stored in a hash table to keep track of named
86 * timers.
87 */
88class nsTimelineServiceTimer {
89 public:
90 nsTimelineServiceTimer();
91 ~nsTimelineServiceTimer();
92 void start();
93
94 /*
95 * Caller passes in "now" rather than having us calculate it so
96 * that we can avoid including timer overhead in the time being
97 * measured.
98 */
99 void stop(PRTime now);
100 void reset();
101 PRTime getAccum();
102 PRTime getAccum(PRTime now);
103
104 private:
105 PRTime mAccum;
106 PRTime mStart;
107 PRInt32 mRunning;
108 PRThread *mOwnerThread; // only used for asserts - could be #if MOZ_DEBUG
109};
110
111#define TIMER_CHECK_OWNER() \
112 NS_ABORT_IF_FALSE(PR_GetCurrentThread() == mOwnerThread, \
113 "Timer used by non-owning thread")
114
115
116nsTimelineServiceTimer::nsTimelineServiceTimer()
117: mAccum(LL_ZERO), mStart(LL_ZERO), mRunning(0),
118 mOwnerThread(PR_GetCurrentThread())
119{
120}
121
122nsTimelineServiceTimer::~nsTimelineServiceTimer()
123{
124}
125
126void nsTimelineServiceTimer::start()
127{
128 TIMER_CHECK_OWNER();
129 if (!mRunning) {
130 mStart = Now();
131 }
132 mRunning++;
133}
134
135void nsTimelineServiceTimer::stop(PRTime now)
136{
137 TIMER_CHECK_OWNER();
138 mRunning--;
139 if (mRunning == 0) {
140 PRTime delta, accum;
141 LL_SUB(delta, now, mStart);
142 LL_ADD(accum, mAccum, delta);
143 mAccum = accum;
144 }
145}
146
147void nsTimelineServiceTimer::reset()
148{
149 TIMER_CHECK_OWNER();
150 mStart = 0;
151 mAccum = 0;
152}
153
154PRTime nsTimelineServiceTimer::getAccum()
155{
156 TIMER_CHECK_OWNER();
157 PRTime accum;
158
159 if (!mRunning) {
160 accum = mAccum;
161 } else {
162 PRTime delta;
163 LL_SUB(delta, Now(), mStart);
164 LL_ADD(accum, mAccum, delta);
165 }
166 return accum;
167}
168
169PRTime nsTimelineServiceTimer::getAccum(PRTime now)
170{
171 TIMER_CHECK_OWNER();
172 PRTime accum;
173
174 if (!mRunning) {
175 accum = mAccum;
176 } else {
177 PRTime delta;
178 LL_SUB(delta, now, mStart);
179 LL_ADD(accum, mAccum, delta);
180 }
181 return accum;
182}
183
184#ifdef XP_MAC
185/*
186 * PR_Now() on the Mac only gives us a resolution of seconds. Using
187 * PR_IntervalNow() gives us better resolution. with the drawback that
188 * the timeline is only good for about six hours.
189 *
190 * PR_IntervalNow() occasionally exhibits discontinuities on Windows,
191 * so we only use it on the Mac. Bleah!
192 */
193static PRTime Now(void)
194{
195 PRIntervalTime numTicks = PR_IntervalNow() - initInterval;
196 PRTime now;
197 LL_ADD(now, initTime, PR_IntervalToMilliseconds(numTicks) * 1000);
198 return now;
199}
200#else
201static PRTime Now(void)
202{
203 return PR_Now();
204}
205#endif
206
207static TimelineThreadData *GetThisThreadData()
208{
209 NS_ABORT_IF_FALSE(gTLSIndex!=BAD_TLS_INDEX, "Our TLS not initialized");
210 TimelineThreadData *new_data = nsnull;
211 TimelineThreadData *data = (TimelineThreadData *)PR_GetThreadPrivate(gTLSIndex);
212 if (data == nsnull) {
213 // First request for this thread - allocate it.
214 new_data = new TimelineThreadData();
215 if (!new_data)
216 goto done;
217
218 // Fill it
219 new_data->timers = PL_NewHashTable(100, PL_HashString, PL_CompareStrings,
220 PL_CompareValues, NULL, NULL);
221 if (new_data->timers==NULL)
222 goto done;
223 new_data->initTime = PR_Now();
224 NS_WARN_IF_FALSE(!gTimelineDisabled,
225 "Why are we creating new state when disabled?");
226 new_data->disabled = PR_FALSE;
227 data = new_data;
228 new_data = nsnull;
229 PR_SetThreadPrivate(gTLSIndex, data);
230 }
231done:
232 if (new_data) // eeek - error during creation!
233 delete new_data;
234 NS_WARN_IF_FALSE(data, "TimelineService could not get thread-local data");
235 return data;
236}
237
238extern "C" {
239 static void ThreadDestruct (void *data);
240 static PRStatus TimelineInit(void);
241};
242
243void ThreadDestruct( void *data )
244{
245 if (data)
246 delete (TimelineThreadData *)data;
247}
248
249/*
250* PRCallOnceFN that initializes stuff for the timing service.
251*/
252static PRCallOnceType initonce;
253
254PRStatus TimelineInit(void)
255{
256 char *timeStr;
257 char *fileName;
258 PRInt32 secs, msecs;
259 PRFileDesc *fd;
260 PRInt64 tmp1, tmp2;
261
262 PRStatus status = PR_NewThreadPrivateIndex( &gTLSIndex, ThreadDestruct );
263 NS_WARN_IF_FALSE(status==0, "TimelineService could not allocate TLS storage.");
264
265 timeStr = PR_GetEnv("NS_TIMELINE_INIT_TIME");
266#ifdef XP_MAC
267 initInterval = PR_IntervalNow();
268#endif
269 // NS_TIMELINE_INIT_TIME only makes sense for the main thread, so if it
270 // exists, set it there. If not, let normal thread management code take
271 // care of setting the init time.
272 if (timeStr != NULL && 2 == PR_sscanf(timeStr, "%d.%d", &secs, &msecs)) {
273 PRTime &initTime = GetThisThreadData()->initTime;
274 LL_MUL(tmp1, (PRInt64)secs, 1000000);
275 LL_MUL(tmp2, (PRInt64)msecs, 1000);
276 LL_ADD(initTime, tmp1, tmp2);
277#ifdef XP_MAC
278 initInterval -= PR_MicrosecondsToInterval(
279 (PRUint32)(PR_Now() - initTime));
280#endif
281 }
282 // Get the log file.
283#ifdef XP_MAC
284 fileName = "timeline.txt";
285#else
286 fileName = PR_GetEnv("NS_TIMELINE_LOG_FILE");
287#endif
288 if (fileName != NULL
289 && (fd = PR_Open(fileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
290 0666)) != NULL) {
291 timelineFD = fd;
292 PR_fprintf(fd,
293 "NOTE: due to asynchrony, the indentation that you see does"
294 " not necessarily correspond to nesting in the code.\n\n");
295 }
296
297 // Runtime disable of timeline
298 if (PR_GetEnv("NS_TIMELINE_ENABLE"))
299 gTimelineDisabled = PR_FALSE;
300 return PR_SUCCESS;
301}
302
303static void ParseTime(PRTime tm, PRInt32& secs, PRInt32& msecs)
304{
305 PRTime llsecs, llmsecs, tmp;
306
307 LL_DIV(llsecs, tm, 1000000);
308 LL_MOD(tmp, tm, 1000000);
309 LL_DIV(llmsecs, tmp, 1000);
310
311 LL_L2I(secs, llsecs);
312 LL_L2I(msecs, llmsecs);
313}
314
315static char *Indent(char *buf)
316{
317 int &indent = GetThisThreadData()->indent;
318 int amount = indent;
319 if (amount > MAXINDENT) {
320 amount = MAXINDENT;
321 }
322 if (amount < 0) {
323 amount = 0;
324 indent = 0;
325 PR_Write(timelineFD, "indent underflow!\n", 18);
326 }
327 while (amount--) {
328 *buf++ = ' ';
329 }
330 return buf;
331}
332
333static void PrintTime(PRTime tm, const char *text, va_list args)
334{
335 PRInt32 secs, msecs;
336 char pbuf[550], *pc, tbuf[550];
337
338 ParseTime(tm, secs, msecs);
339
340 // snprintf/write rather than fprintf because we don't want
341 // messages from multiple threads to garble one another.
342 pc = Indent(pbuf);
343 PR_vsnprintf(pc, sizeof pbuf - (pc - pbuf), text, args);
344 PR_snprintf(tbuf, sizeof tbuf, "%05d.%03d (%08p): %s\n",
345 secs, msecs, PR_GetCurrentThread(), pbuf);
346 PR_Write(timelineFD, tbuf, strlen(tbuf));
347}
348
349/*
350 * Make this public if we need it.
351 */
352static nsresult NS_TimelineMarkV(const char *text, va_list args)
353{
354 PRTime elapsed,tmp;
355
356 PR_CallOnce(&initonce, TimelineInit);
357
358 TimelineThreadData *thread = GetThisThreadData();
359
360 tmp = Now();
361 LL_SUB(elapsed, tmp, thread->initTime);
362
363 PrintTime(elapsed, text, args);
364
365 return NS_OK;
366}
367
368PR_IMPLEMENT(nsresult) NS_TimelineForceMark(const char *text, ...)
369{
370 va_list args;
371 va_start(args, text);
372 NS_TimelineMarkV(text, args);
373
374 return NS_OK;
375}
376
377PR_IMPLEMENT(nsresult) NS_TimelineMark(const char *text, ...)
378{
379 va_list args;
380 va_start(args, text);
381
382 PR_CallOnce(&initonce, TimelineInit);
383
384 if (gTimelineDisabled)
385 return NS_ERROR_NOT_AVAILABLE;
386
387 TimelineThreadData *thread = GetThisThreadData();
388
389 if (thread->disabled)
390 return NS_ERROR_NOT_AVAILABLE;
391
392 NS_TimelineMarkV(text, args);
393
394 return NS_OK;
395}
396
397PR_IMPLEMENT(nsresult) NS_TimelineStartTimer(const char *timerName)
398{
399 PR_CallOnce(&initonce, TimelineInit);
400
401 if (gTimelineDisabled)
402 return NS_ERROR_NOT_AVAILABLE;
403
404 TimelineThreadData *thread = GetThisThreadData();
405
406 if (thread->timers == NULL)
407 return NS_ERROR_FAILURE;
408 if (thread->disabled)
409 return NS_ERROR_NOT_AVAILABLE;
410
411 nsTimelineServiceTimer *timer
412 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
413 if (timer == NULL) {
414 timer = new nsTimelineServiceTimer;
415 if (!timer)
416 return NS_ERROR_OUT_OF_MEMORY;
417
418 PL_HashTableAdd(thread->timers, timerName, timer);
419 }
420 timer->start();
421 return NS_OK;
422}
423
424PR_IMPLEMENT(nsresult) NS_TimelineStopTimer(const char *timerName)
425{
426 if (gTimelineDisabled)
427 return NS_ERROR_NOT_AVAILABLE;
428 /*
429 * Strange-looking now/timer->stop() interaction is to avoid
430 * including time spent in TLS and PL_HashTableLookup in the
431 * timer.
432 */
433 PRTime now = Now();
434
435 TimelineThreadData *thread = GetThisThreadData();
436 if (thread->timers == NULL)
437 return NS_ERROR_FAILURE;
438 if (thread->disabled)
439 return NS_ERROR_NOT_AVAILABLE;
440 nsTimelineServiceTimer *timer
441 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
442 if (timer == NULL) {
443 return NS_ERROR_FAILURE;
444 }
445
446 timer->stop(now);
447
448 return NS_OK;
449}
450
451PR_IMPLEMENT(nsresult) NS_TimelineMarkTimer(const char *timerName, const char *str)
452{
453 PR_CallOnce(&initonce, TimelineInit);
454
455 if (gTimelineDisabled)
456 return NS_ERROR_NOT_AVAILABLE;
457
458 TimelineThreadData *thread = GetThisThreadData();
459 if (thread->timers == NULL)
460 return NS_ERROR_FAILURE;
461 if (thread->disabled)
462 return NS_ERROR_NOT_AVAILABLE;
463 nsTimelineServiceTimer *timer
464 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
465 if (timer == NULL) {
466 return NS_ERROR_FAILURE;
467 }
468 PRTime accum = timer->getAccum();
469
470 char buf[500];
471 PRInt32 sec, msec;
472 ParseTime(accum, sec, msec);
473 if (!str)
474 PR_snprintf(buf, sizeof buf, "%s total: %d.%03d",
475 timerName, sec, msec);
476 else
477 PR_snprintf(buf, sizeof buf, "%s total: %d.%03d (%s)",
478 timerName, sec, msec, str);
479 NS_TimelineMark(buf);
480
481 return NS_OK;
482}
483
484PR_IMPLEMENT(nsresult) NS_TimelineResetTimer(const char *timerName)
485{
486 if (gTimelineDisabled)
487 return NS_ERROR_NOT_AVAILABLE;
488
489 TimelineThreadData *thread = GetThisThreadData();
490 if (thread->timers == NULL)
491 return NS_ERROR_FAILURE;
492 if (thread->disabled)
493 return NS_ERROR_NOT_AVAILABLE;
494 nsTimelineServiceTimer *timer
495 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
496 if (timer == NULL) {
497 return NS_ERROR_FAILURE;
498 }
499
500 timer->reset();
501 return NS_OK;
502}
503
504PR_IMPLEMENT(nsresult) NS_TimelineIndent()
505{
506 if (gTimelineDisabled)
507 return NS_ERROR_NOT_AVAILABLE;
508
509 TimelineThreadData *thread = GetThisThreadData();
510 if (thread->disabled)
511 return NS_ERROR_NOT_AVAILABLE;
512 thread->indent++;
513 return NS_OK;
514}
515
516PR_IMPLEMENT(nsresult) NS_TimelineOutdent()
517{
518 if (gTimelineDisabled)
519 return NS_ERROR_NOT_AVAILABLE;
520
521 TimelineThreadData *thread = GetThisThreadData();
522 if (thread->disabled)
523 return NS_ERROR_NOT_AVAILABLE;
524 thread->indent--;
525 return NS_OK;
526}
527
528PR_IMPLEMENT(nsresult) NS_TimelineEnter(const char *text)
529{
530 nsresult rv = NS_TimelineMark("%s...", text);
531 if (NS_FAILED(rv)) {
532 return rv;
533 }
534 return NS_TimelineIndent();
535}
536
537PR_IMPLEMENT(nsresult) NS_TimelineLeave(const char *text)
538{
539 nsresult rv = NS_TimelineOutdent();
540 if (NS_FAILED(rv)) {
541 return rv;
542 }
543 return NS_TimelineMark("...%s", text);
544}
545
546nsTimelineService::nsTimelineService()
547{
548 /* member initializers and constructor code */
549}
550
551/* void mark (in string text); */
552NS_IMETHODIMP nsTimelineService::Mark(const char *text)
553{
554 return NS_TimelineMark(text);
555}
556
557/* void startTimer (in string timerName); */
558NS_IMETHODIMP nsTimelineService::StartTimer(const char *timerName)
559{
560 return NS_TimelineStartTimer(timerName);
561}
562
563/* void stopTimer (in string timerName); */
564NS_IMETHODIMP nsTimelineService::StopTimer(const char *timerName)
565{
566 return NS_TimelineStopTimer(timerName);
567}
568
569/* void markTimer (in string timerName); */
570NS_IMETHODIMP nsTimelineService::MarkTimer(const char *timerName)
571{
572 return NS_TimelineMarkTimer(timerName);
573}
574
575/* void markTimerWithComment(in string timerName, in string comment); */
576NS_IMETHODIMP nsTimelineService::MarkTimerWithComment(const char *timerName, const char *comment)
577{
578 return NS_TimelineMarkTimer(timerName, comment);
579}
580
581/* void resetTimer (in string timerName); */
582NS_IMETHODIMP nsTimelineService::ResetTimer(const char *timerName)
583{
584 return NS_TimelineResetTimer(timerName);
585}
586
587/* void indent (); */
588NS_IMETHODIMP nsTimelineService::Indent()
589{
590 return NS_TimelineIndent();
591}
592
593/* void outdent (); */
594NS_IMETHODIMP nsTimelineService::Outdent()
595{
596 return NS_TimelineOutdent();
597}
598
599/* void enter (in string text); */
600NS_IMETHODIMP nsTimelineService::Enter(const char *text)
601{
602 return NS_TimelineEnter(text);
603}
604
605/* void leave (in string text); */
606NS_IMETHODIMP nsTimelineService::Leave(const char *text)
607{
608 return NS_TimelineLeave(text);
609}
610
611#endif /* MOZ_TIMELINE */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use