diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h index 7c34585276160..c7494ba6b1957 100644 --- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -204,7 +204,7 @@ class NativeProcessProtocol { 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 +251,9 @@ class NativeProcessProtocol { 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 { diff --git a/lldb/include/lldb/Host/common/NativeThreadProtocol.h b/lldb/include/lldb/Host/common/NativeThreadProtocol.h index 5cf26bd959399..35ccc48d62d8f 100644 --- a/lldb/include/lldb/Host/common/NativeThreadProtocol.h +++ b/lldb/include/lldb/Host/common/NativeThreadProtocol.h @@ -12,9 +12,13 @@ #include #include "lldb/Host/Debug.h" +#include "lldb/Utility/UnimplementedError.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" + namespace lldb_private { // NativeThreadProtocol class NativeThreadProtocol { @@ -47,6 +51,11 @@ class NativeThreadProtocol { virtual Status RemoveHardwareBreakpoint(lldb::addr_t addr) = 0; + virtual llvm::Expected> + GetSiginfo() const { + return llvm::make_error(); + } + protected: NativeProcessProtocol &m_process; lldb::tid_t m_tid; 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 index 97a7543b5c1ef..ef67619e4835b 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -851,6 +851,7 @@ def add_qSupported_packets(self, client_features=[]): "qXfer:libraries:read", "qXfer:libraries-svr4:read", "qXfer:features:read", + "qXfer:siginfo:read", "qEcho", "QPassSignals", "multiprocess", diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp index 7290d300a41f5..25fc7028216e3 100644 --- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp @@ -135,7 +135,8 @@ NativeProcessFreeBSD::Factory::GetSupportedExtensions() const { 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 diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp index 80b3527aebced..20e9d6cfcd7f4 100644 --- a/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp @@ -313,3 +313,27 @@ NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) { } return s; } + +llvm::Expected> +NativeThreadFreeBSD::GetSiginfo() const { + Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); + + struct ptrace_lwpinfo info; + const auto siginfo_err = NativeProcessFreeBSD::PtraceWrapper( + PT_LWPINFO, GetID(), &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 llvm::MemoryBuffer::getMemBufferCopy( + llvm::StringRef(reinterpret_cast(&info.pl_siginfo), + sizeof(info.pl_siginfo))); +} diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h b/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h index 3ec6daa409e44..6294a7a709635 100644 --- a/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h +++ b/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h @@ -47,6 +47,9 @@ class NativeThreadFreeBSD : public NativeThreadProtocol { Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + llvm::Expected> + GetSiginfo() const override; + private: // Interface for friend classes diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 02d2b33c4273c..74366d720814f 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -292,7 +292,8 @@ NativeProcessLinux::Extension 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 @@ Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, 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); } diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index 65f455a109680..407062ad0b462 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -135,6 +135,10 @@ class NativeProcessLinux : public NativeProcessELF, bool SupportHardwareSingleStepping() const; + /// 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) const; + protected: llvm::Expected> GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; @@ -205,10 +209,6 @@ class NativeProcessLinux : public NativeProcessELF, /// stopping for threads being destroyed. Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid); - /// 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); - /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) /// corresponding to the given thread ID to the memory pointed to by @p /// message. diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp index a7e4e9b13ff0a..50d8ce9c161c2 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -536,3 +536,18 @@ void NativeThreadLinux::MaybeLogStateChange(lldb::StateType new_state) { NativeProcessLinux &NativeThreadLinux::GetProcess() { return static_cast(m_process); } + +const NativeProcessLinux &NativeThreadLinux::GetProcess() const { + return static_cast(m_process); +} + +llvm::Expected> +NativeThreadLinux::GetSiginfo() const { + auto siginfo_buf = + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(sizeof(siginfo_t)); + Status error = + GetProcess().GetSignalInfo(GetID(), siginfo_buf->getBufferStart()); + if (!error.Success()) + return error.ToError(); + return siginfo_buf; +} diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h index c18665b0107ef..030a4012f46a7 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -55,6 +55,11 @@ class NativeThreadLinux : public NativeThreadProtocol { NativeProcessLinux &GetProcess(); + const NativeProcessLinux &GetProcess() const; + + llvm::Expected> + GetSiginfo() const override; + private: // Interface for friend classes diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 30f14a52dfb52..a6749274ca997 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2920,6 +2920,18 @@ GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object, return std::move(*buffer_or_error); } + if (object == "siginfo") { + NativeThreadProtocol *thread = m_current_process->GetCurrentThread(); + if (!thread) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "no current thread"); + + auto buffer_or_error = thread->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 +3850,8 @@ std::vector GDBRemoteCommunicationServerLLGS::HandleFeatures( 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)) diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py index 01ae2428f3388..18461568c02ce 100644 --- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ b/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 @@ def test_qSupported_libraries_svr4(self): 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,83 @@ def test_QEnvironmentHexEncoded(self): 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() + 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.add_process_info_collection_packets() + 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. + process_info = self.parse_process_info_response(context) + pad = "" + if process_info["ptrsize"] == "8": + pad = "i" + signo_idx = 0 + errno_idx = 1 + code_idx = 2 + addr_idx = -1 + SEGV_MAPERR = 1 + if process_info["ostype"] == "linux": + # si_signo, si_errno, si_code, [pad], _sifields._sigfault.si_addr + format_str = "iii{}P".format(pad) + elif process_info["ostype"].startswith("freebsd"): + # si_signo, si_errno, si_code, si_pid, si_uid, si_status, si_addr + format_str = "iiiiiiP" + elif process_info["ostype"].startswith("netbsd"): + # _signo, _code, _errno, [pad], _reason._fault._addr + format_str = "iii{}P".format(pad) + errno_idx = 2 + code_idx = 1 + else: + assert False, "unknown ostype" + + 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