| """Test vectors from GDB remote target description XML.""" |
| |
| from textwrap import dedent |
| |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.gdbclientutils import * |
| from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase |
| from lldbsuite.test.lldbtest import * |
| |
| |
| class TestXMLRegisterVector(GDBRemoteTestBase): |
| def setup_multidoc_test(self, docs, register_data): |
| self.server.responder = MockGDBServerXMLResponder(docs, register_data) |
| target = self.dbg.CreateTarget("") |
| |
| if self.TraceOn(): |
| self.runCmd("log enable gdb-remote packets process") |
| self.addTearDownHook( |
| lambda: self.runCmd("log disable gdb-remote packets process") |
| ) |
| |
| process = self.connect(target) |
| lldbutil.expect_state_changes( |
| self, self.dbg.GetListener(), process, [lldb.eStateStopped] |
| ) |
| return process |
| |
| def setup_register_test(self, definitions, register_data, architecture="aarch64"): |
| return self.setup_multidoc_test( |
| { |
| "target.xml": dedent( |
| """\ |
| <?xml version="1.0"?> |
| <target version="1.0"> |
| <architecture>{}</architecture> |
| <feature name="test.register.vectors"> |
| {} |
| </feature> |
| </target>""" |
| ).format(architecture, definitions) |
| }, |
| register_data, |
| ) |
| |
| def assert_float_children(self, value, expected): |
| self.assertTrue(value.IsValid()) |
| self.assertTrue(value.MightHaveChildren()) |
| self.assertEqual(value.GetNumChildren(), len(expected)) |
| for index, expected_value in enumerate(expected): |
| child = value.GetChildAtIndex(index) |
| self.assertTrue(child.IsValid()) |
| self.assertEqual(child.GetName(), "[{}]".format(index)) |
| self.assertEqual(child.GetTypeName(), "float") |
| self.assertAlmostEqual(child.GetData().float[0], expected_value) |
| self.assertAlmostEqual(float(child.GetValue()), expected_value) |
| |
| def assert_vector_info(self, name, byte_size, count): |
| self.expect( |
| "register info {}".format(name), |
| substrs=[ |
| "Size: {} bytes".format(byte_size), |
| "Vector elements: {}".format(count), |
| ], |
| ) |
| |
| def assert_no_vector_info(self, name): |
| self.expect("register info {}".format(name), substrs=["Name: {}".format(name)]) |
| self.expect( |
| "register info {}".format(name), |
| matching=False, |
| substrs=["Vector elements:"], |
| ) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_direct_and_nested_vector_metadata(self): |
| self.setup_register_test( |
| """\ |
| <vector id="v2f" type="ieee_single" count="2"/> |
| <vector id="v3v2f" type="v2f" count="3"/> |
| <reg name="direct" regnum="0" bitsize="64" type="v2f"/> |
| <reg name="nested" regnum="1" bitsize="192" type="v3v2f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "00" * 40, |
| ) |
| |
| self.assert_vector_info("direct", 8, 2) |
| self.assert_vector_info("nested", 24, 3) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_invalid_vectors_are_ignored(self): |
| self.setup_register_test( |
| """\ |
| <vector id="bad_count" type="ieee_single" count="nope"/> |
| <vector id="zero" type="ieee_single" count="0"/> |
| <vector id="too_many" type="ieee_single" count="65537"/> |
| <vector id="unknown" type="missing" count="2"/> |
| <vector id="forward" type="later" count="1"/> |
| <vector id="missing_type" count="2"/> |
| <vector id="missing_count" type="ieee_single"/> |
| <vector id="too_large" type="uint128" count="4097"/> |
| <vector type="ieee_single" count="2"/> |
| <vector id="later" type="ieee_single" count="2"/> |
| <enum id="enumeration" size="4"> |
| <evalue name="zero" value="0"/> |
| </enum> |
| <flags id="flags" size="4"> |
| <field name="bit" start="0" end="0"/> |
| </flags> |
| <vector id="enum_vector" type="enumeration" count="2"/> |
| <vector id="flags_vector" type="flags" count="2"/> |
| <vector id="bad_pointer_size" type="data_ptr" count="3"/> |
| <vector id="pointer_pair" type="data_ptr" count="2"/> |
| <vector id="bad_nested_pointer_size" type="pointer_pair" count="3"/> |
| <vector id="v4f" type="ieee_single" count="4"/> |
| <reg name="bad_count" regnum="0" bitsize="64" type="bad_count"/> |
| <reg name="zero" regnum="1" bitsize="64" type="zero"/> |
| <reg name="too_many" regnum="2" bitsize="64" type="too_many"/> |
| <reg name="unknown" regnum="3" bitsize="64" type="unknown"/> |
| <reg name="forward" regnum="4" bitsize="64" type="forward"/> |
| <reg name="missing_type" regnum="5" bitsize="64" |
| type="missing_type"/> |
| <reg name="missing_count" regnum="6" bitsize="64" |
| type="missing_count"/> |
| <reg name="too_large" regnum="7" bitsize="64" type="too_large"/> |
| <reg name="later" regnum="8" bitsize="64" type="later"/> |
| <reg name="enum_vector" regnum="9" bitsize="64" |
| type="enum_vector"/> |
| <reg name="flags_vector" regnum="10" bitsize="64" |
| type="flags_vector"/> |
| <reg name="bad_size" regnum="11" bitsize="64" type="v4f"/> |
| <reg name="bad_pointer_size" regnum="12" bitsize="80" |
| type="bad_pointer_size"/> |
| <reg name="bad_nested_pointer_size" regnum="13" bitsize="120" |
| type="bad_nested_pointer_size"/> |
| <reg name="pc" bitsize="64"/>""", |
| "00" * 129, |
| ) |
| |
| for name in [ |
| "bad_count", |
| "zero", |
| "too_many", |
| "unknown", |
| "forward", |
| "missing_type", |
| "missing_count", |
| "too_large", |
| "enum_vector", |
| "flags_vector", |
| "bad_size", |
| "bad_pointer_size", |
| "bad_nested_pointer_size", |
| ]: |
| self.assert_no_vector_info(name) |
| |
| self.assert_vector_info("later", 8, 2) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_duplicate_vector_id_uses_first_definition(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="shared" type="ieee_single" count="2"/> |
| <vector id="shared" type="uint16" count="4"/> |
| <reg name="v0" regnum="0" bitsize="64" type="shared"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f00002040" + "00" * 8, |
| ) |
| |
| self.assert_vector_info("v0", 8, 2) |
| self.expect("register info v0", matching=False, substrs=["Vector elements: 4"]) |
| vector = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0") |
| self.assert_float_children(vector, [1.5, 2.5]) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_target_type_precedes_builtin_type(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="ieee_single" type="uint8" count="2"/> |
| <vector id="nested" type="ieee_single" count="3"/> |
| <reg name="v0" regnum="0" bitsize="48" type="nested"/> |
| <reg name="pc" bitsize="64"/>""", |
| "010203040506" + "00" * 8, |
| ) |
| |
| self.assert_vector_info("v0", 6, 3) |
| vector = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0") |
| self.assertEqual(vector.GetNumChildren(), 3) |
| self.assertEqual( |
| [ |
| vector.GetChildAtIndex(outer) |
| .GetChildAtIndex(inner) |
| .GetValueAsUnsigned() |
| for outer in range(3) |
| for inner in range(2) |
| ], |
| [1, 2, 3, 4, 5, 6], |
| ) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_vector_ids_are_scoped_to_included_feature(self): |
| first_register = "0000c03f" "00002040" |
| second_register = "0000c03f" "00002040" "00006040" "00009040" |
| process = self.setup_multidoc_test( |
| { |
| "target.xml": dedent( |
| """\ |
| <?xml version="1.0"?> |
| <target version="1.0"> |
| <architecture>aarch64</architecture> |
| <xi:include href="first.xml"/> |
| <xi:include href="second.xml"/> |
| <xi:include href="third.xml"/> |
| </target>""" |
| ), |
| "first.xml": dedent( |
| """\ |
| <?xml version="1.0"?> |
| <feature name="feature.first"> |
| <vector id="shared" type="ieee_single" count="2"/> |
| <reg name="first" regnum="0" bitsize="64" type="shared"/> |
| </feature>""" |
| ), |
| "second.xml": dedent( |
| """\ |
| <?xml version="1.0"?> |
| <feature name="feature.second"> |
| <vector id="shared" type="ieee_single" count="4"/> |
| <reg name="second" regnum="1" bitsize="128" type="shared"/> |
| </feature>""" |
| ), |
| "third.xml": dedent( |
| """\ |
| <?xml version="1.0"?> |
| <feature name="feature.third"> |
| <reg name="unresolved" regnum="2" bitsize="64" type="shared"/> |
| <reg name="pc" bitsize="64"/> |
| </feature>""" |
| ), |
| }, |
| first_register + second_register + "00" * 16, |
| ) |
| |
| self.assert_vector_info("first", 8, 2) |
| self.assert_vector_info("second", 16, 4) |
| self.assert_no_vector_info("unresolved") |
| |
| frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) |
| self.assert_float_children(frame.FindRegister("first"), [1.5, 2.5]) |
| self.assert_float_children(frame.FindRegister("second"), [1.5, 2.5, 3.5, 4.5]) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_direct_vector_sb_api(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="v4f" type="ieee_single" count="4"/> |
| <reg name="v0" regnum="0" bitsize="128" type="v4f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f000020400000604000009040" + "00" * 8, |
| ) |
| |
| frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) |
| vector = frame.FindRegister("v0") |
| self.assertEqual(vector.GetName(), "v0") |
| self.assertEqual(vector.GetByteSize(), 16) |
| self.assertTrue(vector.GetType().IsValid()) |
| self.assertEqual(vector.GetType().GetByteSize(), 16) |
| self.assertIn("float", vector.GetTypeName()) |
| self.assert_float_children(vector, [1.5, 2.5, 3.5, 4.5]) |
| |
| lane = vector.GetValueForExpressionPath("[2]") |
| self.assertTrue(lane.IsValid()) |
| self.assertAlmostEqual(lane.GetData().float[0], 3.5) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_byte_vector_sb_api(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="bytes32" type="uint8" count="32"/> |
| <reg name="bytes" regnum="0" bitsize="256" type="bytes32"/> |
| <reg name="pc" bitsize="64"/>""", |
| "".join("{:02x}".format(value) for value in range(32)) + "00" * 8, |
| ) |
| |
| frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) |
| vector = frame.FindRegister("bytes") |
| self.assertEqual(vector.GetByteSize(), 32) |
| self.assertEqual(vector.GetNumChildren(), 32) |
| self.assertEqual(vector.GetData().uint8s, list(range(32))) |
| |
| lane = vector.GetValueForExpressionPath("[16]") |
| self.assertTrue(lane.IsValid()) |
| error = lldb.SBError() |
| self.assertEqual(lane.GetValueAsUnsigned(error, 0xDEADBEEF), 0x10) |
| self.assertTrue(error.Success()) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_non_power_of_two_vector_exact_layout(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="v3f" type="ieee_single" count="3"/> |
| <reg name="v0" regnum="0" bitsize="96" type="v3f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f0000204000006040" + "00" * 8, |
| ) |
| |
| vector = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0") |
| self.assertEqual(vector.GetByteSize(), 12) |
| self.assertEqual(vector.GetTypeName(), "float[3]") |
| self.assert_float_children(vector, [1.5, 2.5, 3.5]) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_nested_vector_sb_api(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="v2f" type="ieee_single" count="2"/> |
| <vector id="v2v2f" type="v2f" count="2"/> |
| <reg name="v0" regnum="0" bitsize="128" type="v2v2f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f000020400000604000009040" + "00" * 8, |
| ) |
| |
| vector = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0") |
| self.assertEqual(vector.GetNumChildren(), 2) |
| self.assert_float_children(vector.GetChildAtIndex(0), [1.5, 2.5]) |
| self.assert_float_children(vector.GetChildAtIndex(1), [3.5, 4.5]) |
| |
| lane = vector.GetValueForExpressionPath("[1][0]") |
| self.assertTrue(lane.IsValid()) |
| self.assertAlmostEqual(lane.GetData().float[0], 3.5) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_bool_and_single_uint128_vectors(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="v4b" type="bool" count="4"/> |
| <vector id="v1u128" type="uint128" count="1"/> |
| <reg name="bools" regnum="0" bitsize="32" type="v4b"/> |
| <reg name="wide" regnum="1" bitsize="128" type="v1u128"/> |
| <reg name="pc" bitsize="64"/>""", |
| "00010100" + "01" + "00" * 15 + "00" * 8, |
| ) |
| |
| frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) |
| bools = frame.FindRegister("bools") |
| self.assertEqual(bools.GetTypeName(), "bool[4]") |
| self.assertEqual(bools.GetNumChildren(), 4) |
| self.assertEqual( |
| [bools.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(4)], |
| [0, 1, 1, 0], |
| ) |
| |
| wide = frame.FindRegister("wide") |
| self.assertEqual(wide.GetNumChildren(), 1) |
| wide_child = wide.GetChildAtIndex(0) |
| expected_bytes = [1] + [0] * 15 |
| self.assertEqual(wide.GetData().uint8s, expected_bytes) |
| self.assertEqual(wide_child.GetData().uint8s, expected_bytes) |
| self.assertEqual(wide_child.GetValue(), "1") |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| @skipIfLLVMTargetMissing("SystemZ") |
| def test_big_endian_vector_sb_api(self): |
| process = self.setup_register_test( |
| """\ |
| <vector id="v2f" type="ieee_single" count="2"/> |
| <reg name="v0" regnum="0" bitsize="64" type="v2f"/> |
| <reg name="v1" regnum="1" bitsize="64" type="v2f" |
| encoding="uint"/> |
| <reg name="pswa" regnum="2" bitsize="64"/>""", |
| "3fc0000040200000" * 2 + "00" * 8, |
| architecture="s390x", |
| ) |
| |
| frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0) |
| ull = process.GetTarget().GetBasicType(lldb.eBasicTypeUnsignedLongLong) |
| for name in ["v0", "v1"]: |
| vector = frame.FindRegister(name) |
| self.assert_float_children(vector, [1.5, 2.5]) |
| self.assertEqual(vector.Cast(ull).GetValueAsUnsigned(), 0x3FC0000040200000) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_direct_vector_cli(self): |
| self.setup_register_test( |
| """\ |
| <vector id="v4f" type="ieee_single" count="4"/> |
| <reg name="v0" regnum="0" bitsize="128" type="v4f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f000020400000604000009040" + "00" * 8, |
| ) |
| |
| self.expect( |
| "register read v0", |
| substrs=[ |
| "v0 = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 " |
| "0x00 0x00 0x60 0x40 0x00 0x00 0x90 0x40}\n" |
| " = ([0] = 1.5, [1] = 2.5, [2] = 3.5, [3] = 4.5)" |
| ], |
| ) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| def test_nested_vector_cli(self): |
| self.setup_register_test( |
| """\ |
| <vector id="v2f" type="ieee_single" count="2"/> |
| <vector id="v2v2f" type="v2f" count="2"/> |
| <reg name="v0" regnum="0" bitsize="128" type="v2v2f"/> |
| <reg name="pc" bitsize="64"/>""", |
| "0000c03f000020400000604000009040" + "00" * 8, |
| ) |
| |
| self.expect( |
| "register read v0", |
| substrs=[ |
| "[0] = ([0] = 1.5, [1] = 2.5)", |
| "[1] = ([0] = 3.5, [1] = 4.5)", |
| ], |
| ) |
| |
| @skipIfXmlSupportMissing |
| @skipIfRemote |
| @skipIfLLVMTargetMissing("SystemZ") |
| def test_big_endian_vector_cli(self): |
| self.setup_register_test( |
| """\ |
| <vector id="v2f" type="ieee_single" count="2"/> |
| <reg name="v0" regnum="0" bitsize="64" type="v2f"/> |
| <reg name="v1" regnum="1" bitsize="64" type="v2f" |
| encoding="uint"/> |
| <reg name="pswa" regnum="2" bitsize="64"/>""", |
| "3fc0000040200000" * 2 + "00" * 8, |
| architecture="s390x", |
| ) |
| |
| self.expect( |
| "register read v0", |
| patterns=[ |
| r"v0 = \{0x3f 0xc0 0x00 0x00 0x40 0x20 0x00 0x00\}\r?\n" |
| r" = \(\[0\] = 1\.5, \[1\] = 2\.5\)" |
| ], |
| ) |
| self.expect("register read v1", substrs=["([0] = 1.5, [1] = 2.5)"]) |