Let's use this as our MRE
use futures::executor; // 0.3.5
pub fn exercise() {
executor::block_on(example());
}
#[inline(never)]
async fn example() {
canary()
}
#[inline(never)]
fn canary() {}
If you view the assembly for this, you'll see how the compiler implements async functions. The async
function returns a type implementing impl Future
, which is powered under the hood by a generator:
playground::example:
subq $24, %rsp
movb $0, 16(%rsp)
movzbl 16(%rsp), %edi
callq *core::future::from_generator@GOTPCREL(%rip)
movb %al, 23(%rsp)
movb 23(%rsp), %al
movb %al, 8(%rsp)
movb 8(%rsp), %al
addq $24, %rsp
retq
The actual body of the async function is moved into the generator, which happens to use the name {{closure}}
:
playground::example::{{closure}}:
;; Lots of instructions removed
movq playground::canary@GOTPCREL(%rip), %rcx
callq *%rcx
jmp .LBB20_2
;; Even more removed
Thus, you can set a breakpoint on that generated function:
(lldb) br set -r '.*example.*closure.*'
(lldb) r
Process 28101 launched: '/tmp/f/target/debug/f' (x86_64)
Process 28101 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000ab0 f`f::example::_$u7b$$u7b$closure$u7d$$u7d$::h2b30ad777e7395f3((null)=(pointer = 0x00007ffeefbff328), (null)=ResumeTy @ 0x00007ffeefbff070) at main.rs:8:20
5 }
6
7 #[inline(never)]
-> 8 async fn example() {
9 canary()
10 }
11
Target 0: (f) stopped.
You could also set a breakpoint on the desired line:
(lldb) breakpoint set --file /private/tmp/f/src/main.rs --line 9
(lldb) r
Process 28113 launched: '/tmp/f/target/debug/f' (x86_64)
Process 28113 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000ad8 f`f::example::_$u7b$$u7b$closure$u7d$$u7d$::h2b30ad777e7395f3((null)=(pointer = 0x00007ffeefbff328), (null)=ResumeTy @ 0x00007ffeefbff070) at main.rs:9:5
6
7 #[inline(never)]
8 async fn example() {
-> 9 canary()
10 }
11
12 #[inline(never)]
Target 0: (f) stopped.
See also: