summaryrefslogtreecommitdiffstats
path: root/extensions/openpower-pels/tools/peltool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/openpower-pels/tools/peltool.cpp')
-rw-r--r--extensions/openpower-pels/tools/peltool.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
new file mode 100644
index 0000000..ed6daa2
--- /dev/null
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -0,0 +1,431 @@
+/**
+ * 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 "config.h"
+
+#include "../bcd_time.hpp"
+#include "../paths.hpp"
+#include "../pel.hpp"
+#include "../pel_types.hpp"
+#include "../pel_values.hpp"
+
+#include <CLI/CLI.hpp>
+#include <bitset>
+#include <iostream>
+#include <phosphor-logging/log.hpp>
+#include <regex>
+#include <string>
+#include <xyz/openbmc_project/Common/File/error.hpp>
+
+namespace fs = std::filesystem;
+using namespace phosphor::logging;
+using namespace openpower::pels;
+namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
+namespace message = openpower::pels::message;
+namespace pv = openpower::pels::pel_values;
+
+/**
+ * @brief helper function to get PEL commit timestamp from file name
+ * @retrun BCDTime - PEL commit timestamp
+ * @param[in] std::string - file name
+ */
+BCDTime fileNameToTimestamp(const std::string& fileName)
+{
+ std::string token = fileName.substr(0, fileName.find("_"));
+ int i = 0;
+ BCDTime tmp;
+ if (token.length() >= 14)
+ {
+ try
+ {
+ tmp.yearMSB = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.yearLSB = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.month = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.day = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.hour = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.minutes = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.seconds = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ i += 2;
+ try
+ {
+ tmp.hundredths = std::stoi(token.substr(i, 2), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ }
+ return tmp;
+}
+
+/**
+ * @brief helper function to get PEL id from file name
+ * @retrun uint32_t - PEL id
+ * @param[in] std::string - file name
+ */
+uint32_t fileNameToPELId(const std::string& fileName)
+{
+ uint32_t num = 0;
+ try
+ {
+ num = std::stoi(fileName.substr(fileName.find("_") + 1), 0, 16);
+ }
+ catch (std::exception& err)
+ {
+ std::cout << "Conversion failure: " << err.what() << std::endl;
+ }
+ return num;
+}
+
+/**
+ * @brief helper function to check string suffix
+ * @retrun bool - true with suffix matches
+ * @param[in] std::string - string to check for suffix
+ * @param[in] std::string - suffix string
+ */
+bool ends_with(const std::string& str, const std::string& end)
+{
+ size_t slen = str.size(), elen = end.size();
+ if (slen < elen)
+ return false;
+ while (elen)
+ {
+ if (str[--slen] != end[--elen])
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief get data form raw PEL file.
+ * @param[in] std::string Name of file with raw PEL
+ * @return std::vector<uint8_t> char vector read from raw PEL file.
+ */
+std::vector<uint8_t> getFileData(const std::string& name)
+{
+ std::ifstream file(name, std::ifstream::in);
+ if (file.good())
+ {
+ std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
+ std::istreambuf_iterator<char>()};
+ return data;
+ }
+ else
+ {
+ printf("Can't open raw PEL file");
+ return {};
+ }
+}
+
+template <typename T>
+std::string genPELJSON(T itr, bool hidden, message::Registry& registry)
+{
+ std::size_t found;
+ std::string val;
+ char tmpValStr[50];
+ std::string listStr;
+ char name[50];
+ sprintf(name, "%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X_%.8X", itr.second.yearMSB,
+ itr.second.yearLSB, itr.second.month, itr.second.day,
+ itr.second.hour, itr.second.minutes, itr.second.seconds,
+ itr.second.hundredths, itr.first);
+ std::string fileName(name);
+ fileName = EXTENSION_PERSIST_DIR "/pels/logs/" + fileName;
+ try
+ {
+ std::vector<uint8_t> data = getFileData(fileName);
+ if (!data.empty())
+ {
+ PEL pel{data};
+ std::bitset<16> actionFlags{pel.userHeader().actionFlags()};
+ if (pel.valid() && (hidden || !actionFlags.test(hiddenFlagBit)))
+ {
+ // id
+ sprintf(tmpValStr, "0x%X", pel.privateHeader().id());
+ val = std::string(tmpValStr);
+ listStr += "\t\"" + val + "\": {\n";
+ // ASCII
+ if (pel.primarySRC())
+ {
+ val = pel.primarySRC().value()->asciiString();
+ listStr += "\t\t\"SRC\": \"" +
+ val.substr(0, val.find(0x20)) + "\",\n";
+ // Registry message
+ auto regVal = pel.primarySRC().value()->getErrorDetails(
+ registry, DetailLevel::message, true);
+ if (regVal)
+ {
+ val = regVal.value();
+ listStr += "\t\t\"Message\": \"" + val + "\",\n";
+ }
+ }
+ else
+ {
+ listStr += "\t\t\"SRC\": \"No SRC\",\n";
+ }
+ // platformid
+ sprintf(tmpValStr, "0x%X", pel.privateHeader().plid());
+ val = std::string(tmpValStr);
+ listStr += "\t\t\"PLID\": \"" + val + "\",\n";
+ // creatorid
+ sprintf(tmpValStr, "%c", pel.privateHeader().creatorID());
+ std::string creatorID(tmpValStr);
+ val = pv::creatorIDs.count(creatorID)
+ ? pv::creatorIDs.at(creatorID)
+ : "Unknown Creator ID";
+ listStr += "\t\t\"CreatorID\": \"" + val + "\",\n";
+ // subsytem
+ std::string subsystem = pv::getValue(
+ pel.userHeader().subsystem(), pel_values::subsystemValues);
+ listStr += "\t\t\"Subsystem\": \"" + subsystem + "\",\n";
+ // commit time
+ sprintf(tmpValStr, "%02X/%02X/%02X%02X %02X:%02X:%02X",
+ pel.privateHeader().commitTimestamp().month,
+ pel.privateHeader().commitTimestamp().day,
+ pel.privateHeader().commitTimestamp().yearMSB,
+ pel.privateHeader().commitTimestamp().yearLSB,
+ pel.privateHeader().commitTimestamp().hour,
+ pel.privateHeader().commitTimestamp().minutes,
+ pel.privateHeader().commitTimestamp().seconds);
+ val = std::string(tmpValStr);
+ listStr += "\t\t\"Commit Time\": \"" + val + "\",\n";
+ // severity
+ std::string severity = pv::getValue(pel.userHeader().severity(),
+ pel_values::severityValues);
+ listStr += "\t\t\"Sev\": \"" + severity + "\",\n ";
+ // compID
+ sprintf(tmpValStr, "0x%X",
+ pel.privateHeader().header().componentID);
+ val = std::string(tmpValStr);
+ listStr += "\t\t\"CompID\": \"" + val + "\",\n ";
+
+ found = listStr.rfind(",");
+ if (found != std::string::npos)
+ {
+ listStr.replace(found, 1, "");
+ listStr += "\t}, \n";
+ }
+ }
+ }
+ else
+ {
+ log<level::ERR>("Empty PEL file",
+ entry("FILENAME=%s", fileName.c_str()),
+ entry("ERROR=%s", "Empty PEL file"));
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Hit exception while reading PEL File",
+ entry("FILENAME=%s", fileName.c_str()),
+ entry("ERROR=%s", e.what()));
+ }
+ return listStr;
+}
+/**
+ * @brief Print a list of PELs
+ */
+void printList(bool order, bool hidden)
+{
+ std::string listStr;
+ std::map<uint32_t, BCDTime> PELs;
+ std::size_t found;
+ listStr = "{\n";
+ for (auto it = fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
+ it != fs::directory_iterator(); ++it)
+ {
+ if (!fs::is_regular_file((*it).path()))
+ {
+ continue;
+ }
+ else
+ {
+ PELs.emplace(fileNameToPELId((*it).path().filename()),
+ fileNameToTimestamp((*it).path().filename()));
+ }
+ }
+ message::Registry registry(getMessageRegistryPath() /
+ message::registryFileName);
+ auto buildJSON = [&listStr, &hidden, &registry](const auto& i) {
+ listStr += genPELJSON(i, hidden, registry);
+ };
+ if (order)
+ {
+ std::for_each(PELs.rbegin(), PELs.rend(), buildJSON);
+ }
+ else
+ {
+ std::for_each(PELs.begin(), PELs.end(), buildJSON);
+ }
+
+ found = listStr.rfind(",");
+ if (found != std::string::npos)
+ {
+ listStr.replace(found, 1, "");
+ listStr += "\n}\n";
+ printf("%s", listStr.c_str());
+ }
+}
+
+static void exitWithError(const std::string& help, const char* err)
+{
+ std::cerr << "ERROR: " << err << std::endl << help << std::endl;
+ exit(-1);
+}
+
+int main(int argc, char** argv)
+{
+ CLI::App app{"OpenBMC PEL Tool"};
+ std::string fileName;
+ std::string idPEL;
+ bool listPEL = false;
+ bool listPELDescOrd = false;
+ bool listPELShowHidden = false;
+ app.add_option("-f,--file", fileName,
+ "Display a PEL using its Raw PEL file");
+ app.add_option("-i, --id", idPEL, "Display a PEL based on its ID");
+ app.add_flag("-l", listPEL, "List PELs");
+ app.add_flag("-r", listPELDescOrd, "Reverse order of output");
+ app.add_flag("-s", listPELShowHidden, "Show hidden PELs");
+ CLI11_PARSE(app, argc, argv);
+
+ if (!fileName.empty())
+ {
+ std::vector<uint8_t> data = getFileData(fileName);
+ if (!data.empty())
+ {
+ PEL pel{data};
+ pel.toJSON();
+ }
+ else
+ {
+ exitWithError(app.help("", CLI::AppFormatMode::All),
+ "Raw PEL file can't be read.");
+ }
+ }
+
+ else if (!idPEL.empty())
+ {
+ for (auto it =
+ fs::directory_iterator(EXTENSION_PERSIST_DIR "/pels/logs");
+ it != fs::directory_iterator(); ++it)
+ {
+ if (!fs::is_regular_file((*it).path()))
+ {
+ continue;
+ }
+ try
+ {
+ for (auto& c : idPEL)
+ c = toupper(c);
+ size_t found = idPEL.find("0X");
+ if (found == 0)
+ {
+ idPEL.erase(0, 2);
+ }
+ if (ends_with((*it).path(), idPEL))
+ {
+ std::vector<uint8_t> data = getFileData((*it).path());
+ if (!data.empty())
+ {
+ PEL pel{data};
+ if (pel.valid())
+ {
+ pel.toJSON();
+ }
+ else
+ {
+ log<level::ERR>(
+ "PEL File contains invalid PEL",
+ entry("FILENAME=%s", (*it).path().c_str()),
+ entry("ERROR=%s", "file contains invalid PEL"));
+ }
+ }
+ break;
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Hit exception while reading PEL File",
+ entry("FILENAME=%s", (*it).path().c_str()),
+ entry("ERROR=%s", e.what()));
+ }
+ }
+ }
+ else if (listPEL)
+ {
+
+ printList(listPELDescOrd, listPELShowHidden);
+ }
+ else
+ {
+ exitWithError(app.help("", CLI::AppFormatMode::All),
+ "Raw PEL file path not specified.");
+ }
+ return 0;
+}
OpenPOWER on IntegriCloud