summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp613
1 files changed, 613 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp b/src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp
new file mode 100644
index 00000000..4974f562
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/ds/nsTimelineService.cpp
@@ -0,0 +1,613 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsTimelineService.h"
+#include "prlong.h"
+#include "prprf.h"
+#include "prenv.h"
+#include "plhash.h"
+#include "prlock.h"
+#include "prinit.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+#ifdef MOZ_TIMELINE
+
+#define MAXINDENT 20
+
+#ifdef XP_MAC
+static PRIntervalTime initInterval = 0;
+#endif
+
+static PRFileDesc *timelineFD = PR_STDERR;
+static PRBool gTimelineDisabled = PR_TRUE;
+
+// Notes about threading:
+// We avoid locks as we always use thread-local-storage.
+// This means every other thread has its own private copy of
+// data, and this thread can't re-enter (as our implemenation
+// doesn't call back out anywhere). Thus, we can avoid locks!
+// TLS index
+static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
+static PRUintn gTLSIndex = BAD_TLS_INDEX;
+
+class TimelineThreadData {
+public:
+ TimelineThreadData() : initTime(0), indent(0),
+ disabled(PR_TRUE), timers(nsnull) {}
+ ~TimelineThreadData() {if (timers) PL_HashTableDestroy(timers);}
+ PRTime initTime;
+ PRHashTable *timers;
+ int indent;
+ PRBool disabled;
+};
+
+/* Implementation file */
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimelineService, nsITimelineService)
+
+static PRTime Now(void);
+
+/*
+ * Timer structure stored in a hash table to keep track of named
+ * timers.
+ */
+class nsTimelineServiceTimer {
+ public:
+ nsTimelineServiceTimer();
+ ~nsTimelineServiceTimer();
+ void start();
+
+ /*
+ * Caller passes in "now" rather than having us calculate it so
+ * that we can avoid including timer overhead in the time being
+ * measured.
+ */
+ void stop(PRTime now);
+ void reset();
+ PRTime getAccum();
+ PRTime getAccum(PRTime now);
+
+ private:
+ PRTime mAccum;
+ PRTime mStart;
+ PRInt32 mRunning;
+ PRThread *mOwnerThread; // only used for asserts - could be #if MOZ_DEBUG
+};
+
+#define TIMER_CHECK_OWNER() \
+ NS_ABORT_IF_FALSE(PR_GetCurrentThread() == mOwnerThread, \
+ "Timer used by non-owning thread")
+
+
+nsTimelineServiceTimer::nsTimelineServiceTimer()
+: mAccum(LL_ZERO), mStart(LL_ZERO), mRunning(0),
+ mOwnerThread(PR_GetCurrentThread())
+{
+}
+
+nsTimelineServiceTimer::~nsTimelineServiceTimer()
+{
+}
+
+void nsTimelineServiceTimer::start()
+{
+ TIMER_CHECK_OWNER();
+ if (!mRunning) {
+ mStart = Now();
+ }
+ mRunning++;
+}
+
+void nsTimelineServiceTimer::stop(PRTime now)
+{
+ TIMER_CHECK_OWNER();
+ mRunning--;
+ if (mRunning == 0) {
+ PRTime delta, accum;
+ LL_SUB(delta, now, mStart);
+ LL_ADD(accum, mAccum, delta);
+ mAccum = accum;
+ }
+}
+
+void nsTimelineServiceTimer::reset()
+{
+ TIMER_CHECK_OWNER();
+ mStart = 0;
+ mAccum = 0;
+}
+
+PRTime nsTimelineServiceTimer::getAccum()
+{
+ TIMER_CHECK_OWNER();
+ PRTime accum;
+
+ if (!mRunning) {
+ accum = mAccum;
+ } else {
+ PRTime delta;
+ LL_SUB(delta, Now(), mStart);
+ LL_ADD(accum, mAccum, delta);
+ }
+ return accum;
+}
+
+PRTime nsTimelineServiceTimer::getAccum(PRTime now)
+{
+ TIMER_CHECK_OWNER();
+ PRTime accum;
+
+ if (!mRunning) {
+ accum = mAccum;
+ } else {
+ PRTime delta;
+ LL_SUB(delta, now, mStart);
+ LL_ADD(accum, mAccum, delta);
+ }
+ return accum;
+}
+
+#ifdef XP_MAC
+/*
+ * PR_Now() on the Mac only gives us a resolution of seconds. Using
+ * PR_IntervalNow() gives us better resolution. with the drawback that
+ * the timeline is only good for about six hours.
+ *
+ * PR_IntervalNow() occasionally exhibits discontinuities on Windows,
+ * so we only use it on the Mac. Bleah!
+ */
+static PRTime Now(void)
+{
+ PRIntervalTime numTicks = PR_IntervalNow() - initInterval;
+ PRTime now;
+ LL_ADD(now, initTime, PR_IntervalToMilliseconds(numTicks) * 1000);
+ return now;
+}
+#else
+static PRTime Now(void)
+{
+ return PR_Now();
+}
+#endif
+
+static TimelineThreadData *GetThisThreadData()
+{
+ NS_ABORT_IF_FALSE(gTLSIndex!=BAD_TLS_INDEX, "Our TLS not initialized");
+ TimelineThreadData *new_data = nsnull;
+ TimelineThreadData *data = (TimelineThreadData *)PR_GetThreadPrivate(gTLSIndex);
+ if (data == nsnull) {
+ // First request for this thread - allocate it.
+ new_data = new TimelineThreadData();
+ if (!new_data)
+ goto done;
+
+ // Fill it
+ new_data->timers = PL_NewHashTable(100, PL_HashString, PL_CompareStrings,
+ PL_CompareValues, NULL, NULL);
+ if (new_data->timers==NULL)
+ goto done;
+ new_data->initTime = PR_Now();
+ NS_WARN_IF_FALSE(!gTimelineDisabled,
+ "Why are we creating new state when disabled?");
+ new_data->disabled = PR_FALSE;
+ data = new_data;
+ new_data = nsnull;
+ PR_SetThreadPrivate(gTLSIndex, data);
+ }
+done:
+ if (new_data) // eeek - error during creation!
+ delete new_data;
+ NS_WARN_IF_FALSE(data, "TimelineService could not get thread-local data");
+ return data;
+}
+
+extern "C" {
+ static void ThreadDestruct (void *data);
+ static PRStatus TimelineInit(void);
+};
+
+void ThreadDestruct( void *data )
+{
+ if (data)
+ delete (TimelineThreadData *)data;
+}
+
+/*
+* PRCallOnceFN that initializes stuff for the timing service.
+*/
+static PRCallOnceType initonce;
+
+PRStatus TimelineInit(void)
+{
+ char *timeStr;
+ char *fileName;
+ PRInt32 secs, msecs;
+ PRFileDesc *fd;
+ PRInt64 tmp1, tmp2;
+
+ PRStatus status = PR_NewThreadPrivateIndex( &gTLSIndex, ThreadDestruct );
+ NS_WARN_IF_FALSE(status==0, "TimelineService could not allocate TLS storage.");
+
+ timeStr = PR_GetEnv("NS_TIMELINE_INIT_TIME");
+#ifdef XP_MAC
+ initInterval = PR_IntervalNow();
+#endif
+ // NS_TIMELINE_INIT_TIME only makes sense for the main thread, so if it
+ // exists, set it there. If not, let normal thread management code take
+ // care of setting the init time.
+ if (timeStr != NULL && 2 == PR_sscanf(timeStr, "%d.%d", &secs, &msecs)) {
+ PRTime &initTime = GetThisThreadData()->initTime;
+ LL_MUL(tmp1, (PRInt64)secs, 1000000);
+ LL_MUL(tmp2, (PRInt64)msecs, 1000);
+ LL_ADD(initTime, tmp1, tmp2);
+#ifdef XP_MAC
+ initInterval -= PR_MicrosecondsToInterval(
+ (PRUint32)(PR_Now() - initTime));
+#endif
+ }
+ // Get the log file.
+#ifdef XP_MAC
+ fileName = "timeline.txt";
+#else
+ fileName = PR_GetEnv("NS_TIMELINE_LOG_FILE");
+#endif
+ if (fileName != NULL
+ && (fd = PR_Open(fileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ 0666)) != NULL) {
+ timelineFD = fd;
+ PR_fprintf(fd,
+ "NOTE: due to asynchrony, the indentation that you see does"
+ " not necessarily correspond to nesting in the code.\n\n");
+ }
+
+ // Runtime disable of timeline
+ if (PR_GetEnv("NS_TIMELINE_ENABLE"))
+ gTimelineDisabled = PR_FALSE;
+ return PR_SUCCESS;
+}
+
+static void ParseTime(PRTime tm, PRInt32& secs, PRInt32& msecs)
+{
+ PRTime llsecs, llmsecs, tmp;
+
+ LL_DIV(llsecs, tm, 1000000);
+ LL_MOD(tmp, tm, 1000000);
+ LL_DIV(llmsecs, tmp, 1000);
+
+ LL_L2I(secs, llsecs);
+ LL_L2I(msecs, llmsecs);
+}
+
+static char *Indent(char *buf)
+{
+ int &indent = GetThisThreadData()->indent;
+ int amount = indent;
+ if (amount > MAXINDENT) {
+ amount = MAXINDENT;
+ }
+ if (amount < 0) {
+ amount = 0;
+ indent = 0;
+ PR_Write(timelineFD, "indent underflow!\n", 18);
+ }
+ while (amount--) {
+ *buf++ = ' ';
+ }
+ return buf;
+}
+
+static void PrintTime(PRTime tm, const char *text, va_list args)
+{
+ PRInt32 secs, msecs;
+ char pbuf[550], *pc, tbuf[550];
+
+ ParseTime(tm, secs, msecs);
+
+ // snprintf/write rather than fprintf because we don't want
+ // messages from multiple threads to garble one another.
+ pc = Indent(pbuf);
+ PR_vsnprintf(pc, sizeof pbuf - (pc - pbuf), text, args);
+ PR_snprintf(tbuf, sizeof tbuf, "%05d.%03d (%08p): %s\n",
+ secs, msecs, PR_GetCurrentThread(), pbuf);
+ PR_Write(timelineFD, tbuf, strlen(tbuf));
+}
+
+/*
+ * Make this public if we need it.
+ */
+static nsresult NS_TimelineMarkV(const char *text, va_list args)
+{
+ PRTime elapsed,tmp;
+
+ PR_CallOnce(&initonce, TimelineInit);
+
+ TimelineThreadData *thread = GetThisThreadData();
+
+ tmp = Now();
+ LL_SUB(elapsed, tmp, thread->initTime);
+
+ PrintTime(elapsed, text, args);
+
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineForceMark(const char *text, ...)
+{
+ va_list args;
+ va_start(args, text);
+ NS_TimelineMarkV(text, args);
+ va_end(args);
+
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineMark(const char *text, ...)
+{
+ va_list args;
+
+ PR_CallOnce(&initonce, TimelineInit);
+
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ va_start(args, text);
+ NS_TimelineMarkV(text, args);
+ va_end(args);
+
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineStartTimer(const char *timerName)
+{
+ PR_CallOnce(&initonce, TimelineInit);
+
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+
+ if (thread->timers == NULL)
+ return NS_ERROR_FAILURE;
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsTimelineServiceTimer *timer
+ = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
+ if (timer == NULL) {
+ timer = new nsTimelineServiceTimer;
+ if (!timer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PL_HashTableAdd(thread->timers, timerName, timer);
+ }
+ timer->start();
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineStopTimer(const char *timerName)
+{
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ /*
+ * Strange-looking now/timer->stop() interaction is to avoid
+ * including time spent in TLS and PL_HashTableLookup in the
+ * timer.
+ */
+ PRTime now = Now();
+
+ TimelineThreadData *thread = GetThisThreadData();
+ if (thread->timers == NULL)
+ return NS_ERROR_FAILURE;
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ nsTimelineServiceTimer *timer
+ = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
+ if (timer == NULL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ timer->stop(now);
+
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineMarkTimer(const char *timerName, const char *str)
+{
+ PR_CallOnce(&initonce, TimelineInit);
+
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+ if (thread->timers == NULL)
+ return NS_ERROR_FAILURE;
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ nsTimelineServiceTimer *timer
+ = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
+ if (timer == NULL) {
+ return NS_ERROR_FAILURE;
+ }
+ PRTime accum = timer->getAccum();
+
+ char buf[500];
+ PRInt32 sec, msec;
+ ParseTime(accum, sec, msec);
+ if (!str)
+ PR_snprintf(buf, sizeof buf, "%s total: %d.%03d",
+ timerName, sec, msec);
+ else
+ PR_snprintf(buf, sizeof buf, "%s total: %d.%03d (%s)",
+ timerName, sec, msec, str);
+ NS_TimelineMark(buf);
+
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineResetTimer(const char *timerName)
+{
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+ if (thread->timers == NULL)
+ return NS_ERROR_FAILURE;
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ nsTimelineServiceTimer *timer
+ = (nsTimelineServiceTimer *)PL_HashTableLookup(thread->timers, timerName);
+ if (timer == NULL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ timer->reset();
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineIndent()
+{
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ thread->indent++;
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineOutdent()
+{
+ if (gTimelineDisabled)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ TimelineThreadData *thread = GetThisThreadData();
+ if (thread->disabled)
+ return NS_ERROR_NOT_AVAILABLE;
+ thread->indent--;
+ return NS_OK;
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineEnter(const char *text)
+{
+ nsresult rv = NS_TimelineMark("%s...", text);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_TimelineIndent();
+}
+
+PR_IMPLEMENT(nsresult) NS_TimelineLeave(const char *text)
+{
+ nsresult rv = NS_TimelineOutdent();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_TimelineMark("...%s", text);
+}
+
+nsTimelineService::nsTimelineService()
+{
+ /* member initializers and constructor code */
+}
+
+/* void mark (in string text); */
+NS_IMETHODIMP nsTimelineService::Mark(const char *text)
+{
+ return NS_TimelineMark(text);
+}
+
+/* void startTimer (in string timerName); */
+NS_IMETHODIMP nsTimelineService::StartTimer(const char *timerName)
+{
+ return NS_TimelineStartTimer(timerName);
+}
+
+/* void stopTimer (in string timerName); */
+NS_IMETHODIMP nsTimelineService::StopTimer(const char *timerName)
+{
+ return NS_TimelineStopTimer(timerName);
+}
+
+/* void markTimer (in string timerName); */
+NS_IMETHODIMP nsTimelineService::MarkTimer(const char *timerName)
+{
+ return NS_TimelineMarkTimer(timerName);
+}
+
+/* void markTimerWithComment(in string timerName, in string comment); */
+NS_IMETHODIMP nsTimelineService::MarkTimerWithComment(const char *timerName, const char *comment)
+{
+ return NS_TimelineMarkTimer(timerName, comment);
+}
+
+/* void resetTimer (in string timerName); */
+NS_IMETHODIMP nsTimelineService::ResetTimer(const char *timerName)
+{
+ return NS_TimelineResetTimer(timerName);
+}
+
+/* void indent (); */
+NS_IMETHODIMP nsTimelineService::Indent()
+{
+ return NS_TimelineIndent();
+}
+
+/* void outdent (); */
+NS_IMETHODIMP nsTimelineService::Outdent()
+{
+ return NS_TimelineOutdent();
+}
+
+/* void enter (in string text); */
+NS_IMETHODIMP nsTimelineService::Enter(const char *text)
+{
+ return NS_TimelineEnter(text);
+}
+
+/* void leave (in string text); */
+NS_IMETHODIMP nsTimelineService::Leave(const char *text)
+{
+ return NS_TimelineLeave(text);
+}
+
+#endif /* MOZ_TIMELINE */