Skip to content

Commit fa9fb7a

Browse files
adrian-prantlalexandersandbergcthielen
authored
Add a blog post entry on what's new in Swift debugging in 5.9 (#368)
* Add a blog post entry on what's new in Swift debugging in 5.9 * Update _posts/2023-08-25-whats-new-swift-debugging-5.9.md Co-authored-by: Alexander Sandberg <[email protected]> * Apply suggestions from code review * Streamline scope section * Apply suggestions from code review * Update 2023-08-25-whats-new-swift-debugging-5.9.md Moved LLVM review links * Minor edits --------- Co-authored-by: Alexander Sandberg <[email protected]> Co-authored-by: Christopher Thielen <[email protected]>
1 parent 8dff009 commit fa9fb7a

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

_data/authors.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,22 @@ FranzBusch:
394394
github: FranzBusch
395395
gravatar: 3b34dbe9f3870b0e9bf1f4cb0750fa3d
396396
about: "Franz Busch is a member of a team developing foundational server-side Swift libraries at Apple, and is a member of the SSWG."
397+
398+
adrian-prantl:
399+
name: Adrian Prantl
400+
gravatar: 09d425d65431edfdb8ce3bbb2f2e2bf0
401+
402+
github: adrian-prantl
403+
about: "Adrian Prantl manages the Debugger Compiler Integration team at Apple. He works on debug info in the compiler and the Swift plugin in LLDB."
404+
405+
augustonoronha:
406+
name: Augusto Noronha
407+
408+
github: augusto2112
409+
about: "Augusto Noronha works on Swift debugging and is a member of the Debugger Compiler Integration team at Apple."
410+
411+
kastiglione:
412+
name: Dave Lee
413+
414+
github: kastiglione
415+
about: "Dave Lee works on Swift debugging as a member of the Debugger Compiler Integration team at Apple."
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
layout: post
3+
published: true
4+
date: 2023-09-28 11:00:00
5+
title: Debugging Improvements in Swift 5.9
6+
author: [adrian-prantl, augustonoronha, kastiglione]
7+
---
8+
9+
10+
Swift 5.9 introduced a number of new debugging features to the compiler and [LLDB](https://lldb.llvm.org/ "LLDB project home page") debugger.
11+
12+
Here are three changes that can help with your everyday debugging workflows.
13+
14+
15+
### Faster variable inspection with `p` and `po`
16+
17+
LLDB provides the shorthand `p` command alias to inspect variables and `po` to call the debugDescription property of objects. Originally, these were aliases for the rather heavyweight `expression` and `expression -O` commands. In Swift 5.9, the `p` and `po` command aliases have been redefined to the new [`dwim-print` command](https://reviews.llvm.org/D138315 "LLVM review").
18+
19+
The `dwim-print` command prints values using the most user-friendly implementation. "DWIM" is an acronym for "Do What I Mean". Specifically, when printing variables, `dwim-print` will use the same implementation as `frame variable` or `v` instead of the more expensive expression evaluator.
20+
21+
In addition to being faster, using `p` [no longer creates persistent result variables](https://reviews.llvm.org/D145609 "LLVM review") like `$R0`, which are often unused in debugging sessions. Persistent result variables not only incur overhead but also retain any objects they contain, which can be an unexpected side effect for the program execution.
22+
23+
Users who want persistent results on occasion can use `expression` (or a unique prefix such as `expr`) directly instead of `p`. If you wish to enable persistent results every time, you can take advantage of LLDB's handy alias feature and put the following into the `~/.lldbinit` file:
24+
25+
```
26+
command unalias p
27+
command alias p dwim-print --persistent-result on --
28+
```
29+
30+
The `dwim-print` command also gives `po` new functionality. The `po` command can now print Swift objects *even when only given a raw address*. When running `po <object-address>`, LLDB's embedded Swift compiler will automatically evaluate the expression `unsafeBitCast(<object-address>, to: AnyObject.self)` under the hood to produce the expected result.
31+
32+
Before Swift 5.9:
33+
```
34+
(lldb) po 0x00006000025c43d0
35+
(Int) 105553155867600
36+
```
37+
38+
With Swift 5.9:
39+
```
40+
(lldb) po 0x00006000025c43d0
41+
<MyApp.AppDelegate: 0x6000025c43d0>
42+
```
43+
44+
### Support for generic type parameters in expressions
45+
46+
LLDB now supports referring to generic type parameters in expression evaluation. For example, given the following code:
47+
48+
```swift
49+
func use<T>(_ t: T) {
50+
print(t) // break here
51+
}
52+
53+
use(5)
54+
use("Hello!”)
55+
```
56+
57+
Running `po T.self`, when stopped in `use`, will print `Int` when coming in through the first call, and `String` in the second.
58+
59+
In addition to displaying the concrete type of the generic, you can use this to set conditions that look for concrete types. For example, if you add the condition `T.self == String.self` to the above breakpoint, `use` will only stop when the variable `t` is a `String`. (Note this last example only works on nightly builds of the Swift 5.9 toolchain.)
60+
61+
More details about the implementation of this feature can be found in the original [LLDB pull request](https://github.com/apple/llvm-project/pull/5715).
62+
63+
### Fine-grained scope information
64+
65+
The Swift compiler now emits more precise lexical scopes in debug information, allowing the debugger to better distinguish between different variables, like the many variables named `x` in the following example:
66+
67+
```swift
68+
func f(x: AnyObject?) {
69+
// function parameter `x: AnyObject?`
70+
guard let x else {}
71+
// local variable `x: AnyObject`, which shadows the function argument `x`
72+
...
73+
}
74+
```
75+
76+
In Swift 5.9, the compiler now uses more accurate `ASTScope` information to generate the lexical scope hierarchy in the debug information, which results in some behavior changes in the debugger.
77+
78+
In the example below, the local variable `a` is not yet in scope at the call site of `getInt()` and will only be available after it has been assigned. With the debug information produced by previous versions of the Swift compiler, the debugger might have displayed uninitialized memory as the contents of `a` at the call site of `getInt()`. In Swift 5.9, the variable `a` only becomes visible after it has been initialized:
79+
80+
```
81+
1 func getInt() -> Int { return 42 }
82+
2
83+
3 func f() {
84+
4 let a = getInt()
85+
^
86+
5 print(a)
87+
6 }
88+
89+
(lldb) p a
90+
error: <EXPR>:3:1: error: cannot find 'a' in scope
91+
92+
(lldb) n
93+
3 func f() {
94+
4 let a = getInt()
95+
5 print(a)
96+
^
97+
6 }
98+
99+
(lldb) p a
100+
42
101+
```
102+
103+
<!---
104+
In fact, the Swift language's scoping rules allow some astonishing things to be done with variable bindings:
105+
106+
```swift
107+
enum E<T> {
108+
case A(T)
109+
case B(T)
110+
case C(String)
111+
case D(T, T, T)
112+
}
113+
114+
func f<T>(_ e: E<T>) -> String {
115+
switch e {
116+
case .A(let a), .B(let a): return "One variable \(a): T in scope"
117+
case .C(let a): return "One variable \(a): String in scope"
118+
case .D(let a, _, let c): return "One \(a): T and one \(c): T in scope"
119+
default: return "Only the function argument e is in scope"
120+
}
121+
}
122+
```
123+
124+
All of these can be correctly disambiguated by LLDB thanks to lexical scope debug information.
125+
--->
126+
127+
For more details, see the [pull request](https://github.com/apple/swift/pull/64941) that introduced this change.
128+
129+
130+
### Get involved
131+
132+
If you want to learn more about Swift debugging and LLDB, provide feedback, or contribute to the tooling itself, join us in the [LLDB section](https://forums.swift.org/c/development/lldb/13) of the Swift development forums.

0 commit comments

Comments
 (0)