blob: bf3467c3711e59cc95c00b90b58f3cda7d8bf30c [file] [log] [blame]
Shawn McCarney1df59542020-09-24 18:43:18 -05001/**
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
33namespace phosphor::power::regulators
34{
35
36void 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
44void 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
51void 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
71void 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
78void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
79 const std::string& inventoryPath)
80{
Shawn McCarney1df59542020-09-24 18:43:18 -050081 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +080082 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -050083 logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
84 journal);
85}
86
87void DBusErrorLogging::logWriteVerificationError(
88 Entry::Level severity, Journal& journal, const std::string& inventoryPath)
89{
Shawn McCarney1df59542020-09-24 18:43:18 -050090 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +080091 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -050092 logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
93 severity, additionalData, journal);
94}
95
96FFDCFile 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
141std::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
Shawn McCarney76b76432020-10-15 18:46:50 -0500153 std::vector<std::string> messages =
154 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
Shawn McCarney1df59542020-09-24 18:43:18 -0500155
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
171std::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
184void 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
219void 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