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