Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestNestedRegDefinitions.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestNestedRegDefinitions.py +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestNestedRegDefinitions.py @@ -0,0 +1,238 @@ +from __future__ import print_function +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from gdbclientutils import * + +class TestNestedRegDefinitions(GDBRemoteTestBase): + + @skipIfXmlSupportMissing + @skipIfRemote + def test(self): + """ + Test lldb's parsing of the tag in the target.xml register + description packet. + """ + class MyResponder(MockGDBServerResponder): + + def qXferRead(self, obj, annex, offset, length): + if annex == "target.xml": + return """i386:x86-64""", False + + if annex == "i386-64bit.xml": + return """ + + + + + + + + +""", False + + if annex == "i386-64bit-core.xml": + return """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""", False + + if annex == "i386-64bit-sse.xml": + return """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""", False + + return None, False + + def readRegister(self, regnum): + return "" + + def readRegisters(self): + return "0600000000000000c0b7c00080fffffff021c60080ffffff1a00000000000000020000000000000078b7c00080ffffff203f8ca090ffffff103f8ca090ffffff3025990a80ffffff809698000000000070009f0a80ffffff020000000000000000eae10080ffffff00000000000000001822d74f1a00000078b7c00080ffffff0e12410080ffff004602000008000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000" + + def haltReason(self): + return "T02thread:dead;threads:dead;" + + def qfThreadInfo(self): + return "mdead" + + def qC(self): + return "" + + def qSupported(self, client_supported): + return "PacketSize=4000;qXfer:features:read+" + + def QThreadSuffixSupported(self): + return "OK" + + def QListThreadsInStopReply(self): + return "OK" + + self.server.responder = MyResponder() + if self.TraceOn(): + self.runCmd("log enable gdb-remote packets") + self.addTearDownHook( + lambda: self.runCmd("log disable gdb-remote packets")) + + target = self.dbg.CreateTargetWithFileAndArch(None, None) + + process = self.connect(target) + + if self.TraceOn(): + interp = self.dbg.GetCommandInterpreter() + result = lldb.SBCommandReturnObject() + interp.HandleCommand("target list", result) + print(result.GetOutput()) + + rip_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("rip") + self.assertEqual(rip_valobj.GetValueAsUnsigned(), 0x00ffff800041120e) + + r15_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("r15") + self.assertEqual(r15_valobj.GetValueAsUnsigned(), 0xffffff8000c0b778) + + mxcsr_valobj = process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("mxcsr") + self.assertEqual(mxcsr_valobj.GetValueAsUnsigned(), 0x00001f80) + + gpr_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(0).GetName() + self.assertEqual(gpr_reg_set_name, "general") + + float_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(1).GetName() + self.assertEqual(float_reg_set_name, "float") + + vector_reg_set_name = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters().GetValueAtIndex(2).GetName() + self.assertEqual(vector_reg_set_name, "vector") + + if self.TraceOn(): + print("rip is 0x%x" % rip_valobj.GetValueAsUnsigned()) + print("r15 is 0x%x" % r15_valobj.GetValueAsUnsigned()) + print("mxcsr is 0x%x" % mxcsr_valobj.GetValueAsUnsigned()) Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -383,6 +383,11 @@ DynamicLoader *GetDynamicLoader() override; + bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use, + std::string xml_filename, + uint32_t &cur_reg_num, + uint32_t ®_offset); + // Query remote GDBServer for register information bool GetGDBServerRegisterInfo(ArchSpec &arch); Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -4490,8 +4490,15 @@ // Only update the register set name if we didn't get a "reg_set" // attribute. "set_name" will be empty if we didn't have a "reg_set" // attribute. - if (!set_name && !gdb_group.empty()) - set_name.SetCString(gdb_group.c_str()); + if (!set_name) { + if (!gdb_group.empty()) { + set_name.SetCString(gdb_group.c_str()); + } else { + // If no register group name provided anywhere, + // we'll create a 'general' register set + set_name.SetCString("general"); + } + } reg_info.byte_offset = reg_offset; assert(reg_info.byte_size != 0); @@ -4516,38 +4523,33 @@ } // namespace -// query the target of gdb-remote for extended target information return: -// 'true' on success -// 'false' on failure -bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { - // Make sure LLDB has an XML parser it can use first - if (!XMLDocument::XMLEnabled()) - return false; - - // redirect libxml2's error handler since the default prints to stdout - - GDBRemoteCommunicationClient &comm = m_gdb_comm; - - // check that we have extended feature read support - if (!comm.GetQXferFeaturesReadSupported()) - return false; - +// This method fetches a register description feature xml file from +// the remote stub and adds registers/register groupsets/architecture +// information to the current process. It will call itself recursively +// 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 ®_offset) { // request the target xml file std::string raw; lldb_private::Status lldberr; - if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), + if (!m_gdb_comm.ReadExtFeature(ConstString("features"), + ConstString(xml_filename.c_str()), raw, lldberr)) { return false; } XMLDocument xml_document; - if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { + if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) { GdbServerTargetInfo target_info; + std::vector feature_nodes; + // The top level feature XML file will start with a tag. XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { - std::vector feature_nodes; target_node.ForEachChildElement([&target_info, &feature_nodes]( const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); @@ -4585,32 +4587,48 @@ } return true; // Keep iterating through all children of the target_node }); + } else { + // In an included XML feature file, we're already "inside" the + // tag of the initial XML file; this included file will likely only have + // a tag. Need to check for any more included files in this + // element. + XMLNode feature_node = xml_document.GetRootElement("feature"); + if (feature_node) { + feature_nodes.push_back(feature_node); + feature_node.ForEachChildElement([&target_info]( + const XMLNode &node) -> bool { + llvm::StringRef name = node.GetName(); + if (name == "xi:include" || name == "include") { + llvm::StringRef href = node.GetAttributeValue("href"); + if (!href.empty()) + target_info.includes.push_back(href.str()); + } + return true; + }); + } + } - // If the target.xml includes an architecture entry like - // i386:x86-64 (seen from VMWare ESXi) - // arm (seen from Segger JLink on unspecified arm board) - // use that if we don't have anything better. - if (!arch_to_use.IsValid() && !target_info.arch.empty()) { - if (target_info.arch == "i386:x86-64") { - // We don't have any information about vendor or OS. - arch_to_use.SetTriple("x86_64--"); - GetTarget().MergeArchitecture(arch_to_use); - } - - // SEGGER J-Link jtag boards send this very-generic arch name, - // we'll need to use this if we have absolutely nothing better - // to work with or the register definitions won't be accepted. - if (target_info.arch == "arm") { - arch_to_use.SetTriple("arm--"); - GetTarget().MergeArchitecture(arch_to_use); - } + // If the target.xml includes an architecture entry like + // i386:x86-64 (seen from VMWare ESXi) + // arm (seen from Segger JLink on unspecified arm board) + // use that if we don't have anything better. + if (!arch_to_use.IsValid() && !target_info.arch.empty()) { + if (target_info.arch == "i386:x86-64") { + // We don't have any information about vendor or OS. + arch_to_use.SetTriple("x86_64--"); + GetTarget().MergeArchitecture(arch_to_use); } - // Initialize these outside of ParseRegisters, since they should not be - // reset inside each include feature - uint32_t cur_reg_num = 0; - uint32_t reg_offset = 0; + // SEGGER J-Link jtag boards send this very-generic arch name, + // we'll need to use this if we have absolutely nothing better + // to work with or the register definitions won't be accepted. + if (target_info.arch == "arm") { + arch_to_use.SetTriple("arm--"); + GetTarget().MergeArchitecture(arch_to_use); + } + } + if (arch_to_use.IsValid()) { // Don't use Process::GetABI, this code gets called from DidAttach, and // in that context we haven't set the Target's architecture yet, so the // ABI is also potentially incorrect. @@ -4621,26 +4639,31 @@ } for (const auto &include : target_info.includes) { - // request register file - std::string xml_data; - if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), - xml_data, lldberr)) - continue; - - XMLDocument include_xml_document; - include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), - include.c_str()); - XMLNode include_feature_node = - include_xml_document.GetRootElement("feature"); - if (include_feature_node) { - ParseRegisters(include_feature_node, target_info, - this->m_register_info, abi_to_use_sp, cur_reg_num, - reg_offset); - } + GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include, + cur_reg_num, reg_offset); } - this->m_register_info.Finalize(arch_to_use); } + } else { + return false; } + return true; +} + +// query the target of gdb-remote for extended target information returns +// true on success (got register definitions), false on failure (did not). +bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { + // Make sure LLDB has an XML parser it can use first + if (!XMLDocument::XMLEnabled()) + return false; + + // check that we have extended feature read support + 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)) + this->m_register_info.Finalize(arch_to_use); return m_register_info.GetNumRegisters() > 0; }