blob: 3c1981b1d87f163d34cba3670225470fa866275b [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 "rmad/logs/logs_utils.h"
#include <functional>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/json/json_string_value_serializer.h>
#include <base/memory/scoped_refptr.h>
#include <base/strings/strcat.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_util.h>
#include <base/time/time.h>
#include <base/values.h>
#include "rmad/constants.h"
#include "rmad/logs/logs_constants.h"
#include "rmad/proto_bindings/rmad.pb.h"
#include "rmad/utils/json_store.h"
#include "rmad/utils/type_conversions.h"
namespace rmad {
namespace {
const char* GetStateName(RmadState::StateCase state) {
auto it = kStateNames.find(state);
CHECK(it != kStateNames.end());
return it->second.data();
}
std::string JoinValueList(
const base::Value::List* list,
const std::function<std::string(const base::Value&)>& function,
const std::string& separator) {
auto begin = list->begin();
auto end = list->end();
std::ostringstream list_stream;
if (begin != end) {
list_stream << function(*begin);
while (++begin != end) {
list_stream << separator << function(*begin);
}
}
return list_stream.str();
}
bool AddEventToJson(scoped_refptr<JsonStore> json_store,
RmadState::StateCase state,
LogEventType event_type,
base::Value::Dict&& details) {
base::Value::Dict event;
event.Set(kTimestamp, base::Time::Now().InSecondsFSinceUnixEpoch());
event.Set(kStateId, static_cast<int>(state));
event.Set(kType, static_cast<int>(event_type));
event.Set(kDetails, std::move(details));
base::Value logs(base::Value::Type::DICT);
if (json_store->GetValue(kLogs, &logs)) {
CHECK(logs.is_dict());
}
// EnsureList() returns a pointer to the `events` JSON so no need to add it
// back to `logs`.
base::Value::List* events = logs.GetDict().EnsureList(kEvents);
events->Append(std::move(event));
return json_store->SetValue(kLogs, std::move(logs));
}
std::string GetCalibrationStatusString(const base::Value::Dict& component) {
const std::string component_name = *component.FindString(kLogComponent);
const LogCalibrationStatus status = static_cast<LogCalibrationStatus>(
component.FindInt(kLogCalibrationStatus).value());
auto it = kLogCalibrationStatusMap.find(status);
CHECK(it != kLogCalibrationStatusMap.end());
return base::StrCat({component_name, " - ", it->second});
}
std::string GenerateTextLogString(scoped_refptr<JsonStore> json_store) {
std::string generated_text_log;
base::Value logs(base::Value::Type::DICT);
json_store->GetValue(kLogs, &logs);
const base::Value::List* events = logs.GetDict().FindList(kEvents);
for (const base::Value& event : *events) {
const base::Value::Dict& event_dict = event.GetDict();
const int type = event_dict.FindInt(kType).value();
const int current_state_id = event_dict.FindInt(kStateId).value();
// Append the timestamp prefix.
base::Time::Exploded exploded;
base::Time::FromSecondsSinceUnixEpoch(
event_dict.FindDouble(kTimestamp).value())
.LocalExplode(&exploded);
generated_text_log.append(
base::StringPrintf(kLogTimestampFormat, exploded.year, exploded.month,
exploded.day_of_month, exploded.hour,
exploded.minute, exploded.second));
const base::Value::Dict* details = event_dict.FindDict(kDetails);
switch (static_cast<LogEventType>(type)) {
case LogEventType::kTransition: {
const RmadState::StateCase from_state =
static_cast<RmadState::StateCase>(
details->FindInt(kFromStateId).value());
const RmadState::StateCase to_state = static_cast<RmadState::StateCase>(
details->FindInt(kToStateId).value());
generated_text_log.append(base::StringPrintf(kLogTransitionFormat,
GetStateName(from_state),
GetStateName(to_state)));
break;
}
case LogEventType::kError: {
const RmadErrorCode error_code = static_cast<RmadErrorCode>(
details->FindInt(kOccurredError).value());
generated_text_log.append(base::StringPrintf(
kLogErrorFormat,
GetStateName(static_cast<RmadState::StateCase>(current_state_id)),
RmadErrorCode_Name(error_code).c_str()));
break;
}
case LogEventType::kData: {
const RmadState::StateCase current_state =
static_cast<RmadState::StateCase>(current_state_id);
generated_text_log.append(base::StringPrintf(
kLogDetailPrefixFormat, GetStateName(current_state)));
switch (current_state) {
case RmadState::kWelcome: {
const std::optional<bool> is_compliant =
details->FindBool(kLogIsCompliant);
// `is_compliant` only has a value from the hardware verifier
// event.
if (is_compliant.has_value()) {
if (is_compliant.value()) {
generated_text_log.append(kLogNoUnqualifiedComponentsString);
} else {
const std::string unqualified_components =
*details->FindString(kLogUnqualifiedComponents);
generated_text_log.append(
base::StringPrintf(kLogUnqualifiedComponentsDetectedFormat,
unqualified_components.c_str()));
}
} else {
generated_text_log.append(kLogRepairStartString);
}
break;
}
case RmadState::kComponentsRepair: {
const bool is_mlb_repair =
details->FindBool(kLogReworkSelected).value();
if (is_mlb_repair) {
generated_text_log.append(kLogSelectComponentsReworkString);
} else {
const base::Value::List* components =
details->FindList(kLogReplacedComponents);
const std::string component_list = JoinValueList(
components,
[](const base::Value& value) { return value.GetString(); },
", ");
generated_text_log.append(base::StringPrintf(
kLogSelectComponentsFormat, component_list.c_str()));
}
break;
}
case RmadState::kDeviceDestination: {
generated_text_log.append(base::StringPrintf(
kLogChooseDeviceDestinationFormat,
(*details->FindString(kLogDestination)).c_str()));
break;
}
case RmadState::kWipeSelection: {
const std::string wipe_device =
details->FindBool(kLogWipeDevice).value() ? "wipe" : "keep";
generated_text_log.append(base::StringPrintf(
kLogWipeSelectionFormat, wipe_device.c_str()));
break;
}
case RmadState::kWpDisableMethod: {
generated_text_log.append(base::StringPrintf(
kLogWpDisableFormat,
(*details->FindString(kLogWpDisableMethod)).c_str()));
break;
}
case RmadState::kWpDisableRsu: {
generated_text_log.append(base::StringPrintf(
kLogRsuChallengeFormat,
(*details->FindString(kLogRsuChallengeCode)).c_str()));
break;
}
case RmadState::kRestock: {
generated_text_log.append(
details->FindBool(kLogRestockOption).value()
? kLogRestockShutdownString
: kLogRestockContinueString);
break;
}
case RmadState::kSetupCalibration: {
std::string instruction_string;
const CalibrationSetupInstruction setup_instruction =
static_cast<CalibrationSetupInstruction>(
details->FindInt(kLogCalibrationSetupInstruction).value());
switch (setup_instruction) {
case RMAD_CALIBRATION_INSTRUCTION_PLACE_BASE_ON_FLAT_SURFACE: {
instruction_string = kLogCalibrationSetupBaseString;
break;
}
case RMAD_CALIBRATION_INSTRUCTION_PLACE_LID_ON_FLAT_SURFACE: {
instruction_string = kLogCalibrationSetupLidString;
break;
}
default: {
instruction_string = kLogCalibrationSetupUnknownString;
break;
}
}
generated_text_log.append(instruction_string);
break;
}
case RmadState::kCheckCalibration: {
const base::Value::List* components =
details->FindList(kLogCalibrationComponents);
const std::string component_list = JoinValueList(
components,
[](const base::Value& value) {
return GetCalibrationStatusString(value.GetDict());
},
", ");
generated_text_log.append(base::StringPrintf(
kLogCalibrationFormat, component_list.c_str()));
break;
}
case RmadState::kUpdateRoFirmware: {
const FirmwareUpdateStatus status =
static_cast<FirmwareUpdateStatus>(
details->FindInt(kFirmwareStatus).value());
auto it = kFirmwareUpdateStatusMap.find(status);
CHECK(it != kFirmwareUpdateStatusMap.end());
generated_text_log.append(it->second.data());
break;
}
default:
break;
}
}
}
generated_text_log.append("\n");
}
return generated_text_log;
}
} // namespace
std::string GenerateLogsText(scoped_refptr<JsonStore> json_store) {
return GenerateTextLogString(json_store);
}
std::string GenerateLogsJson(scoped_refptr<JsonStore> json_store) {
base::Value logs(base::Value::Type::DICT);
json_store->GetValue(kLogs, &logs);
if (!logs.is_dict()) {
return "";
}
std::string output;
JSONStringValueSerializer serializer(&output);
serializer.set_pretty_print(true);
serializer.Serialize(logs);
return output;
}
bool RecordStateTransitionToLogs(scoped_refptr<JsonStore> json_store,
RmadState::StateCase from_state,
RmadState::StateCase to_state) {
base::Value::Dict details;
details.Set(kFromStateId, static_cast<int>(from_state));
details.Set(kToStateId, static_cast<int>(to_state));
return AddEventToJson(json_store, from_state, LogEventType::kTransition,
std::move(details));
}
bool RecordOccurredErrorToLogs(scoped_refptr<JsonStore> json_store,
RmadState::StateCase current_state,
RmadErrorCode error) {
base::Value::Dict details;
details.Set(kOccurredError, static_cast<int>(error));
return AddEventToJson(json_store, current_state, LogEventType::kError,
std::move(details));
}
bool RecordRepairStartToLogs(scoped_refptr<JsonStore> json_store) {
// Check to make sure the repair start was not already recorded.
base::Value logs(base::Value::Type::DICT);
json_store->GetValue(kLogs, &logs);
const base::Value::List* events = logs.GetDict().FindList(kEvents);
if (events) {
for (const base::Value& value : *events) {
const base::Value::Dict& event = value.GetDict();
if (event.FindInt(kType) == static_cast<int>(LogEventType::kData) &&
event.FindInt(kStateId) == static_cast<int>(RmadState::kWelcome)) {
return false;
}
}
}
base::Value::Dict details;
return AddEventToJson(json_store, RmadState::kWelcome, LogEventType::kData,
std::move(details));
}
bool RecordUnqualifiedComponentsToLogs(
scoped_refptr<JsonStore> json_store,
bool is_compliant,
const std::string& unqualified_components) {
base::Value::Dict details;
details.Set(kLogIsCompliant, is_compliant);
details.Set(kLogUnqualifiedComponents, unqualified_components);
return AddEventToJson(json_store, RmadState::kWelcome, LogEventType::kData,
std::move(details));
}
bool RecordSelectedComponentsToLogs(
scoped_refptr<JsonStore> json_store,
const std::vector<std::string>& replaced_components,
bool is_mlb_repair) {
base::Value::Dict details;
details.Set(kLogReplacedComponents, ConvertToValue(replaced_components));
details.Set(kLogReworkSelected, is_mlb_repair);
return AddEventToJson(json_store, RmadState::kComponentsRepair,
LogEventType::kData, std::move(details));
}
bool RecordDeviceDestinationToLogs(scoped_refptr<JsonStore> json_store,
const std::string& device_destination) {
base::Value::Dict details;
details.Set(kLogDestination, device_destination);
return AddEventToJson(json_store, RmadState::kDeviceDestination,
LogEventType::kData, std::move(details));
}
bool RecordWipeDeviceToLogs(scoped_refptr<JsonStore> json_store,
bool wipe_device) {
base::Value::Dict details;
details.Set(kLogWipeDevice, wipe_device);
return AddEventToJson(json_store, RmadState::kWipeSelection,
LogEventType::kData, std::move(details));
}
bool RecordWpDisableMethodToLogs(scoped_refptr<JsonStore> json_store,
const std::string& wp_disable_method) {
base::Value::Dict details;
details.Set(kLogWpDisableMethod, wp_disable_method);
return AddEventToJson(json_store, RmadState::kWpDisableMethod,
LogEventType::kData, std::move(details));
}
bool RecordRsuChallengeCodeToLogs(scoped_refptr<JsonStore> json_store,
const std::string& challenge_code,
const std::string& hwid) {
base::Value::Dict details;
details.Set(kLogRsuChallengeCode, challenge_code);
details.Set(kLogRsuHwid, hwid);
return AddEventToJson(json_store, RmadState::kWpDisableRsu,
LogEventType::kData, std::move(details));
}
bool RecordRestockOptionToLogs(scoped_refptr<JsonStore> json_store,
bool restock) {
base::Value::Dict details;
details.Set(kLogRestockOption, restock);
return AddEventToJson(json_store, RmadState::kRestock, LogEventType::kData,
std::move(details));
}
bool RecordCalibrationSetupInstructionToLogs(
scoped_refptr<JsonStore> json_store,
CalibrationSetupInstruction instruction) {
base::Value::Dict details;
details.Set(kLogCalibrationSetupInstruction, static_cast<int>(instruction));
return AddEventToJson(json_store, RmadState::kSetupCalibration,
LogEventType::kData, std::move(details));
}
bool RecordComponentCalibrationStatusToLogs(
scoped_refptr<JsonStore> json_store,
const std::vector<std::pair<std::string, LogCalibrationStatus>>&
component_statuses) {
base::Value::List components;
for (auto& component_status : component_statuses) {
base::Value::Dict component;
component.Set(kLogComponent, component_status.first);
component.Set(kLogCalibrationStatus,
static_cast<int>(component_status.second));
components.Append(std::move(component));
}
base::Value::Dict details;
details.Set(kLogCalibrationComponents, std::move(components));
return AddEventToJson(json_store, RmadState::kCheckCalibration,
LogEventType::kData, std::move(details));
}
bool RecordFirmwareUpdateStatusToLogs(scoped_refptr<JsonStore> json_store,
FirmwareUpdateStatus status) {
// Check to make sure the firmware complete was not already recorded.
if (status == FirmwareUpdateStatus::kFirmwareComplete) {
base::Value logs(base::Value::Type::DICT);
json_store->GetValue(kLogs, &logs);
const base::Value::List* events = logs.GetDict().FindList(kEvents);
if (events) {
for (const base::Value& value : *events) {
const base::Value::Dict& event = value.GetDict();
if (event.FindInt(kType) == static_cast<int>(LogEventType::kData) &&
event.FindInt(kStateId) ==
static_cast<int>(RmadState::kUpdateRoFirmware) &&
event.FindDict(kDetails)->FindInt(kFirmwareStatus) ==
static_cast<int>(FirmwareUpdateStatus::kFirmwareComplete)) {
return false;
}
}
}
}
base::Value::Dict details;
details.Set(kFirmwareStatus, static_cast<int>(status));
return AddEventToJson(json_store, RmadState::kUpdateRoFirmware,
LogEventType::kData, std::move(details));
}
} // namespace rmad