| // Copyright 2021 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_impl.h" | |
| #include <map> | |
| #include <string> | |
| #include <vector> | |
| #include <base/logging.h> | |
| #include <base/memory/scoped_refptr.h> | |
| #include <base/time/time.h> | |
| #include <metrics/structured_events.h> | |
| #include "rmad/constants.h" | |
| #include "rmad/metrics/metrics_constants.h" | |
| #include "rmad/metrics/state_metrics.h" | |
| #include "rmad/utils/json_store.h" | |
| using StructuredShimlessRmaReport = | |
| metrics::structured::events::rmad::ShimlessRmaReport; | |
| using StructuredReplacedComponent = | |
| metrics::structured::events::rmad::ReplacedComponent; | |
| using StructuredOccurredError = | |
| metrics::structured::events::rmad::OccurredError; | |
| using StructuredAdditionalActivity = | |
| metrics::structured::events::rmad::AdditionalActivity; | |
| using StructuredShimlessRmaStateReport = | |
| metrics::structured::events::rmad::ShimlessRmaStateReport; | |
| namespace rmad { | |
| MetricsUtilsImpl::MetricsUtilsImpl(bool record_to_system) | |
| : record_to_system_(record_to_system) {} | |
| bool MetricsUtilsImpl::RecordShimlessRmaReport( | |
| scoped_refptr<JsonStore> json_store) { | |
| auto report = StructuredShimlessRmaReport(); | |
| double current_timestamp = base::Time::Now().InSecondsFSinceUnixEpoch(); | |
| double first_setup_timestamp; | |
| if (!GetMetricsValue(json_store, kMetricsFirstSetupTimestamp, | |
| &first_setup_timestamp)) { | |
| LOG(ERROR) << "Failed to get timestamp of the first setup."; | |
| return false; | |
| } | |
| report.SetOverallTime(current_timestamp - first_setup_timestamp); | |
| double setup_timestamp; | |
| if (!GetMetricsValue(json_store, kMetricsSetupTimestamp, &setup_timestamp) || | |
| !SetMetricsValue(json_store, kMetricsSetupTimestamp, current_timestamp)) { | |
| LOG(ERROR) << "Failed to get and reset setup timestamp for measuring " | |
| "running time."; | |
| return false; | |
| } | |
| double running_time = 0.0; | |
| // It could be the first time we have calculated the running time, thus the | |
| // return value is ignored. | |
| GetMetricsValue(json_store, kMetricsRunningTime, &running_time); | |
| running_time += current_timestamp - setup_timestamp; | |
| report.SetRunningTime(running_time); | |
| if (bool is_complete; | |
| GetMetricsValue(json_store, kMetricsIsComplete, &is_complete)) { | |
| report.SetIsComplete(is_complete); | |
| } else { | |
| report.SetIsComplete(false); | |
| } | |
| RoVerificationStatus ro_verification; | |
| if (std::string ro_verification_str; | |
| GetMetricsValue(json_store, kMetricsRoFirmwareVerified, | |
| &ro_verification_str) && | |
| RoVerificationStatus_Parse(ro_verification_str, &ro_verification)) { | |
| report.SetRoVerificationStatus(ro_verification); | |
| } else { | |
| report.SetRoVerificationStatus(RMAD_RO_VERIFICATION_UNKNOWN); | |
| } | |
| ReturningOwner returning_owner; | |
| if (std::string returning_owner_str; | |
| GetMetricsValue(json_store, kMetricsReturningOwner, | |
| &returning_owner_str) && | |
| ReturningOwner_Parse(returning_owner_str, &returning_owner)) { | |
| report.SetReturningOwner(returning_owner); | |
| } else { | |
| report.SetReturningOwner(RMAD_RETURNING_OWNER_UNKNOWN); | |
| } | |
| MainboardReplacement mlb_replacement; | |
| if (std::string mlb_replacement_str; | |
| GetMetricsValue(json_store, kMetricsMlbReplacement, | |
| &mlb_replacement_str) && | |
| MainboardReplacement_Parse(mlb_replacement_str, &mlb_replacement)) { | |
| report.SetMainboardReplacement(mlb_replacement); | |
| } else { | |
| report.SetMainboardReplacement(RMAD_MLB_REPLACEMENT_UNKNOWN); | |
| } | |
| WpDisableMethod wp_disable_method; | |
| if (std::string wp_disable_method_str; | |
| GetMetricsValue(json_store, kMetricsWpDisableMethod, | |
| &wp_disable_method_str) && | |
| WpDisableMethod_Parse(wp_disable_method_str, &wp_disable_method)) { | |
| report.SetWriteProtectDisableMethod(wp_disable_method); | |
| } else { | |
| report.SetWriteProtectDisableMethod(RMAD_WP_DISABLE_METHOD_UNKNOWN); | |
| } | |
| if (record_to_system_ && !report.Record()) { | |
| LOG(ERROR) << "Failed to record shimless rma report to metrics."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool MetricsUtilsImpl::RecordReplacedComponents( | |
| scoped_refptr<JsonStore> json_store) { | |
| // Ignore the else part, because we may not replace anything. | |
| if (std::vector<std::string> replace_component_names; | |
| MetricsUtils::GetMetricsValue(json_store, kMetricsReplacedComponents, | |
| &replace_component_names)) { | |
| for (const std::string& component_name : replace_component_names) { | |
| if (RmadComponent component; | |
| RmadComponent_Parse(component_name, &component)) { | |
| auto structured_replace_component = StructuredReplacedComponent(); | |
| structured_replace_component.SetComponentCategory(component); | |
| if (record_to_system_ && !structured_replace_component.Record()) { | |
| LOG(ERROR) << "Failed to record replaced component to metrics."; | |
| return false; | |
| } | |
| } else { | |
| LOG(ERROR) << "Failed to parse [" << component_name | |
| << "] as component to append to metrics."; | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool MetricsUtilsImpl::RecordOccurredErrors( | |
| scoped_refptr<JsonStore> json_store) { | |
| // Ignore the else part, because we may have no errors. | |
| if (std::vector<std::string> occurred_errors; | |
| GetMetricsValue(json_store, kMetricsOccurredErrors, &occurred_errors)) { | |
| for (const std::string& occurred_error : occurred_errors) { | |
| if (RmadErrorCode error_code; | |
| RmadErrorCode_Parse(occurred_error, &error_code)) { | |
| auto structured_occurred_error = StructuredOccurredError(); | |
| structured_occurred_error.SetErrorType(error_code); | |
| if (record_to_system_ && !structured_occurred_error.Record()) { | |
| LOG(ERROR) << "Failed to record error code to metrics."; | |
| return false; | |
| } | |
| } else { | |
| LOG(ERROR) << "Failed to parse [" << occurred_error | |
| << "] as error code to append to metrics."; | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool MetricsUtilsImpl::RecordAdditionalActivities( | |
| scoped_refptr<JsonStore> json_store) { | |
| // Ignore the else part, because we may have no additional activities. | |
| if (std::vector<std::string> additional_activities; GetMetricsValue( | |
| json_store, kMetricsAdditionalActivities, &additional_activities)) { | |
| for (std::string activity_name : additional_activities) { | |
| AdditionalActivity additional_activity; | |
| if (AdditionalActivity_Parse(activity_name, &additional_activity) && | |
| additional_activity != RMAD_ADDITIONAL_ACTIVITY_NOTHING) { | |
| auto structured_additional_activity = StructuredAdditionalActivity(); | |
| structured_additional_activity.SetActivityType(additional_activity); | |
| if (record_to_system_ && !structured_additional_activity.Record()) { | |
| LOG(ERROR) << "Failed to record additional activity to metrics."; | |
| return false; | |
| } | |
| } else { | |
| LOG(ERROR) << "Failed to parse [" << activity_name | |
| << "] as additional activity to append to metrics."; | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool MetricsUtilsImpl::RecordShimlessRmaStateReport( | |
| scoped_refptr<JsonStore> json_store) { | |
| std::map<int, StateMetricsData> state_metrics; | |
| if (GetMetricsValue(json_store, kStateMetrics, &state_metrics)) { | |
| for (auto [state_case, data] : state_metrics) { | |
| auto structured_state_report = StructuredShimlessRmaStateReport(); | |
| structured_state_report.SetStateCase(state_case); | |
| structured_state_report.SetIsAborted(data.is_aborted); | |
| if (data.overall_time < 0) { | |
| LOG(ERROR) << state_case | |
| << ": Invalid overall time: " << data.overall_time; | |
| return false; | |
| } | |
| structured_state_report.SetOverallTime(data.overall_time); | |
| if (data.transition_count <= 0) { | |
| LOG(ERROR) << state_case | |
| << ": Invalid transition count: " << data.transition_count; | |
| return false; | |
| } | |
| structured_state_report.SetTransitionCount(data.transition_count); | |
| if (data.get_log_count < 0) { | |
| LOG(ERROR) << state_case | |
| << ": Invalid GetLog count: " << data.get_log_count; | |
| return false; | |
| } | |
| structured_state_report.SetGetLogCount(data.get_log_count); | |
| if (data.save_log_count < 0) { | |
| LOG(ERROR) << state_case | |
| << ": Invalid SaveLog count: " << data.save_log_count; | |
| return false; | |
| } | |
| structured_state_report.SetSaveLogCount(data.save_log_count); | |
| if (record_to_system_ && !structured_state_report.Record()) { | |
| LOG(ERROR) << state_case | |
| << ": Failed to record state report to metrics."; | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool MetricsUtilsImpl::RecordAll(scoped_refptr<JsonStore> json_store) { | |
| return RecordShimlessRmaReport(json_store) && | |
| RecordOccurredErrors(json_store) && | |
| RecordReplacedComponents(json_store) && | |
| RecordAdditionalActivities(json_store) && | |
| RecordShimlessRmaStateReport(json_store); | |
| } | |
| } // namespace rmad |