[lldb-server] Support 'g' packets
authorPavel Labath <pavel@labath.sk>
Thu, 30 May 2019 07:25:22 +0000 (07:25 +0000)
committerPavel Labath <pavel@labath.sk>
Thu, 30 May 2019 07:25:22 +0000 (07:25 +0000)
Differential Revision: https://reviews.llvm.org/D62221
Patch by Guilherme Andrade <guiandrade@google.com>.

llvm-svn: 362063

lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteGPacket.py [deleted file]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/TestGdbRemoteGPacket.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp [new file with mode: 0644]
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
lldb/source/Utility/StringExtractorGDBRemote.cpp

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 (file)
index cfadbc8..0000000
+++ /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()
index da23339..a3bad68 100644 (file)
@@ -558,9 +558,7 @@ class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcod
         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
index 6b807a0..e7c63bf 100644 (file)
@@ -378,6 +378,11 @@ class GdbRemoteTestCaseBase(TestBase):
             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 (file)
index 0000000..314f1cb
--- /dev/null
@@ -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 (file)
index 0000000..e13daeb
--- /dev/null
@@ -0,0 +1,153 @@
+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()
+
+    @skipIf(archs=no_match(["x86_64"]))
+    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 (file)
index 0000000..32eda6d
--- /dev/null
@@ -0,0 +1,54 @@
+#include <cstdint>
+
+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;
+}
index 045f4b4..2778b12 100644 (file)
@@ -187,6 +187,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
       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) {
@@ -1892,6 +1895,61 @@ GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo(
 }
 
 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 &reg_ctx = thread->GetRegisterContext();
+
+  std::vector<uint8_t> 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);
+    }
+
+    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, reg_value.GetBytes(),
+           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));
 
index 1609174..1f626c2 100644 (file)
@@ -178,6 +178,8 @@ protected:
 
   PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_g(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
index 683b62c..905c369 100644 (file)
@@ -377,9 +377,7 @@ StringExtractorGDBRemote::GetServerPacketType() const {
     break;
 
   case 'g':
-    if (packet_size == 1)
-      return eServerPacketType_g;
-    break;
+    return eServerPacketType_g;
 
   case 'G':
     return eServerPacketType_G;