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 "elog_entry.hpp" 17 #include "extensions/openpower-pels/generic.hpp" 18 #include "extensions/openpower-pels/pel.hpp" 19 #include "mocks.hpp" 20 #include "pel_utils.hpp" 21 22 #include <filesystem> 23 #include <fstream> 24 25 #include <gtest/gtest.h> 26 27 namespace fs = std::filesystem; 28 using namespace openpower::pels; 29 using ::testing::NiceMock; 30 using ::testing::Return; 31 32 class PELTest : public CleanLogID 33 { 34 }; 35 36 fs::path makeTempDir() 37 { 38 char path[] = "/tmp/tempdirXXXXXX"; 39 std::filesystem::path dir = mkdtemp(path); 40 return dir; 41 } 42 43 int writeFileAndGetFD(const fs::path& dir, const std::vector<uint8_t>& data) 44 { 45 static size_t count = 0; 46 fs::path path = dir / (std::string{"file"} + std::to_string(count)); 47 std::ofstream stream{path}; 48 count++; 49 50 stream.write(reinterpret_cast<const char*>(data.data()), data.size()); 51 stream.close(); 52 53 FILE* fp = fopen(path.c_str(), "r"); 54 return fileno(fp); 55 } 56 57 TEST_F(PELTest, FlattenTest) 58 { 59 auto data = pelDataFactory(TestPELType::pelSimple); 60 auto pel = std::make_unique<PEL>(data); 61 62 // Check a few fields 63 EXPECT_TRUE(pel->valid()); 64 EXPECT_EQ(pel->id(), 0x80818283); 65 EXPECT_EQ(pel->plid(), 0x50515253); 66 EXPECT_EQ(pel->userHeader().subsystem(), 0x10); 67 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0); 68 69 // Test that data in == data out 70 auto flattenedData = pel->data(); 71 EXPECT_EQ(data, flattenedData); 72 EXPECT_EQ(flattenedData.size(), pel->size()); 73 } 74 75 TEST_F(PELTest, CommitTimeTest) 76 { 77 auto data = pelDataFactory(TestPELType::pelSimple); 78 auto pel = std::make_unique<PEL>(data); 79 80 auto origTime = pel->commitTime(); 81 pel->setCommitTime(); 82 auto newTime = pel->commitTime(); 83 84 EXPECT_NE(origTime, newTime); 85 86 // Make a new PEL and check new value is still there 87 auto newData = pel->data(); 88 auto newPel = std::make_unique<PEL>(newData); 89 EXPECT_EQ(newTime, newPel->commitTime()); 90 } 91 92 TEST_F(PELTest, AssignIDTest) 93 { 94 auto data = pelDataFactory(TestPELType::pelSimple); 95 auto pel = std::make_unique<PEL>(data); 96 97 auto origID = pel->id(); 98 pel->assignID(); 99 auto newID = pel->id(); 100 101 EXPECT_NE(origID, newID); 102 103 // Make a new PEL and check new value is still there 104 auto newData = pel->data(); 105 auto newPel = std::make_unique<PEL>(newData); 106 EXPECT_EQ(newID, newPel->id()); 107 } 108 109 TEST_F(PELTest, WithLogIDTest) 110 { 111 auto data = pelDataFactory(TestPELType::pelSimple); 112 auto pel = std::make_unique<PEL>(data, 0x42); 113 114 EXPECT_TRUE(pel->valid()); 115 EXPECT_EQ(pel->obmcLogID(), 0x42); 116 } 117 118 TEST_F(PELTest, InvalidPELTest) 119 { 120 auto data = pelDataFactory(TestPELType::pelSimple); 121 122 // Too small 123 data.resize(PrivateHeader::flattenedSize()); 124 125 auto pel = std::make_unique<PEL>(data); 126 127 EXPECT_TRUE(pel->privateHeader().valid()); 128 EXPECT_FALSE(pel->userHeader().valid()); 129 EXPECT_FALSE(pel->valid()); 130 131 // Now corrupt the private header 132 data = pelDataFactory(TestPELType::pelSimple); 133 data.at(0) = 0; 134 pel = std::make_unique<PEL>(data); 135 136 EXPECT_FALSE(pel->privateHeader().valid()); 137 EXPECT_TRUE(pel->userHeader().valid()); 138 EXPECT_FALSE(pel->valid()); 139 } 140 141 TEST_F(PELTest, EmptyDataTest) 142 { 143 std::vector<uint8_t> data; 144 auto pel = std::make_unique<PEL>(data); 145 146 EXPECT_FALSE(pel->privateHeader().valid()); 147 EXPECT_FALSE(pel->userHeader().valid()); 148 EXPECT_FALSE(pel->valid()); 149 } 150 151 TEST_F(PELTest, CreateFromRegistryTest) 152 { 153 message::Entry regEntry; 154 uint64_t timestamp = 5; 155 156 regEntry.name = "test"; 157 regEntry.subsystem = 5; 158 regEntry.actionFlags = 0xC000; 159 regEntry.src.type = 0xBD; 160 regEntry.src.reasonCode = 0x1234; 161 162 std::vector<std::string> data{"KEY1=VALUE1"}; 163 AdditionalData ad{data}; 164 NiceMock<MockDataInterface> dataIface; 165 PelFFDC ffdc; 166 167 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error, 168 ad, ffdc, dataIface}; 169 170 EXPECT_TRUE(pel.valid()); 171 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42); 172 EXPECT_EQ(pel.userHeader().severity(), 0x40); 173 174 EXPECT_EQ(pel.primarySRC().value()->asciiString(), 175 "BD051234 "); 176 177 // Check that certain optional sections have been created 178 size_t mtmsCount = 0; 179 size_t euhCount = 0; 180 size_t udCount = 0; 181 182 for (const auto& section : pel.optionalSections()) 183 { 184 if (section->header().id == 185 static_cast<uint16_t>(SectionID::failingMTMS)) 186 { 187 mtmsCount++; 188 } 189 else if (section->header().id == 190 static_cast<uint16_t>(SectionID::extendedUserHeader)) 191 { 192 euhCount++; 193 } 194 else if (section->header().id == 195 static_cast<uint16_t>(SectionID::userData)) 196 { 197 udCount++; 198 } 199 } 200 201 EXPECT_EQ(mtmsCount, 1); 202 EXPECT_EQ(euhCount, 1); 203 EXPECT_EQ(udCount, 2); // AD section and sysInfo section 204 } 205 206 // Test that when the AdditionalData size is over 16KB that 207 // the PEL that's created is exactly 16KB since the UserData 208 // section that contains all that data was pruned. 209 TEST_F(PELTest, CreateTooBigADTest) 210 { 211 message::Entry regEntry; 212 uint64_t timestamp = 5; 213 214 regEntry.name = "test"; 215 regEntry.subsystem = 5; 216 regEntry.actionFlags = 0xC000; 217 regEntry.src.type = 0xBD; 218 regEntry.src.reasonCode = 0x1234; 219 PelFFDC ffdc; 220 221 // Over the 16KB max PEL size 222 std::string bigAD{"KEY1="}; 223 bigAD += std::string(17000, 'G'); 224 225 std::vector<std::string> data{bigAD}; 226 AdditionalData ad{data}; 227 NiceMock<MockDataInterface> dataIface; 228 229 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error, 230 ad, ffdc, dataIface}; 231 232 EXPECT_TRUE(pel.valid()); 233 EXPECT_EQ(pel.size(), 16384); 234 235 // Make sure that there are still 2 UD sections. 236 size_t udCount = 0; 237 for (const auto& section : pel.optionalSections()) 238 { 239 if (section->header().id == static_cast<uint16_t>(SectionID::userData)) 240 { 241 udCount++; 242 } 243 } 244 245 EXPECT_EQ(udCount, 2); // AD section and sysInfo section 246 } 247 248 // Test that we'll create Generic optional sections for sections that 249 // there aren't explicit classes for. 250 TEST_F(PELTest, GenericSectionTest) 251 { 252 auto data = pelDataFactory(TestPELType::pelSimple); 253 254 std::vector<uint8_t> section1{0x58, 0x58, // ID 'XX' 255 0x00, 0x18, // Size 256 0x01, 0x02, // version, subtype 257 0x03, 0x04, // comp ID 258 259 // some data 260 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 261 0x20, 0x31, 0x06, 0x0F, 0x09, 0x22, 0x3A, 262 0x00}; 263 264 std::vector<uint8_t> section2{ 265 0x59, 0x59, // ID 'YY' 266 0x00, 0x20, // Size 267 0x01, 0x02, // version, subtype 268 0x03, 0x04, // comp ID 269 270 // some data 271 0x20, 0x30, 0x05, 0x09, 0x11, 0x1E, 0x1, 0x63, 0x20, 0x31, 0x06, 0x0F, 272 0x09, 0x22, 0x3A, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; 273 274 // Add the new sections at the end 275 data.insert(data.end(), section1.begin(), section1.end()); 276 data.insert(data.end(), section2.begin(), section2.end()); 277 278 // Increment the section count 279 data.at(27) += 2; 280 auto origData = data; 281 282 PEL pel{data}; 283 284 const auto& sections = pel.optionalSections(); 285 286 bool foundXX = false; 287 bool foundYY = false; 288 289 // Check that we can find these 2 Generic sections 290 for (const auto& section : sections) 291 { 292 if (section->header().id == 0x5858) 293 { 294 foundXX = true; 295 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); 296 } 297 else if (section->header().id == 0x5959) 298 { 299 foundYY = true; 300 EXPECT_NE(dynamic_cast<Generic*>(section.get()), nullptr); 301 } 302 } 303 304 EXPECT_TRUE(foundXX); 305 EXPECT_TRUE(foundYY); 306 307 // Now flatten and check 308 auto newData = pel.data(); 309 310 EXPECT_EQ(origData, newData); 311 } 312 313 // Test that an invalid section will still get a Generic object 314 TEST_F(PELTest, InvalidGenericTest) 315 { 316 auto data = pelDataFactory(TestPELType::pelSimple); 317 318 // Not a valid section 319 std::vector<uint8_t> section1{0x01, 0x02, 0x03}; 320 321 data.insert(data.end(), section1.begin(), section1.end()); 322 323 // Increment the section count 324 data.at(27) += 1; 325 326 PEL pel{data}; 327 EXPECT_FALSE(pel.valid()); 328 329 const auto& sections = pel.optionalSections(); 330 331 bool foundGeneric = false; 332 for (const auto& section : sections) 333 { 334 if (dynamic_cast<Generic*>(section.get()) != nullptr) 335 { 336 foundGeneric = true; 337 EXPECT_EQ(section->valid(), false); 338 break; 339 } 340 } 341 342 EXPECT_TRUE(foundGeneric); 343 } 344 345 // Create a UserData section out of AdditionalData 346 TEST_F(PELTest, MakeUDSectionTest) 347 { 348 std::vector<std::string> ad{"KEY1=VALUE1", "KEY2=VALUE2", "KEY3=VALUE3", 349 "ESEL=TEST"}; 350 AdditionalData additionalData{ad}; 351 352 auto ud = util::makeADUserDataSection(additionalData); 353 354 EXPECT_TRUE(ud->valid()); 355 EXPECT_EQ(ud->header().id, 0x5544); 356 EXPECT_EQ(ud->header().version, 0x01); 357 EXPECT_EQ(ud->header().subType, 0x01); 358 EXPECT_EQ(ud->header().componentID, 0x2000); 359 360 const auto& d = ud->data(); 361 362 std::string jsonString{d.begin(), d.end()}; 363 364 std::string expectedJSON = 365 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})"; 366 367 // The actual data is null padded to a 4B boundary. 368 std::vector<uint8_t> expectedData; 369 expectedData.resize(52, '\0'); 370 memcpy(expectedData.data(), expectedJSON.data(), expectedJSON.size()); 371 372 EXPECT_EQ(d, expectedData); 373 374 // Ensure we can read this as JSON 375 auto newJSON = nlohmann::json::parse(jsonString); 376 EXPECT_EQ(newJSON["KEY1"], "VALUE1"); 377 EXPECT_EQ(newJSON["KEY2"], "VALUE2"); 378 EXPECT_EQ(newJSON["KEY3"], "VALUE3"); 379 } 380 381 // Create the UserData section that contains system info 382 TEST_F(PELTest, SysInfoSectionTest) 383 { 384 MockDataInterface dataIface; 385 386 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234")); 387 EXPECT_CALL(dataIface, getBMCState()).WillOnce(Return("State.Ready")); 388 EXPECT_CALL(dataIface, getChassisState()).WillOnce(Return("State.On")); 389 EXPECT_CALL(dataIface, getHostState()).WillOnce(Return("State.Off")); 390 391 std::string pid = "_PID=" + std::to_string(getpid()); 392 std::vector<std::string> ad{pid}; 393 AdditionalData additionalData{ad}; 394 395 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface); 396 397 EXPECT_TRUE(ud->valid()); 398 EXPECT_EQ(ud->header().id, 0x5544); 399 EXPECT_EQ(ud->header().version, 0x01); 400 EXPECT_EQ(ud->header().subType, 0x01); 401 EXPECT_EQ(ud->header().componentID, 0x2000); 402 403 // Pull out the JSON data and check it. 404 const auto& d = ud->data(); 405 std::string jsonString{d.begin(), d.end()}; 406 auto json = nlohmann::json::parse(jsonString); 407 408 // Ensure the 'Process Name' entry contains 'pel_test' 409 auto name = json["Process Name"].get<std::string>(); 410 EXPECT_NE(name.find("pel_test"), std::string::npos); 411 412 auto version = json["BMC Version ID"].get<std::string>(); 413 EXPECT_EQ(version, "ABCD1234"); 414 415 auto state = json["BMCState"].get<std::string>(); 416 EXPECT_EQ(state, "Ready"); 417 418 state = json["ChassisState"].get<std::string>(); 419 EXPECT_EQ(state, "On"); 420 421 state = json["HostState"].get<std::string>(); 422 EXPECT_EQ(state, "Off"); 423 } 424 425 // Test that the sections that override 426 // virtual std::optional<std::string> Section::getJSON() const 427 // return valid JSON. 428 TEST_F(PELTest, SectionJSONTest) 429 { 430 auto data = pelDataFactory(TestPELType::pelSimple); 431 PEL pel{data}; 432 433 // Check that all JSON returned from the sections is 434 // parseable by nlohmann::json, which will throw an 435 // exception and fail the test if there is a problem. 436 437 // The getJSON() response needs to be wrapped in a { } to make 438 // actual valid JSON (PEL::toJSON() usually handles that). 439 440 auto jsonString = pel.privateHeader().getJSON(); 441 442 // PrivateHeader always prints JSON 443 ASSERT_TRUE(jsonString); 444 *jsonString = '{' + *jsonString + '}'; 445 auto json = nlohmann::json::parse(*jsonString); 446 447 jsonString = pel.userHeader().getJSON(); 448 449 // UserHeader always prints JSON 450 ASSERT_TRUE(jsonString); 451 *jsonString = '{' + *jsonString + '}'; 452 json = nlohmann::json::parse(*jsonString); 453 454 for (const auto& section : pel.optionalSections()) 455 { 456 // The optional sections may or may not have implemented getJSON(). 457 jsonString = section->getJSON(); 458 if (jsonString) 459 { 460 *jsonString = '{' + *jsonString + '}'; 461 auto json = nlohmann::json::parse(*jsonString); 462 } 463 } 464 } 465 466 PelFFDCfile getJSONFFDC(const fs::path& dir) 467 { 468 PelFFDCfile ffdc; 469 ffdc.format = UserDataFormat::json; 470 ffdc.subType = 5; 471 ffdc.version = 42; 472 473 auto inputJSON = R"({ 474 "key1": "value1", 475 "key2": 42, 476 "key3" : [1, 2, 3, 4, 5], 477 "key4": {"key5": "value5"} 478 })"_json; 479 480 // Write the JSON to a file and get its descriptor. 481 auto s = inputJSON.dump(); 482 std::vector<uint8_t> data{s.begin(), s.end()}; 483 ffdc.fd = writeFileAndGetFD(dir, data); 484 485 return ffdc; 486 } 487 488 TEST_F(PELTest, MakeJSONFileUDSectionTest) 489 { 490 auto dir = makeTempDir(); 491 492 { 493 auto ffdc = getJSONFFDC(dir); 494 495 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 496 close(ffdc.fd); 497 ASSERT_TRUE(ud); 498 ASSERT_TRUE(ud->valid()); 499 EXPECT_EQ(ud->header().id, 0x5544); 500 501 EXPECT_EQ(ud->header().version, 502 static_cast<uint8_t>(UserDataFormatVersion::json)); 503 EXPECT_EQ(ud->header().subType, 504 static_cast<uint8_t>(UserDataFormat::json)); 505 EXPECT_EQ(ud->header().componentID, 506 static_cast<uint16_t>(ComponentID::phosphorLogging)); 507 508 // Pull the JSON back out of the the UserData section 509 const auto& d = ud->data(); 510 std::string js{d.begin(), d.end()}; 511 auto json = nlohmann::json::parse(js); 512 513 EXPECT_EQ("value1", json["key1"].get<std::string>()); 514 EXPECT_EQ(42, json["key2"].get<int>()); 515 516 std::vector<int> key3Values{1, 2, 3, 4, 5}; 517 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>()); 518 519 std::map<std::string, std::string> key4Values{{"key5", "value5"}}; 520 auto actual = json["key4"].get<std::map<std::string, std::string>>(); 521 EXPECT_EQ(key4Values, actual); 522 } 523 524 { 525 // A bad FD 526 PelFFDCfile ffdc; 527 ffdc.format = UserDataFormat::json; 528 ffdc.subType = 5; 529 ffdc.version = 42; 530 ffdc.fd = 10000; 531 532 // The section shouldn't get made 533 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 534 ASSERT_FALSE(ud); 535 } 536 537 fs::remove_all(dir); 538 } 539 540 PelFFDCfile getCBORFFDC(const fs::path& dir) 541 { 542 PelFFDCfile ffdc; 543 ffdc.format = UserDataFormat::cbor; 544 ffdc.subType = 5; 545 ffdc.version = 42; 546 547 auto inputJSON = R"({ 548 "key1": "value1", 549 "key2": 42, 550 "key3" : [1, 2, 3, 4, 5], 551 "key4": {"key5": "value5"} 552 })"_json; 553 554 // Convert the JSON to CBOR and write it to a file 555 auto data = nlohmann::json::to_cbor(inputJSON); 556 ffdc.fd = writeFileAndGetFD(dir, data); 557 558 return ffdc; 559 } 560 561 TEST_F(PELTest, MakeCBORFileUDSectionTest) 562 { 563 auto dir = makeTempDir(); 564 565 auto ffdc = getCBORFFDC(dir); 566 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 567 close(ffdc.fd); 568 ASSERT_TRUE(ud); 569 ASSERT_TRUE(ud->valid()); 570 EXPECT_EQ(ud->header().id, 0x5544); 571 572 EXPECT_EQ(ud->header().version, 573 static_cast<uint8_t>(UserDataFormatVersion::cbor)); 574 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::cbor)); 575 EXPECT_EQ(ud->header().componentID, 576 static_cast<uint16_t>(ComponentID::phosphorLogging)); 577 578 // Pull the CBOR back out of the PEL section 579 // The number of pad bytes to make the section be 4B aligned 580 // was added at the end, read it and then remove it and the 581 // padding before parsing it. 582 auto data = ud->data(); 583 Stream stream{data}; 584 stream.offset(data.size() - 4); 585 uint32_t pad; 586 stream >> pad; 587 588 data.resize(data.size() - 4 - pad); 589 590 auto json = nlohmann::json::from_cbor(data); 591 592 EXPECT_EQ("value1", json["key1"].get<std::string>()); 593 EXPECT_EQ(42, json["key2"].get<int>()); 594 595 std::vector<int> key3Values{1, 2, 3, 4, 5}; 596 EXPECT_EQ(key3Values, json["key3"].get<std::vector<int>>()); 597 598 std::map<std::string, std::string> key4Values{{"key5", "value5"}}; 599 auto actual = json["key4"].get<std::map<std::string, std::string>>(); 600 EXPECT_EQ(key4Values, actual); 601 602 fs::remove_all(dir); 603 } 604 605 PelFFDCfile getTextFFDC(const fs::path& dir) 606 { 607 PelFFDCfile ffdc; 608 ffdc.format = UserDataFormat::text; 609 ffdc.subType = 5; 610 ffdc.version = 42; 611 612 std::string text{"this is some text that will be used for FFDC"}; 613 std::vector<uint8_t> data{text.begin(), text.end()}; 614 615 ffdc.fd = writeFileAndGetFD(dir, data); 616 617 return ffdc; 618 } 619 620 TEST_F(PELTest, MakeTextFileUDSectionTest) 621 { 622 auto dir = makeTempDir(); 623 624 auto ffdc = getTextFFDC(dir); 625 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 626 close(ffdc.fd); 627 ASSERT_TRUE(ud); 628 ASSERT_TRUE(ud->valid()); 629 EXPECT_EQ(ud->header().id, 0x5544); 630 631 EXPECT_EQ(ud->header().version, 632 static_cast<uint8_t>(UserDataFormatVersion::text)); 633 EXPECT_EQ(ud->header().subType, static_cast<uint8_t>(UserDataFormat::text)); 634 EXPECT_EQ(ud->header().componentID, 635 static_cast<uint16_t>(ComponentID::phosphorLogging)); 636 637 // Get the text back out 638 std::string text{ud->data().begin(), ud->data().end()}; 639 EXPECT_EQ(text, "this is some text that will be used for FFDC"); 640 641 fs::remove_all(dir); 642 } 643 644 PelFFDCfile getCustomFFDC(const fs::path& dir, const std::vector<uint8_t>& data) 645 { 646 PelFFDCfile ffdc; 647 ffdc.format = UserDataFormat::custom; 648 ffdc.subType = 5; 649 ffdc.version = 42; 650 651 ffdc.fd = writeFileAndGetFD(dir, data); 652 653 return ffdc; 654 } 655 656 TEST_F(PELTest, MakeCustomFileUDSectionTest) 657 { 658 auto dir = makeTempDir(); 659 660 { 661 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8}; 662 663 auto ffdc = getCustomFFDC(dir, data); 664 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 665 close(ffdc.fd); 666 ASSERT_TRUE(ud); 667 ASSERT_TRUE(ud->valid()); 668 EXPECT_EQ(ud->header().size, 8 + 8); // data size + header size 669 EXPECT_EQ(ud->header().id, 0x5544); 670 671 EXPECT_EQ(ud->header().version, 42); 672 EXPECT_EQ(ud->header().subType, 5); 673 EXPECT_EQ(ud->header().componentID, 0x2002); 674 675 // Get the data back out 676 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()}; 677 EXPECT_EQ(data, newData); 678 } 679 680 // Do the same thing again, but make it be non 4B aligned 681 // so the data gets padded. 682 { 683 std::vector<uint8_t> data{1, 2, 3, 4, 5, 6, 7, 8, 9}; 684 685 auto ffdc = getCustomFFDC(dir, data); 686 auto ud = util::makeFFDCuserDataSection(0x2002, ffdc); 687 close(ffdc.fd); 688 ASSERT_TRUE(ud); 689 ASSERT_TRUE(ud->valid()); 690 EXPECT_EQ(ud->header().size, 12 + 8); // data size + header size 691 EXPECT_EQ(ud->header().id, 0x5544); 692 693 EXPECT_EQ(ud->header().version, 42); 694 EXPECT_EQ(ud->header().subType, 5); 695 EXPECT_EQ(ud->header().componentID, 0x2002); 696 697 // Get the data back out 698 std::vector<uint8_t> newData{ud->data().begin(), ud->data().end()}; 699 700 // pad the original to 12B so we can compare 701 data.push_back(0); 702 data.push_back(0); 703 data.push_back(0); 704 705 EXPECT_EQ(data, newData); 706 } 707 708 fs::remove_all(dir); 709 } 710 711 // Test Adding FFDC from files to a PEL 712 TEST_F(PELTest, CreateWithFFDCTest) 713 { 714 auto dir = makeTempDir(); 715 message::Entry regEntry; 716 uint64_t timestamp = 5; 717 718 regEntry.name = "test"; 719 regEntry.subsystem = 5; 720 regEntry.actionFlags = 0xC000; 721 regEntry.src.type = 0xBD; 722 regEntry.src.reasonCode = 0x1234; 723 724 std::vector<std::string> additionalData{"KEY1=VALUE1"}; 725 AdditionalData ad{additionalData}; 726 NiceMock<MockDataInterface> dataIface; 727 PelFFDC ffdc; 728 729 std::vector<uint8_t> customData{1, 2, 3, 4, 5, 6, 7, 8}; 730 731 // This will be trimmed when added 732 std::vector<uint8_t> hugeCustomData(17000, 0x42); 733 734 ffdc.emplace_back(std::move(getJSONFFDC(dir))); 735 ffdc.emplace_back(std::move(getCBORFFDC(dir))); 736 ffdc.emplace_back(std::move(getTextFFDC(dir))); 737 ffdc.emplace_back(std::move(getCustomFFDC(dir, customData))); 738 ffdc.emplace_back(std::move(getCustomFFDC(dir, hugeCustomData))); 739 740 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error, 741 ad, ffdc, dataIface}; 742 743 EXPECT_TRUE(pel.valid()); 744 745 // Clipped to the max 746 EXPECT_EQ(pel.size(), 16384); 747 748 // Check for the FFDC sections 749 size_t udCount = 0; 750 Section* ud = nullptr; 751 752 for (const auto& section : pel.optionalSections()) 753 { 754 if (section->header().id == static_cast<uint16_t>(SectionID::userData)) 755 { 756 udCount++; 757 ud = section.get(); 758 } 759 } 760 761 EXPECT_EQ(udCount, 7); // AD section, sysInfo, 5 ffdc sections 762 763 // Check the last section was trimmed to 764 // something a bit less that 17000. 765 EXPECT_GT(ud->header().size, 14000); 766 EXPECT_LT(ud->header().size, 16000); 767 768 fs::remove_all(dir); 769 } 770