blob: b49202deb15aef4ae94ef5ad58503f4396d34907 [file] [log] [blame]
/*
* Copyright 2009, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Implements metrics and metrics collections
#include "metrics.h"
#ifndef OS_WIN
#include <pthread.h>
#else // OS_WIN
#include "lock.h"
#endif // OS_WIN
namespace stats_report {
// Make sure global stats collection is placed in zeroed storage so as to avoid
// initialization order snafus.
MetricCollectionBase g_global_metric_storage = { 0, 0 };
MetricCollection &g_global_metrics =
*static_cast<MetricCollection*>(&g_global_metric_storage);
#ifdef OS_WIN
#pragma warning(push)
#endif
// C4640: construction of local static object is not thread-safe.
// C4073: initializers put in library initialization area.
#ifdef OS_WIN
#pragma warning(disable : 4640 4073)
#endif
// Serialize all metric manipulation and access under this lock.
#ifdef OS_WIN
// Initializes g_lock before other global objects of user defined types.
// It assumes the program is single threaded while executing CRT startup and
// exit code.
#pragma init_seg(lib)
LLock g_lock;
#pragma warning(pop)
#else // OS_WIN
// On non-Windowsen we use a link time initialized pthread mutex.
pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
#endif // OS_WIN
class MetricBase::ObjectLock {
public:
explicit ObjectLock(const MetricBase *metric) : metric_(metric) {
metric_->Lock();
}
~ObjectLock() {
metric_->Unlock();
}
private:
MetricBase const *const metric_;
DISALLOW_COPY_AND_ASSIGN(ObjectLock);
};
void MetricBase::Lock() const {
#ifdef OS_WIN
g_lock.Lock();
#else // OS_WIN
pthread_mutex_lock(&g_lock);
#endif // OS_WIN
}
void MetricBase::Unlock() const {
#ifdef OS_WIN
g_lock.Unlock();
#else // OS_WIN
pthread_mutex_unlock(&g_lock);
#endif // OS_WIN
}
MetricBase::MetricBase(const char *name,
MetricType type,
MetricCollectionBase *coll)
: name_(name), type_(type), next_(coll->first_), coll_(coll) {
DCHECK_NE(static_cast<MetricCollectionBase*>(NULL), coll_);
DCHECK_EQ(false, coll_->initialized_);
coll->first_ = this;
}
MetricBase::MetricBase(const char *name, MetricType type)
: name_(name), type_(type), next_(NULL), coll_(NULL) {
}
MetricBase::~MetricBase() {
if (coll_) {
DCHECK_EQ(this, coll_->first_);
LOG_IF(WARNING, coll_->initialized_)
<< "Warning: Metric destructor called without call to Uninitialize().";
coll_->first_ = next_;
} else {
DCHECK(NULL == next_);
}
}
void IntegerMetricBase::Set(uint64 value) {
ObjectLock lock(this);
value_ = value;
}
uint64 IntegerMetricBase::value() const {
ObjectLock lock(this);
uint64 ret = value_;
return ret;
}
void IntegerMetricBase::Increment() {
ObjectLock lock(this);
++value_;
}
void IntegerMetricBase::Decrement() {
ObjectLock lock(this);
--value_;
}
void IntegerMetricBase::Add(uint64 value) {
ObjectLock lock(this);
value_ += value;
}
void IntegerMetricBase::Subtract(uint64 value) {
ObjectLock lock(this);
if (value_ < value)
value_ = 0;
else
value_ -= value;
}
uint64 CountMetric::Reset() {
ObjectLock lock(this);
uint64 ret = value_;
value_ = 0;
return ret;
}
TimingMetric::TimingData TimingMetric::Reset() {
ObjectLock lock(this);
TimingData ret = data_;
Clear();
return ret;
}
uint32 TimingMetric::count() const {
ObjectLock lock(this);
uint32 ret = data_.count;
return ret;
}
uint64 TimingMetric::sum() const {
ObjectLock lock(this);
uint64 ret = data_.sum;
return ret;
}
uint64 TimingMetric::minimum() const {
ObjectLock lock(this);
uint64 ret = data_.minimum;
return ret;
}
uint64 TimingMetric::maximum() const {
ObjectLock lock(this);
uint64 ret = data_.maximum;
return ret;
}
uint64 TimingMetric::average() const {
ObjectLock lock(this);
uint64 ret = 0;
if (0 == data_.count) {
DCHECK_EQ(0U, data_.sum);
} else {
ret = data_.sum / data_.count;
}
return ret;
}
void TimingMetric::AddSample(uint64 time_ms) {
ObjectLock lock(this);
if (0 == data_.count) {
data_.minimum = time_ms;
data_.maximum = time_ms;
} else {
if (data_.minimum > time_ms)
data_.minimum = time_ms;
if (data_.maximum < time_ms)
data_.maximum = time_ms;
}
data_.count++;
data_.sum += time_ms;
}
void TimingMetric::AddSamples(uint64 count, uint64 total_time_ms) {
if (0 == count)
return;
uint64 time_ms = total_time_ms / count;
ObjectLock lock(this);
if (0 == data_.count) {
data_.minimum = time_ms;
data_.maximum = time_ms;
} else {
if (data_.minimum > time_ms)
data_.minimum = time_ms;
if (data_.maximum < time_ms)
data_.maximum = time_ms;
}
// TODO: truncation from 64 to 32 may occur here.
DCHECK_LE(count, kuint32max);
data_.count += static_cast<uint32>(count);
data_.sum += total_time_ms;
}
void TimingMetric::Clear() {
memset(&data_, 0, sizeof(data_));
}
void BoolMetric::Set(bool value) {
ObjectLock lock(this);
value_ = value ? kBoolTrue : kBoolFalse;
}
BoolMetric::TristateBoolValue BoolMetric::Reset() {
ObjectLock lock(this);
TristateBoolValue ret = value_;
value_ = kBoolUnset;
return ret;
}
void MetricCollection::Initialize() {
DCHECK(!initialized());
initialized_ = true;
}
void MetricCollection::Uninitialize() {
DCHECK(initialized());
initialized_ = false;
}
} // namespace stats_report