xref: /openbmc/phosphor-power/phosphor-regulators/src/error_logging.cpp (revision a76898f11ce599e70e0f367b266b1ee90437f62d)
1 /**
2  * Copyright © 2020 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "error_logging.hpp"
18 
19 #include "exception_utils.hpp"
20 
21 #include <errno.h>     // for errno
22 #include <string.h>    // for strerror()
23 #include <sys/types.h> // for getpid(), lseek(), ssize_t
24 #include <unistd.h>    // for getpid(), lseek(), write()
25 
26 #include <sdbusplus/message.hpp>
27 
28 #include <exception>
29 #include <ios>
30 #include <sstream>
31 #include <stdexcept>
32 
33 namespace phosphor::power::regulators
34 {
35 
36 void DBusErrorLogging::logConfigFileError(Entry::Level severity,
37                                           Journal& journal)
38 {
39     std::map<std::string, std::string> additionalData{};
40     logError("xyz.openbmc_project.Power.Regulators.Error.ConfigFile", severity,
41              additionalData, journal);
42 }
43 
44 void DBusErrorLogging::logDBusError(Entry::Level severity, Journal& journal)
45 {
46     std::map<std::string, std::string> additionalData{};
47     logError("xyz.openbmc_project.Power.Error.DBus", severity, additionalData,
48              journal);
49 }
50 
51 void DBusErrorLogging::logI2CError(Entry::Level severity, Journal& journal,
52                                    const std::string& bus, uint8_t addr,
53                                    int errorNumber)
54 {
55     // Convert I2C address to a hex string
56     std::ostringstream ss;
57     ss << "0x" << std::hex << std::uppercase << static_cast<uint16_t>(addr);
58     std::string addrStr = ss.str();
59 
60     // Convert errno value to an integer string
61     std::string errorNumberStr = std::to_string(errorNumber);
62 
63     std::map<std::string, std::string> additionalData{};
64     additionalData.emplace("CALLOUT_IIC_BUS", bus);
65     additionalData.emplace("CALLOUT_IIC_ADDR", addrStr);
66     additionalData.emplace("CALLOUT_ERRNO", errorNumberStr);
67     logError("xyz.openbmc_project.Power.Error.I2C", severity, additionalData,
68              journal);
69 }
70 
71 void DBusErrorLogging::logInternalError(Entry::Level severity, Journal& journal)
72 {
73     std::map<std::string, std::string> additionalData{};
74     logError("xyz.openbmc_project.Power.Error.Internal", severity,
75              additionalData, journal);
76 }
77 
78 void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
79                                      const std::string& inventoryPath)
80 {
81     std::map<std::string, std::string> additionalData{};
82     additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
83     logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
84              journal);
85 }
86 
87 void DBusErrorLogging::logWriteVerificationError(
88     Entry::Level severity, Journal& journal, const std::string& inventoryPath)
89 {
90     std::map<std::string, std::string> additionalData{};
91     additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
92     logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
93              severity, additionalData, journal);
94 }
95 
96 FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
97 {
98     // Create FFDC file of type Text
99     FFDCFile file{FFDCFormat::Text};
100     int fd = file.getFileDescriptor();
101 
102     // Write lines to file
103     std::string buffer;
104     for (const std::string& line : lines)
105     {
106         // Copy line to buffer.  Add newline if necessary.
107         buffer = line;
108         if (line.empty() || (line.back() != '\n'))
109         {
110             buffer += '\n';
111         }
112 
113         // Write buffer to file
114         const char* bufPtr = buffer.c_str();
115         unsigned int count = buffer.size();
116         while (count > 0)
117         {
118             // Try to write remaining bytes; it might not write all of them
119             ssize_t bytesWritten = write(fd, bufPtr, count);
120             if (bytesWritten == -1)
121             {
122                 throw std::runtime_error{
123                     std::string{"Unable to write to FFDC file: "} +
124                     strerror(errno)};
125             }
126             bufPtr += bytesWritten;
127             count -= bytesWritten;
128         }
129     }
130 
131     // Seek to beginning of file so error logging system can read data
132     if (lseek(fd, 0, SEEK_SET) != 0)
133     {
134         throw std::runtime_error{
135             std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
136     }
137 
138     return file;
139 }
140 
141 std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
142 {
143     std::vector<FFDCFile> files{};
144 
145     // Create FFDC files containing journal messages from relevant executables.
146     // Executables in priority order in case error log cannot hold all the FFDC.
147     std::vector<std::string> executables{"phosphor-regulators", "systemd"};
148     for (const std::string& executable : executables)
149     {
150         try
151         {
152             // Get recent journal messages from the executable
153             std::vector<std::string> messages =
154                 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
155 
156             // Create FFDC file containing the journal messages
157             if (!messages.empty())
158             {
159                 files.emplace_back(createFFDCFile(messages));
160             }
161         }
162         catch (const std::exception& e)
163         {
164             journal.logError(exception_utils::getMessages(e));
165         }
166     }
167 
168     return files;
169 }
170 
171 std::vector<FFDCTuple>
172     DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files)
173 {
174     std::vector<FFDCTuple> ffdcTuples{};
175     for (FFDCFile& file : files)
176     {
177         ffdcTuples.emplace_back(
178             file.getFormat(), file.getSubType(), file.getVersion(),
179             sdbusplus::message::unix_fd(file.getFileDescriptor()));
180     }
181     return ffdcTuples;
182 }
183 
184 void DBusErrorLogging::logError(
185     const std::string& message, Entry::Level severity,
186     std::map<std::string, std::string>& additionalData, Journal& journal)
187 {
188     try
189     {
190         // Add PID to AdditionalData
191         additionalData.emplace("_PID", std::to_string(getpid()));
192 
193         // Create FFDC files containing debug data to store in error log
194         std::vector<FFDCFile> files{createFFDCFiles(journal)};
195 
196         // Create FFDC tuples used to pass FFDC files to D-Bus method
197         std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
198 
199         // Call D-Bus method to create an error log with FFDC files
200         const char* service = "xyz.openbmc_project.Logging";
201         const char* objPath = "/xyz/openbmc_project/logging";
202         const char* interface = "xyz.openbmc_project.Logging.Create";
203         const char* method = "CreateWithFFDCFiles";
204         auto reqMsg = bus.new_method_call(service, objPath, interface, method);
205         reqMsg.append(message, severity, additionalData, ffdcTuples);
206         auto respMsg = bus.call(reqMsg);
207 
208         // Remove FFDC files.  If an exception occurs before this, the files
209         // will be deleted by FFDCFile desctructor but errors will be ignored.
210         removeFFDCFiles(files, journal);
211     }
212     catch (const std::exception& e)
213     {
214         journal.logError(exception_utils::getMessages(e));
215         journal.logError("Unable to log error " + message);
216     }
217 }
218 
219 void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files,
220                                        Journal& journal)
221 {
222     // Explicitly remove FFDC files rather than relying on FFDCFile destructor.
223     // This allows any resulting errors to be written to the journal.
224     for (FFDCFile& file : files)
225     {
226         try
227         {
228             file.remove();
229         }
230         catch (const std::exception& e)
231         {
232             journal.logError(exception_utils::getMessages(e));
233         }
234     }
235 
236     // Clear vector since the FFDCFile objects can no longer be used
237     files.clear();
238 }
239 
240 } // namespace phosphor::power::regulators
241