blob: 3bd52ddd20175d7c2d09eeb850077823ea09b6ac [file] [log] [blame]
// 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 "rmad/metrics/metrics_utils.h"
#include <base/check.h>
#include <base/json/json_string_value_serializer.h>
#include <base/logging.h>
#include <base/memory/scoped_refptr.h>
#include "rmad/constants.h"
#include "rmad/metrics/state_metrics.h"
#include "rmad/proto_bindings/rmad.pb.h"
#include "rmad/utils/json_store.h"
namespace rmad {
namespace {
// TODO(chenghan): Define this in a common header, e.g. common.h.
const char* GetStateName(RmadState::StateCase state) {
auto it = kStateNames.find(state);
CHECK(it != kStateNames.end());
return it->second.data();
}
} // namespace
bool MetricsUtils::UpdateStateMetricsOnAbort(
scoped_refptr<JsonStore> json_store,
RmadState::StateCase state_case,
double timestamp) {
if (!UpdateStateMetricsOnStateTransition(
json_store, state_case, RmadState::STATE_NOT_SET, timestamp)) {
LOG(ERROR) << "Failed to calculate metrics for state "
<< GetStateName(state_case);
return false;
}
StateMetricsMap state_metrics;
GetMetricsValue(json_store, kStateMetrics, &state_metrics);
state_metrics[state_case].is_aborted = true;
return SetMetricsValue(json_store, kStateMetrics, state_metrics);
}
bool MetricsUtils::UpdateStateMetricsOnStateTransition(
scoped_refptr<JsonStore> json_store,
RmadState::StateCase from,
RmadState::StateCase to,
double timestamp) {
StateMetricsMap state_metrics;
// At the beginning, we may have no data, so ignore the return value.
GetMetricsValue(json_store, kStateMetrics, &state_metrics);
// Update the global setup time and first setup time if needed.
if (!SetMetricsValue(json_store, kMetricsSetupTimestamp, timestamp)) {
LOG(ERROR) << "Could not store global setup time";
return false;
}
if (double first_setup_time;
to != kInitialStateCase &&
!GetMetricsValue(json_store, kMetricsFirstSetupTimestamp,
&first_setup_time) &&
!SetMetricsValue(json_store, kMetricsFirstSetupTimestamp, timestamp)) {
LOG(ERROR) << "Could not store global first setup time";
return false;
}
if (from != RmadState::STATE_NOT_SET && to != RmadState::STATE_NOT_SET) {
state_metrics[to].transition_count++;
}
if (!state_metrics.UpdateStateOverallTime(from, timestamp) ||
!state_metrics.InitializeState(to, timestamp)) {
return false;
}
return SetMetricsValue(json_store, kStateMetrics, state_metrics);
}
bool MetricsUtils::UpdateStateMetricsOnGetLog(
scoped_refptr<JsonStore> json_store, RmadState::StateCase state_case) {
StateMetricsMap state_metrics;
// At the beginning, we may have no data, so ignore the return value.
GetMetricsValue(json_store, kStateMetrics, &state_metrics);
state_metrics[state_case].get_log_count++;
return SetMetricsValue(json_store, kStateMetrics, state_metrics);
}
bool MetricsUtils::UpdateStateMetricsOnSaveLog(
scoped_refptr<JsonStore> json_store, RmadState::StateCase state_case) {
StateMetricsMap state_metrics;
// At the beginning, we may have no data, so ignore the return value.
GetMetricsValue(json_store, kStateMetrics, &state_metrics);
state_metrics[state_case].save_log_count++;
return SetMetricsValue(json_store, kStateMetrics, state_metrics);
}
std::string MetricsUtils::GetMetricsSummaryAsString(
scoped_refptr<JsonStore> json_store) {
base::Value metrics;
if (!json_store->GetValue(kMetrics, &metrics)) {
return "";
}
// Since the type might change if we successfully get the value from the json
// store, we need to check here.
CHECK(metrics.is_dict());
// Remove timestamps for the entire process.
metrics.GetDict().Remove(kMetricsFirstSetupTimestamp);
metrics.GetDict().Remove(kMetricsSetupTimestamp);
// Refine readability of state metrics for better understanding.
const base::Value* original_state_metrics =
metrics.GetDict().Find(kStateMetrics);
if (original_state_metrics && original_state_metrics->is_dict()) {
metrics.GetDict().Set(
kStateMetrics,
RefineStateMetricsReadability(original_state_metrics->GetDict()));
}
std::string output;
JSONStringValueSerializer serializer(&output);
serializer.set_pretty_print(true);
serializer.Serialize(metrics);
return output;
}
base::Value::Dict MetricsUtils::RefineStateMetricsReadability(
const base::Value::Dict& original_state_metrics) {
base::Value::Dict new_state_metrics;
for (const auto& [state_case_str, metrics_data] : original_state_metrics) {
// For each state, we should have a dict to store metrics data.
CHECK(metrics_data.is_dict());
auto it = kStateNames.end();
if (int state_case; base::StringToInt(state_case_str, &state_case)) {
it = kStateNames.find(static_cast<RmadState::StateCase>(state_case));
if (it != kStateNames.end()) {
// Remap state_cases to names and remove timestamps for all states.
auto new_metrics_data = metrics_data.Clone();
new_metrics_data.GetDict().Remove(kStateSetupTimestamp);
new_state_metrics.Set(it->second, std::move(new_metrics_data));
}
}
}
return new_state_metrics;
}
} // namespace rmad