Make offset field optional in RegisterInfo packet for Arm64

This patch carries forward our aim to remove offset field from qRegisterInfo
packets and XML register description. I have created a new function which
returns if offset fields are dynamic meaning client can calculate offset on
its own based on register number sequence and register size. For now this
function only returns true for NativeRegisterContextLinux_arm64 but we can
test this for other architectures and make it standard later.

As a consequence we do not send offset field from lldb-server (arm64 for now)
while other stubs dont have an offset field so it wont effect them for now.
On the client side we have replaced previous offset calculation algorithm
with a new scheme, where we sort all primary registers in increasing
order of remote regnum and then calculate offset incrementally.

This committ also includes a test to verify all of above functionality
on Arm64.

Reviewed By: labath

Differential Revision: https://reviews.llvm.org/D91241
This commit is contained in:
Muhammad Omair Javaid 2020-12-02 03:09:14 +05:00
parent 26b8ea2e37
commit 78cb4562fa
11 changed files with 240 additions and 34 deletions

View File

@ -121,6 +121,8 @@ public:
virtual std::vector<uint32_t>
GetExpeditedRegisters(ExpeditedRegs expType) const;
virtual bool RegisterOffsetIsDynamic() const { return false; }
const RegisterInfo *GetRegisterInfoByName(llvm::StringRef reg_name,
uint32_t start_idx = 0);

View File

@ -664,7 +664,10 @@ class GdbRemoteTestCaseBase(TestBase):
# Check the bare-minimum expected set of register info keys.
self.assertTrue("name" in reg_info)
self.assertTrue("bitsize" in reg_info)
self.assertTrue("offset" in reg_info)
if not self.getArchitecture() == 'aarch64':
self.assertTrue("offset" in reg_info)
self.assertTrue("encoding" in reg_info)
self.assertTrue("format" in reg_info)

View File

@ -47,6 +47,8 @@ public:
std::vector<uint32_t>
GetExpeditedRegisters(ExpeditedRegs expType) const override;
bool RegisterOffsetIsDynamic() const override { return true; }
// Hardware breakpoints/watchpoint management functions
uint32_t NumSupportedHardwareBreakpoints() override;

View File

@ -428,9 +428,6 @@ void DynamicRegisterInfo::AddRegister(RegisterInfo &reg_info,
assert(set < m_set_reg_nums.size());
assert(set < m_set_names.size());
m_set_reg_nums[set].push_back(reg_num);
size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
if (m_reg_data_byte_size < end_reg_offset)
m_reg_data_byte_size = end_reg_offset;
}
void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
@ -445,6 +442,28 @@ void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
m_sets[set].registers = m_set_reg_nums[set].data();
}
// We are going to create a map between remote (eRegisterKindProcessPlugin)
// and local (eRegisterKindLLDB) register numbers. This map will give us
// remote register numbers in increasing order for offset calculation.
std::map<uint32_t, uint32_t> remote_to_local_regnum_map;
for (const auto &reg : m_regs)
remote_to_local_regnum_map[reg.kinds[eRegisterKindProcessPlugin]] =
reg.kinds[eRegisterKindLLDB];
// At this stage we manually calculate g/G packet offsets of all primary
// registers, only if target XML or qRegisterInfo packet did not send
// an offset explicitly.
uint32_t reg_offset = 0;
for (auto const &regnum_pair : remote_to_local_regnum_map) {
if (m_regs[regnum_pair.second].byte_offset == LLDB_INVALID_INDEX32 &&
m_regs[regnum_pair.second].value_regs == nullptr) {
m_regs[regnum_pair.second].byte_offset = reg_offset;
reg_offset = m_regs[regnum_pair.second].byte_offset +
m_regs[regnum_pair.second].byte_size;
}
}
// sort and unique all value registers and make sure each is terminated with
// LLDB_INVALID_REGNUM
@ -466,10 +485,24 @@ void DynamicRegisterInfo::Finalize(const ArchSpec &arch) {
// Now update all value_regs with each register info as needed
const size_t num_regs = m_regs.size();
for (size_t i = 0; i < num_regs; ++i) {
if (m_value_regs_map.find(i) != m_value_regs_map.end())
if (m_value_regs_map.find(i) != m_value_regs_map.end()) {
m_regs[i].value_regs = m_value_regs_map[i].data();
else
// Assign a valid offset to all pseudo registers if not assigned by stub.
// Pseudo registers with value_regs list populated will share same offset
// as that of their corresponding primary register in value_regs list.
if (m_regs[i].byte_offset == LLDB_INVALID_INDEX32) {
uint32_t value_regnum = m_regs[i].value_regs[0];
if (value_regnum != LLDB_INVALID_INDEX32)
m_regs[i].byte_offset =
GetRegisterInfoAtIndex(remote_to_local_regnum_map[value_regnum])
->byte_offset;
}
} else
m_regs[i].value_regs = nullptr;
reg_offset = m_regs[i].byte_offset + m_regs[i].byte_size;
if (m_reg_data_byte_size < reg_offset)
m_reg_data_byte_size = reg_offset;
}
// Expand all invalidation dependencies

View File

@ -1788,8 +1788,10 @@ GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo(
response.PutChar(';');
}
response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";",
reg_info->byte_size * 8, reg_info->byte_offset);
response.Printf("bitsize:%" PRIu32 ";", reg_info->byte_size * 8);
if (!reg_context.RegisterOffsetIsDynamic())
response.Printf("offset:%" PRIu32 ";", reg_info->byte_offset);
llvm::StringRef encoding = GetEncodingNameOrEmpty(*reg_info);
if (!encoding.empty())
@ -2861,10 +2863,11 @@ GDBRemoteCommunicationServerLLGS::BuildTargetXml() {
continue;
}
response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32 "\" offset=\"%" PRIu32
"\" regnum=\"%d\" ",
reg_info->name, reg_info->byte_size * 8,
reg_info->byte_offset, reg_index);
response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32 "\" regnum=\"%d\" ",
reg_info->name, reg_info->byte_size * 8, reg_index);
if (!reg_context.RegisterOffsetIsDynamic())
response.Printf("offset=\"%" PRIu32 "\" ", reg_info->byte_offset);
if (reg_info->alt_name && reg_info->alt_name[0])
response.Printf("altname=\"%s\" ", reg_info->alt_name);

View File

@ -451,7 +451,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
return;
char packet[128];
uint32_t reg_offset = 0;
uint32_t reg_offset = LLDB_INVALID_INDEX32;
uint32_t reg_num = 0;
for (StringExtractorGDBRemote::ResponseType response_type =
StringExtractorGDBRemote::eResponse;
@ -564,7 +564,7 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) {
reg_info.byte_offset = reg_offset;
assert(reg_info.byte_size != 0);
reg_offset += reg_info.byte_size;
reg_offset = LLDB_INVALID_INDEX32;
if (!value_regs.empty()) {
value_regs.push_back(LLDB_INVALID_REGNUM);
reg_info.value_regs = value_regs.data();
@ -4276,14 +4276,14 @@ struct GdbServerTargetInfo {
bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp,
uint32_t &cur_reg_num, uint32_t &reg_offset) {
uint32_t &reg_num_remote, uint32_t &reg_num_local) {
if (!feature_node)
return false;
uint32_t reg_offset = LLDB_INVALID_INDEX32;
feature_node.ForEachChildElementWithName(
"reg",
[&target_info, &dyn_reg_info, &cur_reg_num, &reg_offset,
&abi_sp](const XMLNode &reg_node) -> bool {
"reg", [&target_info, &dyn_reg_info, &reg_num_remote, &reg_num_local,
&reg_offset, &abi_sp](const XMLNode &reg_node) -> bool {
std::string gdb_group;
std::string gdb_type;
ConstString reg_name;
@ -4305,8 +4305,8 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
LLDB_INVALID_REGNUM, // eh_frame reg num
LLDB_INVALID_REGNUM, // DWARF reg num
LLDB_INVALID_REGNUM, // generic reg num
cur_reg_num, // process plugin reg num
cur_reg_num // native register number
reg_num_remote, // process plugin reg num
reg_num_local // native register number
},
nullptr,
nullptr,
@ -4433,7 +4433,7 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
reg_info.byte_offset = reg_offset;
assert(reg_info.byte_size != 0);
reg_offset += reg_info.byte_size;
reg_offset = LLDB_INVALID_INDEX32;
if (!value_regs.empty()) {
value_regs.push_back(LLDB_INVALID_REGNUM);
reg_info.value_regs = value_regs.data();
@ -4443,7 +4443,8 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
reg_info.invalidate_regs = invalidate_regs.data();
}
++cur_reg_num;
reg_num_remote = reg_info.kinds[eRegisterKindProcessPlugin] + 1;
++reg_num_local;
reg_info.name = reg_name.AsCString();
if (abi_sp)
abi_sp->AugmentRegisterInfo(reg_info);
@ -4462,8 +4463,8 @@ bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info,
// for nested register definition files. It returns true if it was able
// to fetch and parse an xml file.
bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(
ArchSpec &arch_to_use, std::string xml_filename, uint32_t &cur_reg_num,
uint32_t &reg_offset) {
ArchSpec &arch_to_use, std::string xml_filename, uint32_t &reg_num_remote,
uint32_t &reg_num_local) {
// request the target xml file
std::string raw;
lldb_private::Status lldberr;
@ -4567,12 +4568,12 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(
ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use);
for (auto &feature_node : feature_nodes) {
ParseRegisters(feature_node, target_info, this->m_register_info,
abi_to_use_sp, cur_reg_num, reg_offset);
abi_to_use_sp, reg_num_remote, reg_num_local);
}
for (const auto &include : target_info.includes) {
GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include, cur_reg_num,
reg_offset);
GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include,
reg_num_remote, reg_num_local);
}
}
} else {
@ -4592,9 +4593,10 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) {
if (!m_gdb_comm.GetQXferFeaturesReadSupported())
return false;
uint32_t cur_reg_num = 0;
uint32_t reg_offset = 0;
if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset))
uint32_t reg_num_remote = 0;
uint32_t reg_num_local = 0;
if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml",
reg_num_remote, reg_num_local))
this->m_register_info.Finalize(arch_to_use);
return m_register_info.GetNumRegisters() > 0;

View File

@ -391,8 +391,8 @@ protected:
bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,
std::string xml_filename,
uint32_t &cur_reg_num,
uint32_t &reg_offset);
uint32_t &cur_reg_remote,
uint32_t &cur_reg_local);
// Query remote GDBServer for register information
bool GetGDBServerRegisterInfo(ArchSpec &arch);

View File

@ -0,0 +1,151 @@
from __future__ import print_function
from textwrap import dedent
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from gdbclientutils import *
class MyResponder(MockGDBServerResponder):
def qXferRead(self, obj, annex, offset, length):
if annex == "target.xml":
return dedent("""\
<?xml version="1.0"?>
<target version="1.0">
<architecture>aarch64</architecture>
<feature name="org.gnu.gdb.aarch64.core">
<reg name="cpsr" regnum="33" bitsize="32"/>
<reg name="x0" regnum="0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64"/>
<reg name="pc" bitsize="64"/>
<reg name="w0" bitsize="32" value_regnums="0" invalidate_regnums="0" regnum="34"/>
<reg name="w1" bitsize="32" value_regnums="1" invalidate_regnums="1"/>
<reg name="w2" bitsize="32" value_regnums="2" invalidate_regnums="2"/>
<reg name="w3" bitsize="32" value_regnums="3" invalidate_regnums="3"/>
<reg name="w4" bitsize="32" value_regnums="4" invalidate_regnums="4"/>
<reg name="w5" bitsize="32" value_regnums="5" invalidate_regnums="5"/>
<reg name="w6" bitsize="32" value_regnums="6" invalidate_regnums="6"/>
<reg name="w7" bitsize="32" value_regnums="7" invalidate_regnums="7"/>
<reg name="w8" bitsize="32" value_regnums="8" invalidate_regnums="8"/>
<reg name="w9" bitsize="32" value_regnums="9" invalidate_regnums="9"/>
<reg name="w10" bitsize="32" value_regnums="10" invalidate_regnums="10"/>
<reg name="w11" bitsize="32" value_regnums="11" invalidate_regnums="11"/>
<reg name="w12" bitsize="32" value_regnums="12" invalidate_regnums="12"/>
<reg name="w13" bitsize="32" value_regnums="13" invalidate_regnums="13"/>
<reg name="w14" bitsize="32" value_regnums="14" invalidate_regnums="14"/>
<reg name="w15" bitsize="32" value_regnums="15" invalidate_regnums="15"/>
<reg name="w16" bitsize="32" value_regnums="16" invalidate_regnums="16"/>
<reg name="w17" bitsize="32" value_regnums="17" invalidate_regnums="17"/>
<reg name="w18" bitsize="32" value_regnums="18" invalidate_regnums="18"/>
<reg name="w19" bitsize="32" value_regnums="19" invalidate_regnums="19"/>
<reg name="w20" bitsize="32" value_regnums="20" invalidate_regnums="20"/>
<reg name="w21" bitsize="32" value_regnums="21" invalidate_regnums="21"/>
<reg name="w22" bitsize="32" value_regnums="22" invalidate_regnums="22"/>
<reg name="w23" bitsize="32" value_regnums="23" invalidate_regnums="23"/>
<reg name="w24" bitsize="32" value_regnums="24" invalidate_regnums="24"/>
<reg name="w25" bitsize="32" value_regnums="25" invalidate_regnums="25"/>
<reg name="w26" bitsize="32" value_regnums="26" invalidate_regnums="26"/>
<reg name="w27" bitsize="32" value_regnums="27" invalidate_regnums="27"/>
<reg name="w28" bitsize="32" value_regnums="28" invalidate_regnums="28"/>
</feature>
</target>
"""), False
else:
return None,
def readRegister(self, regnum):
return "E01"
def readRegisters(self):
return "20000000000000002000000000000000f0c154bfffff00005daa985a8fea0b48f0b954bfffff0000ad13cce570150b48380000000000000070456abfffff0000a700000000000000000000000000000001010101010101010000000000000000f0c154bfffff00000f2700000000000008e355bfffff0000080e55bfffff0000281041000000000010de61bfffff00005c05000000000000f0c154bfffff000090fcffffffff00008efcffffffff00008ffcffffffff00000000000000000000001000000000000090fcffffffff000000d06cbfffff0000f0c154bfffff00000100000000000000d0b954bfffff0000e407400000000000d0b954bfffff0000e40740000000000000100000"
class TestAArch64XMLRegOffsets(GDBRemoteTestBase):
@skipIfXmlSupportMissing
@skipIfRemote
@skipIfLLVMTargetMissing("AArch64")
def test_register_gpacket_offsets(self):
"""
Test that we correctly associate the register info with the eh_frame
register numbers.
"""
target = self.createTarget("basic_eh_frame-aarch64.yaml")
self.server.responder = MyResponder()
if self.TraceOn():
self.runCmd("log enable gdb-remote packets")
self.addTearDownHook(
lambda: self.runCmd("log disable gdb-remote packets"))
process = self.connect(target)
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
[lldb.eStateStopped])
registerSet = process.GetThreadAtIndex(
0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(0)
reg_val_dict = {
"x0": 0x0000000000000020, "x1": 0x0000000000000020,
"x2": 0x0000ffffbf54c1f0, "x3": 0x480bea8f5a98aa5d,
"x4": 0x0000ffffbf54b9f0, "x5": 0x480b1570e5cc13ad,
"x6": 0x0000000000000038, "x7": 0x0000ffffbf6a4570,
"x8": 0x00000000000000a7, "x9": 0x0000000000000000,
"x10": 0x0101010101010101, "x11": 0x0000000000000000,
"x12": 0x0000ffffbf54c1f0, "x13": 0x000000000000270f,
"x14": 0x0000ffffbf55e308, "x15": 0x0000ffffbf550e08,
"x16": 0x0000000000411028, "x17": 0x0000ffffbf61de10,
"x18": 0x000000000000055c, "x19": 0x0000ffffbf54c1f0,
"x20": 0x0000fffffffffc90, "x21": 0x0000fffffffffc8e,
"x22": 0x0000fffffffffc8f, "x23": 0x0000000000000000,
"x24": 0x0000000000001000, "x25": 0x0000fffffffffc90,
"x26": 0x0000ffffbf6cd000, "x27": 0x0000ffffbf54c1f0,
"x28": 0x0000000000000001, "x29": 0x0000ffffbf54b9d0,
"x30": 0x00000000004007e4, "sp": 0x0000ffffbf54b9d0,
"pc": 0x00000000004007e4, "cpsr": 0x00001000, "w0": 0x00000020,
"w1": 0x00000020, "w2": 0xbf54c1f0, "w3": 0x5a98aa5d,
"w4": 0xbf54b9f0, "w5": 0xe5cc13ad, "w6": 0x00000038,
"w7": 0xbf6a4570, "w8": 0x000000a7, "w9": 0x00000000,
"w10": 0x01010101, "w11": 0x00000000, "w12": 0xbf54c1f0,
"w13": 0x0000270f, "w14": 0xbf55e308, "w15": 0xbf550e08,
"w16": 0x00411028, "w17": 0xbf61de10, "w18": 0x0000055c,
"w19": 0xbf54c1f0, "w20": 0xfffffc90, "w21": 0xfffffc8e,
"w22": 0xfffffc8f, "w23": 0x00000000, "w24": 0x00001000,
"w25": 0xfffffc90, "w26": 0xbf6cd000, "w27": 0xbf54c1f0,
"w28": 0x00000001
}
for reg in registerSet:
self.assertEqual(reg.GetValueAsUnsigned(),
reg_val_dict[reg.GetName()])

View File

@ -65,5 +65,8 @@ class TestGdbRemoteTargetXmlPacket(gdbremote_testcase.GdbRemoteTestCaseBase):
self.assertEqual(q_info_reg["set"], xml_info_reg.get("group"))
self.assertEqual(q_info_reg["format"], xml_info_reg.get("format"))
self.assertEqual(q_info_reg["bitsize"], xml_info_reg.get("bitsize"))
self.assertEqual(q_info_reg["offset"], xml_info_reg.get("offset"))
if not self.getArchitecture() == 'aarch64':
self.assertEqual(q_info_reg["offset"], xml_info_reg.get("offset"))
self.assertEqual(q_info_reg["encoding"], xml_info_reg.get("encoding"))

View File

@ -171,7 +171,7 @@ Expected<RegisterInfo> RegisterInfoParser::create(StringRef Response) {
Info.byte_size /= CHAR_BIT;
if (!to_integer(Elements["offset"], Info.byte_offset, 10))
return make_parsing_error("qRegisterInfo: offset");
Info.byte_offset = LLDB_INVALID_INDEX32;
Info.encoding = Args::StringToEncoding(Elements["encoding"]);
if (Info.encoding == eEncodingInvalid)

View File

@ -219,6 +219,7 @@ Error TestClient::qProcessInfo() {
}
Error TestClient::qRegisterInfos() {
uint32_t reg_offset = 0;
for (unsigned int Reg = 0;; ++Reg) {
std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str();
Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message);
@ -227,6 +228,12 @@ Error TestClient::qRegisterInfos() {
break;
}
m_register_infos.emplace_back(std::move(*InfoOr));
if (m_register_infos[Reg].byte_offset == LLDB_INVALID_INDEX32)
m_register_infos[Reg].byte_offset = reg_offset;
reg_offset =
m_register_infos[Reg].byte_offset + m_register_infos[Reg].byte_size;
if (m_register_infos[Reg].kinds[eRegisterKindGeneric] ==
LLDB_REGNUM_GENERIC_PC)
m_pc_register = Reg;