diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -284,6 +284,13 @@ return m_resume_id == m_last_user_expression_resume; } + bool IsRunningExpression() const { + // Don't return true if we are no longer running an expression: + if (m_running_user_expression || m_running_utility_function) + return true; + return false; + } + void SetRunningUserExpression(bool on) { if (on) m_running_user_expression++; diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -332,7 +332,7 @@ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0)); Process *process = exe_ctx.GetProcessPtr(); - if (process->GetModIDRef().IsLastResumeForUserExpression()) { + if (process->GetModIDRef().IsRunningExpression()) { // If we are in the middle of evaluating an expression, don't run // asynchronous breakpoint commands or expressions. That could // lead to infinite recursion if the command or condition re-calls diff --git a/lldb/test/API/macosx/objc_exception_recognizer/Makefile b/lldb/test/API/macosx/objc_exception_recognizer/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/objc_exception_recognizer/Makefile @@ -0,0 +1,4 @@ +OBJC_SOURCES := main.m +LD_EXTRAS := -framework Foundation + +include Makefile.rules diff --git a/lldb/test/API/macosx/objc_exception_recognizer/TestObjCRecognizer.py b/lldb/test/API/macosx/objc_exception_recognizer/TestObjCRecognizer.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/objc_exception_recognizer/TestObjCRecognizer.py @@ -0,0 +1,69 @@ +""" +Test that the built in ObjC exception throw recognizer works +""" + +import lldb +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class TestObjCRecognizer(TestBase): + + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_exception_recognizer_sub_class(self): + """There can be many tests in a test case - describe this test here.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.m") + self.objc_recognizer_test(True) + + @skipUnlessDarwin + def test_exception_recognizer_plain(self): + """There can be many tests in a test case - describe this test here.""" + self.build() + self.main_source_file = lldb.SBFileSpec("main.m") + self.objc_recognizer_test(False) + + def objc_recognizer_test(self, sub_class): + """Make sure we stop at the exception and get all the fields out of the recognizer. + If sub_class is True, we make a subclass of NSException and throw that.""" + if sub_class: + bkpt_string = "Set a breakpoint here for MyException" + else: + bkpt_string = "Set a breakpoint here for plain exception" + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + bkpt_string, self.main_source_file) + + # Now turn on the ObjC Exception breakpoint and continue to hit it: + exception_bkpt = target.BreakpointCreateForException(lldb.eLanguageTypeObjC, False, True) + self.assertTrue(exception_bkpt.GetNumLocations() > 0, "Got some exception locations") + + threads = lldbutil.continue_to_breakpoint(process, exception_bkpt) + self.assertEqual(len(threads), 1, "One thread hit exception breakpoint") + frame = threads[0].frame[0] + + var_opts = lldb.SBVariablesOptions() + var_opts.SetIncludeRecognizedArguments(True) + var_opts.SetUseDynamic(True) + vars = frame.GetVariables(var_opts) + self.assertEqual(len(vars), 1, "Got the synthetic argument") + self.assertTrue(vars[0].IsValid(), "Got a valid Exception variable") + + # This will be a pointer + + ns_exception_children = [ValueCheck(type="NSObject"), + ValueCheck(name="name", summary='"NSException"'), + ValueCheck(name="reason", summary='"Simple Reason"'), + ValueCheck(name="userInfo"), + ValueCheck(name="reserved")] + ns_exception = ValueCheck(type="NSException", children=ns_exception_children) + if not sub_class: + simple_check = ValueCheck(name="exception", dereference=ns_exception) + simple_check.check_value(self, vars[0], "Simple exception is right") + else: + my_exception_children = [ns_exception, ValueCheck(name="extra_info", type="int", value="100")] + my_exception = ValueCheck(type="MyException", children=my_exception_children) + sub_check = ValueCheck(name="exception", type="MyException *", dereference=my_exception) + sub_check.check_value(self, vars[0], "Subclass exception is right") diff --git a/lldb/test/API/macosx/objc_exception_recognizer/main.m b/lldb/test/API/macosx/objc_exception_recognizer/main.m new file mode 100644 --- /dev/null +++ b/lldb/test/API/macosx/objc_exception_recognizer/main.m @@ -0,0 +1,37 @@ +#import + +@interface MyException : NSException +{ + int extra_info; +} +- (NSException *) initWithExtraInfo: (int) info; +@end + +@implementation MyException +- (NSException *) initWithExtraInfo: (int) info +{ + [super initWithName: @"NSException" reason: @"Simple Reason" userInfo: nil]; + self->extra_info = info; + return self; +} +@end + +int +main(int argc, char **argv) +{ + // Set a breakpoint here for plain exception: + @try { + NSException *plain_exc = [[NSException alloc] initWithName: @"NSException" reason: @"Simple Reason" userInfo: nil]; + [plain_exc raise]; + } + @catch (id anException) {} + + // Set a breakpoint here for MyException: + @try { + MyException *my_exc = [[MyException alloc] initWithExtraInfo: 100]; + [my_exc raise]; + } + @catch (id anException) {} + + return 0; +}