blob: 0a86a9f5629cccfbad62d8658af1b74415fcba8b [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "metrics/cumulative_metrics.h"
#include <base/bind.h>
#include <base/strings/string_util.h>
#include <base/sys_info.h>
#include <memory>
#include <utility>
using base::Time;
namespace chromeos_metrics {
namespace {
constexpr char kValidPrefixCharacters[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789_.";
} // namespace
CumulativeMetrics::CumulativeMetrics(
const std::string& prefix,
const std::vector<std::string>& names,
base::TimeDelta update_period,
Callback update_callback,
base::TimeDelta accumulation_period,
Callback cycle_end_callback) :
prefix_(prefix), update_period_(update_period),
accumulation_period_(accumulation_period) {
int64_t new_version_hash = 0;
PersistentInteger persistent_version_hash(prefix + "version.hash");
if (!base::ContainsOnlyChars(prefix, kValidPrefixCharacters))
LOG(FATAL) << "invalid cumulative metrics prefix \"" << prefix << "\"";
prefix_ = prefix;
update_callback_ = std::move(update_callback);
cycle_end_callback_ = std::move(cycle_end_callback);
cycle_start_.reset(new PersistentInteger(prefix + "cycle.start"));
last_update_time_ = base::TimeTicks::Now();
// Associate |names| with accumulated values (which may already exist from
// previous sessions).
for (const auto& name : names) {
values_.emplace(name, std::make_unique<PersistentInteger>(prefix + name));
}
// Check version hash. This only needs to happen at init time because we
// assume that the OS version cannot be bumped without restarting the daemon.
std::string version;
if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version)) {
new_version_hash = base::Hash(version);
} else {
LOG(ERROR) << "cannot find CHROMEOS_RELEASE_VERSION";
}
// Check if current cycle has expired.
bool new_cycle_has_started = ProcessCycleEnd();
// On a version change that's not at the end of a cycle, discard the
// currently accumulating quantities, because the samples would be incorrect.
// Then start a new cycle.
int64_t old_version_hash = persistent_version_hash.Get();
if (old_version_hash != new_version_hash) {
persistent_version_hash.Set(new_version_hash);
if (!new_cycle_has_started) {
// OS version has changed. Reset accumulators.
for (const auto& kv : values_) {
kv.second->GetAndClear();
}
// Reset start time.
base::TimeDelta wall_time = Time::Now() - Time::UnixEpoch();
cycle_start_->Set(wall_time.InMicroseconds());
}
}
timer_.Start(FROM_HERE, update_period_, this, &CumulativeMetrics::Update);
}
bool CumulativeMetrics::ProcessCycleEnd() {
base::TimeDelta wall_time = Time::Now() - Time::UnixEpoch();
base::TimeDelta cycle_start =
base::TimeDelta::FromMicroseconds(cycle_start_->Get());
if (wall_time - cycle_start > accumulation_period_) {
cycle_start_->Set(wall_time.InMicroseconds());
return true;
}
return false;
}
base::TimeDelta CumulativeMetrics::ActiveTimeSinceLastUpdate() const {
return base::TimeTicks::Now() - last_update_time_;
}
void CumulativeMetrics::Update() {
update_callback_.Run(this);
if (ProcessCycleEnd()) {
cycle_end_callback_.Run(this);
for (const auto& kv : values_) {
kv.second->GetAndClear();
}
}
last_update_time_ = base::TimeTicks::Now();
}
void CumulativeMetrics::PanicFromBadName(const char* action,
const std::string& name) const {
LOG(FATAL) << "cannot execute action \"" << action << " on \"" << name <<
"\" because it is not in CumulativeMetrics \"" << prefix_ << "\"";
}
PersistentInteger* CumulativeMetrics::Find(const std::string& name) const {
const auto iter = values_.find(name);
if (iter == values_.end())
return nullptr;
return iter->second.get();
}
int64_t CumulativeMetrics::Get(const std::string& name) const {
PersistentInteger* pip = Find(name);
if (pip != nullptr)
return pip->Get();
PanicFromBadName("GET", name);
return 0;
}
void CumulativeMetrics::Set(const std::string& name, int64_t value) {
PersistentInteger* pip = Find(name);
if (pip != nullptr) {
pip->Set(value);
} else {
PanicFromBadName("SET", name);
}
}
void CumulativeMetrics::Add(const std::string& name, int64_t value) {
PersistentInteger* pip = Find(name);
if (pip != nullptr) {
pip->Add(value);
} else {
PanicFromBadName("ADD", name);
}
}
int64_t CumulativeMetrics::GetAndClear(const std::string& name) {
PersistentInteger* pip = Find(name);
if (pip != nullptr)
return pip->GetAndClear();
PanicFromBadName("GETANDCLEAR", name);
return 0;
}
} // namespace chromeos_metrics