| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 1 | #include <unistd.h> | 
 | 2 |  | 
| Ben Tyner | b797b3e | 2020-06-29 10:12:05 -0500 | [diff] [blame] | 3 | #include <attn/attn_logging.hpp> | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 4 | #include <attn/pel/pel_minimal.hpp> | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 5 | #include <phosphor-logging/log.hpp> | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 6 |  | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 7 | namespace attn | 
 | 8 | { | 
 | 9 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 10 | /** @brief Journal entry of type INFO using phosphor logging */ | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 11 | template <> | 
 | 12 | void trace<INFO>(const char* i_message) | 
 | 13 | { | 
 | 14 |     phosphor::logging::log<phosphor::logging::level::INFO>(i_message); | 
 | 15 | } | 
 | 16 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 17 | /** @brief Tuple containing information about ffdc files */ | 
 | 18 | using FFDCTuple = | 
 | 19 |     std::tuple<util::FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>; | 
 | 20 |  | 
 | 21 | /** @brief Gather messages from the journal */ | 
 | 22 | std::vector<std::string> sdjGetMessages(const std::string& field, | 
 | 23 |                                         const std::string& fieldValue, | 
 | 24 |                                         unsigned int max); | 
 | 25 |  | 
 | 26 | /** | 
 | 27 |  * Create FFDCTuple objects corresponding to the specified FFDC files. | 
 | 28 |  * | 
 | 29 |  * The D-Bus method to create an error log requires a vector of tuples to | 
 | 30 |  * pass in the FFDC file information. | 
 | 31 |  * | 
 | 32 |  * @param   files - FFDC files | 
 | 33 |  * @return  vector of FFDCTuple objects | 
 | 34 |  */ | 
 | 35 | std::vector<FFDCTuple> | 
 | 36 |     createFFDCTuples(const std::vector<util::FFDCFile>& files) | 
 | 37 | { | 
 | 38 |     std::vector<FFDCTuple> ffdcTuples{}; | 
| Zane Shelley | a79f6c8 | 2021-01-12 16:38:49 -0600 | [diff] [blame] | 39 |     util::transformFFDC(files, ffdcTuples); | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 40 |  | 
 | 41 |     return ffdcTuples; | 
 | 42 | } | 
 | 43 |  | 
 | 44 | /** | 
 | 45 |  * @brief Create an FFDCFile object containing raw data | 
 | 46 |  * | 
 | 47 |  * Throws an exception if an error occurs. | 
 | 48 |  * | 
 | 49 |  * @param   i_buffer - raw data to add to ffdc faw data file | 
 | 50 |  * @param   i_size - size of the raw data | 
 | 51 |  * @return  FFDCFile object | 
 | 52 |  */ | 
 | 53 | util::FFDCFile createFFDCRawFile(void* i_buffer, size_t i_size) | 
 | 54 | { | 
 | 55 |     util::FFDCFile file{util::FFDCFormat::Custom}; | 
 | 56 |  | 
 | 57 |     // Write buffer to file and then reset file description file offset | 
 | 58 |     int fd = file.getFileDescriptor(); | 
 | 59 |     write(fd, static_cast<char*>(i_buffer), i_size); | 
 | 60 |     lseek(fd, 0, SEEK_SET); | 
 | 61 |  | 
 | 62 |     return file; | 
 | 63 | } | 
 | 64 |  | 
 | 65 | /** | 
 | 66 |  * @brief Create an FFDCFile object containing the specified lines of text data | 
 | 67 |  * | 
 | 68 |  * Throws an exception if an error occurs. | 
 | 69 |  * | 
 | 70 |  * @param   lines - lines of text data to write to file | 
 | 71 |  * @return  FFDCFile object | 
 | 72 |  */ | 
 | 73 | util::FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines) | 
 | 74 | { | 
 | 75 |     // Create FFDC file of type Text | 
 | 76 |     util::FFDCFile file{util::FFDCFormat::Text}; | 
 | 77 |     int fd = file.getFileDescriptor(); | 
 | 78 |  | 
 | 79 |     // Write FFDC lines to file | 
 | 80 |     std::string buffer; | 
 | 81 |     for (const std::string& line : lines) | 
 | 82 |     { | 
 | 83 |         // Copy line to buffer.  Add newline if necessary. | 
 | 84 |         buffer = line; | 
 | 85 |         if (line.empty() || (line.back() != '\n')) | 
 | 86 |         { | 
 | 87 |             buffer += '\n'; | 
 | 88 |         } | 
 | 89 |  | 
 | 90 |         // write buffer to file | 
 | 91 |         write(fd, buffer.c_str(), buffer.size()); | 
 | 92 |     } | 
 | 93 |  | 
 | 94 |     // Seek to beginning of file so error logging system can read data | 
 | 95 |     lseek(fd, 0, SEEK_SET); | 
 | 96 |  | 
 | 97 |     return file; | 
 | 98 | } | 
 | 99 |  | 
 | 100 | /** | 
 | 101 |  * Create FDDC files from journal messages of relevant executables | 
 | 102 |  * | 
 | 103 |  * Parse the system journal looking for log entries created by the executables | 
 | 104 |  * of interest for logging. For each of these entries create a ffdc trace file | 
 | 105 |  * that will be used to create ffdc log entries. These files will be pushed | 
 | 106 |  * onto the stack of ffdc files. | 
 | 107 |  * | 
 | 108 |  * @param   i_files - vector of ffdc files that will become log entries | 
 | 109 |  */ | 
 | 110 | void createFFDCTraceFiles(std::vector<util::FFDCFile>& i_files) | 
 | 111 | { | 
 | 112 |     // Executables of interest | 
 | 113 |     std::vector<std::string> executables{"openpower-hw-diags"}; | 
 | 114 |  | 
 | 115 |     for (const std::string& executable : executables) | 
 | 116 |     { | 
 | 117 |         try | 
 | 118 |         { | 
 | 119 |             // get journal messages | 
 | 120 |             std::vector<std::string> messages = | 
 | 121 |                 sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30); | 
 | 122 |  | 
 | 123 |             // Create FFDC file containing the journal messages | 
 | 124 |             if (!messages.empty()) | 
 | 125 |             { | 
 | 126 |                 i_files.emplace_back(createFFDCTraceFile(messages)); | 
 | 127 |             } | 
 | 128 |         } | 
 | 129 |         catch (const std::exception& e) | 
 | 130 |         { | 
 | 131 |             std::stringstream ss; | 
 | 132 |             ss << "createFFDCFiles: " << e.what(); | 
 | 133 |             trace<level::INFO>(ss.str().c_str()); | 
 | 134 |         } | 
 | 135 |     } | 
 | 136 | } | 
 | 137 |  | 
 | 138 | /** | 
 | 139 |  * Create FFDCFile objects containing debug data to store in the error log. | 
 | 140 |  * | 
 | 141 |  * If an error occurs, the error is written to the journal but an exception | 
 | 142 |  * is not thrown. | 
 | 143 |  * | 
 | 144 |  * @param   i_buffer - raw data (if creating raw dump ffdc entry in log) | 
 | 145 |  * @return  vector of FFDCFile objects | 
 | 146 |  */ | 
 | 147 | std::vector<util::FFDCFile> createFFDCFiles(char* i_buffer = nullptr, | 
 | 148 |                                             size_t i_size  = 0) | 
 | 149 | { | 
 | 150 |     std::vector<util::FFDCFile> files{}; | 
 | 151 |  | 
 | 152 |     // Create raw dump file | 
 | 153 |     if ((nullptr != i_buffer) && (0 != i_size)) | 
 | 154 |     { | 
 | 155 |         files.emplace_back(createFFDCRawFile(i_buffer, i_size)); | 
 | 156 |     } | 
 | 157 |  | 
 | 158 |     // Create trace dump file | 
 | 159 |     createFFDCTraceFiles(files); | 
 | 160 |  | 
 | 161 |     return files; | 
 | 162 | } | 
 | 163 |  | 
 | 164 | /** | 
 | 165 |  * Get file descriptor of exisitng PEL | 
 | 166 |  * | 
 | 167 |  * The backend logging code will search for a PEL having the provided PEL ID | 
 | 168 |  * and return a file descriptor to a file containing this PEL's  raw PEL data. | 
 | 169 |  * | 
 | 170 |  * @param  i_pelid - the PEL ID | 
 | 171 |  * @return file descriptor of file containing the raw PEL data | 
 | 172 |  */ | 
 | 173 | int getPelFd(uint32_t i_pelId) | 
 | 174 | { | 
 | 175 |     // GetPEL returns file descriptor (int) | 
 | 176 |     int fd = -1; | 
 | 177 |  | 
 | 178 |     // Sdbus call specifics | 
 | 179 |     constexpr auto service   = "xyz.openbmc_project.Logging"; | 
 | 180 |     constexpr auto path      = "/xyz/openbmc_project/logging"; | 
 | 181 |     constexpr auto interface = "org.open_power.Logging.PEL"; | 
 | 182 |     constexpr auto function  = "GetPEL"; | 
 | 183 |  | 
 | 184 |     // Get the PEL file descriptor | 
 | 185 |     try | 
 | 186 |     { | 
 | 187 |         auto bus    = sdbusplus::bus::new_default_system(); | 
 | 188 |         auto method = bus.new_method_call(service, path, interface, function); | 
 | 189 |         method.append(i_pelId); | 
 | 190 |  | 
 | 191 |         auto resp = bus.call(method); | 
 | 192 |  | 
 | 193 |         sdbusplus::message::unix_fd msgFd; | 
 | 194 |         resp.read(msgFd); | 
 | 195 |         fd = dup(msgFd); // -1 if not found | 
 | 196 |     } | 
 | 197 |     catch (const sdbusplus::exception::SdBusError& e) | 
 | 198 |     { | 
 | 199 |         std::stringstream ss; | 
 | 200 |         ss << "getPelFd: " << e.what(); | 
 | 201 |         trace<level::INFO>(ss.str().c_str()); | 
 | 202 |     } | 
 | 203 |  | 
 | 204 |     // File descriptor or -1 if not found or call failed | 
 | 205 |     return fd; | 
 | 206 | } | 
 | 207 |  | 
 | 208 | /** | 
 | 209 |  * Create a PEL for the specified event type | 
 | 210 |  * | 
 | 211 |  * The additional data provided in the map will be placed in a user data | 
 | 212 |  * section of the PEL and may additionally contain key words to trigger | 
 | 213 |  * certain behaviors by the backend logging code. Each set of data described | 
 | 214 |  * in the vector of ffdc data will be placed in additional user data sections. | 
 | 215 |  * | 
 | 216 |  * @param  i_event - the event type | 
 | 217 |  * @param  i_additional - map of additional data | 
 | 218 |  * @param  9_ffdc - vector of ffdc data | 
 | 219 |  * @return The created PEL's platform log-id | 
 | 220 |  */ | 
 | 221 | uint32_t createPel(std::string i_event, | 
 | 222 |                    std::map<std::string, std::string> i_additional, | 
 | 223 |                    std::vector<util::FFDCTuple> i_ffdc) | 
 | 224 | { | 
 | 225 |     // CreatePELWithFFDCFiles returns log-id and platform log-id | 
 | 226 |     std::tuple<uint32_t, uint32_t> pelResp = {0, 0}; | 
 | 227 |  | 
 | 228 |     // Sdbus call specifics | 
 | 229 |     constexpr auto level     = "xyz.openbmc_project.Logging.Entry.Level.Error"; | 
 | 230 |     constexpr auto service   = "xyz.openbmc_project.Logging"; | 
 | 231 |     constexpr auto path      = "/xyz/openbmc_project/logging"; | 
 | 232 |     constexpr auto interface = "org.open_power.Logging.PEL"; | 
 | 233 |     constexpr auto function  = "CreatePELWithFFDCFiles"; | 
 | 234 |  | 
 | 235 |     // Need to provide pid when using create or create-with-ffdc methods | 
 | 236 |     i_additional.emplace("_PID", std::to_string(getpid())); | 
 | 237 |  | 
 | 238 |     // Create the PEL | 
 | 239 |     try | 
 | 240 |     { | 
 | 241 |         auto bus    = sdbusplus::bus::new_default_system(); | 
 | 242 |         auto method = bus.new_method_call(service, path, interface, function); | 
 | 243 |         method.append(i_event, level, i_additional, i_ffdc); | 
 | 244 |  | 
 | 245 |         auto resp = bus.call(method); | 
 | 246 |  | 
 | 247 |         resp.read(pelResp); | 
 | 248 |     } | 
 | 249 |     catch (const sdbusplus::exception::SdBusError& e) | 
 | 250 |     { | 
 | 251 |         std::stringstream ss; | 
 | 252 |         ss << "createPel: " << e.what(); | 
 | 253 |         trace<level::INFO>(ss.str().c_str()); | 
 | 254 |     } | 
 | 255 |  | 
 | 256 |     // pelResp<0> == log-id, pelResp<1> = platform log-id | 
 | 257 |     return std::get<1>(pelResp); | 
 | 258 | } | 
 | 259 |  | 
 | 260 | /* | 
 | 261 |  * Create a PEL from raw PEL data | 
 | 262 |  * | 
 | 263 |  * The backend logging code will create a PEL based on the specified PEL data. | 
 | 264 |  * | 
 | 265 |  * @param   i_buffer - buffer containing a raw PEL | 
 | 266 |  */ | 
 | 267 | void createPelRaw(std::vector<uint8_t>& i_buffer) | 
 | 268 | { | 
 | 269 |     // Sdbus call specifics | 
 | 270 |     constexpr auto event     = "xyz.open_power.Attn.Error.Terminate"; | 
 | 271 |     constexpr auto level     = "xyz.openbmc_project.Logging.Entry.Level.Error"; | 
 | 272 |     constexpr auto service   = "xyz.openbmc_project.Logging"; | 
 | 273 |     constexpr auto path      = "/xyz/openbmc_project/logging"; | 
 | 274 |     constexpr auto interface = "xyz.openbmc_project.Logging.Create"; | 
 | 275 |     constexpr auto function  = "Create"; | 
 | 276 |  | 
 | 277 |     // Create FFDC file from buffer data | 
 | 278 |     util::FFDCFile pelFile{util::FFDCFormat::Text}; | 
 | 279 |     auto fd = pelFile.getFileDescriptor(); | 
 | 280 |  | 
 | 281 |     write(fd, i_buffer.data(), i_buffer.size()); | 
 | 282 |     lseek(fd, 0, SEEK_SET); | 
 | 283 |  | 
 | 284 |     auto filePath = pelFile.getPath(); | 
 | 285 |  | 
 | 286 |     // Additional data for log | 
 | 287 |     std::map<std::string, std::string> additional; | 
 | 288 |     additional.emplace("RAWPEL", filePath.string()); | 
 | 289 |     additional.emplace("_PID", std::to_string(getpid())); | 
 | 290 |  | 
 | 291 |     // Create the PEL | 
 | 292 |     try | 
 | 293 |     { | 
 | 294 |         auto bus    = sdbusplus::bus::new_default_system(); | 
 | 295 |         auto method = bus.new_method_call(service, path, interface, function); | 
 | 296 |         method.append(event, level, additional); | 
 | 297 |         bus.call_noreply(method); | 
 | 298 |     } | 
 | 299 |     catch (const sdbusplus::exception::SdBusError& e) | 
 | 300 |     { | 
 | 301 |         std::stringstream ss; | 
 | 302 |         ss << "createPelRaw: " << e.what(); | 
 | 303 |         trace<level::INFO>(ss.str().c_str()); | 
 | 304 |     } | 
 | 305 | } | 
 | 306 |  | 
 | 307 | /** | 
 | 308 |  * Create a PEL from an existing PEL | 
 | 309 |  * | 
 | 310 |  * Create a new PEL based on the specified raw PEL and submit the new PEL | 
 | 311 |  * to the backend logging code as a raw PEL. Note that  additional data map | 
 | 312 |  * here contains data to be committed to the PEL and it can also be used to | 
 | 313 |  * create the PEL as it contains needed information. | 
 | 314 |  * | 
 | 315 |  * @param   i_buffer - buffer containing a raw PEL | 
 | 316 |  * @param   i_additional - additional data to be added to the new PEL | 
 | 317 |  */ | 
 | 318 | void createPelCustom(std::vector<uint8_t>& i_rawPel, | 
 | 319 |                      std::map<std::string, std::string> i_additional) | 
 | 320 | { | 
 | 321 |     // create PEL object from buffer | 
 | 322 |     auto tiPel = std::make_unique<pel::PelMinimal>(i_rawPel); | 
 | 323 |  | 
 | 324 |     // The additional data contains the TI info as well as the value for the | 
 | 325 |     // subystem that provided the TI info. Get the subystem from additional | 
 | 326 |     // data and then populate the prmary SRC and SRC words for the custom PEL | 
 | 327 |     // based on the sybsystem's TI info. | 
 | 328 |     uint8_t subsystem = std::stoi(i_additional["Subsystem"]); | 
 | 329 |     tiPel->setSubsystem(subsystem); | 
 | 330 |  | 
 | 331 |     if (static_cast<uint8_t>(pel::SubsystemID::hypervisor) == subsystem) | 
 | 332 |     { | 
 | 333 |         // populate hypervisor SRC words | 
 | 334 |         tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{ | 
 | 335 |             (uint32_t)std::stoul(i_additional["0x10 SRC Word 12"], 0, 16), | 
 | 336 |             (uint32_t)std::stoul(i_additional["0x14 SRC Word 13"], 0, 16), | 
 | 337 |             (uint32_t)std::stoul(i_additional["0x18 SRC Word 14"], 0, 16), | 
 | 338 |             (uint32_t)std::stoul(i_additional["0x1c SRC Word 15"], 0, 16), | 
 | 339 |             (uint32_t)std::stoul(i_additional["0x20 SRC Word 16"], 0, 16), | 
 | 340 |             (uint32_t)std::stoul(i_additional["0x24 SRC Word 17"], 0, 16), | 
 | 341 |             (uint32_t)std::stoul(i_additional["0x28 SRC Word 18"], 0, 16), | 
 | 342 |             (uint32_t)std::stoul(i_additional["0x2c SRC Word 19"], 0, 16)}); | 
 | 343 |  | 
 | 344 |         // populate hypervisor primary SRC | 
 | 345 |         std::array<char, pel::asciiStringSize> srcChars{'0'}; | 
 | 346 |         std::string srcString = i_additional["SrcAscii"]; | 
 | 347 |         srcString.copy(srcChars.data(), | 
 | 348 |                        std::min(srcString.size(), pel::asciiStringSize), 0); | 
 | 349 |         tiPel->setAsciiString(srcChars); | 
 | 350 |     } | 
 | 351 |     else | 
 | 352 |     { | 
 | 353 |         // Populate hostboot SRC words - note HB word 0 from the shared info | 
 | 354 |         // data (additional data "0x10 HB Word") is reflected in the PEL as | 
 | 355 |         // "reason code" so we zero it here. Also note that the first word | 
 | 356 |         // in this group of words starts at word 0 and word 1 does not exits. | 
 | 357 |         tiPel->setSrcWords(std::array<uint32_t, pel::numSrcWords>{ | 
 | 358 |             (uint32_t)0x00000000, | 
 | 359 |             (uint32_t)std::stoul(i_additional["0x14 HB Word 2"], 0, 16), | 
 | 360 |             (uint32_t)std::stoul(i_additional["0x18 HB Word 3"], 0, 16), | 
 | 361 |             (uint32_t)std::stoul(i_additional["0x1c HB Word 4"], 0, 16), | 
 | 362 |             (uint32_t)std::stoul(i_additional["0x20 HB Word 5"], 0, 16), | 
 | 363 |             (uint32_t)std::stoul(i_additional["0x24 HB Word 6"], 0, 16), | 
 | 364 |             (uint32_t)std::stoul(i_additional["0x28 HB Word 7"], 0, 16), | 
 | 365 |             (uint32_t)std::stoul(i_additional["0x2c HB Word 8"], 0, 16)}); | 
 | 366 |  | 
 | 367 |         // populate hostboot primary SRC | 
 | 368 |         std::array<char, pel::asciiStringSize> srcChars{'0'}; | 
 | 369 |         std::string srcString = i_additional["0x30 error_data"]; | 
 | 370 |         srcString.copy(srcChars.data(), | 
 | 371 |                        std::min(srcString.size(), pel::asciiStringSize), 0); | 
 | 372 |         tiPel->setAsciiString(srcChars); | 
 | 373 |     } | 
 | 374 |  | 
 | 375 |     // set severity, event type and action flags | 
 | 376 |     tiPel->setSeverity(static_cast<uint8_t>(pel::Severity::termination)); | 
 | 377 |     tiPel->setType(static_cast<uint8_t>(pel::EventType::na)); | 
 | 378 |     tiPel->setAction(static_cast<uint16_t>(pel::ActionFlags::service | | 
 | 379 |                                            pel::ActionFlags::report | | 
 | 380 |                                            pel::ActionFlags::call)); | 
 | 381 |  | 
 | 382 |     // The raw PEL that we used as the basis for this custom PEL contains the | 
 | 383 |     // attention handler trace data and does not needed to be in this PEL so | 
 | 384 |     // we remove it here. | 
 | 385 |     tiPel->setSectionCount(tiPel->getSectionCount() - 1); | 
 | 386 |  | 
 | 387 |     // Update the raw PEL with the new custom PEL data | 
 | 388 |     tiPel->raw(i_rawPel); | 
 | 389 |  | 
 | 390 |     // create PEL from raw data | 
 | 391 |     createPelRaw(i_rawPel); | 
 | 392 | } | 
 | 393 |  | 
 | 394 | /** | 
 | 395 |  * Log an event handled by the attention handler | 
 | 396 |  * | 
 | 397 |  * Basic (non TI) events will generate a standard message-registry based PEL | 
 | 398 |  * | 
 | 399 |  * TI events will create two PEL's. One PEL will be informational and will | 
 | 400 |  * contain trace information relevent to attention handler. The second PEL | 
 | 401 |  * will be specific to the TI type (including the primary SRC) and will be | 
 | 402 |  * based off of the TI information provided to the attention handler through | 
 | 403 |  * shared TI info data area. | 
 | 404 |  * | 
 | 405 |  * @param  i_event - The event type | 
 | 406 |  * @param  i_additional - Additional PEL data | 
 | 407 |  * @param  i_ffdc - FFDC PEL data | 
 | 408 |  */ | 
 | 409 | void event(EventType i_event, std::map<std::string, std::string>& i_additional, | 
 | 410 |            const std::vector<util::FFDCFile>& i_ffdc) | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 411 | { | 
 | 412 |     bool eventValid = false; // assume no event created | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 413 |     bool tiEvent    = false; // assume not a terminate event | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 414 |  | 
 | 415 |     std::string eventName; | 
 | 416 |  | 
 | 417 |     switch (i_event) | 
 | 418 |     { | 
 | 419 |         case EventType::Checkstop: | 
 | 420 |             eventName  = "org.open_power.HwDiags.Error.Checkstop"; | 
 | 421 |             eventValid = true; | 
 | 422 |             break; | 
 | 423 |         case EventType::Terminate: | 
 | 424 |             eventName  = "org.open_power.Attn.Error.Terminate"; | 
 | 425 |             eventValid = true; | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 426 |             tiEvent    = true; | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 427 |             break; | 
 | 428 |         case EventType::Vital: | 
 | 429 |             eventName  = "org.open_power.Attn.Error.Vital"; | 
 | 430 |             eventValid = true; | 
 | 431 |             break; | 
 | 432 |         case EventType::HwDiagsFail: | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 433 |         case EventType::AttentionFail: | 
 | 434 |             eventName  = "org.open_power.Attn.Error.Fail"; | 
 | 435 |             eventValid = true; | 
 | 436 |             break; | 
 | 437 |         default: | 
 | 438 |             eventValid = false; | 
 | 439 |             break; | 
 | 440 |     } | 
 | 441 |  | 
 | 442 |     if (true == eventValid) | 
 | 443 |     { | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 444 |         // Create PEL with additional data and FFDC data. The newly created | 
 | 445 |         // PEL's platform log-id will be returned. | 
 | 446 |         auto pelId = | 
 | 447 |             createPel(eventName, i_additional, createFFDCTuples(i_ffdc)); | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 448 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 449 |         // If this is a TI event we will create an additional PEL that is | 
 | 450 |         // specific to the subsystem that generated the TI. | 
 | 451 |         if (true == tiEvent) | 
 | 452 |         { | 
 | 453 |             // get file descriptor and size of information PEL | 
 | 454 |             int pelFd = getPelFd(pelId); | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 455 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 456 |             // if PEL found, read into buffer | 
 | 457 |             if (-1 != pelFd) | 
 | 458 |             { | 
 | 459 |                 auto pelSize = lseek(pelFd, 0, SEEK_END); | 
 | 460 |                 lseek(pelFd, 0, SEEK_SET); | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 461 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 462 |                 // read information PEL into buffer | 
 | 463 |                 std::vector<uint8_t> buffer(pelSize); | 
 | 464 |                 read(pelFd, buffer.data(), buffer.size()); | 
 | 465 |                 close(pelFd); | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 466 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 467 |                 // create PEL from buffer | 
 | 468 |                 createPelCustom(buffer, i_additional); | 
 | 469 |             } | 
 | 470 |         } | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 471 |     } | 
 | 472 | } | 
 | 473 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 474 | /** | 
 | 475 |  * Commit special attention TI event to log | 
 | 476 |  * | 
 | 477 |  * Create a event log with provided additional information and standard | 
 | 478 |  * FFDC data plus TI FFDC data | 
 | 479 |  * | 
 | 480 |  * @param i_additional - Additional log data | 
 | 481 |  * @param i_ti_InfoData - TI FFDC data | 
 | 482 |  */ | 
 | 483 | void eventTerminate(std::map<std::string, std::string> i_additionalData, | 
 | 484 |                     char* i_tiInfoData) | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 485 | { | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 486 |     // Create log event with aodditional data and FFDC data | 
 | 487 |     event(EventType::Terminate, i_additionalData, | 
 | 488 |           createFFDCFiles(i_tiInfoData, 0x53)); | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 489 | } | 
 | 490 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 491 | /** @brief Commit SBE vital event to log */ | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 492 | void eventVital() | 
 | 493 | { | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 494 |     // Additional data for log | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 495 |     std::map<std::string, std::string> additionalData; | 
 | 496 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 497 |     // Create log event with additional data and FFDC data | 
 | 498 |     event(EventType::Vital, additionalData, createFFDCFiles(nullptr, 0)); | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 499 | } | 
 | 500 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 501 | /** | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 502 |  * Commit attention handler failure event to log | 
 | 503 |  * | 
 | 504 |  * Create an event log containing the specified error code. | 
 | 505 |  * | 
 | 506 |  * @param i_error - Error code | 
 | 507 |  */ | 
 | 508 | void eventAttentionFail(int i_error) | 
 | 509 | { | 
 | 510 |     // Additional data for log | 
 | 511 |     std::map<std::string, std::string> additionalData; | 
 | 512 |     additionalData["ERROR_CODE"] = std::to_string(i_error); | 
 | 513 |  | 
 | 514 |     // Create log event with additional data and FFDC data | 
 | 515 |     event(EventType::AttentionFail, additionalData, | 
 | 516 |           createFFDCFiles(nullptr, 0)); | 
 | 517 | } | 
 | 518 |  | 
 | 519 | /** | 
 | 520 |  * Parse systemd journal message field | 
 | 521 |  * | 
 | 522 |  * Parse the journal looking for the specified field and return the journal | 
 | 523 |  * data for that field. | 
 | 524 |  * | 
 | 525 |  * @param  journal - The journal to parse | 
 | 526 |  * @param  field - Field containing the data to retrieve | 
 | 527 |  * @return Data for the speciefied field | 
 | 528 |  */ | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 529 | std::string sdjGetFieldValue(sd_journal* journal, const char* field) | 
 | 530 | { | 
 | 531 |     const char* data{nullptr}; | 
 | 532 |     size_t length{0}; | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 533 |  | 
 | 534 |     // get field value | 
 | 535 |     if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length)) | 
 | 536 |     { | 
| Zane Shelley | 9fb657f | 2021-01-12 15:30:58 -0600 | [diff] [blame] | 537 |         size_t prefix{0}; | 
 | 538 |  | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 539 |         // The data returned  by sd_journal_get_data will be prefixed with the | 
 | 540 |         // field name and "=" | 
 | 541 |         const void* eq = memchr(data, '=', length); | 
 | 542 |         if (nullptr != eq) | 
 | 543 |         { | 
 | 544 |             // get just data following the "=" | 
 | 545 |             prefix = (const char*)eq - data + 1; | 
 | 546 |         } | 
 | 547 |         else | 
 | 548 |         { | 
 | 549 |             // all the data (should not happen) | 
 | 550 |             prefix = 0; | 
 | 551 |             std::string value{}; // empty string | 
 | 552 |         } | 
 | 553 |  | 
 | 554 |         return std::string{data + prefix, length - prefix}; | 
 | 555 |     } | 
 | 556 |     else | 
 | 557 |     { | 
 | 558 |         return std::string{}; // empty string | 
 | 559 |     } | 
 | 560 | } | 
 | 561 |  | 
| Ben Tyner | f5210bb | 2021-01-05 12:58:10 -0600 | [diff] [blame] | 562 | /** | 
 | 563 |  * Gather messages from the journal | 
 | 564 |  * | 
 | 565 |  * Fetch journal entry data for all entries with the specified field equal to | 
 | 566 |  * the specified value. | 
 | 567 |  * | 
 | 568 |  * @param   field - Field to search on | 
 | 569 |  * @param   fieldValue -  Value to search for | 
 | 570 |  * @param   max - Maximum number of messages fetch | 
 | 571 |  * @return  Vector of journal entry data | 
 | 572 |  */ | 
| Ben Tyner | 1b1915e | 2020-10-23 15:13:38 -0500 | [diff] [blame] | 573 | std::vector<std::string> sdjGetMessages(const std::string& field, | 
 | 574 |                                         const std::string& fieldValue, | 
 | 575 |                                         unsigned int max) | 
 | 576 | { | 
 | 577 |     sd_journal* journal; | 
 | 578 |     std::vector<std::string> messages; | 
 | 579 |  | 
 | 580 |     if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY)) | 
 | 581 |     { | 
 | 582 |         SD_JOURNAL_FOREACH_BACKWARDS(journal) | 
 | 583 |         { | 
 | 584 |             // Get input field | 
 | 585 |             std::string value = sdjGetFieldValue(journal, field.c_str()); | 
 | 586 |  | 
 | 587 |             // Compare field value and read data | 
 | 588 |             if (value == fieldValue) | 
 | 589 |             { | 
 | 590 |                 // Get SYSLOG_IDENTIFIER field (process that logged message) | 
 | 591 |                 std::string syslog = | 
 | 592 |                     sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER"); | 
 | 593 |  | 
 | 594 |                 // Get _PID field | 
 | 595 |                 std::string pid = sdjGetFieldValue(journal, "_PID"); | 
 | 596 |  | 
 | 597 |                 // Get MESSAGE field | 
 | 598 |                 std::string message = sdjGetFieldValue(journal, "MESSAGE"); | 
 | 599 |  | 
 | 600 |                 // Get timestamp | 
 | 601 |                 uint64_t usec{0}; | 
 | 602 |                 if (0 == sd_journal_get_realtime_usec(journal, &usec)) | 
 | 603 |                 { | 
 | 604 |  | 
 | 605 |                     // Convert realtime microseconds to date format | 
 | 606 |                     char dateBuffer[80]; | 
 | 607 |                     std::string date; | 
 | 608 |                     std::time_t timeInSecs = usec / 1000000; | 
 | 609 |                     strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S", | 
 | 610 |                              std::localtime(&timeInSecs)); | 
 | 611 |                     date = dateBuffer; | 
 | 612 |  | 
 | 613 |                     // Store value to messages | 
 | 614 |                     value = date + " " + syslog + "[" + pid + "]: " + message; | 
 | 615 |                     messages.insert(messages.begin(), value); | 
 | 616 |                 } | 
 | 617 |             } | 
 | 618 |  | 
 | 619 |             // limit maximum number of messages | 
 | 620 |             if (messages.size() >= max) | 
 | 621 |             { | 
 | 622 |                 break; | 
 | 623 |             } | 
 | 624 |         } | 
 | 625 |  | 
 | 626 |         sd_journal_close(journal); // close journal when done | 
 | 627 |     } | 
 | 628 |  | 
 | 629 |     return messages; | 
 | 630 | } | 
 | 631 |  | 
| Ben Tyner | b1ebfcb | 2020-05-08 18:52:48 -0500 | [diff] [blame] | 632 | } // namespace attn |