1 /** 2 * Copyright © 2019 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 #include "config.h" 17 18 #include "pel.hpp" 19 20 #include "bcd_time.hpp" 21 #include "extended_user_data.hpp" 22 #include "extended_user_header.hpp" 23 #include "failing_mtms.hpp" 24 #include "fru_identity.hpp" 25 #include "json_utils.hpp" 26 #include "log_id.hpp" 27 #include "pel_rules.hpp" 28 #include "pel_values.hpp" 29 #include "section_factory.hpp" 30 #include "src.hpp" 31 #include "stream.hpp" 32 #include "user_data_formats.hpp" 33 34 #ifdef PEL_ENABLE_PHAL 35 #include "phal_service_actions.hpp" 36 #include "sbe_ffdc_handler.hpp" 37 #endif 38 39 #include <fmt/format.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 43 #include <phosphor-logging/log.hpp> 44 45 #include <iostream> 46 47 namespace openpower 48 { 49 namespace pels 50 { 51 namespace pv = openpower::pels::pel_values; 52 using namespace phosphor::logging; 53 54 constexpr auto unknownValue = "Unknown"; 55 56 PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp, 57 phosphor::logging::Entry::Level severity, 58 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn, 59 const DataInterfaceBase& dataIface, const JournalBase& journal) 60 { 61 // No changes in input, for non SBE error related requests 62 PelFFDC ffdcFiles = ffdcFilesIn; 63 64 #ifdef PEL_ENABLE_PHAL 65 // Add sbe ffdc processed data into ffdcfiles. 66 namespace sbe = openpower::pels::sbe; 67 auto processReq = 68 std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) { 69 return file.format == UserDataFormat::custom && 70 file.subType == sbe::sbeFFDCSubType; 71 }); 72 // sbeFFDC can't be destroyed until the end of the PEL constructor 73 // because it needs to keep around the FFDC Files to be used below. 74 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr; 75 if (processReq) 76 { 77 sbeFFDCPtr = std::make_unique<sbe::SbeFFDC>(additionalData, 78 ffdcFilesIn); 79 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC(); 80 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(), 81 sbeFFDCFiles.end()); 82 83 // update pel priority for spare clock failures 84 if (auto customSeverity = sbeFFDCPtr->getSeverity()) 85 { 86 severity = customSeverity.value(); 87 } 88 } 89 #endif 90 91 std::map<std::string, std::vector<std::string>> debugData; 92 nlohmann::json callouts; 93 94 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID, 95 timestamp); 96 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData, 97 dataIface); 98 99 // Extract any callouts embedded in an FFDC file. 100 if (!ffdcFiles.empty()) 101 { 102 try 103 { 104 callouts = getCalloutJSON(ffdcFiles); 105 } 106 catch (const std::exception& e) 107 { 108 debugData.emplace("FFDC file JSON callouts error", 109 std::vector<std::string>{e.what()}); 110 } 111 } 112 113 auto src = std::make_unique<SRC>(regEntry, additionalData, callouts, 114 dataIface); 115 116 if (!src->getDebugData().empty()) 117 { 118 // Something didn't go as planned 119 debugData.emplace("SRC", src->getDebugData()); 120 } 121 122 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src); 123 124 _optionalSections.push_back(std::move(src)); 125 _optionalSections.push_back(std::move(euh)); 126 127 auto mtms = std::make_unique<FailingMTMS>(dataIface); 128 _optionalSections.push_back(std::move(mtms)); 129 130 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); 131 addUserDataSection(std::move(ud)); 132 133 // Check for pel severity of type - 0x51 = critical error, system 134 // termination and update terminate bit in SRC for pels 135 updateTerminateBitInSRCSection(); 136 137 // Create a UserData section from AdditionalData. 138 if (!additionalData.empty()) 139 { 140 ud = util::makeADUserDataSection(additionalData); 141 addUserDataSection(std::move(ud)); 142 } 143 144 // Add any FFDC files into UserData sections 145 for (const auto& file : ffdcFiles) 146 { 147 ud = util::makeFFDCuserDataSection(regEntry.componentID, file); 148 if (!ud) 149 { 150 // Add this error into the debug data UserData section 151 std::ostringstream msg; 152 msg << "Could not make PEL FFDC UserData section from file" 153 << std::hex << regEntry.componentID << " " << file.subType 154 << " " << file.version; 155 if (debugData.count("FFDC File")) 156 { 157 debugData.at("FFDC File").push_back(msg.str()); 158 } 159 else 160 { 161 debugData.emplace("FFDC File", 162 std::vector<std::string>{msg.str()}); 163 } 164 165 continue; 166 } 167 168 addUserDataSection(std::move(ud)); 169 } 170 171 #ifdef PEL_ENABLE_PHAL 172 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID); 173 openpower::pels::phal::createServiceActions(callouts, path, dataIface, 174 plid()); 175 #endif 176 177 // Store in the PEL any important debug data created while 178 // building the PEL sections. 179 if (!debugData.empty()) 180 { 181 nlohmann::json data; 182 data["PEL Internal Debug Data"] = debugData; 183 ud = util::makeJSONUserDataSection(data); 184 185 addUserDataSection(std::move(ud)); 186 187 // Also put in the journal for debug 188 for (const auto& [name, msgs] : debugData) 189 { 190 for (const auto& message : msgs) 191 { 192 std::string entry = name + ": " + message; 193 log<level::INFO>(entry.c_str()); 194 } 195 } 196 } 197 198 addJournalSections(regEntry, journal); 199 200 _ph->setSectionCount(2 + _optionalSections.size()); 201 202 checkRulesAndFix(); 203 } 204 205 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {} 206 207 PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID) 208 { 209 populateFromRawData(data, obmcLogID); 210 } 211 212 void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID) 213 { 214 Stream pelData{data}; 215 _ph = std::make_unique<PrivateHeader>(pelData); 216 if (obmcLogID != 0) 217 { 218 _ph->setOBMCLogID(obmcLogID); 219 } 220 221 _uh = std::make_unique<UserHeader>(pelData); 222 223 // Use the section factory to create the rest of the objects 224 for (size_t i = 2; i < _ph->sectionCount(); i++) 225 { 226 auto section = section_factory::create(pelData); 227 _optionalSections.push_back(std::move(section)); 228 } 229 } 230 231 bool PEL::valid() const 232 { 233 bool valid = _ph->valid(); 234 235 if (valid) 236 { 237 valid = _uh->valid(); 238 } 239 240 if (valid) 241 { 242 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(), 243 [](const auto& section) { return section->valid(); })) 244 { 245 valid = false; 246 } 247 } 248 249 return valid; 250 } 251 252 void PEL::setCommitTime() 253 { 254 auto now = std::chrono::system_clock::now(); 255 _ph->setCommitTimestamp(getBCDTime(now)); 256 } 257 258 void PEL::assignID() 259 { 260 _ph->setID(generatePELID()); 261 } 262 263 void PEL::flatten(std::vector<uint8_t>& pelBuffer) const 264 { 265 Stream pelData{pelBuffer}; 266 267 if (!valid()) 268 { 269 log<level::WARNING>("Unflattening an invalid PEL"); 270 } 271 272 _ph->flatten(pelData); 273 _uh->flatten(pelData); 274 275 for (auto& section : _optionalSections) 276 { 277 section->flatten(pelData); 278 } 279 } 280 281 std::vector<uint8_t> PEL::data() const 282 { 283 std::vector<uint8_t> pelData; 284 flatten(pelData); 285 return pelData; 286 } 287 288 size_t PEL::size() const 289 { 290 size_t size = 0; 291 292 if (_ph) 293 { 294 size += _ph->header().size; 295 } 296 297 if (_uh) 298 { 299 size += _uh->header().size; 300 } 301 302 for (const auto& section : _optionalSections) 303 { 304 size += section->header().size; 305 } 306 307 return size; 308 } 309 310 std::optional<SRC*> PEL::primarySRC() const 311 { 312 auto src = std::find_if( 313 _optionalSections.begin(), _optionalSections.end(), [](auto& section) { 314 return section->header().id == 315 static_cast<uint16_t>(SectionID::primarySRC); 316 }); 317 if (src != _optionalSections.end()) 318 { 319 return static_cast<SRC*>(src->get()); 320 } 321 322 return std::nullopt; 323 } 324 325 void PEL::checkRulesAndFix() 326 { 327 // Only fix if the action flags are at their default value which 328 // means they weren't specified in the registry. Otherwise 329 // assume the user knows what they are doing. 330 if (_uh->actionFlags() == actionFlagsDefault) 331 { 332 auto [actionFlags, eventType] = pel_rules::check(0, _uh->eventType(), 333 _uh->severity()); 334 335 _uh->setActionFlags(actionFlags); 336 _uh->setEventType(eventType); 337 } 338 } 339 340 void PEL::printSectionInJSON(const Section& section, std::string& buf, 341 std::map<uint16_t, size_t>& pluralSections, 342 message::Registry& registry, 343 const std::vector<std::string>& plugins, 344 uint8_t creatorID) const 345 { 346 char tmpB[5]; 347 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8), 348 static_cast<uint8_t>(section.header().id)}; 349 sprintf(tmpB, "%c%c", id[0], id[1]); 350 std::string sectionID(tmpB); 351 std::string sectionName = pv::sectionTitles.count(sectionID) 352 ? pv::sectionTitles.at(sectionID) 353 : "Unknown Section"; 354 355 // Add a count if there are multiple of this type of section 356 auto count = pluralSections.find(section.header().id); 357 if (count != pluralSections.end()) 358 { 359 sectionName += " " + std::to_string(count->second); 360 count->second++; 361 } 362 363 if (section.valid()) 364 { 365 std::optional<std::string> json; 366 if (sectionID == "PS" || sectionID == "SS") 367 { 368 json = section.getJSON(registry, plugins, creatorID); 369 } 370 else if ((sectionID == "UD") || (sectionID == "ED")) 371 { 372 json = section.getJSON(creatorID, plugins); 373 } 374 else 375 { 376 json = section.getJSON(); 377 } 378 379 buf += "\"" + sectionName + "\": {\n"; 380 381 if (json) 382 { 383 buf += *json + "\n},\n"; 384 } 385 else 386 { 387 jsonInsert(buf, pv::sectionVer, 388 getNumberString("%d", section.header().version), 1); 389 jsonInsert(buf, pv::subSection, 390 getNumberString("%d", section.header().subType), 1); 391 jsonInsert(buf, pv::createdBy, 392 getNumberString("0x%X", section.header().componentID), 393 1); 394 395 std::vector<uint8_t> data; 396 Stream s{data}; 397 section.flatten(s); 398 std::string dstr = 399 dumpHex(std::data(data) + SectionHeader::flattenedSize(), 400 data.size() - SectionHeader::flattenedSize(), 2); 401 std::string jsonIndent(indentLevel, 0x20); 402 buf += jsonIndent + "\"Data\": [\n"; 403 buf += dstr; 404 buf += jsonIndent + "]\n"; 405 buf += "},\n"; 406 } 407 } 408 else 409 { 410 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n"; 411 } 412 } 413 414 std::map<uint16_t, size_t> PEL::getPluralSections() const 415 { 416 std::map<uint16_t, size_t> sectionCounts; 417 418 for (const auto& section : optionalSections()) 419 { 420 if (sectionCounts.find(section->header().id) == sectionCounts.end()) 421 { 422 sectionCounts[section->header().id] = 1; 423 } 424 else 425 { 426 sectionCounts[section->header().id]++; 427 } 428 } 429 430 std::map<uint16_t, size_t> sections; 431 for (const auto& [id, count] : sectionCounts) 432 { 433 if (count > 1) 434 { 435 // Start with 0 here and printSectionInJSON() 436 // will increment it as it goes. 437 sections.emplace(id, 0); 438 } 439 } 440 441 return sections; 442 } 443 444 void PEL::toJSON(message::Registry& registry, 445 const std::vector<std::string>& plugins) const 446 { 447 auto sections = getPluralSections(); 448 449 std::string buf = "{\n"; 450 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins); 451 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins); 452 for (auto& section : this->optionalSections()) 453 { 454 printSectionInJSON(*(section.get()), buf, sections, registry, plugins, 455 _ph->creatorID()); 456 } 457 buf += "}"; 458 std::size_t found = buf.rfind(","); 459 if (found != std::string::npos) 460 buf.replace(found, 1, ""); 461 std::cout << buf << std::endl; 462 } 463 464 bool PEL::addUserDataSection(std::unique_ptr<UserData> userData) 465 { 466 if (size() + userData->header().size > _maxPELSize) 467 { 468 if (userData->shrink(_maxPELSize - size())) 469 { 470 _optionalSections.push_back(std::move(userData)); 471 } 472 else 473 { 474 log<level::WARNING>( 475 "Could not shrink UserData section. Dropping", 476 entry("SECTION_SIZE=%d\n", userData->header().size), 477 entry("COMPONENT_ID=0x%02X", userData->header().componentID), 478 entry("SUBTYPE=0x%X", userData->header().subType), 479 entry("VERSION=0x%X", userData->header().version)); 480 return false; 481 } 482 } 483 else 484 { 485 _optionalSections.push_back(std::move(userData)); 486 } 487 return true; 488 } 489 490 nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles) 491 { 492 nlohmann::json callouts; 493 494 for (const auto& file : ffdcFiles) 495 { 496 if ((file.format == UserDataFormat::json) && 497 (file.subType == jsonCalloutSubtype)) 498 { 499 auto data = util::readFD(file.fd); 500 if (data.empty()) 501 { 502 throw std::runtime_error{ 503 "Could not get data from JSON callout file descriptor"}; 504 } 505 506 std::string jsonString{data.begin(), data.begin() + data.size()}; 507 508 callouts = nlohmann::json::parse(jsonString); 509 break; 510 } 511 } 512 513 return callouts; 514 } 515 516 bool PEL::isHwCalloutPresent() const 517 { 518 auto pSRC = primarySRC(); 519 if (!pSRC) 520 { 521 return false; 522 } 523 524 bool calloutPresent = false; 525 if ((*pSRC)->callouts()) 526 { 527 for (auto& i : (*pSRC)->callouts()->callouts()) 528 { 529 if (((*i).fruIdentity())) 530 { 531 auto& fruId = (*i).fruIdentity(); 532 if ((*fruId).failingComponentType() == 533 src::FRUIdentity::hardwareFRU) 534 { 535 calloutPresent = true; 536 break; 537 } 538 } 539 } 540 } 541 542 return calloutPresent; 543 } 544 545 void PEL::updateSysInfoInExtendedUserDataSection( 546 const DataInterfaceBase& dataIface) 547 { 548 const AdditionalData additionalData; 549 550 // Check for PEL from Hostboot 551 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot)) 552 { 553 // Get the ED section from PEL 554 auto op = std::find_if(_optionalSections.begin(), 555 _optionalSections.end(), [](auto& section) { 556 return section->header().id == 557 static_cast<uint16_t>( 558 SectionID::extUserData); 559 }); 560 561 // Check for ED section found and its not the last section of PEL 562 if (op != _optionalSections.end()) 563 { 564 // Get the extended user data class mapped to found section 565 auto extUserData = static_cast<ExtendedUserData*>(op->get()); 566 567 // Check for the creator ID is for OpenBMC 568 if (extUserData->creatorID() == 569 static_cast<uint8_t>(CreatorID::openBMC)) 570 { 571 // Update subtype and component id 572 auto subType = static_cast<uint8_t>(UserDataFormat::json); 573 auto componentId = 574 static_cast<uint16_t>(ComponentID::phosphorLogging); 575 576 // Update system data to ED section 577 auto ud = util::makeSysInfoUserDataSection(additionalData, 578 dataIface, false); 579 extUserData->updateDataSection(subType, componentId, 580 ud->data()); 581 } 582 } 583 } 584 } 585 586 void PEL::updateTerminateBitInSRCSection() 587 { 588 // Check for pel severity of type - 0x51 = critical error, system 589 // termination 590 if (_uh->severity() == 0x51) 591 { 592 // Get the primary SRC section 593 auto pSRC = primarySRC(); 594 if (pSRC) 595 { 596 (*pSRC)->setTerminateBit(); 597 } 598 } 599 } 600 601 void PEL::addJournalSections(const message::Entry& regEntry, 602 const JournalBase& journal) 603 { 604 if (!regEntry.journalCapture) 605 { 606 return; 607 } 608 609 // Write all unwritten journal data to disk. 610 journal.sync(); 611 612 const auto& jc = regEntry.journalCapture.value(); 613 std::vector<std::vector<std::string>> allMessages; 614 615 if (std::holds_alternative<size_t>(jc)) 616 { 617 // Get the previous numLines journal entries 618 const auto& numLines = std::get<size_t>(jc); 619 try 620 { 621 auto messages = journal.getMessages("", numLines); 622 if (!messages.empty()) 623 { 624 allMessages.push_back(std::move(messages)); 625 } 626 } 627 catch (const std::exception& e) 628 { 629 log<level::ERR>( 630 fmt::format("Failed during journal collection: {}", e.what()) 631 .c_str()); 632 } 633 } 634 else if (std::holds_alternative<message::AppCaptureList>(jc)) 635 { 636 // Get journal entries based on the syslog id field. 637 const auto& sections = std::get<message::AppCaptureList>(jc); 638 for (const auto& [syslogID, numLines] : sections) 639 { 640 try 641 { 642 auto messages = journal.getMessages(syslogID, numLines); 643 if (!messages.empty()) 644 { 645 allMessages.push_back(std::move(messages)); 646 } 647 } 648 catch (const std::exception& e) 649 { 650 log<level::ERR>( 651 fmt::format("Failed during journal collection: {}", 652 e.what()) 653 .c_str()); 654 } 655 } 656 } 657 658 // Create the UserData sections 659 for (const auto& messages : allMessages) 660 { 661 auto buffer = util::flattenLines(messages); 662 663 // If the buffer is way too big, it can overflow the uint16_t 664 // PEL section size field that is checked below so do a cursory 665 // check here. 666 if (buffer.size() > _maxPELSize) 667 { 668 log<level::WARNING>( 669 "Journal UserData section does not fit in PEL, dropping"); 670 log<level::WARNING>(fmt::format("PEL size = {}, data size = {}", 671 size(), buffer.size()) 672 .c_str()); 673 continue; 674 } 675 676 // Sections must be 4 byte aligned. 677 while (buffer.size() % 4 != 0) 678 { 679 buffer.push_back(0); 680 } 681 682 auto ud = std::make_unique<UserData>( 683 static_cast<uint16_t>(ComponentID::phosphorLogging), 684 static_cast<uint8_t>(UserDataFormat::text), 685 static_cast<uint8_t>(UserDataFormatVersion::text), buffer); 686 687 if (size() + ud->header().size <= _maxPELSize) 688 { 689 _optionalSections.push_back(std::move(ud)); 690 } 691 else 692 { 693 // Don't attempt to shrink here since we'd be dropping the 694 // most recent journal entries which would be confusing. 695 log<level::WARNING>( 696 "Journal UserData section does not fit in PEL, dropping"); 697 log<level::WARNING>(fmt::format("PEL size = {}, UserData size = {}", 698 size(), ud->header().size) 699 .c_str()); 700 ud.reset(); 701 continue; 702 } 703 } 704 } 705 706 namespace util 707 { 708 709 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json) 710 { 711 auto jsonString = json.dump(); 712 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end()); 713 714 // Pad to a 4 byte boundary 715 while ((jsonData.size() % 4) != 0) 716 { 717 jsonData.push_back(0); 718 } 719 720 return std::make_unique<UserData>( 721 static_cast<uint16_t>(ComponentID::phosphorLogging), 722 static_cast<uint8_t>(UserDataFormat::json), 723 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData); 724 } 725 726 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad) 727 { 728 assert(!ad.empty()); 729 nlohmann::json json; 730 731 // Remove the 'ESEL' entry, as it contains a full PEL in the value. 732 if (ad.getValue("ESEL")) 733 { 734 auto newAD = ad; 735 newAD.remove("ESEL"); 736 json = newAD.toJSON(); 737 } 738 else 739 { 740 json = ad.toJSON(); 741 } 742 743 return makeJSONUserDataSection(json); 744 } 745 746 void addProcessNameToJSON(nlohmann::json& json, 747 const std::optional<std::string>& pid, 748 const DataInterfaceBase& dataIface) 749 { 750 std::string name{unknownValue}; 751 752 try 753 { 754 if (pid) 755 { 756 auto n = dataIface.getProcessName(*pid); 757 if (n) 758 { 759 name = *n; 760 } 761 } 762 } 763 catch (const std::exception& e) 764 {} 765 766 if (pid) 767 { 768 json["Process Name"] = std::move(name); 769 } 770 } 771 772 void addBMCFWVersionIDToJSON(nlohmann::json& json, 773 const DataInterfaceBase& dataIface) 774 { 775 auto id = dataIface.getBMCFWVersionID(); 776 if (id.empty()) 777 { 778 id = unknownValue; 779 } 780 781 json["FW Version ID"] = std::move(id); 782 } 783 784 std::string lastSegment(char separator, std::string data) 785 { 786 auto pos = data.find_last_of(separator); 787 if (pos != std::string::npos) 788 { 789 data = data.substr(pos + 1); 790 } 791 792 return data; 793 } 794 795 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface) 796 { 797 auto keyword = dataIface.getSystemIMKeyword(); 798 799 std::string value{}; 800 801 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) { 802 value += fmt::format("{:02X}", byte); 803 }); 804 805 json["System IM"] = value; 806 } 807 808 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface) 809 { 810 json["BMCState"] = lastSegment('.', dataIface.getBMCState()); 811 json["ChassisState"] = lastSegment('.', dataIface.getChassisState()); 812 json["HostState"] = lastSegment('.', dataIface.getHostState()); 813 json["BootState"] = lastSegment('.', dataIface.getBootState()); 814 } 815 816 void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface) 817 { 818 auto seconds = dataIface.getUptimeInSeconds(); 819 if (seconds) 820 { 821 json["BMCUptime"] = dataIface.getBMCUptime(*seconds); 822 } 823 else 824 { 825 json["BMCUptime"] = ""; 826 } 827 json["BMCLoad"] = dataIface.getBMCLoadAvg(); 828 } 829 830 std::unique_ptr<UserData> 831 makeSysInfoUserDataSection(const AdditionalData& ad, 832 const DataInterfaceBase& dataIface, 833 bool addUptime) 834 { 835 nlohmann::json json; 836 837 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface); 838 addBMCFWVersionIDToJSON(json, dataIface); 839 addIMKeyword(json, dataIface); 840 addStatesToJSON(json, dataIface); 841 842 if (addUptime) 843 { 844 addBMCUptime(json, dataIface); 845 } 846 847 return makeJSONUserDataSection(json); 848 } 849 850 std::vector<uint8_t> readFD(int fd) 851 { 852 std::vector<uint8_t> data; 853 854 // Get the size 855 struct stat s; 856 int r = fstat(fd, &s); 857 if (r != 0) 858 { 859 auto e = errno; 860 log<level::ERR>("Could not get FFDC file size from FD", 861 entry("ERRNO=%d", e)); 862 return data; 863 } 864 865 if (0 == s.st_size) 866 { 867 log<level::ERR>("FFDC file is empty"); 868 return data; 869 } 870 871 data.resize(s.st_size); 872 873 // Make sure its at the beginning, as maybe another 874 // extension already used it. 875 r = lseek(fd, 0, SEEK_SET); 876 if (r == -1) 877 { 878 auto e = errno; 879 log<level::ERR>("Could not seek to beginning of FFDC file", 880 entry("ERRNO=%d", e)); 881 return data; 882 } 883 884 r = read(fd, data.data(), s.st_size); 885 if (r == -1) 886 { 887 auto e = errno; 888 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e)); 889 } 890 else if (r != s.st_size) 891 { 892 log<level::WARNING>("Could not read full FFDC file", 893 entry("FILE_SIZE=%d", s.st_size), 894 entry("SIZE_READ=%d", r)); 895 } 896 897 return data; 898 } 899 900 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID, 901 const PelFFDCfile& file) 902 { 903 auto data = readFD(file.fd); 904 905 if (data.empty()) 906 { 907 return std::unique_ptr<UserData>(); 908 } 909 910 // The data needs 4 Byte alignment, and save amount padded for the 911 // CBOR case. 912 uint32_t pad = 0; 913 while (data.size() % 4) 914 { 915 data.push_back(0); 916 pad++; 917 } 918 919 // For JSON, CBOR, and Text use our component ID, subType, and version, 920 // otherwise use the supplied ones. 921 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging); 922 uint8_t subType{}; 923 uint8_t version{}; 924 925 switch (file.format) 926 { 927 case UserDataFormat::json: 928 subType = static_cast<uint8_t>(UserDataFormat::json); 929 version = static_cast<uint8_t>(UserDataFormatVersion::json); 930 break; 931 case UserDataFormat::cbor: 932 subType = static_cast<uint8_t>(UserDataFormat::cbor); 933 version = static_cast<uint8_t>(UserDataFormatVersion::cbor); 934 935 // The CBOR parser will fail on the extra pad bytes since they 936 // aren't CBOR. Add the amount we padded to the end and other 937 // code will remove it all before parsing. 938 { 939 data.resize(data.size() + 4); 940 Stream stream{data}; 941 stream.offset(data.size() - 4); 942 stream << pad; 943 } 944 945 break; 946 case UserDataFormat::text: 947 subType = static_cast<uint8_t>(UserDataFormat::text); 948 version = static_cast<uint8_t>(UserDataFormatVersion::text); 949 break; 950 case UserDataFormat::custom: 951 default: 952 // Use the passed in values 953 compID = componentID; 954 subType = file.subType; 955 version = file.version; 956 break; 957 } 958 959 return std::make_unique<UserData>(compID, subType, version, data); 960 } 961 962 std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines) 963 { 964 std::vector<uint8_t> out; 965 966 for (const auto& line : lines) 967 { 968 out.insert(out.end(), line.begin(), line.end()); 969 970 if (out.back() != '\n') 971 { 972 out.push_back('\n'); 973 } 974 } 975 976 return out; 977 } 978 979 } // namespace util 980 981 } // namespace pels 982 } // namespace openpower 983