[LLDB] Show sub type of signals when debugging a core file
authorDavid Spickett <david.spickett@linaro.org>
Tue, 14 Mar 2023 11:52:48 +0000 (11:52 +0000)
committerDavid Spickett <david.spickett@linaro.org>
Tue, 21 Mar 2023 09:10:09 +0000 (09:10 +0000)
Previously we only looked at the si_signo field, so you got:
```
(lldb) bt
* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV
  * frame #0: 0x00000000004007f4
```
This patch adds si_code so we can show:
```
(lldb) bt
* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: sync tag check fault
  * frame #0: 0x00000000004007f4
```

The order of errno and code was incorrect in ElfLinuxSigInfo::Parse.
It was the order that a "swapped" siginfo arch would use, which for Linux,
is only MIPS. We removed MIPS Linux support some time ago.

See:
https://github.com/torvalds/linux/blob/fe15c26ee26efa11741a7b632e9f23b01aca4cc6/include/uapi/asm-generic/siginfo.h#L121

A test is added using memory tagging faults. Which were the original
motivation for the changes.

Reviewed By: JDevlieghere

Differential Revision: https://reviews.llvm.org/D146045

lldb/include/lldb/Target/StopInfo.h
lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
lldb/source/Target/StopInfo.cpp
lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
llvm/docs/ReleaseNotes.rst

index 9527a6e..8d6284e 100644 (file)
@@ -115,7 +115,8 @@ public:
 
   static lldb::StopInfoSP
   CreateStopReasonWithSignal(Thread &thread, int signo,
-                             const char *description = nullptr);
+                             const char *description = nullptr,
+                             std::optional<int> code = std::nullopt);
 
   static lldb::StopInfoSP CreateStopReasonToTrace(Thread &thread);
 
index 2771d1d..a0f391b 100644 (file)
@@ -922,6 +922,7 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
       if (status.Fail())
         return status.ToError();
       thread_data.signo = siginfo.si_signo;
+      thread_data.code = siginfo.si_code;
       break;
     }
     case ELF::NT_FILE: {
index bb19010..0191562 100644 (file)
@@ -46,7 +46,8 @@ using namespace lldb_private;
 // Construct a Thread object with given data
 ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
     : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
-      m_signo(td.signo), m_gpregset_data(td.gpregset), m_notes(td.notes) {}
+      m_signo(td.signo), m_code(td.code), m_gpregset_data(td.gpregset),
+      m_notes(td.notes) {}
 
 ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
 
@@ -221,11 +222,12 @@ ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) {
 
 bool ThreadElfCore::CalculateStopInfo() {
   ProcessSP process_sp(GetProcess());
-  if (process_sp) {
-    SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, m_signo));
-    return true;
-  }
-  return false;
+  if (!process_sp)
+    return false;
+
+  SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+      *this, m_signo, /*description=*/nullptr, m_code));
+  return true;
 }
 
 // Parse PRSTATUS from NOTE entry
@@ -409,8 +411,8 @@ Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
   // properly, because the struct is for the 64 bit version
   offset_t offset = 0;
   si_signo = data.GetU32(&offset);
-  si_code = data.GetU32(&offset);
   si_errno = data.GetU32(&offset);
+  si_code = data.GetU32(&offset);
 
   return error;
 }
index 8d973bb..2f3ed2a 100644 (file)
@@ -128,6 +128,7 @@ struct ThreadData {
   std::vector<lldb_private::CoreNote> notes;
   lldb::tid_t tid;
   int signo = 0;
+  int code = 0;
   int prstatus_sig = 0;
   std::string name;
 };
@@ -166,6 +167,7 @@ protected:
   lldb::RegisterContextSP m_thread_reg_ctx_sp;
 
   int m_signo;
+  int m_code;
 
   lldb_private::DataExtractor m_gpregset_data;
   std::vector<lldb_private::CoreNote> m_notes;
index ebc355c..a98fc28 100644 (file)
@@ -1044,8 +1044,9 @@ private:
 
 class StopInfoUnixSignal : public StopInfo {
 public:
-  StopInfoUnixSignal(Thread &thread, int signo, const char *description)
-      : StopInfo(thread, signo) {
+  StopInfoUnixSignal(Thread &thread, int signo, const char *description,
+                     std::optional<int> code)
+      : StopInfo(thread, signo), m_code(code) {
     SetDescription(description);
   }
 
@@ -1100,19 +1101,26 @@ public:
     if (m_description.empty()) {
       ThreadSP thread_sp(m_thread_wp.lock());
       if (thread_sp) {
+        UnixSignalsSP unix_signals = thread_sp->GetProcess()->GetUnixSignals();
         StreamString strm;
-        const char *signal_name =
-            thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(
-                m_value);
-        if (signal_name)
-          strm.Printf("signal %s", signal_name);
+        strm << "signal ";
+
+        std::string signal_name =
+            unix_signals->GetSignalDescription(m_value, m_code);
+        if (signal_name.size())
+          strm << signal_name;
         else
-          strm.Printf("signal %" PRIi64, m_value);
+          strm.Printf("%" PRIi64, m_value);
+
         m_description = std::string(strm.GetString());
       }
     }
     return m_description.c_str();
   }
+
+private:
+  // In siginfo_t terms, if m_value is si_signo, m_code is si_code.
+  std::optional<int> m_code;
 };
 
 // StopInfoTrace
@@ -1371,9 +1379,10 @@ StopInfo::CreateStopReasonWithWatchpointID(Thread &thread, break_id_t watch_id,
 }
 
 StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo,
-                                                const char *description) {
+                                                const char *description,
+                                                std::optional<int> code) {
   thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo);
-  return StopInfoSP(new StopInfoUnixSignal(thread, signo, description));
+  return StopInfoSP(new StopInfoUnixSignal(thread, signo, description, code));
 }
 
 StopInfoSP StopInfo::CreateStopReasonToTrace(Thread &thread) {
index a174616..e742dd0 100644 (file)
@@ -166,3 +166,14 @@ class AArch64LinuxMTEMemoryTagCoreFileTestCase(TestBase):
         # the MTE core file which does support it but does not allow writing tags.
         self.expect("memory tag write 0 1",
                 substrs=["error: Process does not support memory tagging"], error=True)
+
+    @skipIfLLVMTargetMissing("AArch64")
+    def test_mte_tag_fault_reason(self):
+        """ Test that we correctly report the fault reason. """
+        self.runCmd("target create --core core.mte")
+
+        # There is no fault address shown here because core files do not include
+        # si_addr.
+        self.expect("bt", substrs=[
+                "* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: "
+                "sync tag check fault"])
index 9d7b1db..d87d207 100644 (file)
@@ -221,6 +221,9 @@ Changes to LLDB
   omit defaulted template parameters. The full template parameter list can still be
   viewed with ``expr --raw-output``/``frame var --raw-output``. (`D141828 <https://reviews.llvm.org/D141828>`_)
 
+* LLDB is now able to show the subtype of signals found in a core file. For example
+  memory tagging specific segfaults such as ``SIGSEGV: sync tag check fault``.
+
 Changes to Sanitizers
 ---------------------