@@ -19,6 +19,7 @@ use backtrace_ext::SupplementalFrameResolver;
1919use backtrace_ext:: SupplementalInfo ;
2020
2121mod libpython_filter;
22+ mod offsets;
2223
2324/// Setup backtrace-ext to resolve Python frames on supported platforms.
2425/// This function is a no-op if the platform is not supported.
@@ -47,6 +48,7 @@ pub fn init() {
4748#[ derive( Clone , Copy , Debug ) ]
4849pub struct SupportedInfo {
4950 /// Whether the (OS, architecture) combination is supported.
51+ /// Decided by whether the `offsets` can be detected at build time.
5052 pub os_arch : bool ,
5153 /// Whether the C evalframe logic supports frame resolution.
5254 /// This is usually affected by the cpython version.
@@ -60,7 +62,7 @@ impl SupportedInfo {
6062
6163 fn new ( ) -> Self {
6264 Self {
63- os_arch : OFFSET . is_some ( ) ,
65+ os_arch : offsets :: OFFSET_IP . is_some ( ) && offsets :: OFFSET_SP . is_some ( ) ,
6466 c_evalframe : evalframe_sys:: resolve_frame_is_supported ( ) ,
6567 }
6668 }
@@ -71,80 +73,10 @@ pub static SUPPORTED_INFO: LazyLock<SupportedInfo> = LazyLock::new(SupportedInfo
7173#[ derive( Copy , Clone ) ]
7274struct PythonSupplementalFrameResolver ;
7375
74- /// Raw offsets.
75- /// When IP (program counter) is `OFFSET.0 + Sapling_PyEvalFrame`,
76- /// the `PyFrame` can be read at `OFFSET.1 + SP`.
77- const OFFSET : Option < ( usize , usize ) > = {
78- if cfg ! ( all(
79- any( target_os = "linux" , target_os = "macos" ) ,
80- target_arch = "x86_64"
81- ) ) {
82- // Sapling_PyEvalFrame(PyThreadState* tstate, PyFrameObject* f, int exc)
83- // (lldb) disassemble -n Sapling_PyEvalFrame
84- // `Sapling_PyEvalFrame:
85- // <+0>: pushq %rbp
86- // <+1>: movq %rsp, %rbp ; FP
87- // <+4>: subq $0x20, %rsp ; SP = FP - 0x20
88- // <+8>: movq %rdi, -0x18(%rbp)
89- // <+12>: movq %rsi, -0x10(%rbp) ; PyFrame f at FP - 0x10 or SP + 0x10
90- // <+16>: movl %edx, -0x4(%rbp)
91- // <+19>: movq -0x18(%rbp), %rdi
92- // <+23>: movq -0x10(%rbp), %rsi
93- // <+27>: movl -0x4(%rbp), %edx
94- // <+30>: callq 0x8d4eb0 ; symbol stub for: _PyEval_EvalFrameDefault
95- // <+35>: addq $0x20, %rsp
96- // <+39>: popq %rbp
97- // <+40>: retq
98- Some ( ( 35 , 0x10 ) )
99- } else if cfg ! ( all(
100- any( target_os = "linux" , target_os = "macos" ) ,
101- target_arch = "aarch64"
102- ) ) {
103- // <+0>: sub sp, sp, #0x30
104- // <+4>: stp x29, x30, [sp, #0x20]
105- // <+8>: add x29, sp, #0x20 ; FP (x29) = SP + 0x20
106- // <+12>: stur x0, [x29, #-0x8] ; x0 is 1st arg (tstate)
107- // <+16>: str x1, [sp, #0x10] ; x1 is 2nd arg (f), at SP + 0x10
108- // <+20>: str w2, [sp, #0xc]
109- // <+24>: ldur x0, [x29, #-0x8]
110- // <+28>: ldr x1, [sp, #0x10]
111- // <+32>: ldr w2, [sp, #0xc]
112- // <+36>: bl 0x102c76340 ; symbol stub for: _PyEval_EvalFrameDefault
113- // <+40>: ldp x29, x30, [sp, #0x20]
114- // <+44>: add sp, sp, #0x30
115- // <+48>: ret
116- Some ( ( 40 , 0x10 ) )
117- } else if cfg ! ( all(
118- target_os = "windows" ,
119- target_env = "msvc" ,
120- target_arch = "x86_64"
121- ) ) {
122- // <+0>: pushq %rbp
123- // <+1>: subq $0x40, %rsp
124- // <+5>: leaq 0x40(%rsp), %rbp ; FP = SP + 0x40
125- // <+10>: movl %r8d, -0x4(%rbp)
126- // <+14>: movq %rdx, -0x18(%rbp) ; rdx is 2nd arg. FP - 0x18 = SP + 0x28
127- // <+18>: movq %rcx, -0x10(%rbp)
128- // <+22>: movl -0x4(%rbp), %r8d
129- // <+26>: movq -0x18(%rbp), %rdx
130- // <+30>: movq -0x10(%rbp), %rcx
131- // <+34>: callq *0x517e830(%rip)
132- // <+40>: nop
133- // <+41>: addq $0x40, %rsp
134- // <+45>: popq %rbp
135- // <+46>: retq
136- Some ( ( 40 , 0x28 ) )
137- } else {
138- // Unsupported OS or arch.
139- None
140- }
141- } ;
142-
14376impl SupplementalFrameResolver for PythonSupplementalFrameResolver {
14477 fn maybe_extract_supplemental_info ( & self , ip : usize , sp : usize ) -> FrameDecision {
145- let offset: usize = match OFFSET {
146- Some ( o) => o. 0 ,
147- None => return FrameDecision :: Keep ,
78+ let Some ( offset) = offsets:: OFFSET_IP else {
79+ return FrameDecision :: Keep ;
14880 } ;
14981 if ip != ( evalframe_sys:: sapling_py_eval_frame_addr ( ) + offset) {
15082 // Skip native python frames to reduce noise.
@@ -187,7 +119,7 @@ fn extract_python_supplemental_info(sp: usize) -> Option<SupplementalInfo> {
187119 return None ;
188120 }
189121 // Read the `f` variable on stack. See sapling/dbgutil.py, D55728746
190- let offset = OFFSET ? . 1 ;
122+ let offset = offsets :: OFFSET_SP ? ;
191123 let addr = sp. checked_add ( offset) ?;
192124 unsafe {
193125 let frame_ptr: * const * mut libc:: c_void = addr as * const _ ;
0 commit comments