#vbmc - Parse memory errors from CPER
PiperOrigin-RevId: 736238411
Change-Id: Id4bd40b41dce9132e50e8bbc0a076885eef305ca
diff --git a/cper/cper_event.cc b/cper/cper_event.cc
index 9d877a9..88e71df 100644
--- a/cper/cper_event.cc
+++ b/cper/cper_event.cc
@@ -1,13 +1,16 @@
#include "cper/cper_event.h"
#include <cstdint>
+#include <optional>
#include <string>
+#include <utility>
#include <vector>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/numbers.h"
+#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "nlohmann/json.hpp"
@@ -21,6 +24,114 @@
constexpr int kVlogVerbosity = 1;
} // namespace
+static absl::StatusOr<std::string> GetOrigin(
+ const nlohmann::json &parsed_event) {
+ auto links_it = parsed_event.find("Links");
+ if (links_it == parsed_event.end()) {
+ return absl::UnavailableError("Links not present");
+ }
+ auto origin_it = links_it->find("OriginOfCondition");
+ if (origin_it == links_it->end()) {
+ return absl::UnavailableError("OriginOfCondition link is not present");
+ }
+ auto odata_id_it = origin_it->find("@odata.id");
+ if (odata_id_it == origin_it->end()) {
+ return absl::InvalidArgumentError(
+ "Invalid event json, OriginOfCondition does not contain @odata.id");
+ }
+ auto odata_id_ptr = odata_id_it->get_ptr<const std::string *>();
+ if (odata_id_ptr == nullptr) {
+ return absl::InvalidArgumentError(
+ "Invalid event json, @odata.id is not a string");
+ }
+ return *odata_id_ptr;
+}
+
+static absl::StatusOr<const nlohmann::json *> GetCperOem(
+ const nlohmann::json &parsed_event) {
+ auto cper_it = parsed_event.find("CPER");
+ if (cper_it == parsed_event.end()) {
+ return absl::UnavailableError("No CPER found");
+ }
+ auto cper_data_it = cper_it->find("Oem");
+ if (cper_data_it == cper_it->end()) {
+ return absl::InvalidArgumentError("CPER does not contain OEM");
+ }
+
+ if (!cper_data_it->is_object()) {
+ return absl::InvalidArgumentError("CPER OEM is not an object");
+ }
+
+ auto nvidia_it = cper_data_it->find("Nvidia");
+ if (nvidia_it == cper_data_it->end()) {
+ return absl::InvalidArgumentError("CPER OEM does not contain Nvidia");
+ }
+
+ auto odata_type_it = nvidia_it->find("@odata.type");
+ if (odata_type_it == nvidia_it->end()) {
+ LOG(WARNING) << "CPER does not contain @odata.type";
+ return &*nvidia_it;
+ }
+
+ auto odata_type_ptr = odata_type_it->get_ptr<const std::string *>();
+ if (odata_type_ptr == nullptr) {
+ return absl::InvalidArgumentError(
+ "Invalid event json, @odata.type is not a string");
+ }
+ if (!odata_type_ptr->starts_with("#NvidiaCPER.") ||
+ !odata_type_ptr->ends_with(".NvidiaCPER")) {
+ return absl::InvalidArgumentError(
+ absl::StrCat("Unexpected @odata.type ", *odata_type_ptr));
+ }
+ return &*nvidia_it;
+}
+
+static absl::StatusOr<int> GetMemoryErrors(const nlohmann::json &cper_data) {
+ auto memory_errors_it = cper_data.find("Memory");
+ if (memory_errors_it == cper_data.end()) {
+ return absl::UnavailableError("MemoryErrors is not present");
+ }
+ if (!memory_errors_it->is_object()) {
+ return absl::InvalidArgumentError("CPER/Oem/Memory is not an object");
+ }
+ return 1;
+}
+
+static absl::StatusOr<int> GetCpuErrors(const nlohmann::json &cper_data) {
+ return absl::UnavailableError(
+ "TODO:b/402483544 - CpuErrors is not yet implemented");
+}
+
+static absl::StatusOr<Event::CperEventData> ParseCperEventData(
+ const nlohmann::json &parsed_event) {
+ Event::CperEventData cper_event_data;
+
+ ASSIGN_OR_RETURN(const nlohmann::json *cper_data, GetCperOem(parsed_event));
+ absl::StatusOr<std::string> origin = GetOrigin(parsed_event);
+ if (origin.ok()) {
+ cper_event_data.origin = *std::move(origin);
+ } else {
+ LOG(WARNING) << "OriginOfCondition not found: " << origin.status();
+ if (!absl::IsUnavailable(origin.status())) {
+ return origin.status();
+ }
+ }
+
+ if (absl::StatusOr<int> memory_errors = GetMemoryErrors(*cper_data);
+ memory_errors.ok()) {
+ cper_event_data.memory_errors = *memory_errors;
+ } else if (!absl::IsUnavailable(memory_errors.status())) {
+ return memory_errors.status();
+ }
+ if (absl::StatusOr<int> cpu_errors = GetCpuErrors(*cper_data);
+ cpu_errors.ok()) {
+ cper_event_data.cpu_errors = *cpu_errors;
+ } else if (!absl::IsUnavailable(cpu_errors.status())) {
+ return cpu_errors.status();
+ }
+
+ return cper_event_data;
+}
absl::StatusOr<std::vector<Event>> CperEvent::ParseRedfishEvent(
absl::string_view sse_json) {
nlohmann::json parsed = nlohmann::json::parse(sse_json, /*cb=*/nullptr,
@@ -94,7 +205,13 @@
VLOG(kVlogVerbosity) << "EventId: " << event_id;
// Check if the event contains CPER.
- bool is_cper_event = events_it->at(i).contains("CPER");
+ absl::StatusOr<Event::CperEventData> cper_event_data =
+ ParseCperEventData(events_it->at(i));
+ if (!cper_event_data.ok() &&
+ !absl::IsUnavailable(cper_event_data.status())) {
+ return cper_event_data.status();
+ }
+
auto message_severity_ptr =
events_it->at(i)["MessageSeverity"].get_ptr<std::string *>();
if (message_severity_ptr == nullptr) {
@@ -106,8 +223,12 @@
ASSIGN_OR_RETURN(EventSeverity severity,
StringToEventSeverity(*message_severity_ptr));
+ std::optional<Event::CperEventData> cper_event_data_opt = std::nullopt;
+ if (cper_event_data.ok()) {
+ cper_event_data_opt = *std::move(cper_event_data);
+ }
events.push_back(Event(events_it->at(i).dump(), event_id, severity,
- /*reason_id=*/-1, is_cper_event));
+ /*reason_id=*/-1, std::move(cper_event_data_opt)));
}
// Validate `Id` is same as last `EventId`.
diff --git a/sse_plugin/event.h b/sse_plugin/event.h
index 923f69b..9ebe8a7 100644
--- a/sse_plugin/event.h
+++ b/sse_plugin/event.h
@@ -2,7 +2,9 @@
#define PLATFORMS_VBMC_PLATFORM_EIGER_EVENT_H_
#include <cstdint>
+#include <optional>
#include <string>
+#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
@@ -49,14 +51,21 @@
// Serving state
class Event {
public:
+ struct CperEventData {
+ std::string origin;
+ int memory_errors = 0;
+ int cpu_errors = 0;
+ };
+
Event() = default;
Event(absl::string_view event_json, int64_t event_id, EventSeverity severity,
- int reason_id = kInvalidReasonId, bool is_cper_event = false)
+ int reason_id = kInvalidReasonId,
+ std::optional<CperEventData> cper_event_data = std::nullopt)
: event_id_(event_id),
reason_id_(reason_id),
severity_(severity),
event_json_(std::string(event_json)),
- is_cper_event_(is_cper_event) {}
+ cper_event_data_(std::move(cper_event_data)) {}
// Movable
Event(Event&& other) = default;
@@ -70,7 +79,10 @@
EventSeverity severity() const { return severity_; }
std::string event_json() const { return event_json_; }
int reason_id() const { return reason_id_; }
- bool is_cper_event() const { return is_cper_event_; }
+ bool is_cper_event() const { return cper_event_data_.has_value(); }
+ const std::optional<CperEventData>& cper_event_data() const {
+ return cper_event_data_;
+ }
bool IsEmpty() const { return event_id_ == 0; }
@@ -85,7 +97,7 @@
int reason_id_;
EventSeverity severity_;
std::string event_json_;
- bool is_cper_event_ = false;
+ std::optional<CperEventData> cper_event_data_;
};
} // namespace milotic