blob: bc94deb80c4d56e3c384263b24343609e0cc2157 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "secagentd/metrics_sender.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/time/time.h"
#include "metrics/metrics_library.h"
namespace secagentd {
namespace metrics {
bool operator==(const CountMetric& m1, const CountMetric& m2) {
if (std::string_view(m1.name) == std::string_view(m2.name) &&
m1.min == m2.min && m1.max == m2.max && m1.nbuckets == m2.nbuckets) {
return true;
}
return false;
}
} // namespace metrics
MetricsSender& MetricsSender::GetInstance() {
static base::NoDestructor<MetricsSender> instance;
return *instance;
}
void MetricsSender::InitBatchedMetrics() {
flush_batched_metrics_timer_.Start(
FROM_HERE, base::Seconds(metrics::kBatchTimer),
base::BindRepeating(&MetricsSender::Flush, base::Unretained(this)));
}
void MetricsSender::IncrementCountMetric(metrics::CountMetric m, int value) {
unsigned int scaled_value = value / m.nbuckets;
// properly round down if negative.
if (value < 0 && abs(value) % m.nbuckets > m.nbuckets / 2) {
// round down.
scaled_value -= 1;
}
batch_count_map_[m][scaled_value] += 1;
if (batch_count_map_[m][scaled_value] >= metrics::kMaxMapValue) {
Flush();
}
}
void MetricsSender::Flush() {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MetricsSender::SendBatchedMetricsToUMA,
weak_ptr_factory_.GetWeakPtr(), batch_enum_map_,
batch_count_map_));
batch_enum_map_.clear();
batch_count_map_.clear();
// Run registered callbacks.
for (auto cb : metric_callbacks_) {
cb.Run();
}
}
void MetricsSender::SendBatchedMetricsToUMA(
metrics::MetricsMap enum_map_copy,
metrics::MetricsCountMap count_map_copy) {
// Commit enum histogram metrics.
for (auto const& [key, val] : enum_map_copy) {
int pos = key.find_last_of(":");
auto metric_name = key.substr(0, pos);
auto sample = stoi(key.substr(pos + 1));
auto it = exclusive_max_map_.find(metric_name.c_str());
// If sample is success value divide by 100.
int count = val;
if (sample == success_value_map_.find(metric_name)->second) {
count = (count + 100 - 1) / 100;
}
if (!metrics_library_->SendRepeatedEnumToUMA(
base::StrCat({metrics::kMetricNamePrefix, metric_name}), sample,
it->second, count)) {
LOG(ERROR) << "Failed to send batched metrics for " << metric_name;
}
}
// Commit count histogram metrics.
for (auto const& [metric, submap] : count_map_copy) {
std::string metric_name =
base::StrCat({metrics::kMetricNamePrefix, metric.name});
for (auto const& [sample, nsample] : submap) {
// sample was scaled down on storage to conserve memory, scale it back up.
metrics_library_->SendRepeatedToUMA(metric_name, sample * metric.nbuckets,
metric.min, metric.max,
metric.nbuckets, nsample);
}
}
}
void MetricsSender::RegisterMetricOnFlushCallback(
base::RepeatingCallback<void()> cb) {
metric_callbacks_.push_back(std::move(cb));
}
MetricsSender::MetricsSender()
: MetricsSender(std::make_unique<MetricsLibrary>()) {}
MetricsSender::MetricsSender(
std::unique_ptr<MetricsLibraryInterface> metrics_library)
: weak_ptr_factory_(this), metrics_library_(std::move(metrics_library)) {}
} // namespace secagentd