VirtualBox

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

Last change on this file since 62375 was 58104, checked in by vboxsync, 9 years ago

src/libs/xpcom: a couple of missing va_end()

  • 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 va_end(args);
374
375 return NS_OK;
376}
377
378PR_IMPLEMENT(nsresult) NS_TimelineMark(const char *text, ...)
379{
380 va_list args;
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 va_start(args, text);
393 NS_TimelineMarkV(text, args);
394 va_end(args);
395
396 return NS_OK;
397}
398
399PR_IMPLEMENT(nsresult) NS_TimelineStartTimer(const char *timerName)
400{
401 PR_CallOnce(&initonce, TimelineInit);
402
403 if (gTimelineDisabled)
404 return NS_ERROR_NOT_AVAILABLE;
405
406 TimelineThreadData *thread = GetThisThreadData();
407
408 if (thread->timers == NULL)
409 return NS_ERROR_FAILURE;
410 if (thread->disabled)
411 return NS_ERROR_NOT_AVAILABLE;
412
413 nsTimelineServiceTimer *timer
414 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
415 if (timer == NULL) {
416 timer = new nsTimelineServiceTimer;
417 if (!timer)
418 return NS_ERROR_OUT_OF_MEMORY;
419
420 PL_HashTableAdd(thread->timers, timerName, timer);
421 }
422 timer->start();
423 return NS_OK;
424}
425
426PR_IMPLEMENT(nsresult) NS_TimelineStopTimer(const char *timerName)
427{
428 if (gTimelineDisabled)
429 return NS_ERROR_NOT_AVAILABLE;
430 /*
431 * Strange-looking now/timer->stop() interaction is to avoid
432 * including time spent in TLS and PL_HashTableLookup in the
433 * timer.
434 */
435 PRTime now = Now();
436
437 TimelineThreadData *thread = GetThisThreadData();
438 if (thread->timers == NULL)
439 return NS_ERROR_FAILURE;
440 if (thread->disabled)
441 return NS_ERROR_NOT_AVAILABLE;
442 nsTimelineServiceTimer *timer
443 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
444 if (timer == NULL) {
445 return NS_ERROR_FAILURE;
446 }
447
448 timer->stop(now);
449
450 return NS_OK;
451}
452
453PR_IMPLEMENT(nsresult) NS_TimelineMarkTimer(const char *timerName, const char *str)
454{
455 PR_CallOnce(&initonce, TimelineInit);
456
457 if (gTimelineDisabled)
458 return NS_ERROR_NOT_AVAILABLE;
459
460 TimelineThreadData *thread = GetThisThreadData();
461 if (thread->timers == NULL)
462 return NS_ERROR_FAILURE;
463 if (thread->disabled)
464 return NS_ERROR_NOT_AVAILABLE;
465 nsTimelineServiceTimer *timer
466 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
467 if (timer == NULL) {
468 return NS_ERROR_FAILURE;
469 }
470 PRTime accum = timer->getAccum();
471
472 char buf[500];
473 PRInt32 sec, msec;
474 ParseTime(accum, sec, msec);
475 if (!str)
476 PR_snprintf(buf, sizeof buf, "%s total: %d.%03d",
477 timerName, sec, msec);
478 else
479 PR_snprintf(buf, sizeof buf, "%s total: %d.%03d (%s)",
480 timerName, sec, msec, str);
481 NS_TimelineMark(buf);
482
483 return NS_OK;
484}
485
486PR_IMPLEMENT(nsresult) NS_TimelineResetTimer(const char *timerName)
487{
488 if (gTimelineDisabled)
489 return NS_ERROR_NOT_AVAILABLE;
490
491 TimelineThreadData *thread = GetThisThreadData();
492 if (thread->timers == NULL)
493 return NS_ERROR_FAILURE;
494 if (thread->disabled)
495 return NS_ERROR_NOT_AVAILABLE;
496 nsTimelineServiceTimer *timer
497 = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
498 if (timer == NULL) {
499 return NS_ERROR_FAILURE;
500 }
501
502 timer->reset();
503 return NS_OK;
504}
505
506PR_IMPLEMENT(nsresult) NS_TimelineIndent()
507{
508 if (gTimelineDisabled)
509 return NS_ERROR_NOT_AVAILABLE;
510
511 TimelineThreadData *thread = GetThisThreadData();
512 if (thread->disabled)
513 return NS_ERROR_NOT_AVAILABLE;
514 thread->indent++;
515 return NS_OK;
516}
517
518PR_IMPLEMENT(nsresult) NS_TimelineOutdent()
519{
520 if (gTimelineDisabled)
521 return NS_ERROR_NOT_AVAILABLE;
522
523 TimelineThreadData *thread = GetThisThreadData();
524 if (thread->disabled)
525 return NS_ERROR_NOT_AVAILABLE;
526 thread->indent--;
527 return NS_OK;
528}
529
530PR_IMPLEMENT(nsresult) NS_TimelineEnter(const char *text)
531{
532 nsresult rv = NS_TimelineMark("%s...", text);
533 if (NS_FAILED(rv)) {
534 return rv;
535 }
536 return NS_TimelineIndent();
537}
538
539PR_IMPLEMENT(nsresult) NS_TimelineLeave(const char *text)
540{
541 nsresult rv = NS_TimelineOutdent();
542 if (NS_FAILED(rv)) {
543 return rv;
544 }
545 return NS_TimelineMark("...%s", text);
546}
547
548nsTimelineService::nsTimelineService()
549{
550 /* member initializers and constructor code */
551}
552
553/* void mark (in string text); */
554NS_IMETHODIMP nsTimelineService::Mark(const char *text)
555{
556 return NS_TimelineMark(text);
557}
558
559/* void startTimer (in string timerName); */
560NS_IMETHODIMP nsTimelineService::StartTimer(const char *timerName)
561{
562 return NS_TimelineStartTimer(timerName);
563}
564
565/* void stopTimer (in string timerName); */
566NS_IMETHODIMP nsTimelineService::StopTimer(const char *timerName)
567{
568 return NS_TimelineStopTimer(timerName);
569}
570
571/* void markTimer (in string timerName); */
572NS_IMETHODIMP nsTimelineService::MarkTimer(const char *timerName)
573{
574 return NS_TimelineMarkTimer(timerName);
575}
576
577/* void markTimerWithComment(in string timerName, in string comment); */
578NS_IMETHODIMP nsTimelineService::MarkTimerWithComment(const char *timerName, const char *comment)
579{
580 return NS_TimelineMarkTimer(timerName, comment);
581}
582
583/* void resetTimer (in string timerName); */
584NS_IMETHODIMP nsTimelineService::ResetTimer(const char *timerName)
585{
586 return NS_TimelineResetTimer(timerName);
587}
588
589/* void indent (); */
590NS_IMETHODIMP nsTimelineService::Indent()
591{
592 return NS_TimelineIndent();
593}
594
595/* void outdent (); */
596NS_IMETHODIMP nsTimelineService::Outdent()
597{
598 return NS_TimelineOutdent();
599}
600
601/* void enter (in string text); */
602NS_IMETHODIMP nsTimelineService::Enter(const char *text)
603{
604 return NS_TimelineEnter(text);
605}
606
607/* void leave (in string text); */
608NS_IMETHODIMP nsTimelineService::Leave(const char *text)
609{
610 return NS_TimelineLeave(text);
611}
612
613#endif /* MOZ_TIMELINE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette