blob: 330740cdbd0923bdd656d25db240166a0e9c23f3 [file] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include <string>
#include <string_view>
#include <base/containers/fixed_flat_map.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <base/time/time.h>
#include "diagnostics/cros_minidiag/minidiag_metrics.h"
#include "diagnostics/cros_minidiag/minidiag_metrics_names.h"
namespace cros_minidiag {
namespace {
// The type names that showed in elogtool output.
// Some of the type names changed their cases, so we have to cast into lower
// case before looking up.
inline constexpr char kTypeNameMemoryCheckFull[] = "memory check (full)";
inline constexpr char kTypeNameMemoryCheckQuick[] = "memory check (quick)";
inline constexpr char kTypeNameStorageHealthInfo[] = "storage health info";
inline constexpr char kTypeNameStorageSelfTestExtended[] =
"storage self-test (extended)";
inline constexpr char kTypeNameStorageSelfTestShort[] =
"storage self-test (short)";
// The result names that showed in elogtool output.
inline constexpr char kResultPassed[] = "passed";
inline constexpr char kResultError[] = "error";
inline constexpr char kResultFailed[] = "failed";
inline constexpr char kResultAborted[] = "aborted";
// IMPORTANT: To obsolete a metric enum value, just remove it from the map
// initialization and comment it out on the Enum.
constexpr auto kTypeNames =
base::MakeFixedFlatMap<std::string_view, std::string_view>({
{kTypeNameMemoryCheckFull, metrics::kMemoryCheckFull},
{kTypeNameMemoryCheckQuick, metrics::kMemoryCheckQuick},
{kTypeNameStorageHealthInfo, metrics::kStorageHealthInfo},
{kTypeNameStorageSelfTestExtended, metrics::kStorageSelfTestExtended},
{kTypeNameStorageSelfTestShort, metrics::kStorageSelfTestShort},
});
constexpr auto kResultNames =
base::MakeFixedFlatMap<std::string_view, metrics::MiniDiagResultType>({
{kResultPassed, metrics::MiniDiagResultType::kPassed},
{kResultError, metrics::MiniDiagResultType::kError},
{kResultFailed, metrics::MiniDiagResultType::kFailed},
{kResultAborted, metrics::MiniDiagResultType::kAborted},
});
// Since we need to cast the duration (in seconds) to int before sending to
// the metrics library, we need to set its hard limit to INT_MAX.
// Note that this is not the `max` under `SendToUMA`. By the metrics guide, it
// is still allowed to send some data exceeds the `max`; but any data exceeds
// INT_MAX will cause the metrics library reports unexpected data, which is
// invalid.
constexpr base::TimeDelta kTimeLimit =
base::Seconds(std::numeric_limits<int>::max());
} // namespace
MiniDiagMetrics::MiniDiagMetrics() = default;
MiniDiagMetrics::~MiniDiagMetrics() = default;
void MiniDiagMetrics::RecordLaunch(int count) const {
if (!metrics_library_->SendLinearToUMA(metrics::kLaunchHistogram, count,
metrics::kLaunchCountMax))
LOG(ERROR) << "Cannot send MiniDiag launch count to UMA";
}
void MiniDiagMetrics::RecordTestReport(const std::string& type,
const std::string& result,
const base::TimeDelta& time) const {
auto it_type = kTypeNames.find(std::string_view(base::ToLowerASCII(type)));
auto it_result =
kResultNames.find(std::string_view(base::ToLowerASCII(result)));
if (it_type == kTypeNames.end()) {
LOG(ERROR) << "Type name not exist: " << type;
return;
}
if (it_result == kResultNames.end()) {
LOG(ERROR) << "Result not exist: " << result;
return;
}
if (!IsTimeValid(time)) {
LOG(ERROR) << "Open duration not valid: " << time.InSeconds();
return;
}
const std::string& metrics_prefix{it_type->second};
const metrics::MiniDiagResultType& result_code = it_result->second;
// Send Platform.MiniDiag.[Type].Result metrics.
if (!metrics_library_->SendEnumToUMA(
metrics_prefix + metrics::kSuffixResult,
static_cast<int>(result_code),
static_cast<int>(metrics::MiniDiagResultType::kMaxValue))) {
LOG(ERROR) << "Cannot send MiniDiag test result to UMA";
}
// Send Platform.MiniDiag.[Type].OpenDuration metrics.
if (!metrics_library_->SendToUMA(
metrics_prefix + metrics::kSuffixOpenDuration,
static_cast<int>(time.InSeconds()), metrics::kOpenDurationMin,
metrics::kOpenDurationMax, metrics::kOpenDurationBucket)) {
LOG(ERROR) << "Cannot send MiniDiag test open duration to UMA";
}
}
void MiniDiagMetrics::RecordOpenDuration(const base::TimeDelta& time) const {
if (!IsTimeValid(time)) {
LOG(ERROR) << "Open duration not valid: " << time.InSeconds();
return;
}
// Send Platform.MiniDiag.OpenDuration metrics.
if (!metrics_library_->SendToUMA(
metrics::kOpenDurationHistogram, static_cast<int>(time.InSeconds()),
metrics::kOpenDurationMin, metrics::kOpenDurationMax,
metrics::kOpenDurationBucket)) {
LOG(ERROR) << "Cannot send MiniDiag open duration to UMA";
}
}
bool MiniDiagMetrics::IsTimeValid(const base::TimeDelta& time) const {
return (!time.is_negative()) && (time <= kTimeLimit);
}
} // namespace cros_minidiag