xref: /openbmc/phosphor-power/phosphor-regulators/src/error_logging.cpp (revision 92261f88729b618b1c31d20f28328a8aff73b83b)
11df5954cSShawn McCarney /**
21df5954cSShawn McCarney  * Copyright © 2020 IBM Corporation
31df5954cSShawn McCarney  *
41df5954cSShawn McCarney  * Licensed under the Apache License, Version 2.0 (the "License");
51df5954cSShawn McCarney  * you may not use this file except in compliance with the License.
61df5954cSShawn McCarney  * You may obtain a copy of the License at
71df5954cSShawn McCarney  *
81df5954cSShawn McCarney  *     http://www.apache.org/licenses/LICENSE-2.0
91df5954cSShawn McCarney  *
101df5954cSShawn McCarney  * Unless required by applicable law or agreed to in writing, software
111df5954cSShawn McCarney  * distributed under the License is distributed on an "AS IS" BASIS,
121df5954cSShawn McCarney  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131df5954cSShawn McCarney  * See the License for the specific language governing permissions and
141df5954cSShawn McCarney  * limitations under the License.
151df5954cSShawn McCarney  */
161df5954cSShawn McCarney 
171df5954cSShawn McCarney #include "error_logging.hpp"
181df5954cSShawn McCarney 
191df5954cSShawn McCarney #include "exception_utils.hpp"
201df5954cSShawn McCarney 
211df5954cSShawn McCarney #include <errno.h>     // for errno
221df5954cSShawn McCarney #include <string.h>    // for strerror()
231df5954cSShawn McCarney #include <sys/types.h> // for getpid(), lseek(), ssize_t
241df5954cSShawn McCarney #include <unistd.h>    // for getpid(), lseek(), write()
251df5954cSShawn McCarney 
261df5954cSShawn McCarney #include <sdbusplus/message.hpp>
271df5954cSShawn McCarney 
281df5954cSShawn McCarney #include <exception>
291df5954cSShawn McCarney #include <ios>
301df5954cSShawn McCarney #include <sstream>
311df5954cSShawn McCarney #include <stdexcept>
321df5954cSShawn McCarney 
331df5954cSShawn McCarney namespace phosphor::power::regulators
341df5954cSShawn McCarney {
351df5954cSShawn McCarney 
logConfigFileError(Entry::Level severity,Journal & journal)361df5954cSShawn McCarney void DBusErrorLogging::logConfigFileError(Entry::Level severity,
371df5954cSShawn McCarney                                           Journal& journal)
381df5954cSShawn McCarney {
39415094c1SShawn McCarney     std::string message{
40415094c1SShawn McCarney         "xyz.openbmc_project.Power.Regulators.Error.ConfigFile"};
41415094c1SShawn McCarney     if (severity == Entry::Level::Critical)
42415094c1SShawn McCarney     {
43415094c1SShawn McCarney         // Specify a different message property for critical config file errors.
44415094c1SShawn McCarney         // These are logged when a critical operation cannot be performed due to
45415094c1SShawn McCarney         // the lack of a valid config file.  These errors may require special
46415094c1SShawn McCarney         // handling, like stopping a power on attempt.
47415094c1SShawn McCarney         message =
48415094c1SShawn McCarney             "xyz.openbmc_project.Power.Regulators.Error.ConfigFile.Critical";
49415094c1SShawn McCarney     }
50415094c1SShawn McCarney 
511df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
52415094c1SShawn McCarney     logError(message, severity, additionalData, journal);
531df5954cSShawn McCarney }
541df5954cSShawn McCarney 
logDBusError(Entry::Level severity,Journal & journal)551df5954cSShawn McCarney void DBusErrorLogging::logDBusError(Entry::Level severity, Journal& journal)
561df5954cSShawn McCarney {
571df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
581df5954cSShawn McCarney     logError("xyz.openbmc_project.Power.Error.DBus", severity, additionalData,
591df5954cSShawn McCarney              journal);
601df5954cSShawn McCarney }
611df5954cSShawn McCarney 
logI2CError(Entry::Level severity,Journal & journal,const std::string & bus,uint8_t addr,int errorNumber)621df5954cSShawn McCarney void DBusErrorLogging::logI2CError(Entry::Level severity, Journal& journal,
631df5954cSShawn McCarney                                    const std::string& bus, uint8_t addr,
641df5954cSShawn McCarney                                    int errorNumber)
651df5954cSShawn McCarney {
661df5954cSShawn McCarney     // Convert I2C address to a hex string
671df5954cSShawn McCarney     std::ostringstream ss;
681df5954cSShawn McCarney     ss << "0x" << std::hex << std::uppercase << static_cast<uint16_t>(addr);
691df5954cSShawn McCarney     std::string addrStr = ss.str();
701df5954cSShawn McCarney 
711df5954cSShawn McCarney     // Convert errno value to an integer string
721df5954cSShawn McCarney     std::string errorNumberStr = std::to_string(errorNumber);
731df5954cSShawn McCarney 
741df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
751df5954cSShawn McCarney     additionalData.emplace("CALLOUT_IIC_BUS", bus);
761df5954cSShawn McCarney     additionalData.emplace("CALLOUT_IIC_ADDR", addrStr);
771df5954cSShawn McCarney     additionalData.emplace("CALLOUT_ERRNO", errorNumberStr);
781df5954cSShawn McCarney     logError("xyz.openbmc_project.Power.Error.I2C", severity, additionalData,
791df5954cSShawn McCarney              journal);
801df5954cSShawn McCarney }
811df5954cSShawn McCarney 
logInternalError(Entry::Level severity,Journal & journal)821df5954cSShawn McCarney void DBusErrorLogging::logInternalError(Entry::Level severity, Journal& journal)
831df5954cSShawn McCarney {
841df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
851df5954cSShawn McCarney     logError("xyz.openbmc_project.Power.Error.Internal", severity,
861df5954cSShawn McCarney              additionalData, journal);
871df5954cSShawn McCarney }
881df5954cSShawn McCarney 
logPhaseFault(Entry::Level severity,Journal & journal,PhaseFaultType type,const std::string & inventoryPath,std::map<std::string,std::string> additionalData)895dab5d3dSShawn McCarney void DBusErrorLogging::logPhaseFault(
905dab5d3dSShawn McCarney     Entry::Level severity, Journal& journal, PhaseFaultType type,
915dab5d3dSShawn McCarney     const std::string& inventoryPath,
925dab5d3dSShawn McCarney     std::map<std::string, std::string> additionalData)
935dab5d3dSShawn McCarney {
945dab5d3dSShawn McCarney     std::string message =
955dab5d3dSShawn McCarney         (type == PhaseFaultType::n)
965dab5d3dSShawn McCarney             ? "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.N"
975dab5d3dSShawn McCarney             : "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.NPlus1";
985dab5d3dSShawn McCarney     additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
995dab5d3dSShawn McCarney     logError(message, severity, additionalData, journal);
1005dab5d3dSShawn McCarney }
1015dab5d3dSShawn McCarney 
logPMBusError(Entry::Level severity,Journal & journal,const std::string & inventoryPath)1021df5954cSShawn McCarney void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
1031df5954cSShawn McCarney                                      const std::string& inventoryPath)
1041df5954cSShawn McCarney {
1051df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
106a76898f1SBob King     additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
1071df5954cSShawn McCarney     logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
1081df5954cSShawn McCarney              journal);
1091df5954cSShawn McCarney }
1101df5954cSShawn McCarney 
logWriteVerificationError(Entry::Level severity,Journal & journal,const std::string & inventoryPath)1111df5954cSShawn McCarney void DBusErrorLogging::logWriteVerificationError(
1121df5954cSShawn McCarney     Entry::Level severity, Journal& journal, const std::string& inventoryPath)
1131df5954cSShawn McCarney {
1141df5954cSShawn McCarney     std::map<std::string, std::string> additionalData{};
115a76898f1SBob King     additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
1161df5954cSShawn McCarney     logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
1171df5954cSShawn McCarney              severity, additionalData, journal);
1181df5954cSShawn McCarney }
1191df5954cSShawn McCarney 
createFFDCFile(const std::vector<std::string> & lines)1201df5954cSShawn McCarney FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
1211df5954cSShawn McCarney {
1221df5954cSShawn McCarney     // Create FFDC file of type Text
1231df5954cSShawn McCarney     FFDCFile file{FFDCFormat::Text};
1241df5954cSShawn McCarney     int fd = file.getFileDescriptor();
1251df5954cSShawn McCarney 
1261df5954cSShawn McCarney     // Write lines to file
1271df5954cSShawn McCarney     std::string buffer;
1281df5954cSShawn McCarney     for (const std::string& line : lines)
1291df5954cSShawn McCarney     {
1301df5954cSShawn McCarney         // Copy line to buffer.  Add newline if necessary.
1311df5954cSShawn McCarney         buffer = line;
1321df5954cSShawn McCarney         if (line.empty() || (line.back() != '\n'))
1331df5954cSShawn McCarney         {
1341df5954cSShawn McCarney             buffer += '\n';
1351df5954cSShawn McCarney         }
1361df5954cSShawn McCarney 
1371df5954cSShawn McCarney         // Write buffer to file
1381df5954cSShawn McCarney         const char* bufPtr = buffer.c_str();
1391df5954cSShawn McCarney         unsigned int count = buffer.size();
1401df5954cSShawn McCarney         while (count > 0)
1411df5954cSShawn McCarney         {
1421df5954cSShawn McCarney             // Try to write remaining bytes; it might not write all of them
1431df5954cSShawn McCarney             ssize_t bytesWritten = write(fd, bufPtr, count);
1441df5954cSShawn McCarney             if (bytesWritten == -1)
1451df5954cSShawn McCarney             {
1461df5954cSShawn McCarney                 throw std::runtime_error{
1471df5954cSShawn McCarney                     std::string{"Unable to write to FFDC file: "} +
1481df5954cSShawn McCarney                     strerror(errno)};
1491df5954cSShawn McCarney             }
1501df5954cSShawn McCarney             bufPtr += bytesWritten;
1511df5954cSShawn McCarney             count -= bytesWritten;
1521df5954cSShawn McCarney         }
1531df5954cSShawn McCarney     }
1541df5954cSShawn McCarney 
1551df5954cSShawn McCarney     // Seek to beginning of file so error logging system can read data
1561df5954cSShawn McCarney     if (lseek(fd, 0, SEEK_SET) != 0)
1571df5954cSShawn McCarney     {
1581df5954cSShawn McCarney         throw std::runtime_error{
1591df5954cSShawn McCarney             std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
1601df5954cSShawn McCarney     }
1611df5954cSShawn McCarney 
1621df5954cSShawn McCarney     return file;
1631df5954cSShawn McCarney }
1641df5954cSShawn McCarney 
createFFDCFiles(Journal & journal)1651df5954cSShawn McCarney std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
1661df5954cSShawn McCarney {
1671df5954cSShawn McCarney     std::vector<FFDCFile> files{};
1681df5954cSShawn McCarney 
1691df5954cSShawn McCarney     // Create FFDC files containing journal messages from relevant executables.
1701df5954cSShawn McCarney     // Executables in priority order in case error log cannot hold all the FFDC.
1711df5954cSShawn McCarney     std::vector<std::string> executables{"phosphor-regulators", "systemd"};
1721df5954cSShawn McCarney     for (const std::string& executable : executables)
1731df5954cSShawn McCarney     {
1741df5954cSShawn McCarney         try
1751df5954cSShawn McCarney         {
1761df5954cSShawn McCarney             // Get recent journal messages from the executable
17776b7643fSShawn McCarney             std::vector<std::string> messages =
17876b7643fSShawn McCarney                 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
1791df5954cSShawn McCarney 
1801df5954cSShawn McCarney             // Create FFDC file containing the journal messages
1811df5954cSShawn McCarney             if (!messages.empty())
1821df5954cSShawn McCarney             {
1831df5954cSShawn McCarney                 files.emplace_back(createFFDCFile(messages));
1841df5954cSShawn McCarney             }
1851df5954cSShawn McCarney         }
1861df5954cSShawn McCarney         catch (const std::exception& e)
1871df5954cSShawn McCarney         {
1881df5954cSShawn McCarney             journal.logError(exception_utils::getMessages(e));
1891df5954cSShawn McCarney         }
1901df5954cSShawn McCarney     }
1911df5954cSShawn McCarney 
1921df5954cSShawn McCarney     return files;
1931df5954cSShawn McCarney }
1941df5954cSShawn McCarney 
createFFDCTuples(std::vector<FFDCFile> & files)195*92261f88SPatrick Williams std::vector<FFDCTuple> DBusErrorLogging::createFFDCTuples(
196*92261f88SPatrick Williams     std::vector<FFDCFile>& files)
1971df5954cSShawn McCarney {
1981df5954cSShawn McCarney     std::vector<FFDCTuple> ffdcTuples{};
1991df5954cSShawn McCarney     for (FFDCFile& file : files)
2001df5954cSShawn McCarney     {
2011df5954cSShawn McCarney         ffdcTuples.emplace_back(
2021df5954cSShawn McCarney             file.getFormat(), file.getSubType(), file.getVersion(),
2031df5954cSShawn McCarney             sdbusplus::message::unix_fd(file.getFileDescriptor()));
2041df5954cSShawn McCarney     }
2051df5954cSShawn McCarney     return ffdcTuples;
2061df5954cSShawn McCarney }
2071df5954cSShawn McCarney 
logError(const std::string & message,Entry::Level severity,std::map<std::string,std::string> & additionalData,Journal & journal)2081df5954cSShawn McCarney void DBusErrorLogging::logError(
2091df5954cSShawn McCarney     const std::string& message, Entry::Level severity,
2101df5954cSShawn McCarney     std::map<std::string, std::string>& additionalData, Journal& journal)
2111df5954cSShawn McCarney {
2121df5954cSShawn McCarney     try
2131df5954cSShawn McCarney     {
2141df5954cSShawn McCarney         // Add PID to AdditionalData
2151df5954cSShawn McCarney         additionalData.emplace("_PID", std::to_string(getpid()));
2161df5954cSShawn McCarney 
2171df5954cSShawn McCarney         // Create FFDC files containing debug data to store in error log
2181df5954cSShawn McCarney         std::vector<FFDCFile> files{createFFDCFiles(journal)};
2191df5954cSShawn McCarney 
2201df5954cSShawn McCarney         // Create FFDC tuples used to pass FFDC files to D-Bus method
2211df5954cSShawn McCarney         std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
2221df5954cSShawn McCarney 
2231df5954cSShawn McCarney         // Call D-Bus method to create an error log with FFDC files
2241df5954cSShawn McCarney         const char* service = "xyz.openbmc_project.Logging";
2251df5954cSShawn McCarney         const char* objPath = "/xyz/openbmc_project/logging";
2261df5954cSShawn McCarney         const char* interface = "xyz.openbmc_project.Logging.Create";
2271df5954cSShawn McCarney         const char* method = "CreateWithFFDCFiles";
2281df5954cSShawn McCarney         auto reqMsg = bus.new_method_call(service, objPath, interface, method);
2291df5954cSShawn McCarney         reqMsg.append(message, severity, additionalData, ffdcTuples);
2301df5954cSShawn McCarney         auto respMsg = bus.call(reqMsg);
2311df5954cSShawn McCarney 
2321df5954cSShawn McCarney         // Remove FFDC files.  If an exception occurs before this, the files
2331df5954cSShawn McCarney         // will be deleted by FFDCFile desctructor but errors will be ignored.
2341df5954cSShawn McCarney         removeFFDCFiles(files, journal);
2351df5954cSShawn McCarney     }
2361df5954cSShawn McCarney     catch (const std::exception& e)
2371df5954cSShawn McCarney     {
2381df5954cSShawn McCarney         journal.logError(exception_utils::getMessages(e));
2391df5954cSShawn McCarney         journal.logError("Unable to log error " + message);
2401df5954cSShawn McCarney     }
2411df5954cSShawn McCarney }
2421df5954cSShawn McCarney 
removeFFDCFiles(std::vector<FFDCFile> & files,Journal & journal)2431df5954cSShawn McCarney void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files,
2441df5954cSShawn McCarney                                        Journal& journal)
2451df5954cSShawn McCarney {
2461df5954cSShawn McCarney     // Explicitly remove FFDC files rather than relying on FFDCFile destructor.
2471df5954cSShawn McCarney     // This allows any resulting errors to be written to the journal.
2481df5954cSShawn McCarney     for (FFDCFile& file : files)
2491df5954cSShawn McCarney     {
2501df5954cSShawn McCarney         try
2511df5954cSShawn McCarney         {
2521df5954cSShawn McCarney             file.remove();
2531df5954cSShawn McCarney         }
2541df5954cSShawn McCarney         catch (const std::exception& e)
2551df5954cSShawn McCarney         {
2561df5954cSShawn McCarney             journal.logError(exception_utils::getMessages(e));
2571df5954cSShawn McCarney         }
2581df5954cSShawn McCarney     }
2591df5954cSShawn McCarney 
2601df5954cSShawn McCarney     // Clear vector since the FFDCFile objects can no longer be used
2611df5954cSShawn McCarney     files.clear();
2621df5954cSShawn McCarney }
2631df5954cSShawn McCarney 
2641df5954cSShawn McCarney } // namespace phosphor::power::regulators
265