blob: 2b4d855190edc819e95636e9159cb8e85f5e22bc [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.
// CumulativeMetrics helps maintain and report "accumulated" quantities, for
// instance how much data has been sent over WiFi and LTE in a day. Here's
// roughly how a continuously running daemon would do that:
// {
// // initialization, at daemon startup
// ...
// base::FilePath backing_dir("/var/lib/metrics/shill");
// std::vector<std::string> stat_names = {"wifi", "cellular", "total"};
// CumulativeMetrics cm(
// backing_dir,
// stat_names,
// TimeDelta::FromMinutes(5),
// base::BindRepeating(&UpdateConnectivityStats),
// TimeDelta::FromDays(1),
// base::BindRepeating(&ReportConnectivityStats,
// base::Unretained(metrics_lib_));
// ...
// }
// void UpdateConnectivityStats(CumulativeMetrics *cm) {
// if (wifi_connected) {
// cm->Add("wifi", cm->ActiveTimeSinceLastUpdate());
// }
// if (lte_connected) {
// cm->Add("cellular", cm->ActiveTimeSinceLastUpdate());
// }
// cm->Add("total", cm->ActiveTimeSinceLastUpdate());
// }
// void ReportConnectivityStats(MetricsLibrary* ml, CumulativeMetrics* cm) {
// int64_t total = cm->Get("total");
// ml->SendSample(total, ...);
// int64_t wifi_time = cm->Get("wifi");
// ml->SendSample(wifi_time * 100 / total, ...);
// ...
// }
// In the above example, the cumulative metrics object helps maintain three
// quantities (wifi, cellular, and total) persistently across boot sessions and
// other daemon restarts. The quantities are updated every 5 minutes, and
// samples are sent at most once a day.
// The class clears (i.e. sets to 0) all accumulated quantities on an OS
// version change.
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <base/memory/weak_ptr.h>
#include <base/time/time.h>
#include <base/timer/timer.h>
#include "metrics/persistent_integer.h"
namespace chromeos_metrics {
class CumulativeMetrics {
using Callback = base::RepeatingCallback<void(CumulativeMetrics*)>;
// Constructor.
// |backing_dir| points to a subdirectory for the backing files, for instance
// "/var/lib/shill/metrics".
// |names| are the names of the quantities to be maintained. They also name
// the corresponding backing files.
// |update_callback| and |cycle_end_callback| are partial closures which take
// one argument of type CumulativeMetrics* and return void. The former is
// called (roughly) every |update_period_seconds|, and similarly
// |cycle_end_callback| is called every |accumulation_period_seconds| (see
// example at the top of this file).
// Note that the accumulated values are cleared at the end of each cycle
// after calling |cycle_end_callback_|, which typically sends those
// quantities as histogram values. They are also cleared on Chrome OS
// version changes, but in that case |cycle_end_callback_| is not called
// unless the version change happens together with the end of a cycle. The
// reason is that we want to ship correct histograms for each version, so we
// can notice the impact of the version change.
CumulativeMetrics(const base::FilePath& backing_dir,
const std::vector<std::string>& names,
base::TimeDelta update_period,
Callback update_callback,
base::TimeDelta accumulation_period,
Callback cycle_end_callback);
CumulativeMetrics(const CumulativeMetrics&) = delete;
CumulativeMetrics& operator=(const CumulativeMetrics&) = delete;
virtual ~CumulativeMetrics() {}
// Calls |update_callback_|.
// This is automatically called every |update_period_seconds_| of active time,
// but also can be manually called when a relevant change has occurred. Manual
// calls to this method do not cause the timing of automatic invocations to
// change.
// Note that clients who choose to manually call this method must ensure that
// the update callback implementation can properly handle being invoked at a
// variable frequency.
void Update();
// Returns the time delta (in active time, not elapsed wall clock time) since
// the last invocation of Update, or the daemon start. Note that this could
// be a lot smaller than the elapsed time.
// This method is virtual so it can be mocked for testing.
virtual base::TimeDelta ActiveTimeSinceLastUpdate() const;
// Sets the value of |name| to |value|.
void Set(const std::string& name, int64_t value);
// Adds |value| to the current value of |name|.
void Add(const std::string& name, int64_t value);
// Sets |name| to the max of its current value and the specified |value|.
void Max(const std::string& name, int64_t value);
// Gets the value of |name|
int64_t Get(const std::string& name) const;
// Returns the value of |name| and sets it to 0.
int64_t GetAndClear(const std::string& name);
// Checks if the current cycle has expired and takes appropriate actions.
// Returns true if the current cycle has expired, false otherwise.
bool ProcessCycleEnd();
// Returns a pointer to the persistent integer for |name| if |name| is a
// valid cumulative metric. Otherwise returns nullptr.
PersistentInteger* Find(const std::string& name) const;
// Convenience function for reporting uses of invalid metric names.
void PanicFromBadName(const char* action, const std::string& name) const;
// for PersistentInteger backing files
base::FilePath backing_dir_;
// name -> accumulated value
std::map<std::string, std::unique_ptr<PersistentInteger>> values_;
// interval between update callbacks
base::TimeDelta update_period_;
// cycle length
base::TimeDelta accumulation_period_;
// clock at beginning of cycle (usecs)
std::unique_ptr<PersistentInteger> cycle_start_;
// active time at latest update
base::TimeTicks last_update_time_;
// |update_callback_| is called every |update_period_seconds_| to update the
// accumulators.
Callback update_callback_;
// |cycle_end_callback_| is called every |accumulation_period_seconds_| (for
// instance, one day worth) to send histogram samples.
Callback cycle_end_callback_;
base::RepeatingTimer timer_;
} // namespace chromeos_metrics