mgorny created this revision.
mgorny added reviewers: labath, emaste, krytarowski.
Herald added a subscriber: arichardson.
mgorny requested review of this revision.

Implement the qXfer:siginfo:read that is used to read the siginfo_t
(extended signal information) for the current thread.  This is currently
implemented on FreeBSD and Linux.


https://reviews.llvm.org/D117113

Files:
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
  lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/test/API/tools/lldb-server/TestLldbGdbServer.py

Index: lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
===================================================================
--- lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
+++ lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
@@ -12,6 +12,7 @@
 
 import binascii
 import itertools
+import struct
 
 import unittest2
 import gdbremote_testcase
@@ -993,6 +994,13 @@
         self.assertEqual(supported_dict.get('qXfer:libraries-svr4:read', '-'),
                          expected)
 
+    def test_qSupported_siginfo_read(self):
+        expected = ('+' if lldbplatformutil.getPlatform()
+                    in ["freebsd", "linux"] else '-')
+        supported_dict = self.get_qSupported_dict()
+        self.assertEqual(supported_dict.get('qXfer:siginfo:read', '-'),
+                         expected)
+
     def test_qSupported_QPassSignals(self):
         expected = ('+' if lldbplatformutil.getPlatform()
                     in ["freebsd", "linux", "netbsd"] else '-')
@@ -1374,3 +1382,81 @@
             True)
         context = self.expect_gdbremote_sequence()
         self.assertEqual(context["O_content"], b"test\r\na=z\r\na*}#z\r\n")
+
+    @skipUnlessPlatform(oslist=["freebsd", "linux"])
+    @add_test_categories(["llgs"])
+    def test_qXfer_siginfo_read(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        procs = self.prep_debug_monitor_and_inferior(
+                inferior_args=["thread:segfault", "thread:new", "sleep:10"])
+        self.test_sequence.add_log_lines(["read packet: $c#63"], True)
+        self.expect_gdbremote_sequence()
+
+        # Run until SIGSEGV comes in.
+        self.reset_test_sequence()
+        # TODO: freebsd doesn't report crashing thread in T
+        self.test_sequence.add_log_lines(
+            [{"direction": "send",
+              "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);",
+              "capture": {1: "signo", 2: "thread_id"},
+              }], True)
+
+        # Figure out which thread crashed.
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+        self.assertEqual(int(context["signo"], 16),
+                         lldbutil.get_signal_number('SIGSEGV'))
+        crashing_thread = int(context["thread_id"], 16)
+
+        # Grab siginfo for the crashing thread.
+        self.reset_test_sequence()
+        self.test_sequence.add_log_lines(
+            ["read packet: $Hg{:x}#00".format(crashing_thread),
+             "send packet: $OK#00",
+             "read packet: $qXfer:siginfo:read::0,80:#00",
+             {"direction": "send",
+              "regex": re.compile(r"^\$([^E])(.*)#[0-9a-fA-F]{2}$",
+                                  re.MULTILINE | re.DOTALL),
+              "capture": {1: "response_type", 2: "content_raw"},
+              }], True)
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        # Ensure we end up with all data in one packet.
+        self.assertEqual(context.get("response_type"), "l")
+
+        # Decode binary data.
+        content_raw = context.get("content_raw")
+        self.assertIsNotNone(content_raw)
+        content = self.decode_gdbremote_binary(content_raw).encode("latin1")
+
+        # Decode siginfo_t.
+        platform = lldbplatformutil.getPlatform()
+        pad = ""
+        if sys.maxsize > 2**32:
+            pad = "i"
+        signo_idx = 0
+        errno_idx = 1
+        code_idx = 2
+        addr_idx = -1
+        SEGV_MAPERR = 1
+        if platform == "linux":
+            # si_signo, si_errno, si_code, [pad], _sifields._sigfault.si_addr
+            format_str = "iii{}P".format(pad)
+        elif platform == "freebsd":
+            # si_signo, si_errno, si_code, si_pid, si_uid, si_status, si_addr
+            format_str = "iiiiiiP"
+        elif platform == "netbsd":
+            # _signo, _code, _errno, [pad], _reason._fault._addr
+            format_str = "iii{}P".format(pad)
+            errno_idx = 2
+            code_idx = 1
+
+        decoder = struct.Struct(format_str)
+        decoded = decoder.unpack(content[:decoder.size])
+        self.assertEqual(decoded[signo_idx],
+                         lldbutil.get_signal_number('SIGSEGV'))
+        self.assertEqual(decoded[errno_idx], 0)  # si_errno
+        self.assertEqual(decoded[code_idx], SEGV_MAPERR)  # si_code
+        self.assertEqual(decoded[addr_idx], 0)  # si_addr
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -2920,6 +2920,13 @@
     return std::move(*buffer_or_error);
   }
 
+  if (object == "siginfo") {
+    auto buffer_or_error = m_current_process->GetSiginfo();
+    if (!buffer_or_error)
+      return buffer_or_error.takeError();
+    return std::move(*buffer_or_error);
+  }
+
   if (object == "libraries-svr4") {
     auto library_list = m_current_process->GetLoadedSVR4Libraries();
     if (!library_list)
@@ -3838,6 +3845,8 @@
     ret.push_back("qXfer:auxv:read+");
   if (bool(plugin_features & Extension::libraries_svr4))
     ret.push_back("qXfer:libraries-svr4:read+");
+  if (bool(plugin_features & Extension::siginfo_read))
+    ret.push_back("qXfer:siginfo:read+");
   if (bool(plugin_features & Extension::memory_tagging))
     ret.push_back("memory-tagging+");
   if (bool(plugin_features & Extension::savecore))
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -111,6 +111,9 @@
     return getProcFile(GetID(), "auxv");
   }
 
+  llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+  GetSiginfo() const override;
+
   /// Tracing
   /// These methods implement the jLLDBTrace packets
   /// \{
@@ -207,7 +210,7 @@
 
   /// Writes a siginfo_t structure corresponding to the given thread ID to the
   /// memory region pointed to by \p siginfo.
-  Status GetSignalInfo(lldb::tid_t tid, void *siginfo);
+  Status GetSignalInfo(lldb::tid_t tid, void *siginfo) const;
 
   /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG)
   /// corresponding to the given thread ID to the memory pointed to by @p
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -292,7 +292,8 @@
 NativeProcessLinux::Factory::GetSupportedExtensions() const {
   NativeProcessLinux::Extension supported =
       Extension::multiprocess | Extension::fork | Extension::vfork |
-      Extension::pass_signals | Extension::auxv | Extension::libraries_svr4;
+      Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
+      Extension::siginfo_read;
 
 #ifdef __aarch64__
   // At this point we do not have a process so read auxv directly.
@@ -1622,7 +1623,7 @@
   return error;
 }
 
-Status NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) {
+Status NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) const {
   return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo);
 }
 
@@ -1980,3 +1981,13 @@
     return m_intel_pt_manager.GetBinaryData(request);
   return NativeProcessProtocol::TraceGetBinaryData(request);
 }
+
+Expected<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessLinux::GetSiginfo() const {
+  siginfo_t siginfo;
+  Status error = GetSignalInfo(GetCurrentThreadID(), &siginfo);
+  if (!error.Success())
+    return error.ToError();
+  return MemoryBuffer::getMemBufferCopy(
+      StringRef(reinterpret_cast<const char *>(&siginfo), sizeof(siginfo)));
+}
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
@@ -85,6 +85,9 @@
   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
   GetAuxvData() const override;
 
+  llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+  GetSiginfo() const override;
+
   // Interface used by NativeRegisterContext-derived classes.
   static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
                               int data = 0, int *result = nullptr);
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
@@ -135,7 +135,8 @@
       Extension::savecore |
 #endif
       Extension::multiprocess | Extension::fork | Extension::vfork |
-      Extension::pass_signals | Extension::auxv | Extension::libraries_svr4;
+      Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
+      Extension::siginfo_read;
 }
 
 // Public Instance Methods
@@ -1054,3 +1055,27 @@
       "PT_COREDUMP not supported in the FreeBSD version used to build LLDB");
 #endif
 }
+
+Expected<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessFreeBSD::GetSiginfo() const {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+
+  struct ptrace_lwpinfo info;
+  const auto siginfo_err =
+      PtraceWrapper(PT_LWPINFO, GetCurrentThreadID(), &info, sizeof(info));
+  if (siginfo_err.Fail()) {
+    LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err);
+    return siginfo_err.ToError();
+  }
+
+  if (info.pl_event != PL_EVENT_SIGNAL)
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Thread not signaled");
+  if (!(info.pl_flags & PL_FLAG_SI))
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "No siginfo for thread");
+
+  return MemoryBuffer::getMemBufferCopy(
+      StringRef(reinterpret_cast<const char *>(&info.pl_siginfo),
+                sizeof(info.pl_siginfo)));
+}
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -851,6 +851,7 @@
         "qXfer:libraries:read",
         "qXfer:libraries-svr4:read",
         "qXfer:features:read",
+        "qXfer:siginfo:read",
         "qEcho",
         "QPassSignals",
         "multiprocess",
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -192,6 +192,11 @@
   virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
   GetAuxvData() const = 0;
 
+  virtual llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+  GetSiginfo() const {
+    return llvm::make_error<UnimplementedError>();
+  }
+
   // Exit Status
   virtual llvm::Optional<WaitStatus> GetExitStatus();
 
@@ -204,7 +209,7 @@
 
   void SetCurrentThreadID(lldb::tid_t tid) { m_current_thread_id = tid; }
 
-  lldb::tid_t GetCurrentThreadID() { return m_current_thread_id; }
+  lldb::tid_t GetCurrentThreadID() const { return m_current_thread_id; }
 
   NativeThreadProtocol *GetCurrentThread() {
     return GetThreadByID(m_current_thread_id);
@@ -251,8 +256,9 @@
     libraries_svr4 = (1u << 5),
     memory_tagging = (1u << 6),
     savecore = (1u << 7),
+    siginfo_read = (1u << 8),
 
-    LLVM_MARK_AS_BITMASK_ENUM(savecore)
+    LLVM_MARK_AS_BITMASK_ENUM(siginfo_read)
   };
 
   class Factory {
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to