blob: 309d9b6617d55c8bb8661351e175db675f47e910 [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{
81 // Convert relative inventory path to an absolute path
82 std::string absInventoryPath = getAbsoluteInventoryPath(inventoryPath);
83
84 std::map<std::string, std::string> additionalData{};
85 additionalData.emplace("CALLOUT_INVENTORY_PATH", absInventoryPath);
86 logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
87 journal);
88}
89
90void DBusErrorLogging::logWriteVerificationError(
91 Entry::Level severity, Journal& journal, const std::string& inventoryPath)
92{
93 // Convert relative inventory path to an absolute path
94 std::string absInventoryPath = getAbsoluteInventoryPath(inventoryPath);
95
96 std::map<std::string, std::string> additionalData{};
97 additionalData.emplace("CALLOUT_INVENTORY_PATH", absInventoryPath);
98 logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
99 severity, additionalData, journal);
100}
101
102FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
103{
104 // Create FFDC file of type Text
105 FFDCFile file{FFDCFormat::Text};
106 int fd = file.getFileDescriptor();
107
108 // Write lines to file
109 std::string buffer;
110 for (const std::string& line : lines)
111 {
112 // Copy line to buffer. Add newline if necessary.
113 buffer = line;
114 if (line.empty() || (line.back() != '\n'))
115 {
116 buffer += '\n';
117 }
118
119 // Write buffer to file
120 const char* bufPtr = buffer.c_str();
121 unsigned int count = buffer.size();
122 while (count > 0)
123 {
124 // Try to write remaining bytes; it might not write all of them
125 ssize_t bytesWritten = write(fd, bufPtr, count);
126 if (bytesWritten == -1)
127 {
128 throw std::runtime_error{
129 std::string{"Unable to write to FFDC file: "} +
130 strerror(errno)};
131 }
132 bufPtr += bytesWritten;
133 count -= bytesWritten;
134 }
135 }
136
137 // Seek to beginning of file so error logging system can read data
138 if (lseek(fd, 0, SEEK_SET) != 0)
139 {
140 throw std::runtime_error{
141 std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
142 }
143
144 return file;
145}
146
147std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
148{
149 std::vector<FFDCFile> files{};
150
151 // Create FFDC files containing journal messages from relevant executables.
152 // Executables in priority order in case error log cannot hold all the FFDC.
153 std::vector<std::string> executables{"phosphor-regulators", "systemd"};
154 for (const std::string& executable : executables)
155 {
156 try
157 {
158 // Get recent journal messages from the executable
Shawn McCarney76b76432020-10-15 18:46:50 -0500159 std::vector<std::string> messages =
160 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
Shawn McCarney1df59542020-09-24 18:43:18 -0500161
162 // Create FFDC file containing the journal messages
163 if (!messages.empty())
164 {
165 files.emplace_back(createFFDCFile(messages));
166 }
167 }
168 catch (const std::exception& e)
169 {
170 journal.logError(exception_utils::getMessages(e));
171 }
172 }
173
174 return files;
175}
176
177std::vector<FFDCTuple>
178 DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files)
179{
180 std::vector<FFDCTuple> ffdcTuples{};
181 for (FFDCFile& file : files)
182 {
183 ffdcTuples.emplace_back(
184 file.getFormat(), file.getSubType(), file.getVersion(),
185 sdbusplus::message::unix_fd(file.getFileDescriptor()));
186 }
187 return ffdcTuples;
188}
189
190void DBusErrorLogging::logError(
191 const std::string& message, Entry::Level severity,
192 std::map<std::string, std::string>& additionalData, Journal& journal)
193{
194 try
195 {
196 // Add PID to AdditionalData
197 additionalData.emplace("_PID", std::to_string(getpid()));
198
199 // Create FFDC files containing debug data to store in error log
200 std::vector<FFDCFile> files{createFFDCFiles(journal)};
201
202 // Create FFDC tuples used to pass FFDC files to D-Bus method
203 std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
204
205 // Call D-Bus method to create an error log with FFDC files
206 const char* service = "xyz.openbmc_project.Logging";
207 const char* objPath = "/xyz/openbmc_project/logging";
208 const char* interface = "xyz.openbmc_project.Logging.Create";
209 const char* method = "CreateWithFFDCFiles";
210 auto reqMsg = bus.new_method_call(service, objPath, interface, method);
211 reqMsg.append(message, severity, additionalData, ffdcTuples);
212 auto respMsg = bus.call(reqMsg);
213
214 // Remove FFDC files. If an exception occurs before this, the files
215 // will be deleted by FFDCFile desctructor but errors will be ignored.
216 removeFFDCFiles(files, journal);
217 }
218 catch (const std::exception& e)
219 {
220 journal.logError(exception_utils::getMessages(e));
221 journal.logError("Unable to log error " + message);
222 }
223}
224
225void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files,
226 Journal& journal)
227{
228 // Explicitly remove FFDC files rather than relying on FFDCFile destructor.
229 // This allows any resulting errors to be written to the journal.
230 for (FFDCFile& file : files)
231 {
232 try
233 {
234 file.remove();
235 }
236 catch (const std::exception& e)
237 {
238 journal.logError(exception_utils::getMessages(e));
239 }
240 }
241
242 // Clear vector since the FFDCFile objects can no longer be used
243 files.clear();
244}
245
246} // namespace phosphor::power::regulators