diff options
Diffstat (limited to 'o3d/statsreport')
49 files changed, 5649 insertions, 0 deletions
diff --git a/o3d/statsreport/aggregator-mac.h b/o3d/statsreport/aggregator-mac.h new file mode 100644 index 0000000..046e10f --- /dev/null +++ b/o3d/statsreport/aggregator-mac.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + + +// Mac aggregator, which aggregates counters to a keyvaluetable. It is the +// interface to the keyvaluetable for all clients. + +#ifndef O3D_STATSREPORT_AGGREGATOR_MAC_H__ +#define O3D_STATSREPORT_AGGREGATOR_MAC_H__ + +#include <Cocoa/Cocoa.h> + +#include <string> +#include "statsreport/aggregator.h" +#include "base/scoped_ptr.h" + +namespace stats_report { + +class Formatter; + +class MetricsAggregatorMac: public MetricsAggregator { + public: + // @param coll the metrics collection to aggregate, most usually this + // is g_global_metrics. + explicit MetricsAggregatorMac(const MetricCollection &coll); + virtual ~MetricsAggregatorMac(); + + + protected: + virtual bool StartAggregation(); + virtual void EndAggregation(); + + virtual void Aggregate(CountMetric *metric); + virtual void Aggregate(TimingMetric *metric); + virtual void Aggregate(IntegerMetric *metric); + virtual void Aggregate(BoolMetric *metric); + + private: + NSAutoreleasePool *pool_; + NSMutableDictionary *dict_; + NSString *storePath_; + + DISALLOW_COPY_AND_ASSIGN(MetricsAggregatorMac); +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_AGGREGATOR_MAC_H__ + diff --git a/o3d/statsreport/aggregator-mac.mm b/o3d/statsreport/aggregator-mac.mm new file mode 100644 index 0000000..69699ab --- /dev/null +++ b/o3d/statsreport/aggregator-mac.mm @@ -0,0 +1,152 @@ + +/* + * 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. + */ + + +// Implementation of a Mac metrics aggregator. + +#include "aggregator-mac.h" +#include "const-mac.h" +#include "formatter.h" + + +namespace stats_report { + + +MetricsAggregatorMac::MetricsAggregatorMac(const MetricCollection &coll) + : MetricsAggregator(coll), + dict_(nil), + pool_(nil){ + storePath_ = O3DStatsPath(); +} + +MetricsAggregatorMac::~MetricsAggregatorMac() { +} + + +bool MetricsAggregatorMac::StartAggregation() { + pool_ = [[NSAutoreleasePool alloc] init]; + dict_ = [NSMutableDictionary dictionaryWithContentsOfFile:storePath_]; + if (dict_ == nil) + dict_ = [[[NSMutableDictionary alloc] init] autorelease]; + + return true; +} + +void MetricsAggregatorMac::EndAggregation() { + [dict_ writeToFile:storePath_ atomically:YES]; + dict_ = nil; // it will get autoreleased when the pool dies + + [pool_ release]; + pool_ = nil; +} + +void MetricsAggregatorMac::Aggregate(CountMetric *metric) { + // do as little as possible if no value + uint64 value = metric->Reset(); + if (0 == value) + return; + + NSString *keyName = [NSString stringWithFormat:@"C_%s", metric->name()]; + NSNumber *previousValue = [dict_ objectForKey:keyName]; + if (previousValue == nil) + previousValue = [NSNumber numberWithLongLong:0]; + + NSNumber *newTotal = + [NSNumber numberWithLongLong:value + [previousValue longLongValue]]; + [dict_ setObject:newTotal forKey:keyName]; +} + + +static long long LLMin(long long a, long long b) { + return a < b ? a : b; +} + +static long long LLMax(long long a, long long b) { + return a > b ? a : b; +} + +void MetricsAggregatorMac::Aggregate(TimingMetric *metric) { + // do as little as possible if no value + TimingMetric::TimingData newValue = metric->Reset(); + if (0 == newValue.count) + return; + + NSString *keyName = [NSString stringWithFormat:@"T_%s", metric->name()]; + NSArray *previousValue = [dict_ objectForKey:keyName]; + NSArray *newTotal = nil; + + if (previousValue == nil) { + newTotal = [NSArray arrayWithObjects: + [NSNumber numberWithInt:newValue.count], + [NSNumber numberWithLongLong:newValue.sum], + [NSNumber numberWithLongLong:newValue.minimum], + [NSNumber numberWithLongLong:newValue.maximum], + nil]; + } else { + int previousCount = [[previousValue objectAtIndex:0] intValue]; + long long previousSum = [[previousValue objectAtIndex:1] longLongValue]; + long long previousMin = [[previousValue objectAtIndex:2] longLongValue]; + long long previousMax = [[previousValue objectAtIndex:3] longLongValue]; + + newTotal = + [NSArray arrayWithObjects: + [NSNumber numberWithInt:newValue.count + previousCount], + [NSNumber numberWithLongLong:newValue.sum + previousSum], + [NSNumber numberWithLongLong:LLMin(newValue.minimum, previousMin)], + [NSNumber numberWithLongLong:LLMax(newValue.maximum, previousMax)], + nil]; + } + + [dict_ setObject:newTotal forKey:keyName]; +} + +void MetricsAggregatorMac::Aggregate(IntegerMetric *metric) { + // do as little as possible if no value + int64 value = metric->value(); // yes, we only have 63 positive bits here :( + if (0 == value) + return; + + NSString *keyName = [NSString stringWithFormat:@"I_%s", metric->name()]; + [dict_ setObject:[NSNumber numberWithLongLong:value] forKey:keyName]; +} + +void MetricsAggregatorMac::Aggregate(BoolMetric *metric) { + // do as little as possible if no value + int32 value = metric->Reset(); + if (BoolMetric::kBoolUnset == value) + return; + + NSString *keyName = [NSString stringWithFormat:@"B_%s", metric->name()]; + [dict_ setObject:[NSNumber numberWithBool:(value != 0)] forKey:keyName]; +} + +} // namespace stats_report diff --git a/o3d/statsreport/aggregator-posix-inl.h b/o3d/statsreport/aggregator-posix-inl.h new file mode 100644 index 0000000..e147bf6 --- /dev/null +++ b/o3d/statsreport/aggregator-posix-inl.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + + +// Posix aggregator inlines. + +#ifndef O3D_STATSREPORT_AGGREGATOR_POSIX_INL_H__ +#define O3D_STATSREPORT_AGGREGATOR_POSIX_INL_H__ + +#include <string> +#include "aggregator-posix.h" + +namespace stats_report { + +template <typename ValueType> +bool MetricsAggregatorPosix::GetValue(const string &key, ValueType *value) { + StartAggregation(); + bool success = transaction_->Get(key, value); + EndAggregation(); + return success; +} + +template <typename ValueType> +bool MetricsAggregatorPosix::SetValue(const string &key, ValueType value) { + StartAggregation(); + bool success = transaction_->Put(key, value); + EndAggregation(); + return success; +} + +} // namespace stats_report + +#endif // O3D_STATSREPORT_AGGREGATOR_POSIX_INL_H__ diff --git a/o3d/statsreport/aggregator-posix.cc b/o3d/statsreport/aggregator-posix.cc new file mode 100644 index 0000000..11e99fa --- /dev/null +++ b/o3d/statsreport/aggregator-posix.cc @@ -0,0 +1,193 @@ +/* + * 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. + */ + + +// Implementation of a Posix metrics aggregator. + +#include "aggregator-posix.h" +#include "const-posix.h" +#include "formatter.h" +#include "util/endian/endian.h" +#include "statsreport/common/pathhelpers.h" + +namespace stats_report { + +MetricsAggregatorPosix::MetricsAggregatorPosix(const MetricCollection &coll) { + key_value_table_.reset(new KeyValueTable(get_cache_dir() + "stats.sqlite3", + "stats")); +} + +MetricsAggregatorPosix::~MetricsAggregatorPosix() { +} + +void MetricsAggregatorPosix::ResetMetrics() { + StartAggregation(); + (void)transaction_->Clear(); + EndAggregation(); +} + +namespace { + +void AddMetric(const string& key, + const ScopedStatement &statement, + void *ref_con) { + Formatter *formatter = + static_cast<Formatter *>(ref_con); + + string original_key; + scoped_ptr<MetricBase> metric; + + if (key.compare(0, kCountsKeyName.length(), kCountsKeyName) == 0) { + int64 value; + GetColumn(statement.get(), 0, &value); + original_key = key.substr(kCountsKeyName.length()); + metric.reset(new CountMetric(original_key.c_str(), value)); + } else if (key.compare(0, kTimingsKeyName.length(), kTimingsKeyName) == 0) { + std::vector<BYTE> value_bytes; + TimingMetric::TimingData value; + GetColumn(statement.get(), 0, &value_bytes); + memcpy(&value, &value_bytes[0], value_bytes.size()); + value.count = gntohl(value.count); + value.sum = gntohll(value.sum); + value.minimum = gntohll(value.minimum); + value.maximum = gntohll(value.maximum); + metric.reset(new TimingMetric(original_key.c_str(), value)); + } else if (key.compare(0, kIntegersKeyName.length(), kIntegersKeyName) == 0) { + int64 value; + GetColumn(statement.get(), 0, &value); + original_key = key.substr(kIntegersKeyName.length()); + metric.reset(new IntegerMetric(original_key.c_str(), value)); + } else if (key.compare(0, kBooleansKeyName.length(), kBooleansKeyName) == 0) { + int32 value; + GetColumn(statement.get(), 0, &value); + original_key = key.substr(kBooleansKeyName.length()); + metric.reset(new BoolMetric(original_key.c_str(), value)); + } + + if (!original_key.empty()) + formatter->AddMetric(&*metric); +} + +} // namespace + +void MetricsAggregatorPosix::FormatMetrics(Formatter *formatter) { + StartAggregation(); + transaction_->Iterate(AddMetric, formatter); + EndAggregation(); +} + +bool MetricsAggregatorPosix::StartAggregation() { + transaction_.reset(new KeyValueTransaction(&*key_value_table_)); + + return true; +} + +void MetricsAggregatorPosix::EndAggregation() { + transaction_.reset(); +} + +void MetricsAggregatorPosix::Aggregate(CountMetric *metric) { + // do as little as possible if no value + uint64 value = metric->Reset(); + if (0 == value) + return; + + string name(metric->name()); + name.insert(0, kCountsKeyName); + int64 reg_value = 0; // yes, we only have 63 positive bits here :( + if (!transaction_->Get(name, ®_value)) { + // TODO: clean up?? + } + reg_value += value; + + (void)transaction_->Put(name, reg_value); +} + +void MetricsAggregatorPosix::Aggregate(TimingMetric *metric) { + // do as little as possible if no value + TimingMetric::TimingData value = metric->Reset(); + if (0 == value.count) + return; + + string name(metric->name()); + name.insert(0, kTimingsKeyName); + std::vector<BYTE> reg_value_bytes; + TimingMetric::TimingData reg_value; + if (!transaction_->Get(name, ®_value_bytes)) { + reg_value_bytes.resize(sizeof(reg_value)); + memcpy(®_value, &value, sizeof(value)); + } else { + memcpy(®_value, ®_value_bytes[0], reg_value_bytes.size()); + reg_value.count = gntohl(reg_value.count); + reg_value.sum = gntohll(reg_value.sum); + reg_value.minimum = gntohll(reg_value.minimum); + reg_value.maximum = gntohll(reg_value.maximum); + + reg_value.count += value.count; + reg_value.sum += value.sum; + reg_value.minimum = std::min(reg_value.minimum, value.minimum); + reg_value.maximum = std::max(reg_value.maximum, value.maximum); + } + + reg_value.count = ghtonl(reg_value.count); + reg_value.sum = ghtonll(reg_value.sum); + reg_value.minimum = ghtonll(reg_value.minimum); + reg_value.maximum = ghtonll(reg_value.maximum); + + memcpy(®_value_bytes[0], ®_value, sizeof(reg_value)); + (void)transaction_->Put(name, reg_value_bytes); +} + +void MetricsAggregatorPosix::Aggregate(IntegerMetric *metric) { + // do as little as possible if no value + int64 value = metric->value(); // yes, we only have 63 positive bits here :( + if (0 == value) + return; + + string name(metric->name()); + name.insert(0, kIntegersKeyName); + + (void)transaction_->Put(name, value); +} + +void MetricsAggregatorPosix::Aggregate(BoolMetric *metric) { + // do as little as possible if no value + int32 value = metric->Reset(); + if (BoolMetric::kBoolUnset == value) + return; + + string name(metric->name()); + name.insert(0, kBooleansKeyName); + + (void)transaction_->Put(name, value); +} + +} // namespace stats_report diff --git a/o3d/statsreport/aggregator-posix.h b/o3d/statsreport/aggregator-posix.h new file mode 100644 index 0000000..10117a4 --- /dev/null +++ b/o3d/statsreport/aggregator-posix.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + + +// Posix aggregator, which aggregates counters to a keyvaluetable. It is the +// interface to the keyvaluetable for all clients. + +#ifndef O3D_STATSREPORT_AGGREGATOR_POSIX_H__ +#define O3D_STATSREPORT_AGGREGATOR_POSIX_H__ + +#include <string> +#include "aggregator.h" +#include "base/scoped_ptr.h" +#include "backend/keyvaluetable.h" + +namespace stats_report { + +class Formatter; + +class MetricsAggregatorPosix: public MetricsAggregator { + public: + // @param coll the metrics collection to aggregate, most usually this + // is g_global_metrics. + explicit MetricsAggregatorPosix(const MetricCollection &coll); + ~MetricsAggregatorPosix(); + + template <typename ValueType> + bool GetValue(const string &key, ValueType *value); + template <typename ValueType> + bool SetValue(const string &key, ValueType value); + + void ResetMetrics(); + void FormatMetrics(Formatter *formatter); + + protected: + virtual bool StartAggregation(); + virtual void EndAggregation(); + + virtual void Aggregate(CountMetric *metric); + virtual void Aggregate(TimingMetric *metric); + virtual void Aggregate(IntegerMetric *metric); + virtual void Aggregate(BoolMetric *metric); + + private: + // The keyvaluetable + scoped_ptr<KeyValueTable> key_value_table_; + + // The current transaction + scoped_ptr<KeyValueTransaction> transaction_; + + DISALLOW_COPY_AND_ASSIGN(MetricsAggregatorPosix); +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_AGGREGATOR_POSIX_H__ diff --git a/o3d/statsreport/aggregator-win32.cc b/o3d/statsreport/aggregator-win32.cc new file mode 100644 index 0000000..2f5678b --- /dev/null +++ b/o3d/statsreport/aggregator-win32.cc @@ -0,0 +1,172 @@ +/* + * 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. + */ + + +// Implementation of Win32 metrics aggregator. +#include "aggregator-win32.h" +#include "const-win32.h" +#include "util-win32.h" + +namespace stats_report { + +MetricsAggregatorWin32::MetricsAggregatorWin32(const MetricCollection &coll, + const wchar_t *key_name) + : MetricsAggregator(coll), + is_machine_(false) { + DCHECK(NULL != key_name); + + key_name_.Format(kStatsKeyFormatString, key_name); +} + +MetricsAggregatorWin32::MetricsAggregatorWin32(const MetricCollection &coll, + const wchar_t *key_name, + bool is_machine) + : MetricsAggregator(coll), + is_machine_(is_machine) { + DCHECK(NULL != key_name); + + key_name_.Format(kStatsKeyFormatString, key_name); +} + +MetricsAggregatorWin32::~MetricsAggregatorWin32() { +} + +bool MetricsAggregatorWin32::StartAggregation() { + DCHECK(NULL == key_.m_hKey); + + HKEY parent_key = is_machine_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + LONG err = key_.Create(parent_key, key_name_); + if (err != ERROR_SUCCESS) + return false; + + return true; +} + +void MetricsAggregatorWin32::EndAggregation() { + count_key_.Close(); + timing_key_.Close(); + integer_key_.Close(); + bool_key_.Close(); + + key_.Close(); +} + +bool MetricsAggregatorWin32::EnsureKey(const wchar_t *name, CRegKey *key) { + if (NULL != key->m_hKey) + return true; + + LONG err = key->Create(key_, name); + if (ERROR_SUCCESS != err) { + DCHECK(NULL == key->m_hKey); + // TODO: log? + return false; + } + + return true; +} + +void MetricsAggregatorWin32::Aggregate(CountMetric *metric) { + DCHECK(NULL != metric); + + // do as little as possible if no value + uint64 value = metric->Reset(); + if (0 == value) + return; + + if (!EnsureKey(kCountsKeyName, &count_key_)) + return; + + CString name(metric->name()); + uint64 reg_value = 0; + if (!GetData(&count_key_, name, ®_value)) { + // TODO: clean up?? + } + reg_value += value; + + DWORD err = count_key_.SetBinaryValue(name, ®_value, sizeof(reg_value)); +} + +void MetricsAggregatorWin32::Aggregate(TimingMetric *metric) { + DCHECK(NULL != metric); + + // do as little as possible if no value + TimingMetric::TimingData value = metric->Reset(); + if (0 == value.count) + return; + + if (!EnsureKey(kTimingsKeyName, &timing_key_)) + return; + + CString name(metric->name()); + TimingMetric::TimingData reg_value; + if (!GetData(&timing_key_, name, ®_value)) { + memcpy(®_value, &value, sizeof(value)); + } else { + reg_value.count += value.count; + reg_value.sum += value.sum; + reg_value.minimum = std::min(reg_value.minimum, value.minimum); + reg_value.maximum = std::max(reg_value.maximum, value.maximum); + } + + DWORD err = timing_key_.SetBinaryValue(name, ®_value, sizeof(reg_value)); +} + +void MetricsAggregatorWin32::Aggregate(IntegerMetric *metric) { + DCHECK(NULL != metric); + + // do as little as possible if no value + uint64 value = metric->value(); + if (0 == value) + return; + + if (!EnsureKey(kIntegersKeyName, &integer_key_)) + return; + + DWORD err = integer_key_.SetBinaryValue(CString(metric->name()), + &value, sizeof(value)); +} + +void MetricsAggregatorWin32::Aggregate(BoolMetric *metric) { + DCHECK(NULL != metric); + + // do as little as possible if no value + int32 value = metric->Reset(); + if (BoolMetric::kBoolUnset == value) + return; + + if (!EnsureKey(kBooleansKeyName, &bool_key_)) + return; + + DWORD err = bool_key_.SetBinaryValue(CString(metric->name()), + &value, sizeof(value)); +} + +} // namespace stats_report diff --git a/o3d/statsreport/aggregator-win32.h b/o3d/statsreport/aggregator-win32.h new file mode 100644 index 0000000..566de62 --- /dev/null +++ b/o3d/statsreport/aggregator-win32.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + + +// Win32 aggregator, which aggregates counters to registry under a named +// Mutex lock. +#ifndef O3D_STATSREPORT_AGGREGATOR_WIN32_H__ +#define O3D_STATSREPORT_AGGREGATOR_WIN32_H__ + +#include "aggregator.h" +#include <atlbase.h> +#include <atlstr.h> + +namespace stats_report { + +class MetricsAggregatorWin32: public MetricsAggregator { + public: + // @param coll the metrics collection to aggregate, most usually this + // is g_global_metrics. + // @param app_name name of the subkey under HKCU\Software\Google we + // aggregate to. Should be or encode the application name for + // transparency, e.g. "Scour", or "Gears". + MetricsAggregatorWin32(const MetricCollection &coll, + const wchar_t *app_name); + + // @param is_machine specifies the registry hive where the stats are + // aggregated to. + MetricsAggregatorWin32(const MetricCollection &coll, + const wchar_t *app_name, + bool is_machine); + virtual ~MetricsAggregatorWin32(); + + protected: + virtual bool StartAggregation(); + virtual void EndAggregation(); + + virtual void Aggregate(CountMetric *metric); + virtual void Aggregate(TimingMetric *metric); + virtual void Aggregate(IntegerMetric *metric); + virtual void Aggregate(BoolMetric *metric); + private: + enum { + // Max length of time we wait for the mutex on StartAggregation. + kMaxMutexWaitMs = 1000, // 1 second for now + }; + + // Ensures that *key is open, opening it if it's NULL + // @return true on success, false on failure to open key + bool EnsureKey(const wchar_t *name, CRegKey *key); + + // Mutex name for locking access to key + CString mutex_name_; + + // Subkey name, as per constructor docs + CString key_name_; + + // Handle to our subkey under HKCU\Software\Google + CRegKey key_; + + // Subkeys under the above + // @{ + CRegKey count_key_; + CRegKey timing_key_; + CRegKey integer_key_; + CRegKey bool_key_; + // @} + + // Specifies HKLM or HKCU, respectively. + bool is_machine_; + + DISALLOW_COPY_AND_ASSIGN(MetricsAggregatorWin32); +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_AGGREGATOR_WIN32_H__ diff --git a/o3d/statsreport/aggregator-win32_unittest.cc b/o3d/statsreport/aggregator-win32_unittest.cc new file mode 100644 index 0000000..451927b --- /dev/null +++ b/o3d/statsreport/aggregator-win32_unittest.cc @@ -0,0 +1,116 @@ +/* + * 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. + */ + + +// Implementation of Win32 metrics aggregator. +#include "aggregator-win32.h" +#include "aggregator-win32_unittest.h" +#include "aggregator_unittest.h" +#include "gtest/gtest.h" + +using ::stats_report::MetricsAggregatorWin32; +using ::stats_report::TimingMetric; + +#define APP_NAME_STRING L"aggregator-win32_unittest" +#define PREFIX_KEY_STRING L"Software\\Google\\" +#define SUFFIX_KEY_STRING L"\\UsageStats\\Daily" +#define ROOT_KEY_STRING PREFIX_KEY_STRING APP_NAME_STRING +#define KEY_STRING ROOT_KEY_STRING SUFFIX_KEY_STRING + +const wchar_t MetricsAggregatorWin32Test::kAppName[] = APP_NAME_STRING; +const wchar_t MetricsAggregatorWin32Test::kRootKeyName[] = ROOT_KEY_STRING; +const wchar_t MetricsAggregatorWin32Test::kCountsKeyName[] = + KEY_STRING L"\\Counts"; +const wchar_t MetricsAggregatorWin32Test::kTimingsKeyName[] = + KEY_STRING L"\\Timings"; +const wchar_t MetricsAggregatorWin32Test::kIntegersKeyName[] = + KEY_STRING L"\\Integers"; +const wchar_t MetricsAggregatorWin32Test::kBoolsKeyName[] = + KEY_STRING L"\\Booleans"; + + +#define EXPECT_REGVAL_EQ(value, key_name, value_name) do { \ + char buf[sizeof(value)]; \ + ULONG len = sizeof(buf); \ + CRegKey key; \ + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name)); \ + EXPECT_EQ(ERROR_SUCCESS, key.QueryBinaryValue(value_name, buf, &len)); \ + EXPECT_EQ(sizeof(buf), len); \ + EXPECT_EQ(0, memcmp(&value, buf, sizeof(buf))); \ + } while (0) + +TEST_F(MetricsAggregatorWin32Test, AggregateWin32) { + MetricsAggregatorWin32 agg(coll_, kAppName); + + EXPECT_TRUE(agg.AggregateMetrics()); + AddStats(); + EXPECT_TRUE(agg.AggregateMetrics()); + + { + int64 one = 1, two = 2; + EXPECT_REGVAL_EQ(one, kCountsKeyName, L"c1"); + EXPECT_REGVAL_EQ(two, kCountsKeyName, L"c2"); + + TimingMetric::TimingData data1 = { 2, 0, 1500, 500, 1000 }; + TimingMetric::TimingData data2 = { 2, 0, 2030, 30, 2000 }; + EXPECT_REGVAL_EQ(data1, kTimingsKeyName, L"t1"); + EXPECT_REGVAL_EQ(data2, kTimingsKeyName, L"t2"); + + EXPECT_REGVAL_EQ(one, kIntegersKeyName, L"i1"); + EXPECT_REGVAL_EQ(two, kIntegersKeyName, L"i2"); + + int32 bool_true = 1, bool_false = 0; + EXPECT_REGVAL_EQ(bool_true, kBoolsKeyName, L"b1"); + EXPECT_REGVAL_EQ(bool_false, kBoolsKeyName, L"b2"); + } + + AddStats(); + EXPECT_TRUE(agg.AggregateMetrics()); + + { + int64 two = 2, four = 4; + EXPECT_REGVAL_EQ(two, kCountsKeyName, L"c1"); + EXPECT_REGVAL_EQ(four, kCountsKeyName, L"c2"); + + TimingMetric::TimingData data1 = { 4, 0, 3000, 500, 1000 }; + TimingMetric::TimingData data2 = { 4, 0, 4060, 30, 2000 }; + EXPECT_REGVAL_EQ(data1, kTimingsKeyName, L"t1"); + EXPECT_REGVAL_EQ(data2, kTimingsKeyName, L"t2"); + + int64 one = 1; + EXPECT_REGVAL_EQ(one, kIntegersKeyName, L"i1"); + EXPECT_REGVAL_EQ(two, kIntegersKeyName, L"i2"); + + int32 bool_true = 1, bool_false = 0; + EXPECT_REGVAL_EQ(bool_true, kBoolsKeyName, L"b1"); + EXPECT_REGVAL_EQ(bool_false, kBoolsKeyName, L"b2"); + } +} diff --git a/o3d/statsreport/aggregator-win32_unittest.h b/o3d/statsreport/aggregator-win32_unittest.h new file mode 100644 index 0000000..ceefc90 --- /dev/null +++ b/o3d/statsreport/aggregator-win32_unittest.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + + +#ifndef O3D_STATSREPORT_AGGREGATOR_WIN32_UNITTEST_H__ +#define O3D_STATSREPORT_AGGREGATOR_WIN32_UNITTEST_H__ + +#include "aggregator_unittest.h" +#include "aggregator-win32.h" + +// Shared test fixture for win32 unit tests +class MetricsAggregatorWin32Test: public MetricsAggregatorTest { + public: + virtual void SetUp() { + // clean the registry + SHDeleteKey(HKEY_CURRENT_USER, kRootKeyName); + MetricsAggregatorTest::SetUp(); + } + virtual void TearDown() { + MetricsAggregatorTest::TearDown(); + SHDeleteKey(HKEY_CURRENT_USER, kRootKeyName); + } + + void AddStats() { + ++c1_; + ++c2_; + ++c2_; + + t1_.AddSample(1000); + t1_.AddSample(500); + + t2_.AddSample(2000); + t2_.AddSample(30); + + i1_ = 1; + i2_ = 2; + + b1_ = true; + b2_ = false; + } + + static const wchar_t kAppName[]; + static const wchar_t kRootKeyName[]; + static const wchar_t kCountsKeyName[]; + static const wchar_t kTimingsKeyName[]; + static const wchar_t kIntegersKeyName[]; + static const wchar_t kBoolsKeyName[]; +}; + +#endif // O3D_STATSREPORT_AGGREGATOR_WIN32_UNITTEST_H__ diff --git a/o3d/statsreport/aggregator.cc b/o3d/statsreport/aggregator.cc new file mode 100644 index 0000000..41e6fbf --- /dev/null +++ b/o3d/statsreport/aggregator.cc @@ -0,0 +1,94 @@ +/* + * 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. + */ + + +// Implementation of helper classes to aggregate the collected in-memory +// stats to persistent storage. +#include "aggregator.h" + +namespace stats_report { + +bool MetricsAggregator::AggregateMetrics() { + if (!StartAggregation()) + return false; + + MetricIterator it(coll_), end; + for (; it != end; ++it) { + MetricBase *metric = *it; + DCHECK(NULL != metric); + + switch (metric->type()) { + case kCountType: + Aggregate(metric->AsCount()); + break; + case kTimingType: + Aggregate(metric->AsTiming()); + break; + case kIntegerType: + Aggregate(metric->AsInteger()); + break; + case kBoolType: + Aggregate(metric->AsBool()); + break; + default: + DCHECK(false && "Impossible metric type"); + break; + } + } + + // done, close up + EndAggregation(); + + return true; +} + +MetricsAggregator::MetricsAggregator() : coll_(g_global_metrics) { + DCHECK(coll_.initialized()); +} + +MetricsAggregator::MetricsAggregator(const MetricCollection &coll) + : coll_(coll) { + DCHECK(coll_.initialized()); +} + +MetricsAggregator::~MetricsAggregator() { +} + +bool MetricsAggregator::StartAggregation() { + // nothing + return true; +} + +void MetricsAggregator::EndAggregation() { + // nothing +} + +} // namespace stats_report diff --git a/o3d/statsreport/aggregator.h b/o3d/statsreport/aggregator.h new file mode 100644 index 0000000..f5e84e4 --- /dev/null +++ b/o3d/statsreport/aggregator.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + + +// Helper class to aggregate the collected in-memory stats to persistent +// storage. +#ifndef O3D_STATSREPORT_AGGREGATOR_H__ +#define O3D_STATSREPORT_AGGREGATOR_H__ + +#include "metrics.h" + +namespace stats_report { +// TODO: Refactor to avoid cross platform code duplication. + +// Wrapper class and interface for metrics aggregation. This is a platform +// independent class and needs to be subclassed for various platforms and/or +// metrics persistence methods +class MetricsAggregator { + public: + // Aggregate all metrics in the associated collection + // @returns true iff aggregation started successfully, false otherwise. + bool AggregateMetrics(); + + protected: + MetricsAggregator(); + explicit MetricsAggregator(const MetricCollection &coll); + virtual ~MetricsAggregator(); + + // Start aggregation. Override this to grab locks, open files, whatever + // needs to happen or can expedite the individual aggregate steps. + // @return true on success, false on failure. + // @note aggregation will not progress if this function returns false + virtual bool StartAggregation(); + virtual void EndAggregation(); + + virtual void Aggregate(CountMetric *metric) = 0; + virtual void Aggregate(TimingMetric *metric) = 0; + virtual void Aggregate(IntegerMetric *metric) = 0; + virtual void Aggregate(BoolMetric *metric) = 0; + + private: + const MetricCollection &coll_; + + DISALLOW_COPY_AND_ASSIGN(MetricsAggregator); +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_AGGREGATOR_H__ diff --git a/o3d/statsreport/aggregator_unittest.cc b/o3d/statsreport/aggregator_unittest.cc new file mode 100644 index 0000000..44c65cd --- /dev/null +++ b/o3d/statsreport/aggregator_unittest.cc @@ -0,0 +1,140 @@ +/* + * 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. + */ + + +// Implementation of helper classes to aggregate the collected in-memory +// stats to persistent storage. +#include "aggregator.h" +#include "aggregator_unittest.h" +#include "gtest/gtest.h" + +using namespace stats_report; + +class TestMetricsAggregator: public MetricsAggregator { + public: + explicit TestMetricsAggregator(const MetricCollection &coll) + : MetricsAggregator(coll), aggregating_(false), counts_(0), + timings_(0), integers_(0), bools_(0) { + } + + ~TestMetricsAggregator() { + } + + bool aggregating() const { return aggregating_; } + int counts() const { return counts_; } + int timings() const { return timings_; } + int integers() const { return integers_; } + int bools() const { return bools_; } + + protected: + virtual bool StartAggregation() { + aggregating_ = true; + counts_ = 0; + timings_ = 0; + integers_ = 0; + bools_ = 0; + + return true; + } + + virtual void EndAggregation() { + aggregating_ = false; + } + + virtual void Aggregate(CountMetric *metric) { + ASSERT_TRUE(NULL != metric); + EXPECT_TRUE(aggregating()); + metric->Reset(); + ++counts_; + } + + virtual void Aggregate(TimingMetric *metric) { + ASSERT_TRUE(NULL != metric); + EXPECT_TRUE(aggregating()); + metric->Reset(); + ++timings_; + } + virtual void Aggregate(IntegerMetric *metric) { + ASSERT_TRUE(NULL != metric); + EXPECT_TRUE(aggregating()); + // Integer metrics don't get reset on aggregation + ++integers_; + } + virtual void Aggregate(BoolMetric *metric) { + ASSERT_TRUE(NULL != metric); + EXPECT_TRUE(aggregating()); + metric->Reset(); + ++bools_; + } + + private: + bool aggregating_; + int counts_; + int timings_; + int integers_; + int bools_; +}; + +TEST_F(MetricsAggregatorTest, Aggregate) { + TestMetricsAggregator agg(coll_); + + EXPECT_FALSE(agg.aggregating()); + EXPECT_EQ(0, agg.counts()); + EXPECT_EQ(0, agg.timings()); + EXPECT_EQ(0, agg.integers()); + EXPECT_EQ(0, agg.bools()); + EXPECT_TRUE(agg.AggregateMetrics()); + EXPECT_FALSE(agg.aggregating()); + + // check that we saw all counters. + EXPECT_TRUE(kNumCounts == agg.counts()); + EXPECT_TRUE(kNumTimings == agg.timings()); + EXPECT_TRUE(kNumIntegers == agg.integers()); + EXPECT_TRUE(kNumBools == agg.bools()); +} + +class FailureTestMetricsAggregator: public TestMetricsAggregator { + public: + explicit FailureTestMetricsAggregator(const MetricCollection &coll) : + TestMetricsAggregator(coll) { + } + + protected: + virtual bool StartAggregation() { + return false; + } +}; + +TEST_F(MetricsAggregatorTest, AggregateFailure) { + FailureTestMetricsAggregator agg(coll_); + + EXPECT_FALSE(agg.AggregateMetrics()); +} diff --git a/o3d/statsreport/aggregator_unittest.h b/o3d/statsreport/aggregator_unittest.h new file mode 100644 index 0000000..286362e --- /dev/null +++ b/o3d/statsreport/aggregator_unittest.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + + +#ifndef O3D_STATSREPORT_AGGREGATOR_UNITTEST_H__ +#define O3D_STATSREPORT_AGGREGATOR_UNITTEST_H__ + +#include "metrics.h" +#include "gtest/gtest.h" + +// Test fixture shared among aggregator unit tests +class MetricsAggregatorTest: public testing::Test { + public: +#define INIT_METRIC(type, name) name##_(#name, &coll_) +#define DECL_METRIC(type, name) stats_report::type##Metric name##_ + + MetricsAggregatorTest() + : INIT_METRIC(Count, c1), + INIT_METRIC(Count, c2), + INIT_METRIC(Timing, t1), + INIT_METRIC(Timing, t2), + INIT_METRIC(Integer, i1), + INIT_METRIC(Integer, i2), + INIT_METRIC(Bool, b1), + INIT_METRIC(Bool, b2) { + } + + enum { + kNumCounts = 2, + kNumTimings = 2, + kNumIntegers = 2, + kNumBools = 2 + }; + + stats_report::MetricCollection coll_; + DECL_METRIC(Count, c1); + DECL_METRIC(Count, c2); + DECL_METRIC(Timing, t1); + DECL_METRIC(Timing, t2); + DECL_METRIC(Integer, i1); + DECL_METRIC(Integer, i2); + DECL_METRIC(Bool, b1); + DECL_METRIC(Bool, b2); + +#undef INIT_METRIC +#undef DECL_METRIC + + virtual void SetUp() { + coll_.Initialize(); + } + + virtual void TearDown() { + coll_.Uninitialize(); + } +}; + +#endif // O3D_STATSREPORT_AGGREGATOR_UNITTEST_H__ diff --git a/o3d/statsreport/build.scons b/o3d/statsreport/build.scons new file mode 100644 index 0000000..8a844c5 --- /dev/null +++ b/o3d/statsreport/build.scons @@ -0,0 +1,71 @@ +# 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. + + +Import('env') + +env.Append( + LIBPATH = [ '$LIBS_DIR' + ], + LIBS = [ + 'o3d_base', + 'o3dStatsreport_Common', + ], +) + +INPUTS = [ + 'aggregator.cc', + 'formatter.cc', + 'metrics.cc', +] + +if env['TARGET_PLATFORM'] == 'WINDOWS': + INPUTS += [ + 'aggregator-win32.cc', + 'const-win32.cc', + 'persistent_iterator-win32.cc', + 'uploader_aggregation-win32.cc', + 'uploader-win32.cc', + ] + +if env['TARGET_PLATFORM'] == 'MAC': + INPUTS += [ + 'aggregator-mac.mm', + 'const-mac.mm', + 'uploader_aggregation-mac.mm', + 'uploader-mac.mm', + ] + +if env['TARGET_PLATFORM'] == 'POSIX': + INPUTS += [ + 'uploader-posix.cc', + ] + +# Build a library called 'o3dStatsreport' from the input sources. +lib = env.ComponentLibrary('o3dStatsreport', INPUTS) diff --git a/o3d/statsreport/common/build.scons b/o3d/statsreport/common/build.scons new file mode 100644 index 0000000..489e911 --- /dev/null +++ b/o3d/statsreport/common/build.scons @@ -0,0 +1,53 @@ +# 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. + + +Import('env') + +env.Append( + LIBPATH=[ + '$LIBS_DIR' + ], + LIBS = ['o3d_base'] +) + +INPUTS = [] + +if env['TARGET_PLATFORM'] == 'WINDOWS': + INPUTS += [ + 'highres_timer-win32.cc', + ] + +if env['TARGET_PLATFORM'] == 'MAC': + INPUTS += [ + 'highres_timer-mac.cc', + ] + +# Build a library called 'o3dStatsreport_Common' from the input sources. +env.ComponentLibrary('o3dStatsreport_Common', INPUTS) diff --git a/o3d/statsreport/common/const_product.h b/o3d/statsreport/common/const_product.h new file mode 100644 index 0000000..ef4f624 --- /dev/null +++ b/o3d/statsreport/common/const_product.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + + +// Product-specific constants. +// + +#ifndef O3D_STATSREPORT_COMMON_CONST_PRODUCT_H_ +#define O3D_STATSREPORT_COMMON_CONST_PRODUCT_H_ + +#include <build/build_config.h> +#include "base/basictypes.h" + +#define _QUOTEME(x) #x +#define QUOTEME(x) _QUOTEME(x) + +#define _WIDEN(X) L ## X +#define WIDEN(X) _WIDEN(X) + +#define PRODUCT_NAME_STRING "o3d" +#define PRODUCT_VERSION_STRING QUOTEME(O3D_VERSION_NUMBER) + +#define PRODUCT_NAME_STRING_WIDE WIDEN(PRODUCT_NAME_STRING) + +// keep these two in sync +#define REGISTRY_KEY_NAME_STRING "o3d" +#define REGISTRY_KEY_STRING "Software\\Google\\O3D" + +#ifdef OS_WIN +const char kMetricsLockName[] = PRODUCT_NAME_STRING "MetricsAggregationlock"; +#endif + +const char kProductName[] = PRODUCT_NAME_STRING; + +// TODO: Determine if #define below is needed. +// This is a string value to be found under they key +// at REGISTRY_KEY_STRING. This is written by our +// installer +// #define INSTALL_DIR_VALUE_NAME "InstallDir" + +const char kUserAgent[] = "O3D-"; + +const int kStatsUploadIntervalSec = 24 * 60 * 60; // once per day +const int kStatsAggregationIntervalMSec = 5 * 60 * 1000; // every 5 minutes + +// Taken from Google Update +// TODO: CRITICAL: Link to actual Google Update docs so that we don't have +// to keep this up to date. It will mess with our logs. +const wchar_t* const kRegValueUserId = L"ui"; +const wchar_t* const kRelativeGoopdateRegPath = L"Software\\Google\\Update\\"; + +const wchar_t* const kClientstateRegistryKey = + L"Software\\Google\\Update\\ClientState\\" + L"{70308795-045C-42DA-8F4E-D452381A7459}"; +const wchar_t* const kOptInRegistryKey = L"usagestats"; + +#define INTERNET_MAX_PATH_LENGTH 2048 +#define INTERNET_MAX_SCHEME_LENGTH 32 +#define INTERNET_MAX_URL_LENGTH (INTERNET_MAX_SCHEME_LENGTH \ + + sizeof("://") \ + + INTERNET_MAX_PATH_LENGTH) + +#endif // O3D_STATSREPORT_COMMON_CONST_PRODUCT_H_ diff --git a/o3d/statsreport/common/highres_timer-linux.cc b/o3d/statsreport/common/highres_timer-linux.cc new file mode 100644 index 0000000..d577fc3 --- /dev/null +++ b/o3d/statsreport/common/highres_timer-linux.cc @@ -0,0 +1,59 @@ +/* + * 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. + */ + + +#include "statsreport/common/highres_timer.h" + +const uint64 MICROS_IN_MILLI = 1000L; +const uint64 MICROS_IN_HALF_MILLI = 500L; +const uint64 MICROS_IN_HALF_SECOND = 500000L; + +uint64 HighresTimer::GetElapsedMs() const { + uint64 end_time = GetCurrentTicks(); + + // Scale to ms and round to nearest ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + return (static_cast<uint64>(end_time - start_ticks_) + + MICROS_IN_HALF_MILLI) / + MICROS_IN_MILLI; +} + +uint64 HighresTimer::GetElapsedSec() const { + uint64 end_time = GetCurrentTicks(); + + // Scale to ms and round to nearest ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + return (static_cast<uint64>(end_time - start_ticks_) + + MICROS_IN_HALF_SECOND) / + MICROS_IN_SECOND; +} diff --git a/o3d/statsreport/common/highres_timer-linux.h b/o3d/statsreport/common/highres_timer-linux.h new file mode 100644 index 0000000..7574a8f --- /dev/null +++ b/o3d/statsreport/common/highres_timer-linux.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + + +#ifndef O3D_STATSREPORT_COMMON_HIGHRES_TIMER_LINUX_H_ +#define O3D_STATSREPORT_COMMON_HIGHRES_TIMER_LINUX_H_ + +#include "base/basictypes.h" + +#include <sys/time.h> + +const uint64 MICROS_IN_SECOND = 1000000L; + +// A handy class for reliably measuring wall-clock time with decent resolution. +// +// We want to measure time with high resolution on Linux. What to do? +// +// RDTSC? Sure, but how do you convert it to wall clock time? +// clock_gettime? It's not in all Linuxes. +// +// Let's just use gettimeofday; it's good to the microsecond. +class HighresTimer { + public: + // Captures the current start time + HighresTimer(); + + // Captures the current tick, can be used to reset a timer for reuse. + void Start(); + + // Returns the elapsed ticks with full resolution + uint64 GetElapsedTicks() const; + + // Returns the elapsed time in milliseconds, rounded to the nearest + // millisecond. + uint64 GetElapsedMs() const; + + // Returns the elapsed time in seconds, rounded to the nearest second. + uint64 GetElapsedSec() const; + + uint64 start_ticks() const { return start_ticks_; } + + // Returns timer frequency from cache, should be less + // overhead than ::QueryPerformanceFrequency + static uint64 GetTimerFrequency(); + // Returns current ticks + static uint64 GetCurrentTicks(); + + private: + // Captured start time + uint64 start_ticks_; +}; + +inline HighresTimer::HighresTimer() { + Start(); +} + +inline void HighresTimer::Start() { + start_ticks_ = GetCurrentTicks(); +} + +inline uint64 HighresTimer::GetTimerFrequency() { + // fixed; one "tick" is one microsecond + + return MICROS_IN_SECOND; +} + +inline uint64 HighresTimer::GetCurrentTicks() { + timeval tv; + (void)gettimeofday(&tv, 0); + + return tv.tv_sec * MICROS_IN_SECOND + tv.tv_usec; +} + +inline uint64 HighresTimer::GetElapsedTicks() const { + return start_ticks_ - GetCurrentTicks(); +} + +#endif // O3D_STATSREPORT_COMMON_HIGHRES_TIMER_LINUX_H_ diff --git a/o3d/statsreport/common/highres_timer-mac.cc b/o3d/statsreport/common/highres_timer-mac.cc new file mode 100644 index 0000000..aa14525 --- /dev/null +++ b/o3d/statsreport/common/highres_timer-mac.cc @@ -0,0 +1,70 @@ +/* + * 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. + */ + + +#include "statsreport/common/highres_timer.h" + +bool HighresTimer::perf_ratio_collected_ = false; +mach_timebase_info_data_t HighresTimer::perf_ratio_ = {0}; + +const uint64 NANOS_IN_MILLI = 1000000L; +const uint64 NANOS_IN_HALF_MILLI = 500000L; +const uint64 NANOS_IN_SECOND = 1000000000L; +const uint64 NANOS_IN_HALF_SECOND = 500000000L; + +uint64 HighresTimer::GetElapsedMs() const { + uint64 end_time = GetCurrentTicks(); + + // Scale to ms and round to nearest ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + (void)GetTimerFrequency(); + return (static_cast<uint64>(end_time - start_ticks_) * perf_ratio_.numer + + NANOS_IN_HALF_MILLI * perf_ratio_.denom) / + (NANOS_IN_MILLI * perf_ratio_.denom); +} + +uint64 HighresTimer::GetElapsedSec() const { + uint64 end_time = GetCurrentTicks(); + + // Scale to ms and round to nearest ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + (void)GetTimerFrequency(); + return (static_cast<uint64>(end_time - start_ticks_) * perf_ratio_.numer + + NANOS_IN_HALF_SECOND * perf_ratio_.denom) / + (NANOS_IN_SECOND * perf_ratio_.denom); +} + +void HighresTimer::CollectPerfRatio() { + mach_timebase_info(&perf_ratio_); + perf_ratio_collected_ = true; +} diff --git a/o3d/statsreport/common/highres_timer-mac.h b/o3d/statsreport/common/highres_timer-mac.h new file mode 100644 index 0000000..af512ac --- /dev/null +++ b/o3d/statsreport/common/highres_timer-mac.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + + +#ifndef O3D_STATSREPORT_COMMON_HIGHRES_TIMER_MAC_H_ +#define O3D_STATSREPORT_COMMON_HIGHRES_TIMER_MAC_H_ + +#include "base/basictypes.h" +#include <mach/mach.h> +#include <mach/mach_time.h> + +// A handy class for reliably measuring wall-clock time with decent resolution. +class HighresTimer { + public: + // Captures the current start time + HighresTimer(); + + // Captures the current tick, can be used to reset a timer for reuse. + void Start(); + + // Returns the elapsed ticks with full resolution + uint64 GetElapsedTicks() const; + + // Returns the elapsed time in milliseconds, rounded to the nearest + // millisecond. + uint64 GetElapsedMs() const; + + // Returns the elapsed time in seconds, rounded to the nearest second. + uint64 GetElapsedSec() const; + + uint64 start_ticks() const { return start_ticks_; } + + // Returns timer frequency from cache, should be less + // overhead than ::QueryPerformanceFrequency + static uint64 GetTimerFrequency(); + // Returns current ticks + static uint64 GetCurrentTicks(); + + private: + static void CollectPerfRatio(); + + // Captured start time + uint64 start_ticks_; + + // Captured performance counter frequency + static bool perf_ratio_collected_; + static mach_timebase_info_data_t perf_ratio_; +}; + +inline HighresTimer::HighresTimer() { + Start(); +} + +inline void HighresTimer::Start() { + start_ticks_ = GetCurrentTicks(); +} + +inline uint64 HighresTimer::GetTimerFrequency() { + if (!perf_ratio_collected_) + CollectPerfRatio(); + // we're losing precision by doing the division here, but this value is only + // used to estimate tick time by the unit tests, so we're ok + return static_cast<uint64>(perf_ratio_.denom) * 1000000000ULL + / perf_ratio_.numer; +} + +inline uint64 HighresTimer::GetCurrentTicks() { + return mach_absolute_time(); +} + +inline uint64 HighresTimer::GetElapsedTicks() const { + return start_ticks_ - GetCurrentTicks(); +} + +#endif // O3D_STATSREPORT_COMMON_HIGHRES_TIMER_MAC_H_ diff --git a/o3d/statsreport/common/highres_timer-win32.cc b/o3d/statsreport/common/highres_timer-win32.cc new file mode 100644 index 0000000..878b237 --- /dev/null +++ b/o3d/statsreport/common/highres_timer-win32.cc @@ -0,0 +1,72 @@ +/* + * 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. + */ + + +#include "statsreport/common/highres_timer.h" + +bool HighresTimer::perf_freq_collected_ = false; +ULONGLONG HighresTimer::perf_freq_ = 0; + +ULONGLONG HighresTimer::GetElapsedMs() const { + ULONGLONG end_time = GetCurrentTicks(); + + // Scale to ms and round to nearerst ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + // Given infinite resolution, this expression could be written as: + // trunc((end - start (units:freq*sec))/freq (units:sec) * + // 1000 (unit:ms) + 1/2 (unit:ms)) + ULONGLONG freq = GetTimerFrequency(); + return ((end_time - start_ticks_) * 1000L + freq / 2) / freq; +} + +ULONGLONG HighresTimer::GetElapsedSec() const { + ULONGLONG end_time = GetCurrentTicks(); + + // Scale to ms and round to nearerst ms - rounding is important + // because otherwise the truncation error may accumulate e.g. in sums. + // + // Given infinite resolution, this expression could be written as: + // trunc((end - start (units:freq*sec))/freq (unit:sec) + 1/2 (unit:sec)) + ULONGLONG freq = GetTimerFrequency(); + return ((end_time - start_ticks_) + freq / 2) / freq; +} + +void HighresTimer::CollectPerfFreq() { + LARGE_INTEGER freq; + + // Note that this is racy. It's OK, however, because even + // concurrent executions of this are idempotent. + if (::QueryPerformanceFrequency(&freq)) { + perf_freq_ = freq.QuadPart; + perf_freq_collected_ = true; + } +} diff --git a/o3d/statsreport/common/highres_timer-win32.h b/o3d/statsreport/common/highres_timer-win32.h new file mode 100644 index 0000000..378fdeb --- /dev/null +++ b/o3d/statsreport/common/highres_timer-win32.h @@ -0,0 +1,105 @@ +/* + * 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. + */ + + +#ifndef O3D_STATSREPORT_COMMON_HIGHRES_TIMER_WIN32_H_ +#define O3D_STATSREPORT_COMMON_HIGHRES_TIMER_WIN32_H_ + +#include <windows.h> + +// A handy class for reliably measuring wall-clock time with decent resolution, +// even on multi-processor machines and on laptops (where RDTSC potentially +// returns different results on different processors and/or the RDTSC timer +// clocks at different rates depending on the power state of the CPU, +// respectively). +class HighresTimer { + public: + // Captures the current start time + HighresTimer(); + virtual ~HighresTimer() {} + + // Captures the current tick, can be used to reset a timer for reuse. + void Start(); + + // Returns the elapsed ticks with full resolution + ULONGLONG GetElapsedTicks() const; + + // Returns the elapsed time in milliseconds, rounded to the nearest + // millisecond. + virtual ULONGLONG GetElapsedMs() const; + + // Returns the elapsed time in seconds, rounded to the nearest second. + virtual ULONGLONG GetElapsedSec() const; + + ULONGLONG start_ticks() const { return start_ticks_; } + + // Returns timer frequency from cache, should be less + // overhead than ::QueryPerformanceFrequency + static ULONGLONG GetTimerFrequency(); + // Returns current ticks + static ULONGLONG GetCurrentTicks(); + + private: + static void CollectPerfFreq(); + + // Captured start time + ULONGLONG start_ticks_; + + // Captured performance counter frequency + static bool perf_freq_collected_; + static ULONGLONG perf_freq_; +}; + +inline HighresTimer::HighresTimer() { + Start(); +} + +inline void HighresTimer::Start() { + start_ticks_ = GetCurrentTicks(); +} + +inline ULONGLONG HighresTimer::GetTimerFrequency() { + if (!perf_freq_collected_) + CollectPerfFreq(); + return perf_freq_; +} + +inline ULONGLONG HighresTimer::GetCurrentTicks() { + LARGE_INTEGER ticks; + ::QueryPerformanceCounter(&ticks); + return ticks.QuadPart; +} + +inline ULONGLONG HighresTimer::GetElapsedTicks() const { + return start_ticks_ - GetCurrentTicks(); +} + +#endif // O3D_STATSREPORT_COMMON_HIGHRES_TIMER_WIN32_H_ diff --git a/o3d/statsreport/common/highres_timer.h b/o3d/statsreport/common/highres_timer.h new file mode 100644 index 0000000..5842729 --- /dev/null +++ b/o3d/statsreport/common/highres_timer.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + + +#include <build/build_config.h> +#if defined(OS_WIN) +#include "statsreport/common/highres_timer-win32.h" +#elif defined(OS_MACOSX) +#include "statsreport/common/highres_timer-mac.h" +#else +#include "statsreport/common/highres_timer-linux.h" +#endif diff --git a/o3d/statsreport/common/highres_timer_unittest.cc b/o3d/statsreport/common/highres_timer_unittest.cc new file mode 100644 index 0000000..7f40f10 --- /dev/null +++ b/o3d/statsreport/common/highres_timer_unittest.cc @@ -0,0 +1,84 @@ +/* + * 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. + */ + + +#include "statsreport/common/highres_timer.h" +#include "gtest/gtest.h" +#include "base/basictypes.h" + +// These unittests have proven to be flaky on the build server. While +// we don't want them breaking the build, we still build them to guard +// against bitrot. On dev's machines during local builds we leave them +// enabled. +#ifndef BUILD_SERVER_BUILD +TEST(HighresTimer, MillisecondClock) { +#else +TEST(HighresTimer, DISABLED_MillisecondClock) { +#endif + HighresTimer timer; + + // note: this could fail if we context switch between initializing the timer + // and here. Very unlikely however. + EXPECT_EQ(0, timer.GetElapsedMs()); + timer.Start(); + uint64 half_ms = HighresTimer::GetTimerFrequency() / 2000; + // busy wait for half a millisecond + while (timer.start_ticks() + half_ms > HighresTimer::GetCurrentTicks()) { + // Nothing + } + EXPECT_EQ(1, timer.GetElapsedMs()); +} + +#ifndef BUILD_SERVER_BUILD +TEST(HighresTimer, SecondClock) { +#else +TEST(HighresTimer, DISABLED_SecondClock) { +#endif + HighresTimer timer; + + EXPECT_EQ(0, timer.GetElapsedSec()); +#ifdef OS_WIN + ::Sleep(250); +#else + struct timespec ts1 = {0, 250000000}; + nanosleep(&ts1, 0); +#endif + EXPECT_EQ(0, timer.GetElapsedSec()); + EXPECT_LE(230, timer.GetElapsedMs()); + EXPECT_GE(270, timer.GetElapsedMs()); +#ifdef OS_WIN + ::Sleep(251); +#else + struct timespec ts2 = {0, 251000000}; + nanosleep(&ts2, 0); +#endif + EXPECT_EQ(1, timer.GetElapsedSec()); +} diff --git a/o3d/statsreport/const-mac.h b/o3d/statsreport/const-mac.h new file mode 100644 index 0000000..9cc2746 --- /dev/null +++ b/o3d/statsreport/const-mac.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + + +// Constants for Mac stats aggregation and uploading +#ifndef O3D_STATSREPORT_CONST_MAC_H__ +#define O3D_STATSREPORT_CONST_MAC_H__ + +#include <string> +#import <Cocoa/Cocoa.h> + +namespace stats_report { + +extern const std::string kCountsKeyName; +extern const std::string kTimingsKeyName; +extern const std::string kIntegersKeyName; +extern const std::string kBooleansKeyName; + +#define kLastTransmissionTimeValueName @"LastTransmission" +NSString* O3DStatsPath(void); + +} // namespace stats_report + +#endif // O3D_STATSREPORT_CONST_MAC_H__ diff --git a/o3d/statsreport/const-mac.mm b/o3d/statsreport/const-mac.mm new file mode 100644 index 0000000..d8e9bca --- /dev/null +++ b/o3d/statsreport/const-mac.mm @@ -0,0 +1,69 @@ +/* + * 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. + */ + + +// Constants for Posix stats aggregation and uploading +#include "const-mac.h" + +namespace stats_report { + +const std::string kTimingsKeyName("Timings"); +const std::string kCountsKeyName("Counts"); +const std::string kIntegersKeyName("Integers"); +const std::string kBooleansKeyName("Booleans"); + + +static NSString* FindGoogleAppDirectory(void) { + NSString *applicationSupportFolder = + [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + NSUserDomainMask, + YES) objectAtIndex:0]; + + NSString *googleAppSupportPath = + [applicationSupportFolder stringByAppendingString:@"/Google"]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:googleAppSupportPath]) + [[NSFileManager defaultManager] createDirectoryAtPath:googleAppSupportPath + attributes:nil]; + + return googleAppSupportPath; +} + +NSString* O3DStatsPath(void) { + static NSString* the_path = NULL; + if (!the_path) { + the_path = [FindGoogleAppDirectory() stringByAppendingString:@"/O3D_Stats"]; + [the_path retain]; + } + return the_path; +} + +} // namespace stats_report diff --git a/o3d/statsreport/const-posix.cc b/o3d/statsreport/const-posix.cc new file mode 100644 index 0000000..534468b --- /dev/null +++ b/o3d/statsreport/const-posix.cc @@ -0,0 +1,44 @@ +/* + * 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. + */ + + +// Constants for Posix stats aggregation and uploading +#include "const-posix.h" + +namespace stats_report { + +const std::string kTimingsKeyName("Timings"); +const std::string kCountsKeyName("Counts"); +const std::string kIntegersKeyName("Integers"); +const std::string kBooleansKeyName("Booleans"); +const std::string kLastTransmissionTimeValueName("LastTransmission"); + +} // namespace stats_report diff --git a/o3d/statsreport/const-posix.h b/o3d/statsreport/const-posix.h new file mode 100644 index 0000000..271d531 --- /dev/null +++ b/o3d/statsreport/const-posix.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + + +// Constants for Posix stats aggregation and uploading +#ifndef O3D_STATSREPORT_CONST_POSIX_H__ +#define O3D_STATSREPORT_CONST_POSIX_H__ + +#include <string> + +namespace stats_report { + +extern const std::string kCountsKeyName; +extern const std::string kTimingsKeyName; +extern const std::string kIntegersKeyName; +extern const std::string kBooleansKeyName; +extern const std::string kLastTransmissionTimeValueName; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_CONST_POSIX_H__ diff --git a/o3d/statsreport/const-win32.cc b/o3d/statsreport/const-win32.cc new file mode 100644 index 0000000..a4f3b23 --- /dev/null +++ b/o3d/statsreport/const-win32.cc @@ -0,0 +1,46 @@ +/* + * 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. + */ + + +// Constants for Win32 stats aggregation and uploading +#include "const-win32.h" + +namespace stats_report { + +const wchar_t kTimingsKeyName[] = L"Timings"; +const wchar_t kCountsKeyName[] = L"Counts"; +const wchar_t kIntegersKeyName[] = L"Integers"; +const wchar_t kBooleansKeyName[] = L"Booleans"; +const wchar_t kStatsKeyFormatString[] = L"Software\\Google\\" + L"%ws\\UsageStats\\Daily"; +const wchar_t kLastTransmissionTimeValueName[] = L"LastTransmission"; + +} // namespace stats_report diff --git a/o3d/statsreport/const-win32.h b/o3d/statsreport/const-win32.h new file mode 100644 index 0000000..a4897411 --- /dev/null +++ b/o3d/statsreport/const-win32.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + + +// Constants for Win32 stats aggregation and uploading +#ifndef O3D_STATSREPORT_CONST_WIN32_H__ +#define O3D_STATSREPORT_CONST_WIN32_H__ + +namespace stats_report { + +extern const wchar_t kCountsKeyName[]; +extern const wchar_t kTimingsKeyName[]; +extern const wchar_t kIntegersKeyName[]; +extern const wchar_t kBooleansKeyName[]; +extern const wchar_t kStatsKeyFormatString[]; +extern const wchar_t kLastTransmissionTimeValueName[]; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_CONST_WIN32_H__ diff --git a/o3d/statsreport/const_server.h b/o3d/statsreport/const_server.h new file mode 100644 index 0000000..1443486 --- /dev/null +++ b/o3d/statsreport/const_server.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + + +// Product-specific constants for uploading. +// + +#ifndef O3D_STATSREPORT_INTERNAL_CONST_SERVER_H_ +#define O3D_STATSREPORT_INTERNAL_CONST_SERVER_H_ + +#include <build/build_config.h> +#include "base/basictypes.h" + + +// Metrics reporting server constants +#define METRICS_SERVER_NAME "www.google.com" +#define METRICS_SERVER_PORT 80 +#define METRICS_SERVER_PATH "tbproxy/usagestats" + +#define METRICS_TESTING_SERVER_NAME "0.usagestats.server.toolbar-team.eh.borg.google.com"; +#define METRICS_TESTING_SERVER_PORT 25510 + +const char kStatsServerParamSourceId[] = "sourceid"; +const char kStatsServerParamVersion[] = "v"; + +#endif // O3D_STATSREPORT_INTERNAL_CONST_SERVER_H_ diff --git a/o3d/statsreport/formatter.cc b/o3d/statsreport/formatter.cc new file mode 100644 index 0000000..f295b25 --- /dev/null +++ b/o3d/statsreport/formatter.cc @@ -0,0 +1,93 @@ +/* + * 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. + */ + + +#include "formatter.h" + +namespace stats_report { + +Formatter::Formatter(const char *name, uint32 measurement_secs) { + output_ << name << "&" << measurement_secs; +} + +Formatter::~Formatter() { +} + +void Formatter::AddCount(const char *name, uint64 value) { + output_ << "&" << name << ":c=" << value; +} + +void Formatter::AddTiming(const char *name, uint64 num, uint64 avg, + uint64 min, uint64 max) { + output_ << "&" << name << ":t=" << num << ";" + << avg << ";" << min << ";" << max; +} + +void Formatter::AddInteger(const char *name, uint64 value) { + output_ << "&" << name << ":i=" << value; +} + +void Formatter::AddBoolean(const char *name, bool value) { + output_ << "&" << name << ":b=" << (value ? "t" : "f"); +} + +void Formatter::AddMetric(MetricBase *metric) { + switch (metric->type()) { + case kCountType: { + CountMetric *count = metric->AsCount(); + AddCount(count->name(), count->value()); + break; + } + case kTimingType: { + TimingMetric *timing = metric->AsTiming(); + AddTiming(timing->name(), timing->count(), timing->average(), + timing->minimum(), timing->maximum()); + break; + } + case kIntegerType: { + IntegerMetric *integer = metric->AsInteger(); + AddInteger(integer->name(), integer->value()); + break; + } + case kBoolType: { + BoolMetric *boolean = metric->AsBool(); + // TODO: boolean->value() returns a TristateBoolValue. The + // formatter is going to serialize kBoolUnset to true. + DCHECK_NE(boolean->value(), BoolMetric::kBoolUnset); + AddBoolean(boolean->name(), boolean->value() != BoolMetric::kBoolFalse); + break; + } + default: + DCHECK(false && "Impossible metric type"); + } +} + +} // namespace stats_report diff --git a/o3d/statsreport/formatter.h b/o3d/statsreport/formatter.h new file mode 100644 index 0000000..eab534d --- /dev/null +++ b/o3d/statsreport/formatter.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + + +// Utility class to format metrics to a string suitable for posting to +// TB stats server. +#ifndef O3D_STATSREPORT_FORMATTER_H__ +#define O3D_STATSREPORT_FORMATTER_H__ + +#include <strstream> +#include "base/basictypes.h" +#include "metrics.h" + +namespace stats_report { + +// A utility class that knows how to turn metrics into a string for +// reporting to the Toolbar stats server. +class Formatter { + public: + // @param name the name of the application to report stats against + Formatter(const char *name, uint32 measurement_secs); + ~Formatter(); + + // Add metric to the output string + void AddMetric(MetricBase *metric); + + // Add typed metrics to the output string + // @{ + void AddCount(const char *name, uint64 value); + void AddTiming(const char *name, uint64 num, uint64 avg, uint64 min, + uint64 max); + void AddInteger(const char *name, uint64 value); + void AddBoolean(const char *name, bool value); + // @} + + // Terminates the output string and returns it. + // It is an error to add metrics after output() is called. + const char *output() { + output_ << std::ends; + return output_.str(); + } + + private: + mutable std::strstream output_; + + DISALLOW_COPY_AND_ASSIGN(Formatter); +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_FORMATTER_H__ diff --git a/o3d/statsreport/formatter_unittest.cc b/o3d/statsreport/formatter_unittest.cc new file mode 100644 index 0000000..6567efd --- /dev/null +++ b/o3d/statsreport/formatter_unittest.cc @@ -0,0 +1,54 @@ +/* + * 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. + */ + + +#include "gtest/gtest.h" +#include "formatter.h" + +using stats_report::Formatter; + +TEST(Formatter, Format) { + Formatter formatter("test_application", 86400); + + formatter.AddCount("count1", 10); + formatter.AddTiming("timing1", 2, 150, 50, 200); + formatter.AddInteger("integer1", 3000); + formatter.AddBoolean("boolean1", true); + formatter.AddBoolean("boolean2", false); + + EXPECT_STREQ("test_application&86400" + "&count1:c=10" + "&timing1:t=2;150;50;200" + "&integer1:i=3000" + "&boolean1:b=t" + "&boolean2:b=f", + formatter.output()); +} diff --git a/o3d/statsreport/lock.h b/o3d/statsreport/lock.h new file mode 100644 index 0000000..23d4a14 --- /dev/null +++ b/o3d/statsreport/lock.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + + +// Declaration of LLock, the lock implementation we use. +#ifndef O3D_STATSREPORT_LOCK_H_ +#define O3D_STATSREPORT_LOCK_H_ + +#include <windows.h> + +namespace stats_report { + +class LLock { + public: + LLock() { + InitializeCriticalSection(&lock_); + } + ~LLock() { + DeleteCriticalSection(&lock_); + }; + + void Lock() { + EnterCriticalSection(&lock_); + } + void Unlock() { + LeaveCriticalSection(&lock_); + } + + private: + CRITICAL_SECTION lock_; +}; + +} // namespace stats_report + +#endif // O3D_STATSREPORT_LOCK_H_ diff --git a/o3d/statsreport/metrics.cc b/o3d/statsreport/metrics.cc new file mode 100644 index 0000000..17dc3a1 --- /dev/null +++ b/o3d/statsreport/metrics.cc @@ -0,0 +1,272 @@ +/* + * 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); + +#pragma warning(push) +// C4640: construction of local static object is not thread-safe. +// C4073: initializers put in library initialization area. +#pragma warning(disable : 4640 4073) + +// 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(0, 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 diff --git a/o3d/statsreport/metrics.h b/o3d/statsreport/metrics.h new file mode 100644 index 0000000..876b29c --- /dev/null +++ b/o3d/statsreport/metrics.h @@ -0,0 +1,561 @@ +/* + * 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. + */ + + +// Declares the interface to in-memory metrics capture +#ifndef O3D_STATSREPORT_METRICS_H__ +#define O3D_STATSREPORT_METRICS_H__ + +#include <iterator> +#include "base/basictypes.h" +#include "base/logging.h" +#include "statsreport/common/highres_timer.h" + +// Macros to declare & define named & typed metrics. +// Put declarations in headers or in cpp files, where you need access +// to the metrics. For each declared metric, there must be precisely +// one definition in a compilation unit someplace. + +// A count metric should be used to report anything that monotonically +// increases. +// Examples: +// # event count +// how often does this condition hit, this function get called +// # aggregate sums +// how many bytes are written +#define DECLARE_METRIC_count(name) DECLARE_METRIC(CountMetric, name) +#define DEFINE_METRIC_count(name) DEFINE_METRIC(CountMetric, name) + +// Use timing metrics to report on the performance of important things. +// A timing metric will report the count of occurrences, as well as the +// average, min and max times. +// Samples are measured in milliseconds if you use the TIME_SCOPE macro +// or the HighResTimer class to collect samples. +#define DECLARE_METRIC_timing(name) DECLARE_METRIC(TimingMetric, name) +#define DEFINE_METRIC_timing(name) DEFINE_METRIC(TimingMetric, name) + +// Collects a sample from here to the end of the current scope, and +// adds the sample to the timing metric supplied +#define TIME_SCOPE(timing) \ + stats_report::TimingSample __xxsample__(&timing) + +// Use integer metrics to report runtime values that fluctuate. +// Examples: +// # object count +// How many objects of some type exist +// # disk space or memory +// How much disk space or memory is in use +#define DECLARE_METRIC_integer(name) DECLARE_METRIC(IntegerMetric, name) +#define DEFINE_METRIC_integer(name) DEFINE_METRIC(IntegerMetric, name) + + +// Use boolean metrics to report the occurrence of important but rare events +// or conditions. Note that a boolean metric is tri-state, so you typically +// want to set it only in one direction, and typically to true. +// Setting a boolean metric one way or another on a trigger event will report +// the setting of the boolean immediately prior to reporting, which is +// typically not what you want. +#define DECLARE_METRIC_bool(name) DECLARE_METRIC(BoolMetric, name) +#define DEFINE_METRIC_bool(name) DEFINE_METRIC(BoolMetric, name) + + +// Implementation macros +#define DECLARE_METRIC(type, name) \ + namespace o3d_statsreport { \ + extern stats_report::type metric_##name; \ + } \ + using o3d_statsreport::metric_##name + +#define DEFINE_METRIC(type, name) \ + namespace o3d_statsreport { \ + stats_report::type metric_##name(#name, \ + &stats_report::g_global_metric_storage); \ + } \ + using o3d_statsreport::metric_##name + + +namespace stats_report { + +enum MetricType { + // use zero for invalid, because global storage defaults to zero + kInvalidType = 0, + kCountType, + kTimingType, + kIntegerType, + kBoolType +}; + +// fwd. +struct MetricCollectionBase; +class MetricCollection; +class MetricBase; +class IntegerMetricBase; +class CountMetric; +class TimingMetric; +class IntegerMetric; +class BoolMetric; + +// Base class for all stats instances. +// Stats instances are chained together against a MetricCollection to +// allow enumerating stats. +// +// MetricCollection is factored into a class to make it easier to unittest +// the implementation. +class MetricBase { + public: + // @name Safe downcasts + // @{ + CountMetric *AsCount(); + TimingMetric *AsTiming(); + IntegerMetric *AsInteger(); + BoolMetric *AsBool(); + + const CountMetric *AsCount() const; + const TimingMetric *AsTiming() const; + const IntegerMetric *AsInteger() const; + const BoolMetric *AsBool() const; + // @} + + // @name Accessors + // @{ + MetricType type() const { return type_; } + MetricBase *next() const { return next_; } + const char *name() const { return name_; } + // @} + + // TODO: does this need to be virtual? + virtual ~MetricBase() = 0; + + protected: + class ObjectLock; + void Lock() const; + void Unlock() const; + + // Constructs a MetricBase and adds to the provided MetricCollection. + // @note Metrics can only be constructed up to the point where the + // MetricCollection is initialized, and there's no locking performed. + // The assumption is that outside unit tests, Metrics will we declared + // as static/global variables, and initialized at static initialization + // time - and static initialization is single-threaded. + MetricBase(const char *name, MetricType type, MetricCollectionBase *coll); + + // Constructs a named typed MetricBase + MetricBase(const char *name, MetricType type); + + // Our name + char const *const name_; + + // type of this metric + MetricType const type_; + + // chains to next stat instance + MetricBase *const next_; + + // The collection we're created against + MetricCollectionBase *const coll_; + + private: + template <class SubType> + SubType *SafeCast() { + return SubType::kType == type() ? static_cast<SubType*>(this) : NULL; + } + template <class SubType> + const SubType *SafeCast() const { + return SubType::kType == type() ? static_cast<const SubType*>(this) : NULL; + } + + DISALLOW_COPY_AND_ASSIGN(MetricBase); +}; + +// Must be a POD +struct MetricCollectionBase { + bool initialized_; + MetricBase *first_; +}; + +// Inherit from base, which is a POD and can be initialized at link time. +// +// The global MetricCollection is aliased to a link-time initialized +// instance of MetricCollectionBase, and must not extend the size of its +// base class. +class MetricCollection: public MetricCollectionBase { + public: + MetricCollection() { + initialized_ = false; + first_ = NULL; + } + ~MetricCollection() { + DCHECK(NULL == first_); + } + + // Initialize must be called after all metrics have been added to the + // collection, but before enumerating it for e.g. aggregation or reporting. + // The intent is that outside unit tests, there will only be the global + // metrics collection, which will accrue all metrics defined with the + // DEFINE_METRIC_* macros. + // Typically you'd call Initialize very early in your main function, and + // Uninitialize towards the end of main. + // It is an error to Initialize() when the collection is initialized(). + void Initialize(); + + // Uninitialize must be called before removing (deleting or deconstructing) + // metrics from the collection. + // It is an error to Uninitialize() when the collection is !initialized(). + void Uninitialize(); + + MetricBase *first() const { return first_; } + bool initialized() const { return initialized_; } + + private: + using MetricCollectionBase::initialized_; + using MetricCollectionBase::first_; + + // MetricBase is intimate with us + friend class MetricBase; + + DISALLOW_COPY_AND_ASSIGN(MetricCollection); +}; + +// Implements a forward_iterator for MetricCollection. +class MetricIterator: public std::iterator<std::forward_iterator_tag, + MetricBase *> { + public: + MetricIterator() : curr_(NULL) { + } + // We need a non-explicit copy constructor to satisfy the iterator interface. + MetricIterator(const MetricIterator &other) : curr_(other.curr_) { + } + explicit MetricIterator(const MetricCollection &coll) : curr_(coll.first()) { + DCHECK(coll.initialized()); + } + + MetricBase *operator*() const { + return curr_; + } + MetricBase *operator->() const { + return curr_; + } + MetricIterator operator++() { // preincrement + if (curr_) + curr_ = curr_->next(); + + return *this; + } + MetricIterator operator++(int dummy) { // postincrement + MetricIterator ret(*this); + ++*this; + return ret; + } + + private: + MetricBase *curr_; +}; + +inline bool operator == (const MetricIterator &a, const MetricIterator &b) { + return *a == *b; +} +inline bool operator != (const MetricIterator &a, const MetricIterator &b) { + return !operator == (a, b); +} + +// Globally defined counters are registered here +extern MetricCollectionBase g_global_metric_storage; + +// And more conveniently accessed through here +extern MetricCollection &g_global_metrics; + +// Base class for integer metrics +class IntegerMetricBase: public MetricBase { + public: + // Sets the current value + void Set(uint64 value); + + // Retrieves the current value + uint64 value() const; + + void operator ++() { Increment(); } + void operator ++(int dummy) { Increment(); } + void operator +=(uint64 addend) { Add(addend); } + + protected: + IntegerMetricBase(const char *name, + MetricType type, + MetricCollectionBase *coll) + : MetricBase(name, type, coll), value_(0) { + } + IntegerMetricBase(const char *name, MetricType type, uint64 value) + : MetricBase(name, type), value_(value) { + } + + void Increment(); + void Decrement(); + void Add(uint64 value); + void Subtract(uint64 value); + + uint64 value_; + + private: + DISALLOW_COPY_AND_ASSIGN(IntegerMetricBase); +}; + +// A count metric is a cumulative counter of events. +class CountMetric: public IntegerMetricBase { + public: + // Our type. + static const int kType = kCountType; + + CountMetric(const char *name, MetricCollectionBase *coll) + : IntegerMetricBase(name, kCountType, coll) { + } + + CountMetric(const char *name, uint64 value) + : IntegerMetricBase(name, kCountType, value) { + } + + // Nulls the metric and returns the current values. + uint64 Reset(); + + private: + DISALLOW_COPY_AND_ASSIGN(CountMetric); +}; + +class TimingMetric: public MetricBase { + public: + static const int kType = kTimingType; + + struct TimingData { + uint32 count; + uint32 align; // allow access to the alignment gap between count and sum, + // makes it easier to unittest. + uint64 sum; // ms + uint64 minimum; // ms + uint64 maximum; // ms + }; + + TimingMetric(const char *name, MetricCollectionBase *coll) + : MetricBase(name, kTimingType, coll) { + Clear(); + } + + TimingMetric(const char *name, const TimingData &value) + : MetricBase(name, kTimingType), data_(value) { + } + + uint32 count() const; + uint64 sum() const; + uint64 minimum() const; + uint64 maximum() const; + uint64 average() const; + + // Adds a single sample to the metric + // @param time_ms time (in milliseconds) for this sample + void AddSample(uint64 time_ms); + + // Adds count samples to the metric + // @note use this when capturing time over a variable number of items to + // normalize e.g. download time per byte or KB. This records one sample + // over count items, which is numerically more stable for the average + // than dividing the captured time by the item count. As a side benefit + // the timer will also record the item count. + // @note if count == 0, no sample will be recorded + // @param count number of samples to add + // @param total_time_ms the total time consumed by all the "count" samples + void AddSamples(uint64 count, uint64 total_time_ms); + + // Nulls the metric and returns the current values. + TimingData Reset(); + + private: + void Clear(); + + TimingData data_; + + DISALLOW_COPY_AND_ASSIGN(TimingMetric); +}; + +// A convenience class to sample the time from construction to destruction +// against a given timing metric. +class TimingSample { + public: + // @param timing the metric the sample is to be tallied against + explicit TimingSample(TimingMetric *timing) : timing_(timing), count_(1) { + DCHECK(NULL != timing); + } + + // @param timing the metric the sample is to be tallied against + // @param item_count count of items processed, used to divide the sampled + // time so as to capture time per item, which is often a better measure + // than the total time over a varying number of items. + TimingSample(TimingMetric *timing, uint32 item_count) : timing_(timing), + count_(item_count) { + DCHECK(NULL != timing); + } + + ~TimingSample() { + // We discard samples with a zero count + if (1 == count_) + timing_->AddSample(timer_.GetElapsedMs()); + else + timing_->AddSamples(count_, timer_.GetElapsedMs()); + } + + // @name Accessors + // @{ + uint32 count() const { return count_; } + void set_count(uint32 count) { count_ = count; } + // @} + + private: + // Collects the sample for us. + HighresTimer timer_; + + // The metric we tally against. + TimingMetric *timing_; + + // The item count we divide the captured time by + uint32 count_; + + DISALLOW_COPY_AND_ASSIGN(TimingSample); +}; + +// An integer metric is used to sample values that vary over time. +// On aggregation the instantaneous value of the integer metric is captured. +class IntegerMetric: public IntegerMetricBase { + public: + static const int kType = kIntegerType; + + IntegerMetric(const char *name, MetricCollectionBase *coll) + : IntegerMetricBase(name, kIntegerType, coll) { + } + + IntegerMetric(const char *name, uint64 value) + : IntegerMetricBase(name, kIntegerType, value) { + } + + void operator = (uint64 value) { Set(value); } + + void operator --() { Decrement(); } + void operator --(int dummy) { Decrement(); } + void operator -=(uint64 sub) { Subtract(sub); } + + private: + DISALLOW_COPY_AND_ASSIGN(IntegerMetric); +}; + +// A bool metric is tri-state, and can be: +// - unset, +// - true or +// - false +// to match other metrics, which are implicitly unset if they've not changed +// from their initial value. +class BoolMetric: public MetricBase { + public: + static const int kType = kBoolType; + + // Values we can take + enum TristateBoolValue { + kBoolUnset = -1, + kBoolFalse, + kBoolTrue, + }; + + BoolMetric(const char *name, MetricCollectionBase *coll) + : MetricBase(name, kBoolType, coll), value_(kBoolUnset) { + } + + BoolMetric(const char *name, uint32 value) + : MetricBase(name, kBoolType) { + switch (value) { + case kBoolFalse: + case kBoolTrue: + value_ = static_cast<TristateBoolValue>(value); + break; + + default: + DCHECK(false && "Unexpected tristate bool value on construction"); + value_ = kBoolUnset; + } + } + + // Sets the flag to the provided value. + void Set(bool value); + + void operator = (bool value) { + Set(value); + } + + // Nulls the metric and returns the current values. + TristateBoolValue Reset(); + + // Returns the current value - not threadsafe. + TristateBoolValue value() const { return value_; } + + private: + TristateBoolValue value_; + + DISALLOW_COPY_AND_ASSIGN(BoolMetric); +}; + +inline CountMetric *MetricBase::AsCount() { + return SafeCast<CountMetric>(); +} + +inline TimingMetric *MetricBase::AsTiming() { + return SafeCast<TimingMetric>(); +} + +inline IntegerMetric *MetricBase::AsInteger() { + return SafeCast<IntegerMetric>(); +} + +inline BoolMetric *MetricBase::AsBool() { + return SafeCast<BoolMetric>(); +} + +inline const CountMetric *MetricBase::AsCount() const { + return SafeCast<CountMetric>(); +} + +inline const TimingMetric *MetricBase::AsTiming() const { + return SafeCast<TimingMetric>(); +} + +inline const IntegerMetric *MetricBase::AsInteger() const { + return SafeCast<IntegerMetric>(); +} + +inline const BoolMetric *MetricBase::AsBool() const { + return SafeCast<BoolMetric>(); +} + +} // namespace stats_report + +#endif // O3D_STATSREPORT_METRICS_H__ diff --git a/o3d/statsreport/metrics_unittest.cc b/o3d/statsreport/metrics_unittest.cc new file mode 100644 index 0000000..db2fe29 --- /dev/null +++ b/o3d/statsreport/metrics_unittest.cc @@ -0,0 +1,407 @@ +/* + * 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. + */ + + +// Metrics report unit testing +#include <new> +#include <algorithm> +#include "gtest/gtest.h" +#include "metrics.h" + +DECLARE_METRIC_count(count); +DEFINE_METRIC_count(count); + +DECLARE_METRIC_timing(timing); +DEFINE_METRIC_timing(timing); + +DECLARE_METRIC_integer(integer); +DEFINE_METRIC_integer(integer); + +DECLARE_METRIC_bool(bool); +DEFINE_METRIC_bool(bool); + +using ::stats_report::BoolMetric; +using ::stats_report::CountMetric; +using ::stats_report::IntegerMetric; +using ::stats_report::MetricBase; +using ::stats_report::MetricCollection; +using ::stats_report::MetricCollectionBase; +using ::stats_report::MetricIterator; +using ::stats_report::TimingMetric; +using ::stats_report::TimingSample; +using ::stats_report::kBoolType; +using ::stats_report::kCountType; +using ::stats_report::kIntegerType; +using ::stats_report::kTimingType; + +namespace { + +class MetricsTest: public testing::Test { + protected: + MetricCollection coll_; +}; + +class MetricsEnumTest: public MetricsTest { + public: + virtual void SetUp() { + coll_.Initialize(); + } + + virtual void TearDown() { + coll_.Uninitialize(); + } + + protected: + MetricsEnumTest(): count_("count", &coll_), timing_("timing", &coll_), + integer_("integer", &coll_), bool_("bool", &coll_) { + } + + CountMetric count_; + TimingMetric timing_; + IntegerMetric integer_; + BoolMetric bool_; +}; + +} // namespace + +// Validates that the above-declared metrics are available +// in the expected namespace +TEST_F(MetricsTest, Globals) { + EXPECT_EQ(0, ::metric_count.Reset()); + TimingMetric::TimingData data = ::metric_timing.Reset(); + EXPECT_EQ(0, data.count); + EXPECT_EQ(0, data.maximum); + EXPECT_EQ(0, data.minimum); + EXPECT_EQ(0, data.sum); + + EXPECT_EQ(0, ::metric_integer.value()); + EXPECT_EQ(BoolMetric::kBoolUnset, ::metric_bool.Reset()); + + // Check for correct initialization + EXPECT_STREQ("count", metric_count.name()); + EXPECT_STREQ("timing", metric_timing.name()); + EXPECT_STREQ("integer", metric_integer.name()); + EXPECT_STREQ("bool", metric_bool.name()); +} + + +// make GUnit happy +inline std::ostream &operator << (std::ostream &str, const MetricIterator &it) { + str << std::hex << reinterpret_cast<void*>(*it); + return str; +} + +TEST_F(MetricsTest, CollectionInitialization) { + // The global MetricCollection is aliased to zero memory so as to ensure + // no initialization order snafus. If an initialized MetricCollection + // sets any of its storage to non-zero, there's a good chance that e.g. a + // vtbl has snuck in there, which must not happen + char buf1[sizeof(MetricCollection)] = { 0 }; + char buf2[sizeof(MetricCollection)] = { 0 }; + + // Placement new a MetricCollection to one of the buffers + new (buf1) MetricCollection(); + + // and check they're still equivalent + EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(MetricCollection))); + + // MetricCollection must not extend MetricCollectionBase in size + EXPECT_EQ(sizeof(MetricCollection), sizeof(MetricCollectionBase)); +} + +TEST_F(MetricsTest, Count) { + CountMetric foo("foo", &coll_); + + EXPECT_EQ(0, foo.Reset()); + EXPECT_EQ(kCountType, foo.type()); + ASSERT_TRUE(NULL != foo.AsCount()); + ASSERT_TRUE(NULL == foo.AsTiming()); + ASSERT_TRUE(NULL == foo.AsInteger()); + ASSERT_TRUE(NULL == foo.AsBool()); + + ++foo; + EXPECT_EQ(1, foo.value()); + foo++; + EXPECT_EQ(2, foo.value()); + + foo += 100; + EXPECT_EQ(102, foo.value()); +} + +TEST_F(MetricsTest, Timing) { + TimingMetric foo("foo", &coll_); + + EXPECT_EQ(kTimingType, foo.type()); + ASSERT_TRUE(NULL == foo.AsCount()); + ASSERT_TRUE(NULL != foo.AsTiming()); + ASSERT_TRUE(NULL == foo.AsInteger()); + ASSERT_TRUE(NULL == foo.AsBool()); + + foo.AddSample(100); + foo.AddSample(50); + + EXPECT_EQ(2, foo.count()); + EXPECT_EQ(150, foo.sum()); + EXPECT_EQ(100, foo.maximum()); + EXPECT_EQ(50, foo.minimum()); + EXPECT_EQ(75, foo.average()); + + TimingMetric::TimingData data = foo.Reset(); + EXPECT_EQ(2, data.count); + EXPECT_EQ(150, data.sum); + EXPECT_EQ(100, data.maximum); + EXPECT_EQ(50, data.minimum); + + EXPECT_EQ(0, foo.count()); + EXPECT_EQ(0, foo.sum()); + EXPECT_EQ(0, foo.maximum()); + EXPECT_EQ(0, foo.minimum()); + EXPECT_EQ(0, foo.average()); + + // Test counted samples + foo.AddSamples(10, 1000); + foo.AddSamples(10, 500); + EXPECT_EQ(20, foo.count()); + EXPECT_EQ(1500, foo.sum()); + EXPECT_EQ(100, foo.maximum()); + EXPECT_EQ(50, foo.minimum()); + EXPECT_EQ(75, foo.average()); +} + +TEST_F(MetricsTest, TimingSample) { + TimingMetric foo("foo", &coll_); + + // add a sample to foo + { + TimingSample sample(&foo); + + ::Sleep(30); + } + + TimingMetric::TimingData data = foo.Reset(); + + // Should be precisely one sample in there + EXPECT_EQ(1, data.count); + + // Disable flaky tests on build server, unfortunately this reduces coverage + // too, but it seems preferrable to breaking the build on a regular basis. +#ifndef BUILD_SERVER_BUILD + // Let's hope the scheduler doesn't leave us hanging more than 10 ms. + EXPECT_GT(40, data.sum); + // The sleep above seems to often terminate early on the build server, + // I've observed captured times down to 18 ms, which is strange. + // TODO: figure out whether the timer is broken or whether + // sleep is breaking its promise, or whether e.g. we're getting different + // walltimes on different CPUs due to BIOS bugs on the build server + EXPECT_LT(15, data.sum); +#endif + + // again, this time with a non-unity count + { + TimingSample sample(&foo, 2); + + EXPECT_EQ(2, sample.count()); + ::Sleep(30); + } + + data = foo.Reset(); + + // Should be precisely two samples in there + EXPECT_EQ(2, data.count); + + // Disable flaky tests on build server, unfortunately this reduces coverage + // too, but it seems preferrable to breaking the build on a regular basis. +#ifndef BUILD_SERVER_BUILD + // Let's hope the scheduler doesn't leave us hanging more than 10 ms. + EXPECT_GT(40, data.sum); + EXPECT_LT(15, data.sum); +#endif + + // now with zero count + { + TimingSample sample(&foo, 0); + } + + data = foo.Reset(); + + // Should be no samples in there + EXPECT_EQ(0, data.count); +} + +TEST_F(MetricsTest, Integer) { + IntegerMetric foo("foo", &coll_); + + EXPECT_EQ(kIntegerType, foo.type()); + ASSERT_TRUE(NULL == foo.AsCount()); + ASSERT_TRUE(NULL == foo.AsTiming()); + ASSERT_TRUE(NULL != foo.AsInteger()); + ASSERT_TRUE(NULL == foo.AsBool()); + + EXPECT_EQ(0, foo.value()); + foo.Set(1005); + EXPECT_EQ(1005, foo.value()); + foo = 1009UL; + EXPECT_EQ(1009, foo.value()); + + foo.Set(0); + + ++foo; + EXPECT_EQ(1, foo.value()); + foo++; + EXPECT_EQ(2, foo.value()); + + foo += 100; + EXPECT_EQ(102, foo.value()); + + foo -= 100; + EXPECT_EQ(2, foo.value()); + foo--; + EXPECT_EQ(1, foo.value()); + --foo; + EXPECT_EQ(0, foo.value()); +} + +TEST_F(MetricsTest, Bool) { + BoolMetric foo("foo", &coll_); + + EXPECT_EQ(kBoolType, foo.type()); + ASSERT_TRUE(NULL == foo.AsCount()); + ASSERT_TRUE(NULL == foo.AsTiming()); + ASSERT_TRUE(NULL == foo.AsInteger()); + ASSERT_TRUE(NULL != foo.AsBool()); + + EXPECT_EQ(BoolMetric::kBoolUnset, foo.Reset()); + foo.Set(true); + EXPECT_EQ(BoolMetric::kBoolTrue, foo.Reset()); + foo.Set(false); + EXPECT_EQ(BoolMetric::kBoolFalse, foo.Reset()); + EXPECT_EQ(BoolMetric::kBoolUnset, foo.Reset()); +} + +TEST_F(MetricsEnumTest, Enumeration) { + MetricBase *metrics[] = { + &count_, + &timing_, + &integer_, + &bool_, + }; + + for (int i = 0; i < sizeof(metrics) / sizeof(metrics[0]); ++i) { + MetricBase *stat = metrics[i]; + MetricBase *curr = coll_.first(); + + for (; NULL != curr; curr = curr->next()) { + if (stat == curr) + break; + } + + // if NULL, we didn't find our counter + EXPECT_TRUE(NULL != curr); + } +} + +TEST_F(MetricsEnumTest, Iterator) { + typedef MetricBase *MetricBasePtr; + MetricBasePtr metrics[] = { &count_, &timing_, &integer_, &bool_, }; + int num_stats = arraysize(metrics); + + MetricIterator it(coll_), end; + EXPECT_NE(it, end); + + // copy construction + EXPECT_EQ(it, MetricIterator(it)); + EXPECT_EQ(end, MetricIterator(end)); + + // # of iterations + int i = 0; + while (it++ != end) + ++i; + ASSERT_EQ(num_stats, i); + ASSERT_EQ(end, it); + + // increment past end is idempotent + ++it; + ASSERT_EQ(end, it); + + // Check that we return no garbage or nonsense + for (it = MetricIterator(coll_); it != end; ++it) { + MetricBasePtr *stats_end = &metrics[num_stats]; + EXPECT_NE(stats_end, std::find(metrics, stats_end, *it)); + } + + // and that all metrics can be found + for (int i = 0; i < sizeof(metrics) / sizeof(metrics[0]); ++i) { + MetricBase *stat = metrics[i]; + + EXPECT_EQ(stat, *std::find(MetricIterator(coll_), end, stat)); + } +} + +TEST_F(MetricsTest, SimpleConstruction) { + const CountMetric c("c", 100); + + EXPECT_EQ(100, c.value()); + EXPECT_EQ(kCountType, c.type()); + EXPECT_STREQ("c", c.name()); + EXPECT_TRUE(NULL == c.next()); + + TimingMetric::TimingData data = { 10, 0, 1000, 10, 500 }; + const TimingMetric t("t", data); + + EXPECT_EQ(10, t.count()); + EXPECT_EQ(1000, t.sum()); + EXPECT_EQ(10, t.minimum()); + EXPECT_EQ(500, t.maximum()); + EXPECT_EQ(kTimingType, t.type()); + EXPECT_STREQ("t", t.name()); + EXPECT_TRUE(NULL == t.next()); + + const IntegerMetric i("i", 200); + + EXPECT_EQ(200, i.value()); + EXPECT_EQ(kIntegerType, i.type()); + EXPECT_STREQ("i", i.name()); + EXPECT_TRUE(NULL == i.next()); + + const BoolMetric bool_true("bool_true", BoolMetric::kBoolTrue); + + EXPECT_EQ(BoolMetric::kBoolTrue, bool_true.value()); + EXPECT_EQ(kBoolType, bool_true.type()); + EXPECT_STREQ("bool_true", bool_true.name()); + EXPECT_TRUE(NULL == bool_true.next()); + + const BoolMetric bool_false("bool_false", BoolMetric::kBoolFalse); + + EXPECT_EQ(BoolMetric::kBoolFalse, bool_false.value()); + EXPECT_EQ(kBoolType, bool_false.type()); + EXPECT_STREQ("bool_false", bool_false.name()); + EXPECT_TRUE(NULL == bool_false.next()); +} diff --git a/o3d/statsreport/persistent_iterator-win32.cc b/o3d/statsreport/persistent_iterator-win32.cc new file mode 100644 index 0000000..edf687b --- /dev/null +++ b/o3d/statsreport/persistent_iterator-win32.cc @@ -0,0 +1,163 @@ +/* + * 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. + */ + + +// Iterator over persisted metrics +#include "persistent_iterator-win32.h" + +namespace stats_report { + +void PersistentMetricsIteratorWin32::Next() { + current_value_.reset(); + + // Try to open the top-level key if we didn't already. + if (NULL == key_.m_hKey) { + HKEY parent_key = is_machine_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + LONG err = key_.Open(parent_key, key_name_, KEY_READ); + if (err != ERROR_SUCCESS) + return; + } + + // Loop until we find a value + while (state_ != kFinished) { + if (NULL == sub_key_.m_hKey) { + const wchar_t *subkey_name = NULL; + switch (state_) { + case kUninitialized: + state_ = kCounts; + subkey_name = kCountsKeyName; + break; + case kCounts: + state_ = kTimings; + subkey_name = kTimingsKeyName; + break; + case kTimings: + state_ = kIntegers; + subkey_name = kIntegersKeyName; + break; + case kIntegers: + state_ = kBooleans; + subkey_name = kBooleansKeyName; + break; + case kBooleans: + state_ = kFinished; + break; + case kFinished: + break; + } + + if (NULL != subkey_name) { + LONG err = sub_key_.Open(key_, subkey_name, KEY_READ); + // go around the loop on error to try the next key type + if (ERROR_SUCCESS != err) + continue; + } + + // reset value enumeration + value_index_ = 0; + } + + if (state_ != kFinished) { + DCHECK(NULL != sub_key_.m_hKey); + CString wide_value_name; + DWORD value_name_len = 255; + DWORD value_type = 0; + BYTE buf[sizeof(TimingMetric::TimingData)]; + DWORD value_len = sizeof(buf); + + // Get the next key and value + LONG err = ::RegEnumValue(sub_key_, + value_index_, + CStrBuf(wide_value_name, value_name_len), + &value_name_len, + 0, + &value_type, + buf, + &value_len); + + ++value_index_; + + if (ERROR_NO_MORE_ITEMS == err) { + // done with this subkey, go around again + sub_key_.Close(); + continue; + } else if (ERROR_SUCCESS != err) { + // some other error, broken into a separate case for ease of debugging + DCHECK(false && "Unexpected error during reg value enumeration"); + } else { + DCHECK(ERROR_SUCCESS == err); + + // convert value to ASCII + current_value_name_ = wide_value_name; + + switch (state_) { + case kCounts: + if (value_len != sizeof(uint64)) + continue; + current_value_.reset( + new CountMetric(current_value_name_ .GetString(), + *reinterpret_cast<uint64*>(&buf[0]))); + break; + case kTimings: + if (value_len != sizeof(TimingMetric::TimingData)) + continue; + current_value_.reset( + new TimingMetric( + current_value_name_.GetString(), + *reinterpret_cast<TimingMetric::TimingData*>(&buf[0]))); + break; + case kIntegers: + if (value_len != sizeof(uint64)) + continue; + current_value_.reset(new IntegerMetric( + current_value_name_.GetString(), + *reinterpret_cast<uint64*>(&buf[0]))); + break; + case kBooleans: + if (value_len != sizeof(uint32)) + continue; + current_value_.reset( + new BoolMetric(current_value_name_.GetString(), + *reinterpret_cast<uint32*>(&buf[0]))); + break; + default: + DCHECK(false && "Impossible state during reg value enumeration"); + break; + } + + if (current_value_.get()) + return; + } + } + } +} + +} // namespace stats_report diff --git a/o3d/statsreport/persistent_iterator-win32.h b/o3d/statsreport/persistent_iterator-win32.h new file mode 100644 index 0000000..ceaf851 --- /dev/null +++ b/o3d/statsreport/persistent_iterator-win32.h @@ -0,0 +1,152 @@ +/* + * 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. + */ + + +// Iterator over persisted metrics +#ifndef O3D_STATSREPORT_PERSISTENT_ITERATOR_WIN32_H__ +#define O3D_STATSREPORT_PERSISTENT_ITERATOR_WIN32_H__ + +#include <atlbase.h> +#include <atlstr.h> +#include <iterator> +#include "base/scoped_ptr.h" +#include "metrics.h" +#include "const-win32.h" + +namespace stats_report { + +// Forward iterator for persisted metrics +class PersistentMetricsIteratorWin32 + : public std::iterator<std::forward_iterator_tag, const MetricBase *> { + public: + // @param app_name see MetricsAggregatorWin32 + explicit PersistentMetricsIteratorWin32(const wchar_t *app_name) + : state_(kUninitialized), + is_machine_(false) { + key_name_.Format(kStatsKeyFormatString, app_name); + Next(); + } + + // @param app_name see MetricsAggregatorWin32 + // @param is_machine specifies the registry hive + PersistentMetricsIteratorWin32(const wchar_t *app_name, bool is_machine) + : state_(kUninitialized), + is_machine_(is_machine) { + key_name_.Format(kStatsKeyFormatString, app_name); + Next(); + } + + // Constructs the at-end iterator + PersistentMetricsIteratorWin32() : state_(kUninitialized) { + } + + MetricBase *operator* () { + return Current(); + } + MetricBase *operator-> () { + return Current(); + } + + // Preincrement, we don't implement postincrement because we don't + // want to deal with making iterators copyable, comparable etc. + PersistentMetricsIteratorWin32 &operator++() { + Next(); + + return (*this); + } + + // Compare for equality with o. + bool equals(const PersistentMetricsIteratorWin32 &o) const { + // compare equal to self, and end iterators compare equal + if ((this == &o) || (NULL == current_value_.get() && + NULL == o.current_value_.get())) + return true; + + return false; + } + + private: + MetricBase *Current() { + DCHECK(current_value_.get()); + return current_value_.get(); + } + + enum IterationState { + kUninitialized, + kCounts, + kTimings, + kIntegers, + kBooleans, + kFinished, + }; + + // Walk to the next key/value under iteration + void Next(); + + // Keeps track of which subkey we're iterating over + IterationState state_; + + // The full path from HKCU to the key we iterate over + CString key_name_; + + // The top-level key we're iterating over, valid only + // after first call to Next(). + CRegKey key_; + + // The subkey we're currently enumerating over + CRegKey sub_key_; + + // Current value we're indexing over + DWORD value_index_; + + // Name of the value under the iterator + CStringA current_value_name_; + + // The metric under the iterator + scoped_ptr<MetricBase> current_value_; + + // Specifies HKLM or HKCU, respectively. + bool is_machine_; +}; + +inline bool operator == (const PersistentMetricsIteratorWin32 &a, + const PersistentMetricsIteratorWin32 &b) { + return a.equals(b); +} + +inline bool operator != (const PersistentMetricsIteratorWin32 &a, + const PersistentMetricsIteratorWin32 &b) { + return !a.equals(b); +} + +} // namespace stats_report + +#endif // O3D_STATSREPORT_PERSISTENT_ITERATOR_WIN32_H__ diff --git a/o3d/statsreport/persistent_iterator-win32_unittest.cc b/o3d/statsreport/persistent_iterator-win32_unittest.cc new file mode 100644 index 0000000..69e6f55 --- /dev/null +++ b/o3d/statsreport/persistent_iterator-win32_unittest.cc @@ -0,0 +1,164 @@ +/* + * 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. + */ + + +#include <atlbase.h> +#include <atlcom.h> +#include <iterator> +#include <map> +#include "gtest/gtest.h" +#include "const-win32.h" +#include "aggregator-win32_unittest.h" +#include "persistent_iterator-win32.h" + +using ::stats_report::BoolMetric; +using ::stats_report::CountMetric; +using ::stats_report::IntegerMetric; +using ::stats_report::MetricBase; +using ::stats_report::MetricCollection; +using ::stats_report::MetricCollectionBase; +using ::stats_report::MetricIterator; +using ::stats_report::MetricsAggregatorWin32; +using ::stats_report::PersistentMetricsIteratorWin32; +using ::stats_report::TimingMetric; +using ::stats_report::TimingSample; +using ::stats_report::kBoolType; +using ::stats_report::kCountType; +using ::stats_report::kIntegerType; +using ::stats_report::kInvalidType; +using ::stats_report::kTimingType; + +namespace { + +class PersistentMetricsIteratorWin32Test: public MetricsAggregatorWin32Test { + public: + bool WriteStats() { + // put some persistent metrics into the registry + MetricsAggregatorWin32 agg(coll_, kAppName); + AddStats(); + bool ret = agg.AggregateMetrics(); + + // Reset the stats, we should now have the same stats + // in our collection as in registry. + AddStats(); + + return ret; + } + + typedef std::map<std::string, MetricBase*> MetricsMap; + void IndexMetrics(MetricsMap *metrics) { + // build a map over the metrics in our collection + MetricIterator it(coll_), end; + + for (; it != end; ++it) { + metrics->insert(std::make_pair(std::string(it->name()), *it)); + } + } +}; + +// compare two metrics instances for equality +bool equals(MetricBase *a, MetricBase *b) { + if (!a || !b) + return false; + + if (a->type() != b->type() || 0 != strcmp(a->name(), b->name())) + return false; + + switch (a->type()) { + case kCountType: + return a->AsCount()->value() == b->AsCount()->value(); + break; + case kTimingType: { + TimingMetric *at = a->AsTiming(); + TimingMetric *bt = b->AsTiming(); + + return at->count() == bt->count() && + at->sum() == bt->sum() && + at->minimum() == bt->minimum() && + at->maximum() == bt->maximum(); + break; + } + case kIntegerType: + return a->AsInteger()->value() == b->AsInteger()->value(); + break; + case kBoolType: + return a->AsBool()->value() == b->AsBool()->value(); + break; + case kInvalidType: + default: + LOG(FATAL) << "Impossible metric type"; + } + + return false; +} + +} // namespace + +TEST_F(PersistentMetricsIteratorWin32Test, Basic) { + EXPECT_TRUE(WriteStats()); + PersistentMetricsIteratorWin32 a; + PersistentMetricsIteratorWin32 b; + PersistentMetricsIteratorWin32 c(kAppName); + + EXPECT_TRUE(a == b); + EXPECT_TRUE(b == a); + + EXPECT_FALSE(a == c); + EXPECT_FALSE(b == c); + EXPECT_FALSE(c == a); + EXPECT_FALSE(c == b); + + ++a; + EXPECT_TRUE(a == b); + EXPECT_TRUE(b == a); +} + +// Test to see whether we can reliably roundtrip metrics through +// the registry without molestation +TEST_F(PersistentMetricsIteratorWin32Test, UnmolestedValues) { + EXPECT_TRUE(WriteStats()); + + MetricsMap metrics; + IndexMetrics(&metrics); + + PersistentMetricsIteratorWin32 it(kAppName), end; + int count = 0; + for (; it != end; ++it) { + MetricsMap::iterator found = metrics.find(it->name()); + + // make sure we found it, and that it's unmolested in value + EXPECT_TRUE(found != metrics.end() && equals(found->second, *it)); + count++; + } + + // Did we visit all metrics? + EXPECT_EQ(count, metrics.size()); +} diff --git a/o3d/statsreport/uploader-mac.mm b/o3d/statsreport/uploader-mac.mm new file mode 100644 index 0000000..803b800 --- /dev/null +++ b/o3d/statsreport/uploader-mac.mm @@ -0,0 +1,97 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. + +#import <Cocoa/Cocoa.h> +#include "base/logging.h" + +#include "statsreport/uploader.h" +#include "statsreport/aggregator-mac.h" +#include "statsreport/const-mac.h" +#include "statsreport/formatter.h" +#include "statsreport/common/const_product.h" +#include "statsreport/const_server.h" + +const float kTimeOutInterval = 10.0; + +namespace stats_report { + // TODO: Refactor to avoid cross platform code duplication. + +bool UploadMetrics(const char* extra_url_data, const char* user_agent, + const char *content) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + + // TODO: refactor to where there's a platform-independent HTTP + // interface to the ConnectionManager and pass it through into here. + DLOG(INFO) << "Uploading metrics . . . "; + + NSString *url_string = + [NSString stringWithFormat:@"http://%s:%d/%s?%s=%s&%s=%s&%s", + METRICS_SERVER_NAME, + METRICS_SERVER_PORT, + METRICS_SERVER_PATH, + kStatsServerParamSourceId, + PRODUCT_NAME_STRING, + kStatsServerParamVersion, + PRODUCT_VERSION_STRING, + extra_url_data]; + DLOG(INFO) << "Url: " << [url_string UTF8String]; + + NSURL *url = [NSURL URLWithString:url_string]; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + + // don't send cookies or user-identifiable info with our request. + [request setHTTPShouldHandleCookies:NO]; + [request setTimeoutInterval:kTimeOutInterval]; + int content_len = (content == NULL) ? 0 : strlen(content); + if (content_len) { + NSData *content_data = [NSData dataWithBytesNoCopy:(void*)content + length:content_len]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:content_data]; + } + NSURLResponse *response = nil; + NSError *error = nil; + + [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; + + [pool release]; + + return (error == nil); +} + +} // namespace stats_report diff --git a/o3d/statsreport/uploader-posix.cc b/o3d/statsreport/uploader-posix.cc new file mode 100644 index 0000000..7275b81 --- /dev/null +++ b/o3d/statsreport/uploader-posix.cc @@ -0,0 +1,76 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. +#include "backend/serverconnectionmanager.h" +#include "statsreport/common/const_product.h" +#include "statsreport/const_server.h" +#include "iobuffer/iobuffer-inl.h" +#include "statsreport/aggregator-posix-inl.h" +#include "statsreport/const-posix.h" +#include "statsreport/formatter.h" +#include "statsreport/uploader.h" + + +DECLARE_int32(metrics_aggregation_interval); +DECLARE_int32(metrics_upload_interval); +DECLARE_string(metrics_server_name); +DECLARE_int32(metrics_server_port); + +using stats_report::Formatter; +using stats_report::MetricsAggregatorPosix; + +namespace { + +bool UploadMetrics(const char* extra_url_data, const char* user_agent, + const char *content) { + o3d_transfer_service::ServerConnectionManager scm( + METRICS_SERVER_NAME, + METRICS_SERVER_PORT, + false, + PRODUCT_VERSION_STRING, + PRODUCT_NAME_STRING); + + PathString path = PathString("/" METRICS_SERVER_PATH "?") + + kStatsServerParamSourceId + "=" + + PRODUCT_NAME_STRING + "&" + + kStatsServerParamVersion + "=" + + PRODUCT_VERSION_STRING + "&" + + extra_url_data; + + IOBuffer buffer_out; + o3d_transfer_service::HttpResponse response; + return scm.SimplePost(path, const_cast<char *>(content), + strlen(content), &buffer_out, &response); +} + +} // namespace stats_report diff --git a/o3d/statsreport/uploader-win32.cc b/o3d/statsreport/uploader-win32.cc new file mode 100644 index 0000000..ba08e53 --- /dev/null +++ b/o3d/statsreport/uploader-win32.cc @@ -0,0 +1,106 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. + +#include <atlbase.h> +#include <atlcom.h> +#include <atlsafe.h> +#include <time.h> +#include "base/logging.h" + +#include "statsreport/uploader.h" +#include "statsreport/aggregator-win32.h" +#include "statsreport/const-win32.h" +#include "statsreport/persistent_iterator-win32.h" +#include "statsreport/formatter.h" +#include "statsreport/common/const_product.h" +#include "statsreport/const_server.h" + +namespace stats_report { +// TODO: Refactor to avoid cross platform code duplication. + +bool UploadMetrics(const char* extra_url_data, const char* user_agent, + const char *content) { + // TODO: refactor to where there's a platform-independent HTTP + // interface to the ConnectionManager and pass it through into here. + DLOG(INFO) << "Uploading metrics . . . "; + CString url; + url.Format(L"http://%hs:%d/%hs?%hs=%hs&%hs=%hs&%hs", + METRICS_SERVER_NAME, + METRICS_SERVER_PORT, + METRICS_SERVER_PATH, + kStatsServerParamSourceId, + PRODUCT_NAME_STRING, + kStatsServerParamVersion, + PRODUCT_VERSION_STRING, + extra_url_data); + DLOG(INFO) << "Url: " << url; + + CComPtr<IXMLHttpRequest> request; + // create the http request object + HRESULT hr = request.CoCreateInstance(CLSID_XMLHTTPRequest); + if (FAILED(hr)) + return false; + + DLOG(INFO) << "Created request."; + // open the request + // TODO: async? + CComVariant empty; + CComVariant var_false(false); + hr = request->open(CComBSTR("POST"), CComBSTR(url), + var_false, empty, empty); + if (FAILED(hr)) + return false; + DLOG(INFO) << "Opened request."; + + hr = request->setRequestHeader(CComBSTR("User-Agent"), CComBSTR(user_agent)); + if (FAILED(hr)) + DLOG(WARNING) << "Failed to set user-agent"; + + // and send the file + // TODO: progress? + CComSafeArray<BYTE> content_array(strlen(content)); + hr = request->send(CComVariant(content)); + if (FAILED(hr)) + return false; + DLOG(INFO) << "Sent content_array."; + +#ifndef NDEBUG + CComBSTR response; + hr = request->get_responseText(&response); +#endif + + return true; +} + +} // namespace stats_report diff --git a/o3d/statsreport/uploader.h b/o3d/statsreport/uploader.h new file mode 100644 index 0000000..f514171 --- /dev/null +++ b/o3d/statsreport/uploader.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. +#ifndef O3D_STATSREPORT_UPLOADER_H_ +#define O3D_STATSREPORT_UPLOADER_H_ + +#ifdef OS_WINDOWS +#include <atlstr.h> +#endif + +#include "statsreport/metrics.h" + +namespace stats_report { + +class StatsUploader { + public: + StatsUploader() {} + virtual ~StatsUploader() {} + virtual bool UploadMetrics(const char* extra_url_data, + const char* user_agent, + const char *content); + private: + DISALLOW_COPY_AND_ASSIGN(StatsUploader); +}; + +bool AggregateMetrics(); +bool AggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report); +bool TestableAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report, + StatsUploader* stats_uploader); +bool UploadMetrics(const char* extra_url_data, const char* user_agent, + const char *content); + +#ifdef OS_WINDOWS +void ResetPersistentMetrics(CRegKey *key); +#else +void ResetPersistentMetrics(void); +#endif + +} // namespace stats_report + +#endif // O3D_STATSREPORT_UPLOADER_H_ diff --git a/o3d/statsreport/uploader_aggregation-mac.mm b/o3d/statsreport/uploader_aggregation-mac.mm new file mode 100644 index 0000000..ff7d86ef --- /dev/null +++ b/o3d/statsreport/uploader_aggregation-mac.mm @@ -0,0 +1,245 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. +#include "statsreport/aggregator-mac.h" +#include "statsreport/common/const_product.h" +#include "statsreport/const-mac.h" +#include "statsreport/formatter.h" +#include "statsreport/uploader.h" + + +using stats_report::Formatter; +using stats_report::MetricsAggregatorMac; + +namespace stats_report { + +bool AggregateMetrics() { + using stats_report::MetricsAggregatorMac; + MetricsAggregatorMac aggregator(g_global_metrics); + if (!aggregator.AggregateMetrics()) { + DLOG(WARNING) << "Metrics aggregation failed for reasons unknown"; + return false; + } + + return true; +} + + +static bool ReportMetrics(const char* extra_url_data, + const char* user_agent, + uint32 interval, + StatsUploader* stats_uploader) { + NSDictionary *dict = + [NSDictionary dictionaryWithContentsOfFile:O3DStatsPath()]; + + if (dict == nil) + return false; + + Formatter formatter(PRODUCT_NAME_STRING, interval); + NSArray *keys = [dict allKeys]; + int count = [keys count]; + + for(int x = 0; x < count; ++x) { + NSString *name = [keys objectAtIndex:x]; + NSString *short_name = [name substringFromIndex:2]; + + if ([short_name length] == 0) + continue; + + const char *short_name_c_str = [short_name UTF8String]; + id item = [dict objectForKey:name]; + MetricBase *current = NULL; + switch([name characterAtIndex:0]) { + case 'C': + current = new CountMetric(short_name_c_str, + [item longLongValue]); + break; + case 'B': + current = new BoolMetric(short_name_c_str, + [item boolValue] == YES); + break; + case 'T': { + TimingMetric::TimingData data; + data.count = [[item objectAtIndex:0] intValue]; + data.sum = [[item objectAtIndex:1] longLongValue]; + data.minimum = [[item objectAtIndex:2] longLongValue]; + data.maximum = [[item objectAtIndex:3] longLongValue]; + current = new TimingMetric(short_name_c_str, data); + } + break; + case 'I': + current = new IntegerMetric(short_name_c_str, + [item longLongValue]); + break; + // We don't send this piece of data as it's not a metric. + case 'L': // "LastTransmission" + break; + } + if (current != NULL) + formatter.AddMetric(current); + } + + DLOG(INFO) << "formatter.output() = " << formatter.output(); + return stats_uploader->UploadMetrics(extra_url_data, + user_agent, + formatter.output()); +} + +void ResetPersistentMetrics() { + NSError *error = nil; + /* + [[NSFileManager defaultManager] removeItemAtPath:O3DStatsPath() + error:&error]; + */ + [[NSFileManager defaultManager] removeFileAtPath:O3DStatsPath() + handler:nil]; +} + + +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool AggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report) { + StatsUploader stats_uploader; + return TestableAggregateAndReportMetrics(extra_url_arguments, user_agent, + force_report, &stats_uploader); +} + +static int GetLastTransmissionTime() { + NSDictionary *dict = + [NSDictionary dictionaryWithContentsOfFile:O3DStatsPath()]; + + if (dict == nil) + return 0; + + NSNumber *when = [dict objectForKey:kLastTransmissionTimeValueName]; + + if (when == nil) + return 0; + + return [when intValue]; +} + +static void SetLastTransmissionTime(int when) { + NSMutableDictionary *dict = + [NSMutableDictionary dictionaryWithContentsOfFile:O3DStatsPath()]; + + if (dict == nil) + dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:when] + forKey:kLastTransmissionTimeValueName]; + else + [dict setObject:[NSNumber numberWithInt:when] + forKey:kLastTransmissionTimeValueName]; + + [dict writeToFile:O3DStatsPath() atomically:YES]; +} + + +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool TestableAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report, + StatsUploader* stats_uploader) { + // Open the store + MetricsAggregatorMac aggregator(g_global_metrics); + + int32 now = static_cast<int32>(time(NULL)); + + // Retrieve the last transmission time + int32 last_transmission_time = GetLastTransmissionTime(); + + // if last transmission time is missing or at all dodgy, then + // let's wipe all info and start afresh. + if (last_transmission_time == 0 || last_transmission_time > now) { + LOG(WARNING) << "dodgy or missing last transmission time, wiping stats"; + + ResetPersistentMetrics(); + + SetLastTransmissionTime(now); + + // Force a report of the stats so we get everything currently in there. + force_report = true; + } + + if (!AggregateMetrics()) { + DLOG(INFO) << "AggregateMetrics returned false"; + return false; + } + + DLOG(INFO) << "Last transmission time: " << last_transmission_time; + DLOG(INFO) << "Now: " << now; + DLOG(INFO) << "Now - Last transmission time: " + << now - last_transmission_time; + DLOG(INFO) << "Compared to: " << kStatsUploadIntervalSec; + + // Set last_transmission_time such that it will force + // an upload of the metrics + if (force_report) { + last_transmission_time = now - kStatsUploadIntervalSec; + } + if (now - last_transmission_time >= kStatsUploadIntervalSec) { + bool report_result = ReportMetrics(extra_url_arguments, user_agent, + now - last_transmission_time, + stats_uploader); + if (report_result) { + DLOG(INFO) << "Stats upload successful, resetting metrics"; + + ResetPersistentMetrics(); + } else { + DLOG(WARNING) << "Stats upload failed"; + } + + // No matter what, wait another upload interval to try again. It's better + // to report older stats than hammer on the stats server exactly when it + // has failed. + SetLastTransmissionTime(now); + return report_result; + } + return false; +} + +// Used primarily for testing. Default functionality. +bool StatsUploader::UploadMetrics(const char* extra_url_data, + const char* user_agent, + const char *content) { + return stats_report::UploadMetrics(extra_url_data, user_agent, content); +} + + +} // namespace stats_report diff --git a/o3d/statsreport/uploader_aggregation-posix.cc b/o3d/statsreport/uploader_aggregation-posix.cc new file mode 100644 index 0000000..dca530b --- /dev/null +++ b/o3d/statsreport/uploader_aggregation-posix.cc @@ -0,0 +1,150 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. +#include "backend/serverconnectionmanager.h" +#include "statsreport/common/const_product.h" +#include "iobuffer/iobuffer-inl.h" +#include "statsreport/aggregator-posix-inl.h" +#include "statsreport/const-posix.h" +#include "statsreport/formatter.h" +#include "statsreport/uploader.h" + + +DECLARE_int32(metrics_aggregation_interval); +DECLARE_int32(metrics_upload_interval); +DECLARE_string(metrics_server_name); +DECLARE_int32(metrics_server_port); + +using stats_report::Formatter; +using stats_report::MetricsAggregatorPosix; + +namespace { + +bool AggregateMetrics(MetricsAggregatorPosix *aggregator) { + if (!aggregator->AggregateMetrics()) { + LOG(WARNING) << "Metrics aggregation failed for reasons unknown"; + return false; + } + + return true; +} + + +bool ReportMetrics(MetricsAggregatorPosix *aggregator, + const char* extra_url_data, + const char* user_agent, + uint32 interval) { + Formatter formatter(PRODUCT_NAME_STRING, interval); + aggregator->FormatMetrics(&formatter); + + return UploadMetrics(extra_url_data, user_agent, formatter.output()); +} + +} // namespace + +namespace stats_report { + +bool AggregateMetrics() { + MetricsAggregatorPosix aggregator(g_global_metrics); + return ::AggregateMetrics(&aggregator); +} + +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool AggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report) { + StatsUploader stats_uploader; + return TestableAggregateAndReportMetrics(extra_url_arguments, user_agent, + force_report, &stats_uploader); +} +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool TestableAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report, + StatsUploader* stats_uploader) { + // Open the store + MetricsAggregatorPosix aggregator(g_global_metrics); + + int32 now = static_cast<int32>(time(NULL)); + + // Retrieve the last transmission time + int32 last_transmission_time; + bool success = aggregator.GetValue(kLastTransmissionTimeValueName, + &last_transmission_time); + + // if last transmission time is missing or at all hinky, then + // let's wipe all info and start afresh. + if (!success || last_transmission_time > now) { + LOG(WARNING) << "Hinky or missing last transmission time, wiping stats"; + + aggregator.ResetMetrics(); + + success = aggregator.SetValue(kLastTransmissionTimeValueName, now); + if (!success) + LOG(ERROR) << "Unable to write last transmission value"; + + // we just wiped everything, let's not waste any more time + return false; + } + + if (!::AggregateMetrics(&aggregator)) + return false; + + if (now - last_transmission_time >= kStatsUploadInterval) { + bool report_result = ReportMetrics(&aggregator, extra_url_arguments, + user_agent, + now - last_transmission_time); + if (report_result) { + LOG(INFO) << "Stats upload successful, resetting metrics"; + + aggregator.ResetMetrics(); + } else { + LOG(WARNING) << "Stats upload failed"; + } + + // No matter what, wait another upload interval to try again. It's better + // to report older stats than hammer on the stats server exactly when it's + // failed. + (void)aggregator.SetValue(kLastTransmissionTimeValueName, now); + return report_result; + } + return false; +} + +} // namespace stats_report diff --git a/o3d/statsreport/uploader_aggregation-win32.cc b/o3d/statsreport/uploader_aggregation-win32.cc new file mode 100644 index 0000000..a22dcf3 --- /dev/null +++ b/o3d/statsreport/uploader_aggregation-win32.cc @@ -0,0 +1,190 @@ +/* + * 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. + */ + + +// Helper class to manage the process of uploading metrics. + +#include <atlbase.h> +#include <atlcom.h> +#include <atlsafe.h> +#include <time.h> +#include "base/logging.h" + +#include "statsreport/uploader.h" +#include "statsreport/aggregator-win32.h" +#include "statsreport/const-win32.h" +#include "statsreport/persistent_iterator-win32.h" +#include "statsreport/formatter.h" +#include "statsreport/common/const_product.h" + +namespace stats_report { +// TODO: Refactor to avoid cross platform code duplication. + +bool AggregateMetrics() { + using stats_report::MetricsAggregatorWin32; + MetricsAggregatorWin32 aggregator(g_global_metrics, PRODUCT_NAME_STRING_WIDE); + if (!aggregator.AggregateMetrics()) { + DLOG(WARNING) << "Metrics aggregation failed for reasons unknown"; + return false; + } + + return true; +} + + +static bool ReportMetrics(const char* extra_url_data, + const char* user_agent, + DWORD interval, + StatsUploader* stats_uploader) { + PersistentMetricsIteratorWin32 it(PRODUCT_NAME_STRING_WIDE), end; + Formatter formatter(CStringA(PRODUCT_NAME_STRING), interval); + + for (; it != end; ++it) + formatter.AddMetric(*it); + DLOG(INFO) << "formatter.output() = " << formatter.output(); + return stats_uploader->UploadMetrics(extra_url_data, user_agent, + formatter.output()); +} + +void ResetPersistentMetrics(CRegKey *key) { + key->DeleteValue(kLastTransmissionTimeValueName); + key->DeleteSubKey(kCountsKeyName); + key->DeleteSubKey(kTimingsKeyName); + key->DeleteSubKey(kIntegersKeyName); + key->DeleteSubKey(kBooleansKeyName); +} + +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool AggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report) { + StatsUploader stats_uploader; + return TestableAggregateAndReportMetrics(extra_url_arguments, user_agent, + force_report, &stats_uploader); +} +// Returns: +// true if metrics were uploaded successfully, false otherwise +// Note: False does not necessarily mean an error, just that no metrics +// were uploaded +bool TestableAggregateAndReportMetrics(const char* extra_url_arguments, + const char* user_agent, + bool force_report, + StatsUploader* stats_uploader) { + CString key_name; + key_name.Format(kStatsKeyFormatString, PRODUCT_NAME_STRING_WIDE); + + CRegKey key; + LONG err = key.Create(HKEY_CURRENT_USER, key_name); + if (ERROR_SUCCESS != err) { + DLOG(WARNING) << "Unable to open metrics key"; + return false; + } + + DWORD now = static_cast<DWORD>(time(NULL)); + + // Retrieve the last transmission time + DWORD last_transmission_time; + DWORD value_type; + ULONG value_len = sizeof(last_transmission_time); + err = key.QueryValue(kLastTransmissionTimeValueName, &value_type, + &last_transmission_time, &value_len); + + // if last transmission time is missing or at all hinky, then + // let's wipe all info and start afresh. + if (ERROR_SUCCESS != err || REG_DWORD != value_type || + sizeof(last_transmission_time) != value_len || + last_transmission_time > now) { + DLOG(WARNING) << "Hinky or missing last transmission time, wiping stats"; + + ResetPersistentMetrics(&key); + + err = key.SetValue(kLastTransmissionTimeValueName, REG_DWORD, + &now, sizeof(now)); + if (ERROR_SUCCESS != err) { + DLOG(ERROR) << "Unable to write last transmission value, error " + << std::hex << err; + } + // Force a report of the stats so we get everything currently in there. + force_report = true; + + // we just wiped everything, let's not waste any more time + // return; <-- skipping this since we still want to aggregate + } + + if (!AggregateMetrics()) { + DLOG(INFO) << "AggregateMetrics returned false"; + return false; + } + + DLOG(INFO) << "Last transimission time: " << last_transmission_time; + DLOG(INFO) << "Now: " << now; + DLOG(INFO) << "Now - Last transmission time: " + << now - last_transmission_time; + DLOG(INFO) << "Compared to: " << kStatsUploadIntervalSec; + + // Set last_transmission_time such that it will force + // an upload of the metrics + if (force_report) { + last_transmission_time = now - kStatsUploadIntervalSec; + } + if (now - last_transmission_time >= kStatsUploadIntervalSec) { + bool report_result = ReportMetrics(extra_url_arguments, user_agent, + now - last_transmission_time, + stats_uploader); + if (report_result) { + DLOG(INFO) << "Stats upload successful, resetting metrics"; + + ResetPersistentMetrics(&key); + } else { + DLOG(WARNING) << "Stats upload failed"; + } + + // No matter what, wait another upload interval to try again. It's better + // to report older stats than hammer on the stats server exactly when it's + // failed. + err = key.SetValue(kLastTransmissionTimeValueName, REG_DWORD, + &now, sizeof(now)); + return report_result; + } + return false; +} + +// Used primarily for testing. Default functionality. +bool StatsUploader::UploadMetrics(const char* extra_url_data, + const char* user_agent, + const char *content) { + return stats_report::UploadMetrics(extra_url_data, user_agent, content); +} + +} // namespace stats_report diff --git a/o3d/statsreport/util-win32.h b/o3d/statsreport/util-win32.h new file mode 100644 index 0000000..f15dfac --- /dev/null +++ b/o3d/statsreport/util-win32.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + + +// Utility functions for Win32 stats aggregation and uploading +#ifndef O3D_STATSREPORT_UTIL_WIN32_H__ +#define O3D_STATSREPORT_UTIL_WIN32_H__ + +namespace stats_report { + +template <class ValueType> +bool GetData(CRegKey *parent, const wchar_t *value_name, ValueType *value) { + ULONG len = sizeof(ValueType); + LONG err = parent->QueryBinaryValue(value_name, value, &len); + if (ERROR_SUCCESS != err || len != sizeof(ValueType)) { + memset(value, 0, sizeof(ValueType)); + return false; + } + + return true; +} + +} // namespace stats_report + +#endif // O3D_STATSREPORT_UTIL_WIN32_H__ |