| // 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/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 |