blob: c6a29b65c8f7dac5048f4b65b0ad1059b325efe4 [file] [log] [blame]
Matt Spinler97f7abc2019-11-06 09:40:23 -06001/**
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 */
Matt Spinlerb8323632019-09-20 15:11:04 -050016#include "elog_entry.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050017#include "extensions/openpower-pels/generic.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050018#include "extensions/openpower-pels/pel.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050019#include "mocks.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050020#include "pel_utils.hpp"
21
22#include <filesystem>
23#include <fstream>
24
25#include <gtest/gtest.h>
26
27namespace fs = std::filesystem;
28using namespace openpower::pels;
Matt Spinler56ad2a02020-03-26 14:00:52 -050029using ::testing::NiceMock;
Matt Spinler677381b2020-01-23 10:04:29 -060030using ::testing::Return;
Matt Spinlercb6b0592019-07-16 15:58:51 -050031
32class PELTest : public CleanLogID
33{
34};
35
Matt Spinler5b289b22020-03-26 14:27:19 -050036fs::path makeTempDir()
37{
38 char path[] = "/tmp/tempdirXXXXXX";
39 std::filesystem::path dir = mkdtemp(path);
40 return dir;
41}
42
43int 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
Matt Spinlercb6b0592019-07-16 15:58:51 -050057TEST_F(PELTest, FlattenTest)
58{
Matt Spinler42828bd2019-10-11 10:39:30 -050059 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler42828bd2019-10-11 10:39:30 -050060 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050061
62 // Check a few fields
63 EXPECT_TRUE(pel->valid());
64 EXPECT_EQ(pel->id(), 0x80818283);
65 EXPECT_EQ(pel->plid(), 0x50515253);
Matt Spinler97d19b42019-10-29 11:34:03 -050066 EXPECT_EQ(pel->userHeader().subsystem(), 0x10);
67 EXPECT_EQ(pel->userHeader().actionFlags(), 0x80C0);
Matt Spinlercb6b0592019-07-16 15:58:51 -050068
69 // Test that data in == data out
70 auto flattenedData = pel->data();
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060071 EXPECT_EQ(data, flattenedData);
72 EXPECT_EQ(flattenedData.size(), pel->size());
Matt Spinlercb6b0592019-07-16 15:58:51 -050073}
74
75TEST_F(PELTest, CommitTimeTest)
76{
Matt Spinler42828bd2019-10-11 10:39:30 -050077 auto data = pelDataFactory(TestPELType::pelSimple);
78 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050079
80 auto origTime = pel->commitTime();
81 pel->setCommitTime();
82 auto newTime = pel->commitTime();
83
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060084 EXPECT_NE(origTime, newTime);
Matt Spinlercb6b0592019-07-16 15:58:51 -050085
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);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -060089 EXPECT_EQ(newTime, newPel->commitTime());
Matt Spinlercb6b0592019-07-16 15:58:51 -050090}
91
92TEST_F(PELTest, AssignIDTest)
93{
Matt Spinler42828bd2019-10-11 10:39:30 -050094 auto data = pelDataFactory(TestPELType::pelSimple);
95 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -050096
97 auto origID = pel->id();
98 pel->assignID();
99 auto newID = pel->id();
100
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600101 EXPECT_NE(origID, newID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500102
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);
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600106 EXPECT_EQ(newID, newPel->id());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500107}
108
109TEST_F(PELTest, WithLogIDTest)
110{
Matt Spinler42828bd2019-10-11 10:39:30 -0500111 auto data = pelDataFactory(TestPELType::pelSimple);
112 auto pel = std::make_unique<PEL>(data, 0x42);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500113
114 EXPECT_TRUE(pel->valid());
115 EXPECT_EQ(pel->obmcLogID(), 0x42);
116}
117
118TEST_F(PELTest, InvalidPELTest)
119{
Matt Spinler42828bd2019-10-11 10:39:30 -0500120 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500121
122 // Too small
Matt Spinler42828bd2019-10-11 10:39:30 -0500123 data.resize(PrivateHeader::flattenedSize());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500124
Matt Spinler42828bd2019-10-11 10:39:30 -0500125 auto pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500126
Matt Spinler97d19b42019-10-29 11:34:03 -0500127 EXPECT_TRUE(pel->privateHeader().valid());
128 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500129 EXPECT_FALSE(pel->valid());
130
Matt Spinlercb6b0592019-07-16 15:58:51 -0500131 // Now corrupt the private header
Matt Spinler42828bd2019-10-11 10:39:30 -0500132 data = pelDataFactory(TestPELType::pelSimple);
133 data.at(0) = 0;
134 pel = std::make_unique<PEL>(data);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500135
Matt Spinler97d19b42019-10-29 11:34:03 -0500136 EXPECT_FALSE(pel->privateHeader().valid());
137 EXPECT_TRUE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500138 EXPECT_FALSE(pel->valid());
139}
140
141TEST_F(PELTest, EmptyDataTest)
142{
143 std::vector<uint8_t> data;
144 auto pel = std::make_unique<PEL>(data);
145
Matt Spinler97d19b42019-10-29 11:34:03 -0500146 EXPECT_FALSE(pel->privateHeader().valid());
147 EXPECT_FALSE(pel->userHeader().valid());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500148 EXPECT_FALSE(pel->valid());
149}
Matt Spinlerb8323632019-09-20 15:11:04 -0500150
151TEST_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;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500159 regEntry.src.type = 0xBD;
160 regEntry.src.reasonCode = 0x1234;
Matt Spinlerb8323632019-09-20 15:11:04 -0500161
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600162 std::vector<std::string> data{"KEY1=VALUE1"};
163 AdditionalData ad{data};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500164 NiceMock<MockDataInterface> dataIface;
165 PelFFDC ffdc;
Matt Spinlerbd716f02019-10-15 10:54:11 -0500166
Matt Spinler56ad2a02020-03-26 14:00:52 -0500167 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
168 ad, ffdc, dataIface};
Matt Spinlerb8323632019-09-20 15:11:04 -0500169
170 EXPECT_TRUE(pel.valid());
Matt Spinler97d19b42019-10-29 11:34:03 -0500171 EXPECT_EQ(pel.privateHeader().obmcLogID(), 42);
172 EXPECT_EQ(pel.userHeader().severity(), 0x40);
Matt Spinlerb8323632019-09-20 15:11:04 -0500173
Matt Spinlerbd716f02019-10-15 10:54:11 -0500174 EXPECT_EQ(pel.primarySRC().value()->asciiString(),
175 "BD051234 ");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600176
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
Matt Spinlerb8323632019-09-20 15:11:04 -0500204}
Matt Spinler131870c2019-09-25 13:29:04 -0500205
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500206// 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.
209TEST_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;
Matt Spinler56ad2a02020-03-26 14:00:52 -0500219 PelFFDC ffdc;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500220
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};
Matt Spinler56ad2a02020-03-26 14:00:52 -0500227 NiceMock<MockDataInterface> dataIface;
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500228
Matt Spinler56ad2a02020-03-26 14:00:52 -0500229 PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error,
230 ad, ffdc, dataIface};
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500231
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
Matt Spinler131870c2019-09-25 13:29:04 -0500248// Test that we'll create Generic optional sections for sections that
249// there aren't explicit classes for.
250TEST_F(PELTest, GenericSectionTest)
251{
Matt Spinler42828bd2019-10-11 10:39:30 -0500252 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500253
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
Matt Spinler42828bd2019-10-11 10:39:30 -0500275 data.insert(data.end(), section1.begin(), section1.end());
276 data.insert(data.end(), section2.begin(), section2.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500277
278 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500279 data.at(27) += 2;
280 auto origData = data;
Matt Spinler131870c2019-09-25 13:29:04 -0500281
Matt Spinler42828bd2019-10-11 10:39:30 -0500282 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500283
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);
Matt Spinler07eefc52019-09-26 11:18:26 -0500306
307 // Now flatten and check
308 auto newData = pel.data();
309
310 EXPECT_EQ(origData, newData);
Matt Spinler131870c2019-09-25 13:29:04 -0500311}
312
313// Test that an invalid section will still get a Generic object
314TEST_F(PELTest, InvalidGenericTest)
315{
Matt Spinler42828bd2019-10-11 10:39:30 -0500316 auto data = pelDataFactory(TestPELType::pelSimple);
Matt Spinler131870c2019-09-25 13:29:04 -0500317
318 // Not a valid section
319 std::vector<uint8_t> section1{0x01, 0x02, 0x03};
320
Matt Spinler42828bd2019-10-11 10:39:30 -0500321 data.insert(data.end(), section1.begin(), section1.end());
Matt Spinler131870c2019-09-25 13:29:04 -0500322
323 // Increment the section count
Matt Spinler42828bd2019-10-11 10:39:30 -0500324 data.at(27) += 1;
Matt Spinler131870c2019-09-25 13:29:04 -0500325
Matt Spinler42828bd2019-10-11 10:39:30 -0500326 PEL pel{data};
Matt Spinler131870c2019-09-25 13:29:04 -0500327 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}
Matt Spinlerafa857c2019-10-24 13:03:46 -0500344
345// Create a UserData section out of AdditionalData
346TEST_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()};
Matt Spinler53407be2019-11-18 09:16:31 -0600363
364 std::string expectedJSON =
Matt Spinlerafa857c2019-10-24 13:03:46 -0500365 R"({"KEY1":"VALUE1","KEY2":"VALUE2","KEY3":"VALUE3"})";
Matt Spinler53407be2019-11-18 09:16:31 -0600366
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);
Matt Spinlerafa857c2019-10-24 13:03:46 -0500373
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");
Matt Spinler97d19b42019-10-29 11:34:03 -0500379}
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600380
381// Create the UserData section that contains system info
Matt Spinler677381b2020-01-23 10:04:29 -0600382TEST_F(PELTest, SysInfoSectionTest)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600383{
384 MockDataInterface dataIface;
385
Matt Spinler677381b2020-01-23 10:04:29 -0600386 EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
Matt Spinler4aa23a12020-02-03 15:05:09 -0600387 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"));
Matt Spinler677381b2020-01-23 10:04:29 -0600390
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600391 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);
Matt Spinler677381b2020-01-23 10:04:29 -0600411
412 auto version = json["BMC Version ID"].get<std::string>();
413 EXPECT_EQ(version, "ABCD1234");
Matt Spinler4aa23a12020-02-03 15:05:09 -0600414
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");
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600423}
Matt Spinlerce3f4502020-01-22 15:44:35 -0600424
425// Test that the sections that override
426// virtual std::optional<std::string> Section::getJSON() const
427// return valid JSON.
428TEST_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}
Matt Spinler5b289b22020-03-26 14:27:19 -0500465
466PelFFDCfile 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
488TEST_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
540PelFFDCfile 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
561TEST_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
605PelFFDCfile 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
620TEST_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
644PelFFDCfile 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
656TEST_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
712TEST_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}