[Lldb-commits] [lldb] r215458 - llgs: corrected Linux signal reception notification for SIGABRT, SIGSEGV and their ilk.

Todd Fiala todd.fiala at gmail.com
Tue Aug 12 10:02:07 PDT 2014


Author: tfiala
Date: Tue Aug 12 12:02:07 2014
New Revision: 215458

URL: http://llvm.org/viewvc/llvm-project?rev=215458&view=rev
Log:
llgs: corrected Linux signal reception notification for SIGABRT, SIGSEGV and their ilk.

Added llgs/debugserver gdb-remote tests around SIGABRT and SIGSEGV signal reception
notification.  Found a few bugs in exception signal handling in Linux llgs.  Fixed those.

Added:
    lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/
    lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/Makefile
    lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteAbort.py
    lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteSegFault.py
    lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/main.cpp
Modified:
    lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
    lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp?rev=215458&r1=215457&r2=215458&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp Tue Aug 12 12:02:07 2014
@@ -2154,77 +2154,78 @@ NativeProcessLinux::MonitorSignal(const
                             info->si_pid,
                             (info->si_pid == getpid ()) ? "is monitor" : "is not monitor",
                             pid);
+    }
 
-        if ((info->si_pid == 0) && info->si_code == SI_USER)
+    // Check for new thread notification.
+    if ((info->si_pid == 0) && (info->si_code == SI_USER))
+    {
+        // A new thread creation is being signaled.  This is one of two parts that come in
+        // a non-deterministic order.  pid is the thread id.
+        if (log)
+            log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " tid %" PRIu64 ": new thread notification",
+                     __FUNCTION__, GetID (), pid);
+
+        // Did we already create the thread?
+        bool already_tracked = false;
+        thread_sp = GetOrCreateThread (pid, already_tracked);
+        assert (thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread");
+
+        // If the thread was already tracked, it means the main thread already received its SIGTRAP for the create.
+        if (already_tracked)
+        {
+            // We can now resume this thread up since it is fully created.
+            reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetRunning ();
+            Resume (thread_sp->GetID (), LLDB_INVALID_SIGNAL_NUMBER);
+        }
+        else
         {
-            // A new thread creation is being signaled.  This is one of two parts that come in
-            // a non-deterministic order.  pid is the thread id.
-            if (log)
-                log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " tid %" PRIu64 ": new thread notification",
-                         __FUNCTION__, GetID (), pid);
-
-            // Did we already create the thread?
-            bool already_tracked = false;
-            thread_sp = GetOrCreateThread (pid, already_tracked);
-            assert (thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread");
-
-            // If the thread was already tracked, it means the main thread already received its SIGTRAP for the create.
-            if (already_tracked)
-            {
-                // We can now resume this thread up since it is fully created.
-                reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetRunning ();
-                Resume (thread_sp->GetID (), LLDB_INVALID_SIGNAL_NUMBER);
-            }
-            else
-            {
-                // Mark the thread as currently launching.  Need to wait for SIGTRAP clone on the main thread before
-                // this thread is ready to go.
-                reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetLaunching ();
-            }
+            // Mark the thread as currently launching.  Need to wait for SIGTRAP clone on the main thread before
+            // this thread is ready to go.
+            reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetLaunching ();
         }
-        else if (info->si_pid == getpid () && (signo == SIGSTOP))
+
+        // Done handling.
+        return;
+    }
+
+    // Check for thread stop notification.
+    if ((info->si_pid == getpid ()) && (info->si_code == SI_TKILL) && (signo == SIGSTOP))
+    {
+        // This is a tgkill()-based stop.
+        if (thread_sp)
         {
-            // This is a tgkill()-based stop.
-            if (thread_sp)
-            {
-                // An inferior thread just stopped.  Mark it as such.
-                reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetStoppedBySignal (signo);
-                SetCurrentThreadID (thread_sp->GetID ());
+            // An inferior thread just stopped.  Mark it as such.
+            reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetStoppedBySignal (signo);
+            SetCurrentThreadID (thread_sp->GetID ());
 
-                // Remove this tid from the wait-for-stop set.
-                Mutex::Locker locker (m_wait_for_stop_tids_mutex);
+            // Remove this tid from the wait-for-stop set.
+            Mutex::Locker locker (m_wait_for_stop_tids_mutex);
 
-                auto removed_count = m_wait_for_stop_tids.erase (thread_sp->GetID ());
-                if (removed_count < 1)
-                {
-                    log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " tid %" PRIu64 ": tgkill()-stopped thread not in m_wait_for_stop_tids",
-                                 __FUNCTION__, GetID (), thread_sp->GetID ());
+            auto removed_count = m_wait_for_stop_tids.erase (thread_sp->GetID ());
+            if (removed_count < 1)
+            {
+                log->Printf ("NativeProcessLinux::%s() pid = %" PRIu64 " tid %" PRIu64 ": tgkill()-stopped thread not in m_wait_for_stop_tids",
+                             __FUNCTION__, GetID (), thread_sp->GetID ());
 
-                }
+            }
 
-                // If this is the last thread in the m_wait_for_stop_tids, we need to notify
-                // the delegate that a stop has occurred now that every thread that was supposed
-                // to stop has stopped.
-                if (m_wait_for_stop_tids.empty ())
+            // If this is the last thread in the m_wait_for_stop_tids, we need to notify
+            // the delegate that a stop has occurred now that every thread that was supposed
+            // to stop has stopped.
+            if (m_wait_for_stop_tids.empty ())
+            {
+                if (log)
                 {
-                    if (log)
-                    {
-                        log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", setting process state to stopped now that all tids marked for stop have completed",
-                                     __FUNCTION__,
-                                     GetID (),
-                                     pid);
-                    }
-                    SetState (StateType::eStateStopped, true);
+                    log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " tid %" PRIu64 ", setting process state to stopped now that all tids marked for stop have completed",
+                                 __FUNCTION__,
+                                 GetID (),
+                                 pid);
                 }
+                SetState (StateType::eStateStopped, true);
             }
         }
-        else
-        {
-            // Hmm, not sure what to do with this.
-            if (log)
-                log->Printf ("NativeProcessLinux::%s() pid %" PRIu64 " unsure how to handle SI_KILL or SI_USER signal", __FUNCTION__, GetID ());
-        }
 
+        // Done handling.
         return;
     }
 
@@ -2265,38 +2266,40 @@ NativeProcessLinux::MonitorSignal(const
         }
         break;
 
+    case SIGABRT:
     case SIGILL:
-        {
-            // lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
-            // Can get the reason from here.
-            // ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info);
-            // FIXME save the crash reason
-            SetState (StateType::eStateCrashed, true);
-        }
-        break;
-
     case SIGFPE:
-        {
-            // lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
-            // Can get the crash reason from below.
-            // ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info);
-            // FIXME save the crash reason
-            SetState (StateType::eStateCrashed, true);
-        }
-        break;
-
     case SIGBUS:
         {
-            // lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
-            // Can get the crash reason from below.
-            // ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info);
-            // FIXME save the crash reason
-            SetState (StateType::eStateCrashed);
+            // Break these out into separate cases once I have more data for each type of signal.
+            lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+            if (!exited)
+            {
+                // This is just a pre-signal-delivery notification of the incoming signal.
+                // Send a stop to the debugger.
+                if (thread_sp)
+                {
+                    reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetStoppedBySignal (signo);
+                    SetCurrentThreadID (thread_sp->GetID ());
+                }
+                SetState (StateType::eStateStopped, true);
+            }
+            else
+            {
+                if (thread_sp)
+                {
+                    // FIXME figure out how to report exit by signal correctly.
+                    const uint64_t exception_type = static_cast<uint64_t> (SIGABRT);
+                    reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetCrashedWithException (exception_type, fault_addr);
+                }
+                SetState (StateType::eStateCrashed, true);
+            }
         }
         break;
 
     default:
-        // FIXME Stop all threads here.
+        if (log)
+            log->Printf ("NativeProcessLinux::%s unhandled signal %s (%d)", __FUNCTION__, GetUnixSignals ().GetSignalAsCString (signo), signo);
         break;
     }
 }

Modified: lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py?rev=215458&r1=215457&r2=215458&view=diff
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py (original)
+++ lldb/trunk/test/tools/lldb-gdbserver/gdbremote_testcase.py Tue Aug 12 12:02:07 2014
@@ -284,11 +284,12 @@ class GdbRemoteTestCaseBase(TestBase):
 
         raise Exception("failed to create a socket to the launched debug monitor after %d tries" % attempts)
 
-    def launch_process_for_attach(self,inferior_args=None, sleep_seconds=3):
+    def launch_process_for_attach(self,inferior_args=None, sleep_seconds=3, exe_path=None):
         # We're going to start a child process that the debug monitor stub can later attach to.
         # This process needs to be started so that it just hangs around for a while.  We'll
         # have it sleep.
-        exe_path = os.path.abspath("a.out")
+        if not exe_path:
+            exe_path = os.path.abspath("a.out")
 
         args = [exe_path]
         if inferior_args:
@@ -298,7 +299,7 @@ class GdbRemoteTestCaseBase(TestBase):
 
         return subprocess.Popen(args)
 
-    def prep_debug_monitor_and_inferior(self, inferior_args=None, inferior_sleep_seconds=3):
+    def prep_debug_monitor_and_inferior(self, inferior_args=None, inferior_sleep_seconds=3, inferior_exe_path=None):
         """Prep the debug monitor, the inferior, and the expected packet stream.
 
         Handle the separate cases of using the debug monitor in attach-to-inferior mode
@@ -323,7 +324,7 @@ class GdbRemoteTestCaseBase(TestBase):
 
         if self._inferior_startup == self._STARTUP_ATTACH or self._inferior_startup == self._STARTUP_ATTACH_MANUALLY:
             # Launch the process that we'll use as the inferior.
-            inferior = self.launch_process_for_attach(inferior_args=inferior_args, sleep_seconds=inferior_sleep_seconds)
+            inferior = self.launch_process_for_attach(inferior_args=inferior_args, sleep_seconds=inferior_sleep_seconds, exe_path=inferior_exe_path)
             self.assertIsNotNone(inferior)
             self.assertTrue(inferior.pid > 0)
             if self._inferior_startup == self._STARTUP_ATTACH:
@@ -336,7 +337,9 @@ class GdbRemoteTestCaseBase(TestBase):
 
         if self._inferior_startup == self._STARTUP_LAUNCH:
             # Build launch args
-            launch_args = [os.path.abspath('a.out')]
+            if not inferior_exe_path:
+                inferior_exe_path = os.path.abspath("a.out")
+            launch_args = [inferior_exe_path]
             if inferior_args:
                 launch_args.extend(inferior_args)
 

Added: lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/Makefile?rev=215458&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/Makefile (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/Makefile Tue Aug 12 12:02:07 2014
@@ -0,0 +1,8 @@
+LEVEL = ../../../make
+
+CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -std=c++11
+# LD_EXTRAS := -lpthread
+CXX_SOURCES := main.cpp
+MAKE_DSYM :=NO
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteAbort.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteAbort.py?rev=215458&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteAbort.py (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteAbort.py Tue Aug 12 12:02:07 2014
@@ -0,0 +1,45 @@
+import unittest2
+
+# Add the directory above ours to the python library path since we
+# will import from there.
+import os.path
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
+
+import gdbremote_testcase
+import signal
+from lldbtest import *
+
+class TestGdbRemoteAbort(gdbremote_testcase.GdbRemoteTestCaseBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    def inferior_abort_received(self):
+        procs = self.prep_debug_monitor_and_inferior(inferior_args=["abort"])
+        self.assertIsNotNone(procs)
+
+        self.test_sequence.add_log_lines([
+            "read packet: $vCont;c#00",
+            {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", "capture":{ 1:"hex_exit_code"} },
+            ], True)
+
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        hex_exit_code = context.get("hex_exit_code")
+        self.assertIsNotNone(hex_exit_code)
+        self.assertEquals(int(hex_exit_code, 16), signal.SIGABRT)
+
+    @debugserver_test
+    @dsym_test
+    def test_inferior_abort_received_debugserver_dsym(self):
+        self.init_debugserver_test()
+        self.buildDsym()
+        self.inferior_abort_received()
+
+    @llgs_test
+    @dwarf_test
+    def test_inferior_abort_received_llgs_dwarf(self):
+        self.init_llgs_test()
+        self.buildDwarf()
+        self.inferior_abort_received()
+

Added: lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteSegFault.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteSegFault.py?rev=215458&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteSegFault.py (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/TestGdbRemoteSegFault.py Tue Aug 12 12:02:07 2014
@@ -0,0 +1,46 @@
+import unittest2
+
+# Add the directory above ours to the python library path since we
+# will import from there.
+import os.path
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
+
+import gdbremote_testcase
+# import signal
+from lldbtest import *
+
+class TestGdbRemoteSegFault(gdbremote_testcase.GdbRemoteTestCaseBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    GDB_REMOTE_STOP_CODE_BAD_ACCESS = 0x91
+
+    def inferior_seg_fault_received(self):
+        procs = self.prep_debug_monitor_and_inferior(inferior_args=["segfault"])
+        self.assertIsNotNone(procs)
+
+        self.test_sequence.add_log_lines([
+            "read packet: $vCont;c#00",
+            {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", "capture":{ 1:"hex_exit_code"} },
+            ], True)
+
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context)
+
+        hex_exit_code = context.get("hex_exit_code")
+        self.assertIsNotNone(hex_exit_code)
+        self.assertEquals(int(hex_exit_code, 16), self.GDB_REMOTE_STOP_CODE_BAD_ACCESS)
+
+    @debugserver_test
+    @dsym_test
+    def test_inferior_seg_fault_received_debugserver_dsym(self):
+        self.init_debugserver_test()
+        self.buildDsym()
+        self.inferior_seg_fault_received()
+
+    @llgs_test
+    @dwarf_test
+    def test_inferior_seg_fault_received_llgs_dwarf(self):
+        self.init_llgs_test()
+        self.buildDwarf()
+        self.inferior_seg_fault_received()

Added: lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/main.cpp?rev=215458&view=auto
==============================================================================
--- lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/main.cpp (added)
+++ lldb/trunk/test/tools/lldb-gdbserver/inferior-crash/main.cpp Tue Aug 12 12:02:07 2014
@@ -0,0 +1,39 @@
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+namespace
+{
+    const char *const SEGFAULT_COMMAND = "segfault";
+    const char *const ABORT_COMMAND = "abort";
+}
+
+int main (int argc, char **argv)
+{
+    if (argc < 2)
+    {
+        std::cout << "expected at least one command provided on the command line" << std::endl;
+    }
+
+	// Process command line args.
+    for (int i = 1; i < argc; ++i)
+    {
+        const char *const command = argv[i];
+        if (std::strstr (command, SEGFAULT_COMMAND))
+        {
+            // Perform a null pointer access.
+            int *const null_int_ptr = nullptr;
+            *null_int_ptr = 0xDEAD;
+        }
+        else if (std::strstr (command, ABORT_COMMAND))
+        {
+            std::abort();
+        }
+        else
+        {
+            std::cout << "Unsupported command: " << command << std::endl;
+        }
+    }
+
+    return 0;
+}





More information about the lldb-commits mailing list