Skip to content

Commit 80533d2

Browse files
committed
Fix the check in StopInfoBreakpoint for "are we currently running an expression"
We were checking "WasTheLastResumeForUserExpression" but that returns true even if that expression was completed, provided we haven't run again. This uses a better check. This is actually fairly hard to trigger. It happens the first time you hit an objc_exception_throw breakpoint and invoke that frame recognizer for that. But I couldn't trigger it using a Python based frame recognizer. So I wrote a test for the objc_exception_throw_breakpoint recognizer which should have been there anyway... It fails (the target auto-continues) w/o this patch and succeeds with it. Differential Revision: https://reviews.llvm.org/D147587 (cherry picked from commit f79c037)
1 parent d41f263 commit 80533d2

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,13 @@ class ProcessModID {
287287
return m_resume_id == m_last_user_expression_resume;
288288
}
289289

290+
bool IsRunningExpression() const {
291+
// Don't return true if we are no longer running an expression:
292+
if (m_running_user_expression || m_running_utility_function)
293+
return true;
294+
return false;
295+
}
296+
290297
void SetRunningUserExpression(bool on) {
291298
if (on)
292299
m_running_user_expression++;

lldb/source/Target/StopInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ class StopInfoBreakpoint : public StopInfo {
332332

333333
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
334334
Process *process = exe_ctx.GetProcessPtr();
335-
if (process->GetModIDRef().IsLastResumeForUserExpression()) {
335+
if (process->GetModIDRef().IsRunningExpression()) {
336336
// If we are in the middle of evaluating an expression, don't run
337337
// asynchronous breakpoint commands or expressions. That could
338338
// lead to infinite recursion if the command or condition re-calls
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
OBJC_SOURCES := main.m
2+
LD_EXTRAS := -framework Foundation
3+
4+
include Makefile.rules
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Test that the built in ObjC exception throw recognizer works
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
import lldbsuite.test.lldbutil as lldbutil
8+
from lldbsuite.test.lldbtest import *
9+
10+
class TestObjCRecognizer(TestBase):
11+
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
@skipUnlessDarwin
15+
def test_exception_recognizer_sub_class(self):
16+
"""There can be many tests in a test case - describe this test here."""
17+
self.build()
18+
self.main_source_file = lldb.SBFileSpec("main.m")
19+
self.objc_recognizer_test(True)
20+
21+
@skipUnlessDarwin
22+
def test_exception_recognizer_plain(self):
23+
"""There can be many tests in a test case - describe this test here."""
24+
self.build()
25+
self.main_source_file = lldb.SBFileSpec("main.m")
26+
self.objc_recognizer_test(False)
27+
28+
def objc_recognizer_test(self, sub_class):
29+
"""Make sure we stop at the exception and get all the fields out of the recognizer.
30+
If sub_class is True, we make a subclass of NSException and throw that."""
31+
if sub_class:
32+
bkpt_string = "Set a breakpoint here for MyException"
33+
else:
34+
bkpt_string = "Set a breakpoint here for plain exception"
35+
36+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
37+
bkpt_string, self.main_source_file)
38+
39+
# Now turn on the ObjC Exception breakpoint and continue to hit it:
40+
exception_bkpt = target.BreakpointCreateForException(lldb.eLanguageTypeObjC, False, True)
41+
self.assertTrue(exception_bkpt.GetNumLocations() > 0, "Got some exception locations")
42+
43+
threads = lldbutil.continue_to_breakpoint(process, exception_bkpt)
44+
self.assertEqual(len(threads), 1, "One thread hit exception breakpoint")
45+
frame = threads[0].frame[0]
46+
47+
var_opts = lldb.SBVariablesOptions()
48+
var_opts.SetIncludeRecognizedArguments(True)
49+
var_opts.SetUseDynamic(True)
50+
vars = frame.GetVariables(var_opts)
51+
self.assertEqual(len(vars), 1, "Got the synthetic argument")
52+
self.assertTrue(vars[0].IsValid(), "Got a valid Exception variable")
53+
54+
# This will be a pointer
55+
56+
ns_exception_children = [ValueCheck(type="NSObject"),
57+
ValueCheck(name="name", summary='"NSException"'),
58+
ValueCheck(name="reason", summary='"Simple Reason"'),
59+
ValueCheck(name="userInfo"),
60+
ValueCheck(name="reserved")]
61+
ns_exception = ValueCheck(type="NSException", children=ns_exception_children)
62+
if not sub_class:
63+
simple_check = ValueCheck(name="exception", dereference=ns_exception)
64+
simple_check.check_value(self, vars[0], "Simple exception is right")
65+
else:
66+
my_exception_children = [ns_exception, ValueCheck(name="extra_info", type="int", value="100")]
67+
my_exception = ValueCheck(type="MyException", children=my_exception_children)
68+
sub_check = ValueCheck(name="exception", type="MyException *", dereference=my_exception)
69+
sub_check.check_value(self, vars[0], "Subclass exception is right")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface MyException : NSException
4+
{
5+
int extra_info;
6+
}
7+
- (NSException *) initWithExtraInfo: (int) info;
8+
@end
9+
10+
@implementation MyException
11+
- (NSException *) initWithExtraInfo: (int) info
12+
{
13+
[super initWithName: @"NSException" reason: @"Simple Reason" userInfo: nil];
14+
self->extra_info = info;
15+
return self;
16+
}
17+
@end
18+
19+
int
20+
main(int argc, char **argv)
21+
{
22+
// Set a breakpoint here for plain exception:
23+
@try {
24+
NSException *plain_exc = [[NSException alloc] initWithName: @"NSException" reason: @"Simple Reason" userInfo: nil];
25+
[plain_exc raise];
26+
}
27+
@catch (id anException) {}
28+
29+
// Set a breakpoint here for MyException:
30+
@try {
31+
MyException *my_exc = [[MyException alloc] initWithExtraInfo: 100];
32+
[my_exc raise];
33+
}
34+
@catch (id anException) {}
35+
36+
return 0;
37+
}

0 commit comments

Comments
 (0)