gecko-dev/xpcom/ds/nsStopwatch.cpp
1999-12-29 20:54:13 +00:00

405 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsStopwatch.h"
#ifdef NS_ENABLE_STOPWATCH
#include "nsILoggingService.h"
#include "prthread.h"
#include <math.h>
extern NS_DECL_LOG(LogInfo);
////////////////////////////////////////////////////////////////////////////////
// nsStopwatchService
nsStopwatchService::nsStopwatchService()
: mStopwatches(16)
{
NS_INIT_REFCNT();
}
nsStopwatchService::~nsStopwatchService()
{
DescribeTimings(LogInfo);
}
NS_IMPL_ISUPPORTS1(nsStopwatchService, nsIStopwatchService)
nsresult
nsStopwatchService::Init()
{
return NS_OK;
}
NS_METHOD
nsStopwatchService::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
nsresult rv;
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsStopwatchService* sw = new nsStopwatchService();
if (sw == NULL)
return NS_ERROR_OUT_OF_MEMORY;
rv = sw->Init();
if (NS_FAILED(rv)) {
delete sw;
return rv;
}
rv = sw->QueryInterface(aIID, aInstancePtr);
if (NS_FAILED(rv)) {
delete sw;
return rv;
}
return rv;
}
NS_IMETHODIMP
nsStopwatchService::CreateStopwatch(const char* name, const char* countUnits,
PRBool perThread, nsIStopwatch* *result)
{
nsStopwatch* sw = new nsStopwatch();
if (sw == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(sw);
nsresult rv = sw->Init(name, countUnits, perThread);
if (NS_FAILED(rv)) goto done;
rv = Define(name, sw);
if (NS_FAILED(rv)) goto done;
*result = sw;
NS_ADDREF(*result);
done:
NS_RELEASE(sw);
return rv;
}
NS_IMETHODIMP
nsStopwatchService::Define(const char *prop, nsISupports *initialValue)
{
nsStringKey key(prop);
nsCOMPtr<nsIStopwatch> prev = (nsStopwatch*)mStopwatches.Get(&key);
NS_ASSERTION(prev == nsnull, "stopwatch redefinition");
if (prev != nsnull)
return NS_ERROR_FAILURE;
mStopwatches.Put(&key, initialValue);
return NS_OK;
}
NS_IMETHODIMP
nsStopwatchService::Undefine(const char *prop)
{
nsStringKey key(prop);
nsCOMPtr<nsIStopwatch> prev = (nsStopwatch*)mStopwatches.Get(&key);
NS_ASSERTION(prev != nsnull, "stopwatch undefined");
if (prev == nsnull)
return NS_ERROR_FAILURE;
mStopwatches.Remove(&key);
return NS_OK;
}
NS_IMETHODIMP
nsStopwatchService::Get(const char *prop, const nsIID & uuid, void * *result)
{
nsStringKey key(prop);
nsCOMPtr<nsISupports> sw = (nsStopwatch*)mStopwatches.Get(&key);
NS_ASSERTION(sw != nsnull, "stopwatch undefined");
if (sw == nsnull)
return NS_ERROR_FAILURE;
return sw->QueryInterface(uuid, result);
}
NS_IMETHODIMP
nsStopwatchService::Set(const char *prop, nsISupports *value)
{
nsStringKey key(prop);
nsCOMPtr<nsISupports> prev = (nsStopwatch*)mStopwatches.Get(&key);
NS_ASSERTION(prev != nsnull, "stopwatch undefined");
if (prev == nsnull)
return NS_ERROR_FAILURE;
mStopwatches.Put(&key, value);
return NS_OK;
}
NS_IMETHODIMP
nsStopwatchService::Has(const char *prop, const nsIID & uuid, nsISupports *value,
PRBool *result)
{
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsStopwatch
nsStopwatch::nsStopwatch()
: mName(nsnull),
mCountUnits(nsnull),
mPerThread(PR_TRUE),
mThreadTimingDataIndex(0)
{
NS_INIT_REFCNT();
}
nsStopwatch::~nsStopwatch()
{
if (mName) nsCRT::free(mName);
if (mCountUnits) nsCRT::free(mCountUnits);
}
NS_IMPL_ISUPPORTS1(nsStopwatch, nsIStopwatch)
NS_METHOD
nsStopwatch::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
nsresult rv;
if (outer)
return NS_ERROR_NO_AGGREGATION;
nsStopwatch* sw = new nsStopwatch();
if (sw == NULL)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(sw);
rv = sw->QueryInterface(aIID, aInstancePtr);
NS_RELEASE(sw);
return rv;
}
void
nsStopwatch::TimeUnits(double timeInMilliSeconds,
double *adjustedValue, const char* *adjustedUnits,
double *factorResult)
{
double time = timeInMilliSeconds;
const char* units = "ms";
double factor = 1;
if (time < 1) {
time *= 1000;
factor *= 1000;
units = "us";
if (time < 1) {
time *= 1000;
factor *= 1000;
units = "ns";
}
}
else if (time > 1000) {
time /= 1000;
factor /= 1000;
units = "sec";
if (time > 60) {
time /= 60;
factor /= 60;
units = "min";
if (time > 60) {
time /= 60;
factor /= 60;
units = "hours";
if (time > 24) {
time /= 24;
factor /= 24;
units = "days";
}
}
}
}
*adjustedValue = time;
*adjustedUnits = units;
*factorResult = factor;
}
////////////////////////////////////////////////////////////////////////////////
static void PR_CALLBACK
DeleteTimingData(void *priv)
{
nsTimingData* data = (nsTimingData*)priv;
delete data;
}
NS_IMETHODIMP
nsStopwatch::Init(const char *name, const char* countUnits, PRBool perThread)
{
if (mName) nsCRT::free(mName);
if (mCountUnits) nsCRT::free(mCountUnits);
Reset();
mName = nsCRT::strdup(name);
if (mName == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
mCountUnits = nsCRT::strdup(countUnits);
if (mCountUnits == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
mPerThread = perThread;
PRStatus status = PR_NewThreadPrivateIndex(&mThreadTimingDataIndex,
DeleteTimingData);
if (status != PR_SUCCESS)
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
nsStopwatch::GetName(char * *aName)
{
*aName = nsCRT::strdup(mName);
return *aName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsStopwatch::Start(void)
{
nsTimingData* data;
if (mPerThread) {
data = (nsTimingData*)PR_GetThreadPrivate(mThreadTimingDataIndex);
if (data == nsnull) {
data = new nsTimingData;
if (data == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
PRStatus status = PR_SetThreadPrivate(mThreadTimingDataIndex, data);
if (status != PR_SUCCESS)
return NS_ERROR_FAILURE;
}
}
else {
data = &mTimingData;
}
PR_ASSERT(data->mStartTime == 0);
if (data->mStartTime != 0)
return NS_ERROR_FAILURE;
data->mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHODIMP
nsStopwatch::Stop(double count, PRIntervalTime *elapsedTime)
{
nsTimingData* data;
if (mPerThread)
data = (nsTimingData*)PR_GetThreadPrivate(mThreadTimingDataIndex);
else
data = &mTimingData;
PR_ASSERT(data->mStartTime != 0);
if (data->mStartTime == 0)
return NS_ERROR_FAILURE;
PRIntervalTime elapsed = PR_IntervalNow();
elapsed -= data->mStartTime;
data->mStartTime = 0;
data->mCount++;
data->mTotalTime += elapsed;
data->mTotalSquaredTime += elapsed * elapsed;
*elapsedTime = elapsed;
if (mPerThread) {
// dump per-thread data into per-log data:
mTimingData.mTotalTime += data->mTotalTime;
mTimingData.mTotalSquaredTime += data->mTotalSquaredTime;
mTimingData.mCount += data->mCount;
// destroy TLS:
PRStatus status = PR_SetThreadPrivate(mThreadTimingDataIndex, nsnull);
if (status != PR_SUCCESS)
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsStopwatch::Reset(void)
{
mTimingData.mStartTime = 0;
mTimingData.mTotalTime = 0;
mTimingData.mTotalSquaredTime = 0;
mTimingData.mCount = 0;
return NS_OK;
}
NS_IMETHODIMP
nsStopwatch::GetRealTimeStats(double *realTimeSamples, double *realTimeMean, double *realTimeStdDev)
{
PRUint32 tps = PR_TicksPerSecond();
*realTimeSamples = mTimingData.mCount;
double mean = mTimingData.mTotalTime / mTimingData.mCount;
*realTimeMean = (PRIntervalTime)mean * 1000 / tps;
double variance = fabs(mTimingData.mTotalSquaredTime / mTimingData.mCount - mean * mean);
*realTimeStdDev = sqrt(variance) * 1000 / tps;
return NS_OK;
}
NS_IMETHODIMP
nsStopwatch::GetCPUTimeStats(double *cpuTimeSamples, double *cpuTimeMean, double *cpuTimeStdDev)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsStopwatch::Describe(nsILog *out, const char* msg)
{
nsresult rv;
double realTimeSamples, realTimeMean, realTimeStdDev;
rv = GetRealTimeStats(&realTimeSamples, &realTimeMean, &realTimeStdDev);
if (NS_FAILED(rv)) return rv;
double mean, factor, stdDev;
const char* timeUnits;
TimeUnits(realTimeMean, &mean, &timeUnits, &factor);
stdDev = realTimeStdDev * factor;
if (realTimeSamples > 1) {
NS_LOG(out, STDOUT, ("%s elapsed time: %.2f +/- %.2f %s (%d %s) [%s]\n",
mName, mean, stdDev, timeUnits,
(PRUint32)realTimeSamples, mCountUnits, msg));
}
else {
NS_LOG(out, STDOUT, ("%s elapsed time: %.2f %s [%s]\n",
mName, mean, timeUnits, msg));
}
TimeUnits(1/realTimeMean, &mean, &timeUnits, &factor);
NS_LOG(out, STDOUT, ("==> %.2f %s/%s\n", mean, mCountUnits, timeUnits));
double cpuTimeSamples, cpuTimeMean, cpuTimeStdDev;
rv = GetCPUTimeStats(&cpuTimeSamples, &cpuTimeMean, &cpuTimeStdDev);
if (NS_SUCCEEDED(rv)) {
TimeUnits(cpuTimeMean, &mean, &timeUnits, &factor);
stdDev = cpuTimeStdDev * factor;
NS_LOG(out, STDOUT, ("%s cpu time: %.2f +/- %.2f %s (%d %s)\n",
mName, mean, stdDev, timeUnits,
cpuTimeSamples, mCountUnits));
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
#endif // NS_ENABLE_STOPWATCH