Add support for collecting data for BMC dumps
When occ-control receives a USR1 signal, it will trigger the code to
collect the following data:
- number of OCC objects created
- for each active OCC: state, role, hwmon path, poll response, and WOF
data
The data will be written in JSON format to a file which can be collected
and added to a dump. (/tmp/occ_control_dump.json)
To force the data collection:
killall -s SIGUSR1 openpower-occ-control
Change-Id: I7a304f7ce0eb1c9109f630f187adf9c95722652e
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/app.cpp b/app.cpp
index f0929d7..24f24fb 100644
--- a/app.cpp
+++ b/app.cpp
@@ -7,6 +7,8 @@
#include <org/open_power/OCC/Device/error.hpp>
#include <phosphor-logging/lg2.hpp>
+#include <sdeventplus/source/signal.hpp>
+#include <stdplus/signal.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
using namespace sdbusplus::org::open_power::OCC::Device::Error;
@@ -44,6 +46,22 @@
open_power::occ::Manager mgr(eventP);
mgr.createPldmHandle();
+ try
+ {
+ // Enable SIGUSR1 handling to collect data on dump request
+ stdplus::signal::block(SIGUSR1);
+ sdeventplus::source::Signal sigUsr1(
+ eventP.get(), SIGUSR1,
+ std::bind(&open_power::occ::Manager::collectDumpData, &mgr,
+ std::placeholders::_1, std::placeholders::_2));
+ sigUsr1.set_floating(true);
+ lg2::info("USR1 signal handler enabled");
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error("Failed to enable SIGUSR1 handler: {ERR}", "ERR", e.what());
+ }
+
// Claim the bus since all the house keeping is done now
bus.request_name(OCC_CONTROL_BUSNAME);
diff --git a/occ_manager.cpp b/occ_manager.cpp
index 302a733..6b735f7 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -6,6 +6,7 @@
#include "occ_errors.hpp"
#include "utils.hpp"
+#include <nlohmann/json.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/lg2.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
@@ -28,9 +29,11 @@
constexpr auto maxSuffix = "max";
const auto HOST_ON_FILE = "/run/openbmc/host@0-on";
+const std::string Manager::dumpFile = "/tmp/occ_control_dump.json";
using namespace phosphor::logging;
using namespace std::literals::chrono_literals;
+using json = nlohmann::json;
template <typename T>
T readFile(const std::string& path)
@@ -1722,5 +1725,91 @@
resetInstance = 255;
}
+void Manager::collectDumpData(sdeventplus::source::Signal&,
+ const struct signalfd_siginfo*)
+{
+ json data;
+ lg2::info("collectDumpData()");
+ data["objectCount"] = std::to_string(statusObjects.size()) + " OCC objects";
+ if (statusObjects.size() > 0)
+ {
+ try
+ {
+ for (auto& occ : statusObjects)
+ {
+ json occData;
+ auto instance = occ->getOccInstanceID();
+ std::string occName = "occ" + std::to_string(instance);
+
+ if (occ->occActive())
+ {
+ // OCC General Info
+ occData["occState"] = "ACTIVE";
+ occData["occRole"] =
+ occ->isMasterOcc() ? "MASTER" : "SECONDARY";
+ occData["occHwmonPath"] =
+ occ->getHwmonPath().generic_string();
+
+ // OCC Poll Response
+ std::vector<std::uint8_t> cmd = {0x00, 0x00, 0x01, 0x20};
+ std::vector<std::uint8_t> rsp;
+ std::vector<std::string> rspHex;
+ rsp = passThroughObjects[instance]->send(cmd);
+ if (rsp.size() > 5)
+ {
+ rsp.erase(rsp.begin(),
+ rsp.begin() + 5); // Strip rsp header
+ rspHex = utils::hex_dump(rsp);
+ occData["pollResponse"] = rspHex;
+ }
+
+ // Debug Data: WOF Dynamic Data
+ cmd = {0x40, 0x00, 0x01, 0x01};
+ rsp = passThroughObjects[instance]->send(cmd);
+ if (rsp.size() > 5)
+ {
+ rsp.erase(rsp.begin(),
+ rsp.begin() + 5); // Strip rsp header
+ rspHex = utils::hex_dump(rsp);
+ occData["wofDataDynamic"] = rspHex;
+ }
+
+ // Debug Data: WOF Dynamic Data
+ cmd = {0x40, 0x00, 0x01, 0x0A};
+ rsp = passThroughObjects[instance]->send(cmd);
+ if (rsp.size() > 5)
+ {
+ rsp.erase(rsp.begin(),
+ rsp.begin() + 5); // Strip rsp header
+ rspHex = utils::hex_dump(rsp);
+ occData["wofDataStatic"] = rspHex;
+ }
+ }
+ else
+ {
+ occData["occState"] = "NOT ACTIVE";
+ }
+
+ data[occName] = occData;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ lg2::error("Failed to collect OCC dump data: {ERR}", "ERR",
+ e.what());
+ }
+ }
+
+ std::ofstream file{Manager::dumpFile};
+ if (!file)
+ {
+ lg2::error("Failed to open {FILE} for occ-control data", "FILE",
+ Manager::dumpFile);
+ return;
+ }
+
+ file << std::setw(4) << data;
+}
+
} // namespace occ
} // namespace open_power
diff --git a/occ_manager.hpp b/occ_manager.hpp
index a2f1b8a..3758952 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -13,7 +13,9 @@
#include <sdbusplus/bus.hpp>
#include <sdeventplus/event.hpp>
+#include <sdeventplus/source/signal.hpp>
#include <sdeventplus/utility/timer.hpp>
+#include <stdplus/signal.hpp>
#include <cstring>
#include <functional>
@@ -144,6 +146,15 @@
* is off */
void hostPoweredOff();
+ /** @brief Collect data to include in BMC dumps
+ * This will get called when app receives a SIGUSR1 signal
+ */
+ void collectDumpData(sdeventplus::source::Signal&,
+ const struct signalfd_siginfo*);
+
+ /** @brief Name of file to put the occ-control dump data */
+ static const std::string dumpFile;
+
private:
/** @brief Creates the OCC D-Bus objects.
*/
diff --git a/utils.cpp b/utils.cpp
index efb7abd..6498dbd 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -247,6 +247,39 @@
return false;
}
+// Convert vector to hex dump string
+std::vector<std::string> hex_dump(const std::vector<std::uint8_t>& data,
+ const unsigned int data_len)
+{
+ unsigned int dump_length = data.size();
+ if ((data_len > 0) && (data_len < dump_length))
+ {
+ dump_length = data_len;
+ }
+ std::vector<std::string> dumpString;
+ std::string s;
+ for (uint32_t i = 0; i < dump_length; i++)
+ {
+ if (i % 16 == 0)
+ {
+ s += std::format("{:04X}: ", i);
+ }
+ else if (i % 4 == 0)
+ {
+ s += " ";
+ }
+
+ s += std::format("{:02X}", data.at(i));
+
+ if ((i % 16 == 15) || (i == (dump_length - 1)))
+ {
+ dumpString.push_back(s);
+ s.clear();
+ }
+ }
+ return dumpString;
+}
+
} // namespace utils
} // namespace occ
} // namespace open_power
diff --git a/utils.hpp b/utils.hpp
index 2085bf4..0fec395 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -96,6 +96,17 @@
*/
bool isHostRunning();
+/**
+ * @brief Convert vector to hex dump string
+ *
+ * @param[in] data - vector of uint8_ data
+ * @param[in] path - length of data to use (0 = all data)
+ *
+ * @return vector of strings
+ */
+std::vector<std::string> hex_dump(const std::vector<std::uint8_t>& data,
+ const unsigned int data_len = 0);
+
} // namespace utils
} // namespace occ
} // namespace open_power