Add Dump Monitoring Support
This commit introduces the DumpMonitor class to monitor DBus signals
for dump creation and manage the collection process. It aims to
abstract the complexities of dump collection from the
phosphor-dump-collector and consolidate system-specific handling
within the openpower-dump-collector.
Rationale:
- By moving dump collection responsibilities to the
openpower-dump-collector, we ensure that the
phosphor-dump-collector` remains focused solely on hosting dump
entries and interacting with external interfaces.
- The openpower-dump-collector now handles all system-specific
dump collection, reducing potential impacts on the
phosphor-dump-collector.
- Simplified Management: This change simplifies the overall management
of dumps by centralizing dump collection logic in one place.
Key Changes:
1. A new DumpMonitor class monitors DBus signals for dump creation.
2. The executeCollectionScript function handles the actual dump
collection and copies the dump to a specified location for the
phosphor-dump-manager to pick up.
3. Once the dump is copied to the specific folder, the phosphor dump
manager will pick up the file and update its status.
3. In the case of collection failure, the monitor updates the progress
status to indicate that the dump collection failed.
Change-Id: Id5bac5f3fbc8ef7a4bd9e93ac91796206b3cfda9
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/dump/dump_monitor.cpp b/dump/dump_monitor.cpp
new file mode 100644
index 0000000..aba424a
--- /dev/null
+++ b/dump/dump_monitor.cpp
@@ -0,0 +1,150 @@
+#include "dump_monitor.hpp"
+
+#include "dump_utils.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <regex>
+
+namespace openpower::dump
+{
+
+constexpr auto dumpOutPath = "/var/lib/phosphor-debug-collector/opdump";
+constexpr auto dumpStatusFailed =
+ "xyz.openbmc_project.Common.Progress.OperationStatus.Failed";
+
+void DumpMonitor::executeCollectionScript(
+ const sdbusplus::message::object_path& path, const PropertyMap& properties)
+{
+ std::vector<std::string> args = {"opdreport"};
+ std::regex idFormat("^[a-fA-F0-9]{8}$");
+ auto dumpIdStr = path.filename();
+ if (!std::regex_match(dumpIdStr, idFormat))
+ {
+ lg2::error("Failed to extract dump id from path {PATH}", "PATH", path);
+ updateProgressStatus(path, dumpStatusFailed);
+ return;
+ }
+
+ uint32_t dumpId = std::strtoul(dumpIdStr.c_str(), nullptr, 16);
+ int dumpType = getDumpTypeFromId(dumpId);
+ std::filesystem::path dumpPath = std::filesystem::path(dumpOutPath) /
+ dumpIdStr;
+
+ // Add type, ID, and dump path to args
+ args.push_back("-t");
+ args.push_back(std::to_string(dumpType));
+ args.push_back("-i");
+ args.push_back(dumpIdStr);
+ args.push_back("-d");
+ args.push_back(dumpPath.string());
+
+ // Optionally add ErrorLogId and FailingUnitId
+ auto errorLogIdIt = properties.find("ErrorLogId");
+ if (errorLogIdIt != properties.end())
+ {
+ uint32_t errorLogId = std::get<uint32_t>(errorLogIdIt->second);
+ args.push_back("-e");
+ args.push_back(std::to_string(errorLogId));
+ }
+
+ auto failingUnitIdIt = properties.find("FailingUnitId");
+ if (failingUnitIdIt != properties.end())
+ {
+ uint32_t failingUnitId = std::get<uint32_t>(failingUnitIdIt->second);
+ args.push_back("-f");
+ args.push_back(std::to_string(failingUnitId));
+ }
+
+ std::vector<char*> argv;
+ for (auto& arg : args)
+ {
+ argv.push_back(arg.data());
+ }
+
+ argv.push_back(nullptr);
+ pid_t pid = fork();
+ if (pid == -1)
+ {
+ lg2::error("Failed to fork, cannot collect dump, {ERRRNO}", "ERRNO",
+ errno);
+ updateProgressStatus(path, dumpStatusFailed);
+ exit(EXIT_FAILURE); // Exit explicitly with failure status
+ }
+ else if (pid == 0)
+ {
+ // Child process
+ execvp("opdreport", argv.data());
+ perror("execvp"); // execvp only returns on error
+ updateProgressStatus(path, dumpStatusFailed);
+ exit(EXIT_FAILURE); // Exit explicitly with failure status
+ }
+ else
+ {
+ // Parent process: wait for the child to terminate
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status))
+ {
+ int exit_status = WEXITSTATUS(status);
+
+ if (exit_status != 0)
+ {
+ // Handle failure
+ lg2::error("Dump failed updating status {PATH}", "PATH", path);
+ updateProgressStatus(path, dumpStatusFailed);
+ }
+ }
+ }
+}
+
+void DumpMonitor::handleDBusSignal(sdbusplus::message::message& msg)
+{
+ sdbusplus::message::object_path objectPath;
+ InterfaceMap interfaces;
+
+ msg.read(objectPath, interfaces);
+
+ lg2::info("Signal received at {PATH}: ", "PATH", objectPath);
+
+ // There can be new a entry created after the completion of the collection
+ // with completed status, so pick only entries with InProgress status.
+ if (isInProgress(interfaces))
+ {
+ for (const auto& interfaceName : monitoredInterfaces)
+ {
+ auto it = interfaces.find(interfaceName);
+ if (it != interfaces.end())
+ {
+ lg2::info("An entry created, collecting a new dump {DUMP}",
+ "DUMP", interfaceName);
+ executeCollectionScript(objectPath, it->second);
+ }
+ }
+ }
+}
+
+void DumpMonitor::updateProgressStatus(const std::string& path,
+ const std::string& status)
+{
+ auto bus = sdbusplus::bus::new_default();
+ const std::string interfaceName = "xyz.openbmc_project.Common.Progress";
+ const std::string propertyName = "Status";
+ const std::string serviceName = "xyz.openbmc_project.Dump.Manager";
+
+ try
+ {
+ auto statusVariant = std::variant<std::string>(status);
+ util::setProperty<std::variant<std::string>>(
+ interfaceName, propertyName, path, bus, statusVariant);
+ lg2::info("Status updated successfully to {STATUS} {PATH}", "STATUS",
+ status, "PATH", path);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error("Failed to update status {STATUS} {PATH} {ERROR}", "STATUS",
+ status, "PATH", path, "ERROR", e);
+ }
+}
+
+} // namespace openpower::dump
diff --git a/dump/dump_monitor.hpp b/dump/dump_monitor.hpp
new file mode 100644
index 0000000..0935492
--- /dev/null
+++ b/dump/dump_monitor.hpp
@@ -0,0 +1,134 @@
+#pragma once
+
+#include "dump_utils.hpp"
+#include "sbe_consts.hpp"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <xyz/openbmc_project/Common/Progress/common.hpp>
+
+#include <iostream>
+#include <string>
+#include <variant>
+
+namespace openpower::dump
+{
+
+using PropertyMap = std::map<std::string, std::variant<uint32_t, std::string>>;
+using InterfaceMap = std::map<std::string, PropertyMap>;
+/**
+ * @class DumpMonitor
+ * @brief Monitors DBus signals for dump creation and handles them.
+ */
+class DumpMonitor
+{
+ public:
+ /**
+ * @brief Constructor for DumpMonitor.
+ * Initializes the DBus connection and signal match for monitoring dump
+ * creation.
+ */
+ DumpMonitor() :
+ bus(sdbusplus::bus::new_default()),
+ match(bus,
+ sdbusplus::bus::match::rules::interfacesAdded(
+ "/xyz/openbmc_project/dump") +
+ sdbusplus::bus::match::rules::sender(
+ "xyz.openbmc_project.Dump.Manager"),
+ [this](sdbusplus::message_t& msg) { handleDBusSignal(msg); })
+ {}
+
+ /**
+ * @brief Runs the monitor to continuously listen for DBus signals.
+ */
+ void run()
+ {
+ bus.process_loop();
+ }
+
+ private:
+ /* @brief sdbusplus handler for a bus to use */
+ sdbusplus::bus_t bus;
+
+ /* @brief Monitores dump interfaces */
+ const std::vector<std::string> monitoredInterfaces = {
+ "com.ibm.Dump.Entry.Hardware", "com.ibm.Dump.Entry.Hostboot",
+ "com.ibm.Dump.Entry.SBE"};
+
+ /* @brief InterfaceAdded match */
+ sdbusplus::bus::match_t match;
+
+ /**
+ * @brief Handles the received DBus signal for dump creation.
+ * @param[in] msg - The DBus message received.
+ */
+ void handleDBusSignal(sdbusplus::message::message& msg);
+
+ /**
+ * @brief Checks if the dump creation is in progress.
+ * @param[in] interfaces - The map of interfaces and their properties.
+ * @return True if the dump is in progress, false otherwise.
+ */
+ inline bool isInProgress(const InterfaceMap& interfaces)
+ {
+ using namespace sdbusplus::common::xyz::openbmc_project::common;
+ auto progressIt = interfaces.find(Progress::interface);
+ if (progressIt != interfaces.end())
+ {
+ auto statusIt = progressIt->second.find("Status");
+ if (statusIt != progressIt->second.end())
+ {
+ std::string status = std::get<std::string>(statusIt->second);
+ return status == Progress::convertOperationStatusToString(
+ Progress::OperationStatus::InProgress);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Executes the script to collect the dump.
+ * @param[in] path - The object path of the dump entry.
+ * @param[in] properties - The properties of the dump entry.
+ */
+ void executeCollectionScript(const sdbusplus::message::object_path& path,
+ const PropertyMap& properties);
+
+ /**
+ * @brief Updates the progress status of the dump.
+ * @param[in] path - The object path of the dump entry.
+ * @param[in] status - The status to be set.
+ */
+ void updateProgressStatus(const std::string& path,
+ const std::string& status);
+
+ /**
+ * @brief Gets the dump type from the dump ID.
+ * @param[in] id - The dump ID.
+ * @return The dump type.
+ */
+ inline uint32_t getDumpTypeFromId(uint32_t id)
+ {
+ using namespace openpower::dump::SBE;
+ uint8_t type = (id >> 28) & 0xF;
+ if (type == 0)
+ {
+ return SBE_DUMP_TYPE_HARDWARE;
+ }
+ else if (type == 2)
+ {
+ return SBE_DUMP_TYPE_HOSTBOOT;
+ }
+ else if (type == 3)
+ {
+ return SBE_DUMP_TYPE_SBE;
+ }
+ return 0;
+ }
+};
+
+} // namespace openpower::dump
diff --git a/dump/dump_monitor_main.cpp b/dump/dump_monitor_main.cpp
new file mode 100644
index 0000000..5904432
--- /dev/null
+++ b/dump/dump_monitor_main.cpp
@@ -0,0 +1,15 @@
+#include "dump_monitor.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <exception>
+#include <iostream>
+#include <variant>
+
+int main()
+{
+ openpower::dump::DumpMonitor monitor;
+ monitor.run();
+ return 0;
+}
diff --git a/dump/meson.build b/dump/meson.build
index 25017e4..2640c7c 100644
--- a/dump/meson.build
+++ b/dump/meson.build
@@ -2,6 +2,8 @@
cxx = meson.get_compiler('cpp')
+sdeventplus_dep = dependency('sdeventplus')
+
collect_deps = [
CLI11_dep,
phosphorlogging,
@@ -10,6 +12,11 @@
cxx.find_library('phal'),
]
+monitor_deps = [
+ sdbusplus_dep,
+ phosphorlogging,
+]
+
# source files
collect_src = files(
@@ -21,6 +28,12 @@
'sbe_type.cpp',
)
+monitor_src = files(
+ 'dump_monitor.cpp',
+ 'dump_monitor_main.cpp',
+ 'dump_utils.cpp',
+)
+
executable('dump-collect',
collect_src,
dependencies: collect_deps,
@@ -28,6 +41,13 @@
install: true
)
+executable('openpower-dump-monitor',
+ monitor_src,
+ dependencies: monitor_deps,
+ implicit_include_directories: true,
+ install: true
+)
+
bindir = get_option('bindir')
dreport_include_dir = join_paths(get_option('datadir'), 'dreport.d/include.d')