diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteGPacket.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteGPacket.py deleted file mode 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteGPacket.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import print_function - - -import gdbremote_testcase -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class TestGdbRemoteGPacket(gdbremote_testcase.GdbRemoteTestCaseBase): - - mydir = TestBase.compute_mydir(__file__) - - def run_test_g_packet(self): - self.build() - self.prep_debug_monitor_and_inferior() - self.test_sequence.add_log_lines( - ["read packet: $g#67", - {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", - "capture": {1: "register_bank"}}], - True) - self.connect_to_debug_monitor() - context = self.expect_gdbremote_sequence() - register_bank = context.get("register_bank") - self.assertTrue(register_bank[0] != 'E') - - self.test_sequence.add_log_lines( - ["read packet: $G" + register_bank + "#00", - {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", - "capture": {1: "G_reply"}}], - True) - context = self.expect_gdbremote_sequence() - self.assertTrue(context.get("G_reply")[0] != 'E') - - - @skipIfOutOfTreeDebugserver - @debugserver_test - @skipIfDarwinEmbedded - def test_g_packet_debugserver(self): - self.init_debugserver_test() - self.run_test_g_packet() diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py @@ -558,9 +558,7 @@ self.assertIsNotNone(reg_infos) self.assertTrue(len(reg_infos) > 0) - inferior_exe_path = self.getBuildArtifact("a.out") - Target = self.dbg.CreateTarget(inferior_exe_path) - byte_order = Target.GetByteOrder() + byte_order = self.get_target_byte_order() # Read value for each register. reg_index = 0 diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -378,6 +378,11 @@ commandline_args += ["--named-pipe", self.named_pipe_path] return commandline_args + def get_target_byte_order(self): + inferior_exe_path = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(inferior_exe_path) + return target.GetByteOrder() + def launch_debug_monitor(self, attach_pid=None, logfile=None): # Create the command line. commandline_args = self.get_debug_monitor_command_line_args( diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/Makefile b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/Makefile new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/TestGdbRemoteGPacket.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/TestGdbRemoteGPacket.py new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/TestGdbRemoteGPacket.py @@ -0,0 +1,152 @@ +from __future__ import print_function + + +import gdbremote_testcase +import textwrap +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +def _extract_register_value(reg_info, reg_bank, byte_order, bytes_per_entry=8): + reg_offset = int(reg_info["offset"])*2 + reg_byte_size = int(2 * int(reg_info["bitsize"]) / 8) + # Create slice with the contents of the register. + reg_slice = reg_bank[reg_offset:reg_offset+reg_byte_size] + + reg_value = [] + # Wrap slice according to bytes_per_entry. + for entry in textwrap.wrap(reg_slice, 2 * bytes_per_entry): + # Invert the bytes order if target uses little-endian. + if byte_order == lldb.eByteOrderLittle: + entry = "".join(reversed([entry[i:i+2] for i in range(0, + len(entry),2)])) + reg_value.append("0x" + entry) + + return reg_value + + +class TestGdbRemoteGPacket(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def run_test_g_packet(self): + self.build() + self.prep_debug_monitor_and_inferior() + self.test_sequence.add_log_lines( + ["read packet: $g#67", + {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", + "capture": {1: "register_bank"}}], + True) + self.connect_to_debug_monitor() + context = self.expect_gdbremote_sequence() + register_bank = context.get("register_bank") + self.assertTrue(register_bank[0] != 'E') + + self.test_sequence.add_log_lines( + ["read packet: $G" + register_bank + "#00", + {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", + "capture": {1: "G_reply"}}], + True) + context = self.expect_gdbremote_sequence() + self.assertTrue(context.get("G_reply")[0] != 'E') + + @skipIfOutOfTreeDebugserver + @debugserver_test + @skipIfDarwinEmbedded + def test_g_packet_debugserver(self): + self.init_debugserver_test() + self.run_test_g_packet() + + def g_returns_correct_data(self, with_suffix): + procs = self.prep_debug_monitor_and_inferior() + + self.add_register_info_collection_packets() + if with_suffix: + self.add_thread_suffix_request_packets() + self.add_threadinfo_collection_packets() + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather register info. + reg_infos = self.parse_register_info_packets(context) + self.assertIsNotNone(reg_infos) + self.add_lldb_register_index(reg_infos) + # Index register info entries by name. + reg_infos = {info['name']: info for info in reg_infos} + + # Gather thread info. + if with_suffix: + threads = self.parse_threadinfo_packets(context) + self.assertIsNotNone(threads) + thread_id = threads[0] + self.assertIsNotNone(thread_id) + else: + thread_id = None + + # Send vCont packet to resume the inferior. + self.test_sequence.add_log_lines(["read packet: $vCont;c#a8", + {"direction": "send", + "regex": r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", + "capture": {1: "hex_exit_code"}}, + ], + True) + + # Send g packet to retrieve the register bank + if thread_id: + g_request = "read packet: $g;thread:{:x}#00".format(thread_id) + else: + g_request = "read packet: $g#00" + self.test_sequence.add_log_lines( + [g_request, + {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", + "capture": {1: "register_bank"}}], + True) + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + reg_bank = context.get("register_bank") + self.assertTrue(reg_bank[0] != 'E') + + byte_order = self.get_target_byte_order() + get_reg_value = lambda reg_name : _extract_register_value( + reg_infos[reg_name], reg_bank, byte_order) + + self.assertEqual(['0x0102030405060708'], get_reg_value('r8')) + self.assertEqual(['0x1112131415161718'], get_reg_value('r9')) + self.assertEqual(['0x2122232425262728'], get_reg_value('r10')) + self.assertEqual(['0x3132333435363738'], get_reg_value('r11')) + self.assertEqual(['0x4142434445464748'], get_reg_value('r12')) + self.assertEqual(['0x5152535455565758'], get_reg_value('r13')) + self.assertEqual(['0x6162636465666768'], get_reg_value('r14')) + self.assertEqual(['0x7172737475767778'], get_reg_value('r15')) + + self.assertEqual( + ['0x020406080a0c0e01', '0x030507090b0d0f00'], get_reg_value('xmm8')) + self.assertEqual( + ['0x121416181a1c1e11', '0x131517191b1d1f10'], get_reg_value('xmm9')) + self.assertEqual( + ['0x222426282a2c2e21', '0x232527292b2d2f20'], get_reg_value('xmm10')) + self.assertEqual( + ['0x323436383a3c3e31', '0x333537393b3d3f30'], get_reg_value('xmm11')) + self.assertEqual( + ['0x424446484a4c4e41', '0x434547494b4d4f40'], get_reg_value('xmm12')) + self.assertEqual( + ['0x525456585a5c5e51', '0x535557595b5d5f50'], get_reg_value('xmm13')) + self.assertEqual( + ['0x626466686a6c6e61', '0x636567696b6d6f60'], get_reg_value('xmm14')) + self.assertEqual( + ['0x727476787a7c7e71', '0x737577797b7d7f70'], get_reg_value('xmm15')) + + @llgs_test + def test_g_returns_correct_data_with_suffix_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.g_returns_correct_data(True) + + @llgs_test + def test_g_returns_correct_data_no_suffix_llgs(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + self.g_returns_correct_data(False) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp @@ -0,0 +1,54 @@ +#include + +struct alignas(16) xmm_t { + uint64_t a, b; +}; + +int main() { + uint64_t r8 = 0x0102030405060708; + uint64_t r9 = 0x1112131415161718; + uint64_t r10 = 0x2122232425262728; + uint64_t r11 = 0x3132333435363738; + uint64_t r12 = 0x4142434445464748; + uint64_t r13 = 0x5152535455565758; + uint64_t r14 = 0x6162636465666768; + uint64_t r15 = 0x7172737475767778; + + xmm_t xmm8 = {0x020406080A0C0E01, 0x030507090B0D0F00}; + xmm_t xmm9 = {0x121416181A1C1E11, 0x131517191B1D1F10}; + xmm_t xmm10 = {0x222426282A2C2E21, 0x232527292B2D2F20}; + xmm_t xmm11 = {0x323436383A3C3E31, 0x333537393B3D3F30}; + xmm_t xmm12 = {0x424446484A4C4E41, 0x434547494B4D4F40}; + xmm_t xmm13 = {0x525456585A5C5E51, 0x535557595B5D5F50}; + xmm_t xmm14 = {0x626466686A6C6E61, 0x636567696B6D6F60}; + xmm_t xmm15 = {0x727476787A7C7E71, 0x737577797B7D7F70}; + + asm volatile("movq %0, %%r8\n\t" + "movq %1, %%r9\n\t" + "movq %2, %%r10\n\t" + "movq %3, %%r11\n\t" + "movq %4, %%r12\n\t" + "movq %5, %%r13\n\t" + "movq %6, %%r14\n\t" + "movq %7, %%r15\n\t" + "\n\t" + "movaps %8, %%xmm8\n\t" + "movaps %9, %%xmm9\n\t" + "movaps %10, %%xmm10\n\t" + "movaps %11, %%xmm11\n\t" + "movaps %12, %%xmm12\n\t" + "movaps %13, %%xmm13\n\t" + "movaps %14, %%xmm14\n\t" + "movaps %15, %%xmm15\n\t" + "\n\t" + "int3" + : + : "g"(r8), "g"(r9), "g"(r10), "g"(r11), "g"(r12), "g"(r13), + "g"(r14), "g"(r15), "m"(xmm8), "m"(xmm9), "m"(xmm10), + "m"(xmm11), "m"(xmm12), "m"(xmm13), "m"(xmm14), "m"(xmm15) + : "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", + "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", + "%xmm14", "%xmm15"); + + return 0; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -178,6 +178,8 @@ PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); + PacketResult Handle_g(StringExtractorGDBRemote &packet); + void SetCurrentThreadID(lldb::tid_t tid); lldb::tid_t GetCurrentThreadID() const; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -187,6 +187,9 @@ StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead, &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g, + &GDBRemoteCommunicationServerLLGS::Handle_g); + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt, bool &quit) { @@ -1891,6 +1894,65 @@ return SendPacketNoLock("l"); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_g(StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // Move past packet name. + packet.SetFilePos(strlen("g")); + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + LLDB_LOG(log, "failed, no thread available"); + return SendErrorResponse(0x15); + } + + // Get the thread's register context. + NativeRegisterContext ®_ctx = thread->GetRegisterContext(); + + std::vector regs_buffer; + for (uint32_t reg_num = 0; reg_num < reg_ctx.GetUserRegisterCount(); + ++reg_num) { + const RegisterInfo *reg_info = reg_ctx.GetRegisterInfoAtIndex(reg_num); + + if (reg_info == nullptr) { + LLDB_LOG(log, "failed to get register info for register index {0}", + reg_num); + return SendErrorResponse(0x15); + } + + if (reg_info->value_regs != nullptr) + continue; // skip registers that are contained in other registers + + RegisterValue reg_value; + Status error = reg_ctx.ReadRegister(reg_info, reg_value); + if (error.Fail()) { + LLDB_LOG(log, "failed to read register at index {0}", reg_num); + return SendErrorResponse(0x15); + } + + // Write the register data to the buffer. + const uint8_t *const data = + reinterpret_cast(reg_value.GetBytes()); + + if (reg_info->byte_offset + reg_info->byte_size >= regs_buffer.size()) + // Resize the buffer to guarantee it can store the register offsetted + // data. + regs_buffer.resize(reg_info->byte_offset + reg_info->byte_size); + + // Copy the register offsetted data to the buffer. + memcpy(regs_buffer.data() + reg_info->byte_offset, data, + reg_info->byte_size); + } + + // Write the response. + StreamGDBRemote response; + response.PutBytesAsRawHex8(regs_buffer.data(), regs_buffer.size()); + + return SendPacketNoLock(response.GetString()); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -377,9 +377,7 @@ break; case 'g': - if (packet_size == 1) - return eServerPacketType_g; - break; + return eServerPacketType_g; case 'G': return eServerPacketType_G;