Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions edk2toolext/perf/fpdt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
)
from io import TextIOWrapper
from typing import BinaryIO
from xml.dom import minidom

FPDT_PARSER_VER = "3.00"

Expand Down Expand Up @@ -576,7 +577,7 @@ def to_xml(self) -> ET.Element:
guid_xml = ET.SubElement(xml_repr, "GUID")
guid_xml.set(
"Value",
"%08X-%04X-%04X-%02X%02X-%02X%02X-%02X%02X-%02X%02X"
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
% (
self.guid_uint32,
self.guid_uint16_0,
Expand Down Expand Up @@ -705,7 +706,7 @@ def to_xml(self) -> ET.Element:
guid_xml = ET.SubElement(xml_repr, "GUID")
guid_xml.set(
"Value",
"%08X-%04X-%04X-%02X%02X-%02X%02X-%02X%02X-%02X%02X"
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
% (
self.guid_uint32,
self.guid_uint16_0,
Expand Down Expand Up @@ -873,15 +874,15 @@ def to_xml(self) -> ET.Element:
guid1_xml.set(
"Value",
f"{self.guid1_uint32:08X}-{self.guid1_uint16_0:04X}-{self.guid1_uint16_1:04X}-"
f"{self.guid1_uint8_0:02X}{self.guid1_uint8_1:02X}{self.guid1_uint8_2:02X}{self.guid1_uint8_3:02X}"
f"{self.guid1_uint8_0:02X}{self.guid1_uint8_1:02X}-{self.guid1_uint8_2:02X}{self.guid1_uint8_3:02X}"
f"{self.guid1_uint8_4:02X}{self.guid1_uint8_5:02X}{self.guid1_uint8_6:02X}{self.guid1_uint8_7:02X}",
)

guid2_xml = ET.SubElement(xml_repr, "GUID2")
guid2_xml.set(
"Value",
f"{self.guid2_uint32:08X}-{self.guid2_uint16_0:04X}-{self.guid2_uint16_1:04X}-"
f"{self.guid2_uint8_0:02X}{self.guid2_uint8_1:02X}{self.guid2_uint8_2:02X}{self.guid2_uint8_3:02X}"
f"{self.guid2_uint8_0:02X}{self.guid2_uint8_1:02X}-{self.guid2_uint8_2:02X}{self.guid2_uint8_3:02X}"
f"{self.guid2_uint8_4:02X}{self.guid2_uint8_5:02X}{self.guid2_uint8_6:02X}{self.guid2_uint8_7:02X}",
)

Expand Down Expand Up @@ -1473,6 +1474,7 @@ def __init__(self) -> None:
self.text_log = self.handle_output_file()
self.handle_input_file()
self.uefi_version, self.model = self.get_uefi_version_model()
self.fbpt_tree = None # Initialize FBPT container element

self.write_text_header()
self.xml_tree = self.write_xml_header()
Expand Down Expand Up @@ -1612,9 +1614,9 @@ def write_fbpt(self, fbpt_file: BinaryIO) -> None:
if self.options.output_text_file:
self.text_log.write(str(fbpt_header))
if self.options.output_xml_file:
# Store FBPT header and records under a separate element under FPDT
fbpt_tree = fbpt_header.to_xml()
self.xml_tree.append(fbpt_tree)
# Store FBPT header as a container element that will hold all records
self.fbpt_tree = fbpt_header.to_xml()
# Don't append to xml_tree yet - we'll do that after adding all records

def gather_fbpt_records(self, fbpt_file: BinaryIO) -> list:
"""Collects FBPT records from an input file."""
Expand All @@ -1638,11 +1640,20 @@ def gather_fbpt_records(self, fbpt_file: BinaryIO) -> list:
def write_records(self, fbpt_records_list: list) -> int:
"""Writes FBPT records to an output file."""
if self.options.output_xml_file:
# Add all records to the FBPT container element
for record in fbpt_records_list:
self.xml_tree.append(record.to_xml())
self.fbpt_tree.append(record.to_xml())

with open(self.options.output_xml_file, "wb") as xml_file:
xml_file.write(ET.tostring(self.xml_tree))
# Now append the complete FBPT container to the main XML tree
self.xml_tree.append(self.fbpt_tree)

# Format XML properly with indentation
rough_string = ET.tostring(self.xml_tree, encoding="unicode")
reparsed = minidom.parseString(rough_string)
formatted_xml = reparsed.toprettyxml(indent=" ")

with open(self.options.output_xml_file, "w", encoding="utf-8") as xml_file:
xml_file.write(formatted_xml)

if self.options.output_text_file:
for record in fbpt_records_list:
Expand Down
13 changes: 8 additions & 5 deletions tests.unit/test_fpdt_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def test_to_xml(self, mock_header: MockFbptRecordHeader, sample_data: bytes) ->
assert xml_elem.find("ApicID").get("Value") == "0x56789ABC"
assert xml_elem.find("Timestamp").get("RawValue") == "0xABCDEF0123456789"
assert xml_elem.find("Timestamp").get("ValueInMilliseconds") == "12379813738877.119141"
assert xml_elem.find("GUID").get("Value") == "DEADBEEF-BEEF-FEED-DEAD-BEEF-0123-4567"
assert xml_elem.find("GUID").get("Value") == "DEADBEEF-BEEF-FEED-DEAD-BEEF01234567"


class TestDynamicStringEventRecord:
Expand Down Expand Up @@ -614,8 +614,8 @@ def test_to_xml(self, mock_header: MockFbptRecordHeader) -> None:
assert xml.find("ApicID").attrib["Value"] == "0x2"
assert xml.find("Timestamp").attrib["RawValue"] == "0x3"
assert xml.find("Timestamp").attrib["ValueInMilliseconds"] == "0.000003"
assert xml.find("GUID1").attrib["Value"] == "00000004-0005-0006-0708090A0B0C0D0E"
assert xml.find("GUID2").attrib["Value"] == "0000000F-0010-0011-1213141516171819"
assert xml.find("GUID1").attrib["Value"] == "00000004-0005-0006-0708-090A0B0C0D0E"
assert xml.find("GUID2").attrib["Value"] == "0000000F-0010-0011-1213-141516171819"
assert xml.find("String").attrib["Value"] == "BootMsg"


Expand Down Expand Up @@ -1115,6 +1115,7 @@ def mock_parser_app(

app.text_log = mock.Mock(spec=["write", "close"])
app.xml_tree = mock.Mock(spec=["append"])
app.fbpt_tree = mock.Mock(spec=["append"])

return app

Expand Down Expand Up @@ -1268,7 +1269,7 @@ def test_write_fbpt(self) -> None:
with mock.patch.object(FwBasicBootPerformanceTableHeader, "__new__", return_value=mock_header):
mock_app.write_fbpt(mock_fbpt_file)
mock_app.text_log.write.assert_called_once_with("Fake FBPT Header String")
mock_app.xml_tree.append.assert_called_once_with("<fbpt_header_xml>fake_xml</fbpt_header_xml>")
assert mock_app.fbpt_tree == "<fbpt_header_xml>fake_xml</fbpt_header_xml>"

def test_gather_fbpt_records_parse_failure(self) -> None:
"""Tests error handling when a FBPT record has an invalid format."""
Expand Down Expand Up @@ -1297,10 +1298,12 @@ def test_write_records(self) -> None:
with (
mock.patch("builtins.open", mock.mock_open()),
mock.patch("xml.etree.ElementTree.tostring"),
mock.patch("xml.dom.minidom.parseString"),
):
result = mock_app.write_records(fbpt_records_list)
assert result == 1
mock_app.xml_tree.append.assert_any_call("<Record1></Record1>")
mock_app.fbpt_tree.append.assert_any_call("<Record1></Record1>")
mock_app.xml_tree.append.assert_any_call(mock_app.fbpt_tree)
mock_app.text_log.write.assert_any_call("Record1")


Expand Down