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);
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 {
#include <memory>
#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 {
virtual Status RemoveHardwareBreakpoint(lldb::addr_t addr) = 0;
+ virtual llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+ GetSiginfo() const {
+ return llvm::make_error<UnimplementedError>();
+ }
+
protected:
NativeProcessProtocol &m_process;
lldb::tid_t m_tid;
"qXfer:libraries:read",
"qXfer:libraries-svr4:read",
"qXfer:features:read",
+ "qXfer:siginfo:read",
"qEcho",
"QPassSignals",
"multiprocess",
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
}
return s;
}
+
+llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+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<const char *>(&info.pl_siginfo),
+ sizeof(info.pl_siginfo)));
+}
Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
+ llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+ GetSiginfo() const override;
+
private:
// Interface for friend classes
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.
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);
}
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<llvm::ArrayRef<uint8_t>>
GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
/// 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.
NativeProcessLinux &NativeThreadLinux::GetProcess() {
return static_cast<NativeProcessLinux &>(m_process);
}
+
+const NativeProcessLinux &NativeThreadLinux::GetProcess() const {
+ return static_cast<const NativeProcessLinux &>(m_process);
+}
+
+llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+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;
+}
NativeProcessLinux &GetProcess();
+ const NativeProcessLinux &GetProcess() const;
+
+ llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
+ GetSiginfo() const override;
+
private:
// Interface for friend classes
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)
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))
import binascii
import itertools
+import struct
import unittest2
import gdbremote_testcase
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 '-')
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