It completes the job of using EvaluateExpressionOptions consistently throughout the inferior function calling mechanism in lldb begun in Greg's patch r194009. It removes a handful of alternate calls into the ClangUserExpression/ClangFunction/ThreadPlanCallFunction which were there for convenience. Using the EvaluateExpressionOptions removes the need for them. Using that it gets the --debug option from Greg's patch to work cleanly. It also adds another EvaluateExpressionOption to not trap exceptions when running expressions. You shouldn't use this option unless you KNOW your expression can't throw beyond itself. This is: <rdar://problem/15374885> At present this is only available through the SB API's or python. It fixes a bug where function calls would unset the ObjC & C++ exception breakpoints without checking whether they were set by somebody else already. llvm-svn: 194182
125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
"""
|
|
Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception.
|
|
"""
|
|
|
|
import unittest2
|
|
import lldb
|
|
import lldbutil
|
|
from lldbtest import *
|
|
|
|
class ExprCommandWithThrowTestCase(TestBase):
|
|
|
|
mydir = os.path.join("expression_command", "call-throws")
|
|
|
|
def setUp(self):
|
|
# Call super's setUp().
|
|
TestBase.setUp(self)
|
|
|
|
self.main_source = "call-throws.m"
|
|
self.main_source_spec = lldb.SBFileSpec (self.main_source)
|
|
|
|
|
|
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
|
@dsym_test
|
|
def test_with_dsym(self):
|
|
"""Test calling a function that throws and ObjC exception."""
|
|
self.buildDsym()
|
|
self.call_function()
|
|
|
|
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin due to ObjC test case")
|
|
@dwarf_test
|
|
def test_with_dwarf(self):
|
|
"""Test calling a function that throws and ObjC exception."""
|
|
self.buildDwarf()
|
|
self.call_function()
|
|
|
|
def check_after_call (self):
|
|
# Check that we are back where we were before:
|
|
frame = self.thread.GetFrameAtIndex(0)
|
|
self.assertTrue (self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly")
|
|
|
|
|
|
def call_function(self):
|
|
"""Test calling function that throws."""
|
|
exe_name = "a.out"
|
|
exe = os.path.join(os.getcwd(), exe_name)
|
|
|
|
target = self.dbg.CreateTarget(exe)
|
|
self.assertTrue(target, VALID_TARGET)
|
|
|
|
breakpoint = target.BreakpointCreateBySourceRegex('I am about to throw.',self.main_source_spec)
|
|
self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT)
|
|
|
|
# Launch the process, and do not stop at the entry point.
|
|
process = target.LaunchSimple(None, None, os.getcwd())
|
|
|
|
self.assertTrue(process, PROCESS_IS_VALID)
|
|
|
|
# Frame #0 should be at our breakpoint.
|
|
threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint)
|
|
|
|
self.assertTrue(len(threads) == 1)
|
|
self.thread = threads[0]
|
|
|
|
options = lldb.SBExpressionOptions()
|
|
options.SetUnwindOnError(True)
|
|
|
|
frame = self.thread.GetFrameAtIndex(0)
|
|
# Store away the PC to check that the functions unwind to the right place after calls
|
|
self.orig_frame_pc = frame.GetPC()
|
|
|
|
value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
|
|
self.assertTrue (value.IsValid())
|
|
self.assertTrue (value.GetError().Success() == False)
|
|
|
|
self.check_after_call()
|
|
|
|
# Okay, now try with a breakpoint in the called code in the case where
|
|
# we are ignoring breakpoint hits.
|
|
handler_bkpt = target.BreakpointCreateBySourceRegex("I felt like it", self.main_source_spec)
|
|
self.assertTrue (handler_bkpt.GetNumLocations() > 0)
|
|
options.SetIgnoreBreakpoints(True)
|
|
options.SetUnwindOnError(True)
|
|
|
|
value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
|
|
|
|
self.assertTrue (value.IsValid() and value.GetError().Success() == False)
|
|
self.check_after_call()
|
|
|
|
# Now set the ObjC language breakpoint and make sure that doesn't interfere with the call:
|
|
exception_bkpt = target.BreakpointCreateForException (lldb.eLanguageTypeObjC, False, True)
|
|
self.assertTrue(exception_bkpt.GetNumLocations() > 0)
|
|
|
|
options.SetIgnoreBreakpoints(True)
|
|
options.SetUnwindOnError(True)
|
|
|
|
value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
|
|
|
|
self.assertTrue (value.IsValid() and value.GetError().Success() == False)
|
|
self.check_after_call()
|
|
|
|
|
|
# Now turn off exception trapping, and call a function that catches the exceptions,
|
|
# and make sure the function actually completes, and we get the right value:
|
|
options.SetTrapExceptions(False)
|
|
value = frame.EvaluateExpression ("[my_class iCatchMyself]", options)
|
|
self.assertTrue (value.IsValid())
|
|
self.assertTrue (value.GetError().Success() == True)
|
|
self.assertTrue (value.GetValueAsUnsigned() == 57)
|
|
self.check_after_call()
|
|
options.SetTrapExceptions(True)
|
|
|
|
# Now set this unwind on error to false, and make sure that we stop where the exception was thrown
|
|
options.SetUnwindOnError(False)
|
|
value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
|
|
|
|
|
|
self.assertTrue (value.IsValid() and value.GetError().Success() == False)
|
|
self.check_after_call()
|
|
|
|
if __name__ == '__main__':
|
|
import atexit
|
|
lldb.SBDebugger.Initialize()
|
|
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
|
unittest2.main()
|