/** * Copyright © 2019 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pel.hpp" #include "bcd_time.hpp" #include "extended_user_header.hpp" #include "failing_mtms.hpp" #include "json_utils.hpp" #include "log_id.hpp" #include "pel_rules.hpp" #include "pel_values.hpp" #include "section_factory.hpp" #include "src.hpp" #include "stream.hpp" #include "user_data_formats.hpp" #include #include namespace openpower { namespace pels { namespace message = openpower::pels::message; namespace pv = openpower::pels::pel_values; constexpr auto unknownValue = "Unknown"; PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp, phosphor::logging::Entry::Level severity, const AdditionalData& additionalData, const DataInterfaceBase& dataIface) { _ph = std::make_unique(entry.componentID, obmcLogID, timestamp); _uh = std::make_unique(entry, severity); auto src = std::make_unique(entry, additionalData); auto euh = std::make_unique(dataIface, entry, *src); _optionalSections.push_back(std::move(src)); _optionalSections.push_back(std::move(euh)); auto mtms = std::make_unique(dataIface); _optionalSections.push_back(std::move(mtms)); auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); _optionalSections.push_back(std::move(ud)); if (!additionalData.empty()) { ud = util::makeADUserDataSection(additionalData); // To be safe, check there isn't too much data if (size() + ud->header().size <= _maxPELSize) { _optionalSections.push_back(std::move(ud)); } } _ph->setSectionCount(2 + _optionalSections.size()); checkRulesAndFix(); } PEL::PEL(std::vector& data) : PEL(data, 0) { } PEL::PEL(std::vector& data, uint32_t obmcLogID) { populateFromRawData(data, obmcLogID); } void PEL::populateFromRawData(std::vector& data, uint32_t obmcLogID) { Stream pelData{data}; _ph = std::make_unique(pelData); if (obmcLogID != 0) { _ph->setOBMCLogID(obmcLogID); } _uh = std::make_unique(pelData); // Use the section factory to create the rest of the objects for (size_t i = 2; i < _ph->sectionCount(); i++) { auto section = section_factory::create(pelData); _optionalSections.push_back(std::move(section)); } } bool PEL::valid() const { bool valid = _ph->valid(); if (valid) { valid = _uh->valid(); } if (valid) { if (!std::all_of(_optionalSections.begin(), _optionalSections.end(), [](const auto& section) { return section->valid(); })) { valid = false; } } return valid; } void PEL::setCommitTime() { auto now = std::chrono::system_clock::now(); _ph->setCommitTimestamp(getBCDTime(now)); } void PEL::assignID() { _ph->setID(generatePELID()); } void PEL::flatten(std::vector& pelBuffer) const { Stream pelData{pelBuffer}; if (!valid()) { using namespace phosphor::logging; log("Unflattening an invalid PEL"); } _ph->flatten(pelData); _uh->flatten(pelData); for (auto& section : _optionalSections) { section->flatten(pelData); } } std::vector PEL::data() const { std::vector pelData; flatten(pelData); return pelData; } size_t PEL::size() const { size_t size = 0; if (_ph) { size += _ph->header().size; } if (_uh) { size += _uh->header().size; } for (const auto& section : _optionalSections) { size += section->header().size; } return size; } std::optional PEL::primarySRC() const { auto src = std::find_if( _optionalSections.begin(), _optionalSections.end(), [](auto& section) { return section->header().id == static_cast(SectionID::primarySRC); }); if (src != _optionalSections.end()) { return static_cast(src->get()); } return std::nullopt; } void PEL::checkRulesAndFix() { auto [actionFlags, eventType] = pel_rules::check(_uh->actionFlags(), _uh->eventType(), _uh->severity()); _uh->setActionFlags(actionFlags); _uh->setEventType(eventType); } void PEL::printSectionInJSON(const Section& section, std::string& buf, std::map& pluralSections) const { char tmpB[5]; uint8_t id[] = {static_cast(section.header().id >> 8), static_cast(section.header().id)}; sprintf(tmpB, "%c%c", id[0], id[1]); std::string sectionID(tmpB); std::string sectionName = pv::sectionTitles.count(sectionID) ? pv::sectionTitles.at(sectionID) : "Unknown Section"; // Add a count if there are multiple of this type of section auto count = pluralSections.find(section.header().id); if (count != pluralSections.end()) { sectionName += " " + std::to_string(count->second); count->second++; } if (section.valid()) { auto json = section.getJSON(); if (json) { buf += "\n\"" + sectionName + "\": {\n"; buf += *json + "\n},\n"; } else { buf += "\n\"" + sectionName + "\": [\n"; std::vector data; Stream s{data}; section.flatten(s); std::string dstr = dumpHex(std::data(data), data.size()); buf += dstr + "],\n"; } } else { buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n"; } } std::map PEL::getPluralSections() const { std::map sectionCounts; for (const auto& section : optionalSections()) { if (sectionCounts.find(section->header().id) == sectionCounts.end()) { sectionCounts[section->header().id] = 1; } else { sectionCounts[section->header().id]++; } } std::map sections; for (const auto& [id, count] : sectionCounts) { if (count > 1) { // Start with 0 here and printSectionInJSON() // will increment it as it goes. sections.emplace(id, 0); } } return sections; } void PEL::toJSON() const { auto sections = getPluralSections(); std::string buf = "{"; printSectionInJSON(*(_ph.get()), buf, sections); printSectionInJSON(*(_uh.get()), buf, sections); for (auto& section : this->optionalSections()) { printSectionInJSON(*(section.get()), buf, sections); } buf += "}"; std::size_t found = buf.rfind(","); if (found != std::string::npos) buf.replace(found, 1, ""); std::cout << buf << std::endl; } namespace util { std::unique_ptr makeJSONUserDataSection(const nlohmann::json& json) { auto jsonString = json.dump(); std::vector jsonData(jsonString.begin(), jsonString.end()); // Pad to a 4 byte boundary while ((jsonData.size() % 4) != 0) { jsonData.push_back(0); } return std::make_unique( static_cast(ComponentID::phosphorLogging), static_cast(UserDataFormat::json), static_cast(UserDataFormatVersion::json), jsonData); } std::unique_ptr makeADUserDataSection(const AdditionalData& ad) { assert(!ad.empty()); nlohmann::json json; // Remove the 'ESEL' entry, as it contains a full PEL in the value. if (ad.getValue("ESEL")) { auto newAD = ad; newAD.remove("ESEL"); json = newAD.toJSON(); } else { json = ad.toJSON(); } return makeJSONUserDataSection(json); } void addProcessNameToJSON(nlohmann::json& json, const std::optional& pid, const DataInterfaceBase& dataIface) { std::string name{unknownValue}; try { if (pid) { auto n = dataIface.getProcessName(*pid); if (n) { name = *n; } } } catch (std::exception& e) { } json["Process Name"] = std::move(name); } void addBMCFWVersionIDToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) { auto id = dataIface.getBMCFWVersionID(); if (id.empty()) { id = unknownValue; } json["BMC Version ID"] = std::move(id); } std::unique_ptr makeSysInfoUserDataSection(const AdditionalData& ad, const DataInterfaceBase& dataIface) { nlohmann::json json; addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); addBMCFWVersionIDToJSON(json, dataIface); return makeJSONUserDataSection(json); } } // namespace util } // namespace pels } // namespace openpower