From 83bdf32cf4576bb97a7d828beb25e861895fd7e0 Mon Sep 17 00:00:00 2001 From: Ilia K Date: Wed, 11 Feb 2015 04:58:41 +0000 Subject: [PATCH] Fix segfault notification in lldb-mi Summary: This patch adds system exception handling in lldb-mi + tests. All tests pass on OS X. Reviewers: zturner, abidh, clayborg Reviewed By: clayborg Subscribers: emaste, lldb-commits, zturner, clayborg, abidh Differential Revision: http://reviews.llvm.org/D7500 llvm-svn: 228803 --- lldb/test/tools/lldb-mi/TestMiNotification.py | 84 ++++++++++++++++++++++ lldb/test/tools/lldb-mi/main.c | 4 +- .../lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp | 39 ++++++++++ lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h | 1 + 4 files changed, 127 insertions(+), 1 deletion(-) diff --git a/lldb/test/tools/lldb-mi/TestMiNotification.py b/lldb/test/tools/lldb-mi/TestMiNotification.py index 41f4627..ac2751d 100644 --- a/lldb/test/tools/lldb-mi/TestMiNotification.py +++ b/lldb/test/tools/lldb-mi/TestMiNotification.py @@ -122,5 +122,89 @@ class MiNotificationTestCase(lldbmi_testcase.MiTestCaseBase): # Clean up debugserver_child.terminate(force = True) + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + def test_lldbmi_stopped_when_segfault_local(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped when segfault occurred (local).""" + + self.spawnLldbMi(args = None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Set dosegfault=1 and run (to cause a segfault error) + self.runCmd("-data-evaluate-expression \"dosegfault=1\"") + self.expect("\^done,value=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that *stopped is printed + self.expect("\*stopped,reason=\"exception-received\",exception=\"EXC_BAD_ACCESS \(code=1, address=0x0\)\",thread-id=\"1\",stopped-threads=\"all\"") + + @lldbmi_test + @expectedFailureWindows("llvm.org/pr22274: need a pexpect replacement for windows") + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_lldbmi_stopped_when_segfault_remote(self): + """Test that 'lldb-mi --interpreter' notifies after it was stopped when segfault occurred (remote).""" + + # Prepare debugserver + import os, sys + lldb_gdbserver_folder = os.path.abspath(os.path.join(os.path.dirname(os.getcwd()), "lldb-gdbserver")) + sys.path.append(lldb_gdbserver_folder) + import lldbgdbserverutils + debugserver_exe = lldbgdbserverutils.get_debugserver_exe() + if not debugserver_exe: + raise Exception("debugserver not found") + hostname = "localhost" + import random + port = 12000 + random.randint(0,3999) # the same as GdbRemoteTestCaseBase.get_next_port + import pexpect + debugserver_child = pexpect.spawn("%s %s:%d" % (debugserver_exe, hostname, port)) + + self.spawnLldbMi(args = None) + + # Connect to debugserver + self.runCmd("-interpreter-exec command \"platform select remote-macosx --sysroot /\"") + self.expect("\^done") + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + self.runCmd("-interpreter-exec command \"process connect connect://%s:%d\"" % (hostname, port)) + self.expect("\^done") + + try: + # Run to main + self.runCmd("-break-insert -f main") + self.expect("\^done,bkpt={number=\"1\"") + #FIXME -exec-run doesn't work + self.runCmd("-interpreter-exec command \"process launch\"") #FIXME: self.runCmd("-exec-run") + self.expect("\^done") #FIXME: self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\"") + + # Set dosegfault=1 and run (to cause a segfault error) + self.runCmd("-data-evaluate-expression \"dosegfault=1\"") + self.expect("\^done,value=\"1\"") + self.runCmd("-exec-continue") + self.expect("\^running") + + # Test that *stopped is printed + self.expect("\*stopped,reason=\"exception-received\",exception=\"EXC_BAD_ACCESS \(code=1, address=0x0\)\",thread-id=\"1\",stopped-threads=\"all\"") + + # Exit + self.runCmd("-gdb-exit") + self.runCmd("") #FIXME lldb-mi hangs here on Linux; extra return is needed + self.expect("\^exit") + + finally: + # Clean up + debugserver_child.terminate(force = True) + if __name__ == '__main__': unittest2.main() diff --git a/lldb/test/tools/lldb-mi/main.c b/lldb/test/tools/lldb-mi/main.c index d43e440..6549739 100644 --- a/lldb/test/tools/lldb-mi/main.c +++ b/lldb/test/tools/lldb-mi/main.c @@ -11,7 +11,7 @@ extern int a_MyFunction(); extern int b_MyFunction(); extern int infloop(); -int doloop; +int doloop, dosegfault; int g_MyVar = 3; static int s_MyVar = 4; int main (int argc, char const *argv[]) @@ -24,6 +24,8 @@ int main (int argc, char const *argv[]) //BP_localstest -- it must be at line #24 (or fix it in main*.micmds) if (doloop) // BP_doloop infloop(); + if (dosegfault) + *(volatile int *)NULL = 1; if (argc > 1 && *argv[1] == 'l') { a++; printf("a=%d, argv[1]=%s\n", a, argv[1]); //BP_argtest diff --git a/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp b/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp index 56423f6..8a09be2 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp +++ b/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.cpp @@ -796,6 +796,7 @@ CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateStopped(bool &vwrbShouldB break; case lldb::eStopReasonException: pEventType = "eStopReasonException"; + bOk = HandleProcessEventStopException(); break; case lldb::eStopReasonExec: pEventType = "eStopReasonExec"; @@ -935,6 +936,44 @@ CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopSignal(bool &vwrbShouldBrk } //++ ------------------------------------------------------------------------------------ +// Details: Asynchronous event handler for LLDB Process stop exception. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool +CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopException(void) +{ + const lldb::SBProcess sbProcess = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread sbThread = sbProcess.GetSelectedThread(); + const size_t nStopDescriptionLen = sbThread.GetStopDescription(nullptr, 0); + std::shared_ptr spStopDescription(new char[nStopDescriptionLen]); + sbThread.GetStopDescription(spStopDescription.get(), nStopDescriptionLen); + + // MI print "*stopped,reason=\"exception-received\",exception=\"%s\",thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("exception-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord(CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMIUtilString strReason(spStopDescription.get()); + const CMICmnMIValueConst miValueConst2(strReason); + const CMICmnMIValueResult miValueResult2("exception", miValueConst2); + bool bOk = miOutOfBandRecord.Add(miValueResult2); + const CMIUtilString strThreadId(CMIUtilString::Format("%d", sbThread.GetIndexID())); + const CMICmnMIValueConst miValueConst3(strThreadId); + const CMICmnMIValueResult miValueResult3("thread-id", miValueConst3); + bOk = bOk && miOutOfBandRecord.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("all"); + const CMICmnMIValueResult miValueResult4("stopped-threads", miValueConst4); + bOk = bOk && miOutOfBandRecord.Add(miValueResult4); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && TextToStdout("(gdb)"); + + return bOk; +} + +//++ ------------------------------------------------------------------------------------ // Details: Form partial MI response in a MI value tuple object. // Type: Method. // Args: vwrMiValueTuple - (W) MI value tuple object. diff --git a/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h b/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h index 65b397c..5eb4297 100644 --- a/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h +++ b/lldb/tools/lldb-mi/MICmnLLDBDebuggerHandleEvents.h @@ -77,6 +77,7 @@ class CMICmnLLDBDebuggerHandleEvents : public CMICmnBase, public MI::ISingleton< bool HandleProcessEventStopReasonTrace(void); bool HandleProcessEventStopReasonBreakpoint(void); bool HandleProcessEventStopSignal(bool &vwrbShouldBrk); + bool HandleProcessEventStopException(void); bool HandleProcessEventStateSuspended(const lldb::SBEvent &vEvent); bool MiHelpGetCurrentThreadFrame(CMICmnMIValueTuple &vwrMiValueTuple); bool MiResultRecordToStdout(const CMICmnMIResultRecord &vrMiResultRecord); -- 2.7.4