зеркало из https://github.com/mozilla/gecko-dev.git
405 строки
11 KiB
C++
405 строки
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
|