Skip to content

Commit 77290cc

Browse files
[lldb][swift] Disable breakpoint filtering by default
A previous patch had introduced the notion of Language breakpoint filtering when setting line breakpoints. While this is generally a good idea, the swift implementation was motivated by statements like this: ``` async let x = ... await foo() await x ``` All of these lines have many different breakpoint locations associated with them, creating many pauses while stepping; the intent behind the filtering was to provide a smoother stepping experience. The implementation filters breakpoints by "funclet" numbers, disabling all but the lowest such funclet. Unfortunately, this fails when the CFG of an async funclet needs to be cloned in non-trivial ways. For example: ``` func foo(_ argument: Int) async { do { switch argument { case 1: try await willthrow(1) case 2: try await willthrow(2) default: return } } catch { print("breakhere") } } ``` The breakpoint in "breakhere" has two funclets associated with it, one when we throw from case 1, one from case 2. Filtering is incorrect in this situation. This patch disables filtering until we can solve this at a compiler level. While we are disabling this through the TargetProperties setting, which could affect other languages, Swift is the only such language using that setting.
1 parent cdb3381 commit 77290cc

File tree

11 files changed

+76
-4
lines changed

11 files changed

+76
-4
lines changed

lldb/source/Target/TargetProperties.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,6 @@ let Definition = "thread" in {
380380

381381
let Definition = "language" in {
382382
def EnableFilterForLineBreakpoints: Property<"enable-filter-for-line-breakpoints", "Boolean">,
383-
DefaultTrue,
383+
DefaultFalse,
384384
Desc<"If true, allow Language plugins to filter locations when setting breakpoints by line number or regex.">;
385385
}

lldb/test/API/lang/swift/async/stepping/step-in/TestSwiftStepInAsync.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class TestCase(lldbtest.TestBase):
1313
def test(self):
1414
"""Test step-in to async functions"""
1515
self.build()
16+
self.runCmd("settings set language.enable-filter-for-line-breakpoints true")
1617
src = lldb.SBFileSpec('main.swift')
1718
_, process, _, _ = lldbutil.run_to_source_breakpoint(self, 'await', src)
1819

lldb/test/API/lang/swift/async/stepping/step_over/TestSwiftAsyncStepOver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def test(self):
2222
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
2323
self, "BREAK HERE", source_file
2424
)
25+
bkpt.SetEnabled(False) # avoid hitting multiple locations in async breakpoints
2526

2627
expected_line_nums = [4] # print(x)
2728
expected_line_nums += [5, 6, 7, 5, 6, 7, 5] # two runs over the loop

lldb/test/API/lang/swift/async/unwind/backtrace_locals/TestSwiftAsyncBacktraceLocals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def test(self):
2323
self.build()
2424
target, process, thread, main_bkpt = lldbutil.run_to_source_breakpoint(
2525
self, 'main breakpoint', self.src)
26+
main_bkpt.SetEnabled(False) # avoid hitting multiple locations in async breakpoints
2627
self.run_fibo_tests(target, process)
2728

2829
@swiftTest
@@ -34,6 +35,7 @@ def test_actor(self):
3435
self.build()
3536
target, process, thread, main_bkpt = lldbutil.run_to_source_breakpoint(
3637
self, 'main actor breakpoint', self.src)
38+
main_bkpt.SetEnabled(False) # avoid hitting multiple locations in async breakpoints
3739
self.run_fibo_tests(target, process)
3840

3941

lldb/test/API/lang/swift/async/unwind/unwind_in_all_instructions/main.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ func ASYNC___5___() async -> Int {
4343

4444
@main struct Main {
4545
static func main() async {
46-
let result = await ASYNC___5___() // BREAK HERE
46+
print("BREAK HERE")
47+
let result = await ASYNC___5___()
4748
print(result)
4849
}
4950
}

lldb/test/API/lang/swift/async_breakpoints/TestSwiftAsyncBreakpoints.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class TestSwiftAsyncBreakpoints(lldbtest.TestBase):
1313
def test(self):
1414
"""Test async breakpoints"""
1515
self.build()
16+
self.runCmd("settings set language.enable-filter-for-line-breakpoints true")
1617
filespec = lldb.SBFileSpec("main.swift")
1718
target, process, thread, breakpoint1 = lldbutil.run_to_source_breakpoint(
1819
self, "Breakpoint1", filespec
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SWIFT_SOURCES := main.swift
2+
3+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
import lldbsuite.test.lldbtest as lldbtest
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
class TestSwiftAsyncBreakpoints(lldbtest.TestBase):
8+
@swiftTest
9+
@skipIfLinux
10+
def test(self):
11+
"""Test async that async breakpoints are not filtered when the same
12+
statement is present across multiple funclets"""
13+
self.build()
14+
filespec = lldb.SBFileSpec("main.swift")
15+
target, process, thread, breakpoint1 = lldbutil.run_to_source_breakpoint(
16+
self, "breakpoint_start", filespec
17+
)
18+
breakpoint = target.BreakpointCreateBySourceRegex("breakhere", filespec)
19+
self.assertEquals(breakpoint.GetNumLocations(), 2)
20+
21+
process.Continue()
22+
self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonBreakpoint)
23+
self.assertEquals(thread.GetStopDescription(128), "breakpoint 2.1")
24+
self.expect("expr argument", substrs=["1"])
25+
26+
process.Continue()
27+
self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonBreakpoint)
28+
self.assertEquals(thread.GetStopDescription(128), "breakpoint 2.2")
29+
self.expect("expr argument", substrs=["2"])
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
enum MyError: Error {
2+
case MyError1
3+
case MyError2
4+
}
5+
6+
func willthrow(_ arg: Int) async throws {
7+
if arg == 1 { throw MyError.MyError1 } else { throw MyError.MyError2 }
8+
}
9+
10+
func foo(_ argument: Int) async {
11+
do {
12+
switch argument {
13+
case 1:
14+
try await willthrow(1)
15+
case 2:
16+
try await willthrow(2)
17+
default:
18+
return
19+
}
20+
} catch {
21+
print("breakhere")
22+
}
23+
}
24+
25+
print("breakpoint_start")
26+
await foo(1)
27+
await foo(2)

lldb/test/API/lang/swift/protocols/stepping_through_witness/TestSwiftSteppingThroughWitness.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ def test_step_in_and_out(self):
2222
self, "break here", lldb.SBFileSpec("main.swift")
2323
)
2424

25+
# First, get to the await call.
26+
thread.StepOver()
27+
2528
thread.StepInto()
2629
stop_reason = thread.GetStopReason()
2730
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
@@ -48,11 +51,14 @@ def test_step_over(self):
4851
self, "break here", lldb.SBFileSpec("main.swift")
4952
)
5053

54+
# First, get to the await call.
55+
thread.StepOver()
56+
5157
thread.StepOver()
5258
stop_reason = thread.GetStopReason()
5359
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
5460
frame0 = thread.frames[0]
5561
self.assertIn("doMath", frame0.GetFunctionName())
5662

5763
line_entry = frame0.GetLineEntry()
58-
self.assertEqual(14, line_entry.GetLine())
64+
self.assertEqual(15, line_entry.GetLine())

lldb/test/API/lang/swift/protocols/stepping_through_witness/main.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class SlowRandomNumberGenerator: RandomNumberGenerator {
1010
}
1111

1212
func doMath<RNG: RandomNumberGenerator>(with rng: RNG) async {
13-
let y = await rng.random(in: 101...200) // break here
13+
print("break here")
14+
let y = await rng.random(in: 101...200)
1415
print("Y is \(y)")
1516
}
1617

0 commit comments

Comments
 (0)