-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[LLDB][ELF Core] Support all the Generic (Negative) SI Codes. #140150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-lldb Author: Jacob Lalonde (Jlalond) ChangesRecently, I was on an issue that generated a large number of Coredumps, and every time in both LLDB and GDB the signal was just This was frustrating because we would expect a Excerpted from the sigaction man page.
For which I added all of the si_codes to every Linux signal. Now for the Coredump that triggered this whole investigation we get the accurate and now very informative summary. <img width="524" alt="image" src="https://github.com/user-attachments/assets/5149f781-ef21-4491-a077-8fac862fbc20" /> Importantly, I didn't add an equivalent of Patch is 24.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140150.diff 6 Files Affected:
diff --git a/lldb/include/lldb/Target/UnixSignals.h b/lldb/include/lldb/Target/UnixSignals.h
index b3605ccefddbe..a1807d69f329b 100644
--- a/lldb/include/lldb/Target/UnixSignals.h
+++ b/lldb/include/lldb/Target/UnixSignals.h
@@ -36,7 +36,9 @@ class UnixSignals {
std::optional<int32_t> code = std::nullopt,
std::optional<lldb::addr_t> addr = std::nullopt,
std::optional<lldb::addr_t> lower = std::nullopt,
- std::optional<lldb::addr_t> upper = std::nullopt) const;
+ std::optional<lldb::addr_t> upper = std::nullopt,
+ std::optional<uint32_t> pid = std::nullopt,
+ std::optional<uint32_t> uid = std::nullopt) const;
bool SignalIsValid(int32_t signo) const;
@@ -105,7 +107,7 @@ class UnixSignals {
llvm::StringRef description,
llvm::StringRef alias = llvm::StringRef());
- enum SignalCodePrintOption { None, Address, Bounds };
+ enum SignalCodePrintOption { None, Address, Bounds, Sender };
// Instead of calling this directly, use a ADD_SIGCODE macro to get compile
// time checks when on the native platform.
diff --git a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
index 9c4fe55147a28..76c32e376eb4b 100644
--- a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
+++ b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
@@ -38,6 +38,28 @@
#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \
AddSignalCode(signal_value, code_value, __VA_ARGS__)
#endif /* if defined(__linux__) && !defined(__mips__) */
+// See siginfo.h in the Linux Kernel, these codes can be sent for any signal.
+#define ADD_LINUX_SIGNAL(signo, name, ...) \
+ AddSignal(signo, name, __VA_ARGS__); \
+ ADD_SIGCODE(signo, signo, SI_QUEUE, -1, "sent by sigqueue", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_TIMER, -2, "sent by timer expiration", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_MESGQ, -3, \
+ "sent by real time mesq state change", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_ASYNCIO, -4, "sent by AIO completion", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_SIGIO, -5, "sent by queued SIGIO", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_TKILL, -6, "sent by tkill system call", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_DETHREAD, -7, \
+ "sent by execve() killing subsidiary threads", \
+ SignalCodePrintOption::Sender); \
+ ADD_SIGCODE(signo, signo, SI_ASYNCNL, -60, \
+ "sent by glibc async name lookup completion", \
+ SignalCodePrintOption::Sender);
using namespace lldb_private;
@@ -46,13 +68,13 @@ LinuxSignals::LinuxSignals() : UnixSignals() { Reset(); }
void LinuxSignals::Reset() {
m_signals.clear();
// clang-format off
- // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
- // ====== ============== ======== ====== ====== ===================================================
- AddSignal(1, "SIGHUP", false, true, true, "hangup");
- AddSignal(2, "SIGINT", true, true, true, "interrupt");
- AddSignal(3, "SIGQUIT", false, true, true, "quit");
+ // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============== ======== ====== ====== ===================================================
+ ADD_LINUX_SIGNAL(1, "SIGHUP", false, true, true, "hangup");
+ ADD_LINUX_SIGNAL(2, "SIGINT", true, true, true, "interrupt");
+ ADD_LINUX_SIGNAL(3, "SIGQUIT", false, true, true, "quit");
- AddSignal(4, "SIGILL", false, true, true, "illegal instruction");
+ ADD_LINUX_SIGNAL(4, "SIGILL", false, true, true, "illegal instruction");
ADD_SIGCODE(SIGILL, 4, ILL_ILLOPC, 1, "illegal opcode");
ADD_SIGCODE(SIGILL, 4, ILL_ILLOPN, 2, "illegal operand");
ADD_SIGCODE(SIGILL, 4, ILL_ILLADR, 3, "illegal addressing mode");
@@ -62,15 +84,15 @@ void LinuxSignals::Reset() {
ADD_SIGCODE(SIGILL, 4, ILL_COPROC, 7, "coprocessor error");
ADD_SIGCODE(SIGILL, 4, ILL_BADSTK, 8, "internal stack error");
- AddSignal(5, "SIGTRAP", true, true, true, "trace trap (not reset when caught)");
- AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
+ ADD_LINUX_SIGNAL(5, "SIGTRAP", true, true, true, "trace trap (not reset when caught)");
+ ADD_LINUX_SIGNAL(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT");
- AddSignal(7, "SIGBUS", false, true, true, "bus error");
+ ADD_LINUX_SIGNAL(7, "SIGBUS", false, true, true, "bus error");
ADD_SIGCODE(SIGBUS, 7, BUS_ADRALN, 1, "illegal alignment");
ADD_SIGCODE(SIGBUS, 7, BUS_ADRERR, 2, "illegal address");
ADD_SIGCODE(SIGBUS, 7, BUS_OBJERR, 3, "hardware error");
- AddSignal(8, "SIGFPE", false, true, true, "floating point exception");
+ ADD_LINUX_SIGNAL(8, "SIGFPE", false, true, true, "floating point exception");
ADD_SIGCODE(SIGFPE, 8, FPE_INTDIV, 1, "integer divide by zero");
ADD_SIGCODE(SIGFPE, 8, FPE_INTOVF, 2, "integer overflow");
ADD_SIGCODE(SIGFPE, 8, FPE_FLTDIV, 3, "floating point divide by zero");
@@ -80,10 +102,10 @@ void LinuxSignals::Reset() {
ADD_SIGCODE(SIGFPE, 8, FPE_FLTINV, 7, "floating point invalid operation");
ADD_SIGCODE(SIGFPE, 8, FPE_FLTSUB, 8, "subscript out of range");
- AddSignal(9, "SIGKILL", false, true, true, "kill");
- AddSignal(10, "SIGUSR1", false, true, true, "user defined signal 1");
+ ADD_LINUX_SIGNAL(9, "SIGKILL", false, true, true, "kill");
+ ADD_LINUX_SIGNAL(10, "SIGUSR1", false, true, true, "user defined signal 1");
- AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ ADD_LINUX_SIGNAL(11, "SIGSEGV", false, true, true, "segmentation violation");
ADD_SIGCODE(SIGSEGV, 11, SEGV_MAPERR, 1, "address not mapped to object", SignalCodePrintOption::Address);
ADD_SIGCODE(SIGSEGV, 11, SEGV_ACCERR, 2, "invalid permissions for mapped object", SignalCodePrintOption::Address);
ADD_SIGCODE(SIGSEGV, 11, SEGV_BNDERR, 3, "failed address bounds checks", SignalCodePrintOption::Bounds);
@@ -94,58 +116,58 @@ void LinuxSignals::Reset() {
// codes. One way to get this is via unaligned SIMD loads. Treat it as invalid address.
ADD_SIGCODE(SIGSEGV, 11, SI_KERNEL, 0x80, "invalid address", SignalCodePrintOption::Address);
- AddSignal(12, "SIGUSR2", false, true, true, "user defined signal 2");
- AddSignal(13, "SIGPIPE", false, true, true, "write to pipe with reading end closed");
- AddSignal(14, "SIGALRM", false, false, false, "alarm");
- AddSignal(15, "SIGTERM", false, true, true, "termination requested");
- AddSignal(16, "SIGSTKFLT", false, true, true, "stack fault");
- AddSignal(17, "SIGCHLD", false, false, true, "child status has changed", "SIGCLD");
- AddSignal(18, "SIGCONT", false, false, true, "process continue");
- AddSignal(19, "SIGSTOP", true, true, true, "process stop");
- AddSignal(20, "SIGTSTP", false, true, true, "tty stop");
- AddSignal(21, "SIGTTIN", false, true, true, "background tty read");
- AddSignal(22, "SIGTTOU", false, true, true, "background tty write");
- AddSignal(23, "SIGURG", false, true, true, "urgent data on socket");
- AddSignal(24, "SIGXCPU", false, true, true, "CPU resource exceeded");
- AddSignal(25, "SIGXFSZ", false, true, true, "file size limit exceeded");
- AddSignal(26, "SIGVTALRM", false, true, true, "virtual time alarm");
- AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm");
- AddSignal(28, "SIGWINCH", false, true, true, "window size changes");
- AddSignal(29, "SIGIO", false, true, true, "input/output ready/Pollable event", "SIGPOLL");
- AddSignal(30, "SIGPWR", false, true, true, "power failure");
- AddSignal(31, "SIGSYS", false, true, true, "invalid system call");
- AddSignal(32, "SIG32", false, false, false, "threading library internal signal 1");
- AddSignal(33, "SIG33", false, false, false, "threading library internal signal 2");
- AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0");
- AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
- AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
- AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
- AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
- AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
- AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
- AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
- AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
- AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
- AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
- AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
- AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
- AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
- AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
- AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
- AddSignal(50, "SIGRTMAX-14", false, false, false, "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill -l" output
- AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
- AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
- AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
- AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
- AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
- AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
- AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
- AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
- AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
- AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
- AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
- AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
- AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
- AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30");
+ ADD_LINUX_SIGNAL(12, "SIGUSR2", false, true, true, "user defined signal 2");
+ ADD_LINUX_SIGNAL(13, "SIGPIPE", false, true, true, "write to pipe with reading end closed");
+ ADD_LINUX_SIGNAL(14, "SIGALRM", false, false, false, "alarm");
+ ADD_LINUX_SIGNAL(15, "SIGTERM", false, true, true, "termination requested");
+ ADD_LINUX_SIGNAL(16, "SIGSTKFLT", false, true, true, "stack fault");
+ ADD_LINUX_SIGNAL(17, "SIGCHLD", false, false, true, "child status has changed", "SIGCLD");
+ ADD_LINUX_SIGNAL(18, "SIGCONT", false, false, true, "process continue");
+ ADD_LINUX_SIGNAL(19, "SIGSTOP", true, true, true, "process stop");
+ ADD_LINUX_SIGNAL(20, "SIGTSTP", false, true, true, "tty stop");
+ ADD_LINUX_SIGNAL(21, "SIGTTIN", false, true, true, "background tty read");
+ ADD_LINUX_SIGNAL(22, "SIGTTOU", false, true, true, "background tty write");
+ ADD_LINUX_SIGNAL(23, "SIGURG", false, true, true, "urgent data on socket");
+ ADD_LINUX_SIGNAL(24, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ ADD_LINUX_SIGNAL(25, "SIGXFSZ", false, true, true, "file size limit exceeded");
+ ADD_LINUX_SIGNAL(26, "SIGVTALRM", false, true, true, "virtual time alarm");
+ ADD_LINUX_SIGNAL(27, "SIGPROF", false, false, false, "profiling time alarm");
+ ADD_LINUX_SIGNAL(28, "SIGWINCH", false, true, true, "window size changes");
+ ADD_LINUX_SIGNAL(29, "SIGIO", false, true, true, "input/output ready/Pollable event", "SIGPOLL");
+ ADD_LINUX_SIGNAL(30, "SIGPWR", false, true, true, "power failure");
+ ADD_LINUX_SIGNAL(31, "SIGSYS", false, true, true, "invalid system call");
+ ADD_LINUX_SIGNAL(32, "SIG32", false, false, false, "threading library internal signal 1");
+ ADD_LINUX_SIGNAL(33, "SIG33", false, false, false, "threading library internal signal 2");
+ ADD_LINUX_SIGNAL(34, "SIGRTMIN", false, false, false, "real time signal 0");
+ ADD_LINUX_SIGNAL(35, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ ADD_LINUX_SIGNAL(36, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ ADD_LINUX_SIGNAL(37, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ ADD_LINUX_SIGNAL(38, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ ADD_LINUX_SIGNAL(39, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ ADD_LINUX_SIGNAL(40, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ ADD_LINUX_SIGNAL(41, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ ADD_LINUX_SIGNAL(42, "SIGRTMIN+8", false, false, false, "real time signal 8");
+ ADD_LINUX_SIGNAL(43, "SIGRTMIN+9", false, false, false, "real time signal 9");
+ ADD_LINUX_SIGNAL(44, "SIGRTMIN+10", false, false, false, "real time signal 10");
+ ADD_LINUX_SIGNAL(45, "SIGRTMIN+11", false, false, false, "real time signal 11");
+ ADD_LINUX_SIGNAL(46, "SIGRTMIN+12", false, false, false, "real time signal 12");
+ ADD_LINUX_SIGNAL(47, "SIGRTMIN+13", false, false, false, "real time signal 13");
+ ADD_LINUX_SIGNAL(48, "SIGRTMIN+14", false, false, false, "real time signal 14");
+ ADD_LINUX_SIGNAL(49, "SIGRTMIN+15", false, false, false, "real time signal 15");
+ ADD_LINUX_SIGNAL(50, "SIGRTMAX-14", false, false, false, "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill -l" output
+ ADD_LINUX_SIGNAL(51, "SIGRTMAX-13", false, false, false, "real time signal 17");
+ ADD_LINUX_SIGNAL(52, "SIGRTMAX-12", false, false, false, "real time signal 18");
+ ADD_LINUX_SIGNAL(53, "SIGRTMAX-11", false, false, false, "real time signal 19");
+ ADD_LINUX_SIGNAL(54, "SIGRTMAX-10", false, false, false, "real time signal 20");
+ ADD_LINUX_SIGNAL(55, "SIGRTMAX-9", false, false, false, "real time signal 21");
+ ADD_LINUX_SIGNAL(56, "SIGRTMAX-8", false, false, false, "real time signal 22");
+ ADD_LINUX_SIGNAL(57, "SIGRTMAX-7", false, false, false, "real time signal 23");
+ ADD_LINUX_SIGNAL(58, "SIGRTMAX-6", false, false, false, "real time signal 24");
+ ADD_LINUX_SIGNAL(59, "SIGRTMAX-5", false, false, false, "real time signal 25");
+ ADD_LINUX_SIGNAL(60, "SIGRTMAX-4", false, false, false, "real time signal 26");
+ ADD_LINUX_SIGNAL(61, "SIGRTMAX-3", false, false, false, "real time signal 27");
+ ADD_LINUX_SIGNAL(62, "SIGRTMAX-2", false, false, false, "real time signal 28");
+ ADD_LINUX_SIGNAL(63, "SIGRTMAX-1", false, false, false, "real time signal 29");
+ ADD_LINUX_SIGNAL(64, "SIGRTMAX", false, false, false, "real time signal 30");
// clang-format on
}
diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
index a0cd0ee5025bd..907e009bc7b80 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -584,22 +584,26 @@ Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch,
// 64b ELF have a 4 byte pad.
if (data.GetAddressByteSize() == 8)
offset += 4;
- // Not every stop signal has a valid address, but that will get resolved in
- // the unix_signals.GetSignalDescription() call below.
- if (unix_signals.GetShouldStop(si_signo)) {
+
+ if (si_code < 0) {
+ sifields.kill.pid = data.GetU32(&offset);
+ sifields.kill.uid = data.GetU32(&offset);
+ } else if (unix_signals.GetShouldStop(si_signo)) {
+ // Not every stop signal has a valid address, but that will get resolved in
+ // the unix_signals.GetSignalDescription() call below.
// Instead of memcpy we call all these individually as the extractor will
// handle endianness for us.
- sigfault.si_addr = data.GetAddress(&offset);
- sigfault.si_addr_lsb = data.GetU16(&offset);
- if (data.GetByteSize() - offset >= sizeof(sigfault.bounds)) {
- sigfault.bounds._addr_bnd._lower = data.GetAddress(&offset);
- sigfault.bounds._addr_bnd._upper = data.GetAddress(&offset);
- sigfault.bounds._pkey = data.GetU32(&offset);
+ sifields.sigfault.si_addr = data.GetAddress(&offset);
+ sifields.sigfault.si_addr_lsb = data.GetU16(&offset);
+ if (data.GetByteSize() - offset >= sizeof(sifields.sigfault.bounds)) {
+ sifields.sigfault.bounds._addr_bnd._lower = data.GetAddress(&offset);
+ sifields.sigfault.bounds._addr_bnd._upper = data.GetAddress(&offset);
+ sifields.sigfault.bounds._pkey = data.GetU32(&offset);
} else {
// Set these to 0 so we don't use bogus data for the description.
- sigfault.bounds._addr_bnd._lower = 0;
- sigfault.bounds._addr_bnd._upper = 0;
- sigfault.bounds._pkey = 0;
+ sifields.sigfault.bounds._addr_bnd._lower = 0;
+ sifields.sigfault.bounds._addr_bnd._upper = 0;
+ sifields.sigfault.bounds._pkey = 0;
}
}
@@ -609,13 +613,18 @@ Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch,
std::string ELFLinuxSigInfo::GetDescription(
const lldb_private::UnixSignals &unix_signals) const {
if (unix_signals.GetShouldStop(si_signo) && note_type == eNT_SIGINFO) {
- if (sigfault.bounds._addr_bnd._upper != 0)
+ if (si_code < 0)
+ return unix_signals.GetSignalDescription(
+ si_signo, si_code, std::nullopt, std::nullopt, std::nullopt,
+ sifields.kill.pid, sifields.kill.uid);
+ else if (sifields.sigfault.bounds._addr_bnd._upper != 0)
return unix_signals.GetSignalDescription(
- si_signo, si_code, sigfault.si_addr, sigfault.bounds._addr_bnd._lower,
- sigfault.bounds._addr_bnd._upper);
+ si_signo, si_code, sifields.sigfault.si_addr,
+ sifields.sigfault.bounds._addr_bnd._lower,
+ sifie...
[truncated]
|
sifields.kill.pid = data.GetU32(&offset); | ||
sifields.kill.uid = data.GetU32(&offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these populating to the same ints if the offset is the same into the data object/struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the data extractor will increment the offset for each call, so the first call will take the first 0-4 bytes, and the second call the next 4-8 bytes.
// the unix_signals.GetSignalDescription() call below. | ||
if (unix_signals.GetShouldStop(si_signo)) { | ||
|
||
if (si_code < 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this check is my primary concern with my own code. In the Linux source I linked they define user space as <0
, and I followed suite here. I would ideally like to make this a static function on LinuxSignals, but wanted to get feedback on how we want to implement this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this functionality, but I'm not overall very happy with how this code ended up assuming a certain siginfo layout (which is likely going to be wrong for non-linux OSes).
Ideally, I think this should be the responsibility of the platform class so that it can be customized for each OS and so that it's available to non-corefile processes as well. The platform class already knows the correct layout for the given architecture (PlatformLinux::GetSiginfoType
) and in the case of a live process we can use the thread siginfo
command to dump the contents of the siginfo_t
structure. I think it would be reasonable to add a function that takes the raw bytes and gives you a short summary of the signal (which you can then put in the stop description).
I refactored to the best of my ability the signal parsing. Thankfully even getting However I'm unhappy with how I'm handling the ValueObject in the Linux Signals, I couldn't find an easy way to make this into some generic siginfo linux type, so I'm directly accessing members. While it's good I made a better way of doing this, I feel this is worse in terms of safety due to having to explore the ValueObject hierarchy. Any advice here? Secondly, while testing I found the layout in the valueobject differs to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a pretty good attempt. Thanks for trying it out.
My main problem with it is that I still think that this functionality should be in the platform class. I have a couple of reasons for that:
- the platform class is the one who defines the layout of the type, so it makes sense for it to be interpreting it as well
- this won't work with the way that live processes work now. Linux process will not get a LinuxSignals object -- they get a GDBRemoteSignals instance instead. It should be easier to make live processes work if the interpretation is in the platform class (and the signals object just provides the data).
- UnixSignals are used in lldb-server and ValueObject is too high-level for it. Having UnixSignals depend on that might explode the dependency tree of lldb-server
Basically, what I'm imagining is to take the function you've written here (GetSignalDescriptionFromSiginfo) and put that into the platform class instead -- plus or minus some interface tweaks (I'd consider having the function return a full-blown StopInfo object instead of just a description).
I don't have a silver bullet. I think it of as a price to pay for being generic. Moving to the platform class should help as then the same piece of code will be responsible for creating the type and traversing it. What might help is to change the interface so that instead of a finished ValueObject, the platform method gets just the raw data (in whatever form is suitable) -- and then it creates the value object internally. That would help because then there would be no doubt that the value/type you get here is the one you created a couple of lines above -- and then I think you could freely access the members without being too defensive. If the data buffer is of the right size, there's no reason why getting any of the siginfo members should fail. I say "might" because this may require some refactoring of Thread::GetSiginfo value to avoid duplicating its implementation -- and I don't know how hard that would be.
That's definitely possible, but I think that just means the platform definition of the struct has a bug somewhere -- maybe it's missing some |
@labath I think I misunderstand the vision you're trying to get at. Are you suggesting we have Platform determine the Signal description and the ProcessElfThread would call out to the platform_sp for it? |
Correct. |
… then parse that in thlinux signals
…gnal differs from the linux definition
b83eae4
to
503e58a
Compare
@labath So I moved the behavior to platform, I agree it's cleaner this way. One area that I couldn't decide is if the ValueObj should be constructed in Platform or not. Because Thread calls out to platform I just decided to call the existing method, but I wanted your feedback on this because it does feel somewhat split purpose. It might make sense to move the code into platform and have thread call platform to create the SP, the issue is that the value object needs a target reference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One area that I couldn't decide is if the ValueObj should be constructed in Platform or not. Because Thread calls out to platform I just decided to call the existing method, but I wanted your feedback on this because it does feel somewhat split purpose. It might make sense to move the code into platform and have thread call platform to create the SP, the issue is that the value object needs a target reference.
I'm not sure about that myself. I think it would make sense, but it's hard to say without seeing how it would look like. I don't think it needs to be done here, but I would encourage you to give it a shot afterwards.
Now for tests. The tests for the UnixSignals part are great, but I'm not sure if there's a test for the end-to-end flow. It possible this automatically code is tested by one of the existing core file. Could you check if anything breaks if you e.g. change GetDescriptionFromSiginfo
to return an empty string? If it does, then it's probably fine. If not, could you check if one of our existing core files has the siginfo stuff we'd need to write a test for this?
int code = siginfo_sp->GetChildMemberWithName("si_code")->GetValueAsSigned(0); | ||
int signo = | ||
siginfo_sp->GetChildMemberWithName("si_signo")->GetValueAsSigned(-1); | ||
// si_code = 0 is SI_NOINFO, we just want the description with nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is. The only reference I can find is https://github.com/torvalds/linux/blob/94305e83eccb3120c921cd3a015cd74731140bac/arch/sparc/include/uapi/asm/siginfo.h#L14 and there it's set to 32767.
0 is SI_USER, which is used by kill
, so in theory uid/pid should be set. However, it's also used as a default value, so maybe not printing the uid/pid in this case is actually correct:
https://github.com/torvalds/linux/blob/94305e83eccb3120c921cd3a015cd74731140bac/kernel/signal.c#L589-L599
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure where I got this information, but it shouldn't matter because the code is not defined. Good eye.
if (siginfo_description.empty()) | ||
return StopInfo::CreateStopReasonWithSignal( | ||
thread, signo_sp->GetValueAsUnsigned(-1)); | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep! I broke basically every core test on my 5th commit, plenty of shell tests that cover this and I ensured I maintained the existing behavior. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, looks good then. Thanks for your patience.
This looks like a good candidate for a release note. You can add that here or in a follow up PR. |
@DavidSpickett I'll make a follow up patch! |
There's a very good chance this is breaking a lot of tests on mac: https://green.lab.llvm.org/job/as-lldb-cmake/26580/console Could you have a look? The only other commit there is a table gen warning fix |
@felipepiovezan I am indeed the problem here. I'm confused how they're passing on Linux but failing on Mac. I'll investigate. |
If you think this might take a while, can we revert this in the meantime? |
@JDevlieghere reverted while I investigate |
…) SI Codes." (#141645) Reverts llvm/llvm-project#140150 Broke the Darwin tests, but they pass on Linux. Reverting to make the build healthy while I investigate
We also saw a build failure on our Linux builders:
|
@petrhosek Hey Petr, can you provide more information? For Linux environments, SI_DETHREAD should be present. The commit for this signal specifically is 13 years old, @DavidSpickett do you have any insight as to why this is happening? (Updated with an actually useful Linux line number) |
I'm not sure what the issue is yet, I checked |
Because the Linux CI is passing, are you opposed to merging the reapply? I'm not sure how I would help debug your builders failing to link it. We could also drop SI_DETHREAD for now and follow up in the future. |
This is a linux kernel header, and is not included when building regular applications. For me, the definition of SI_DETHREAD comes from /usr/include/bits/siginfo-consts.h (which comes from (g)libc). Maybe you don't have it if you have an old libc version. I also checked the glibc version used internally at google (2.27, which is "only" seven years old :/), and it also does not have this symbol.
It's true that these libraries are old, but it seems wrong to make this not build on (old) linuxes if the code actually builds on windows. We are actually providing the hardcoded values of these constants. The only reason we're referencing the os-defined values is so we can validate our values for them. There's already a precedent for handling unavailable constants, so you could just do the same with these. (Although overall, I think it be better to move these checks to a test where the checks for individual constants could be guarded by |
Recently, I was on an issue that generated a large number of Coredumps, and every time in both LLDB and GDB the signal was just
SIGSEGV
.This was frustrating because we would expect a
SIGSEGV
to have an address, or ideally even bounds. After some digging I found thesi_code
consistently was -6. With some help from @cdown, we found neither LLDB or GDB supports the si_codes sent from user space.Excerpted from the sigaction man page.
For which I added all of the si_codes to every Linux signal. Now for the Coredump that triggered this whole investigation we get the accurate and now very informative summary.
Additionally from @labath's suggestion to move this to platform and leverage the existing
getSiginfo()
call on thread, we can now inspect the siginfo struct itself viathread siginfo
. Giving us another towards GDB parity on elf cores.