blob: 6700c51961139a4931cb238ecc2408bd94929ce8 [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/check.h>
#include <base/files/file_path.h>
#include <base/strings/string_util.h>
#include <base/system/sys_info.h>
#include <memory>
#include <utility>
using base::FilePath;
using base::Time;
namespace chromeos_metrics {
namespace {
constexpr char kValidNameCharacters[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789_.";
} // namespace
CumulativeMetrics::CumulativeMetrics(const FilePath& backing_dir,
const std::vector<std::string>& names,
base::TimeDelta update_period,
Callback update_callback,
base::TimeDelta accumulation_period,
Callback cycle_end_callback)
: backing_dir_(backing_dir),
update_period_(update_period),
accumulation_period_(accumulation_period) {
int64_t new_version_hash = 0;
PersistentInteger persistent_version_hash(backing_dir.Append("version.hash"));
update_callback_ = std::move(update_callback);
cycle_end_callback_ = std::move(cycle_end_callback);
cycle_start_.reset(new PersistentInteger(backing_dir.Append("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) {
CHECK(base::ContainsOnlyChars(name, kValidNameCharacters))
<< "bad cumulative metrics name \"" << name << "\"";
values_.emplace(
name, std::make_unique<PersistentInteger>(backing_dir.Append(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 << "\": unknown name \""
<< name << "\"";
}
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);
}
}
void CumulativeMetrics::Max(const std::string& name, int64_t value) {
PersistentInteger* pip = Find(name);
if (pip != nullptr) {
pip->Max(value);
} else {
PanicFromBadName("MAX", 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