@@ -6,7 +6,6 @@ mod decl {
66 use crate :: vm:: {
77 PyObjectRef , PyResult , VirtualMachine ,
88 frame:: Frame ,
9- frame_snapshot:: SNAPSHOT_ENABLED ,
109 function:: { ArgIntoFloat , OptionalArg } ,
1110 } ;
1211 use alloc:: sync:: Arc ;
@@ -228,41 +227,59 @@ mod decl {
228227 puts ( fd, " (most recent call first):\n " ) ;
229228 }
230229
231- // dump_frame (traceback.c:1037-1087)
230+ /// Dump the current thread's live frame chain to fd (signal-safe).
231+ /// Walks the `Frame.previous` pointer chain starting from the
232+ /// thread-local current frame pointer.
232233 #[ cfg( any( unix, windows) ) ]
233- fn dump_frame ( fd : i32 , filename : & [ u8 ] , lineno : u32 , funcname : & [ u8 ] ) {
234- puts ( fd, " File \" " ) ;
235- puts_bytes ( fd, filename) ;
236- puts ( fd, "\" , line " ) ;
237- dump_decimal ( fd, lineno as usize ) ;
238- puts ( fd, " in " ) ;
239- puts_bytes ( fd, funcname) ;
240- puts ( fd, "\n " ) ;
234+ fn dump_live_frames ( fd : i32 ) {
235+ const MAX_FRAME_DEPTH : usize = 100 ;
236+
237+ let mut frame_ptr = crate :: vm:: vm:: thread:: get_current_frame ( ) ;
238+ if frame_ptr. is_null ( ) {
239+ puts ( fd, " <no Python frame>\n " ) ;
240+ return ;
241+ }
242+ let mut depth = 0 ;
243+ while !frame_ptr. is_null ( ) && depth < MAX_FRAME_DEPTH {
244+ let frame = unsafe { & * frame_ptr } ;
245+ dump_frame_from_raw ( fd, frame) ;
246+ frame_ptr = frame. previous_frame ( ) ;
247+ depth += 1 ;
248+ }
249+ if depth >= MAX_FRAME_DEPTH && !frame_ptr. is_null ( ) {
250+ puts ( fd, " ...\n " ) ;
251+ }
241252 }
242253
243- /// Dump frame snapshots to fd (signal-safe)
254+ /// Dump a single frame's info to fd (signal-safe), reading live data.
244255 #[ cfg( any( unix, windows) ) ]
245- fn dump_snapshot_frames ( fd : i32 ) {
246- use crate :: vm:: frame_snapshot:: { FRAME_SNAPSHOTS , SNAPSHOT_COUNT } ;
247- let count = SNAPSHOT_COUNT . load ( Ordering :: Acquire ) ;
248- if count > 0 {
249- #[ allow( clippy:: needless_range_loop) ]
250- for i in 0 ..count {
251- unsafe {
252- let snap = & FRAME_SNAPSHOTS [ i] ;
253- if snap. filename_len > 0 {
254- dump_frame (
255- fd,
256- & snap. filename [ ..snap. filename_len ] ,
257- snap. lineno ,
258- & snap. funcname [ ..snap. funcname_len ] ,
259- ) ;
260- }
261- }
256+ fn dump_frame_from_raw ( fd : i32 , frame : & Frame ) {
257+ let filename = frame. code . source_path . as_str ( ) ;
258+ let funcname = frame. code . obj_name . as_str ( ) ;
259+ let lasti = frame. lasti ( ) ;
260+ let lineno = if lasti == 0 {
261+ frame. code . first_line_number . map ( |n| n. get ( ) ) . unwrap_or ( 1 ) as u32
262+ } else {
263+ let idx = ( lasti as usize ) . saturating_sub ( 1 ) ;
264+ if idx < frame. code . locations . len ( ) {
265+ frame. code . locations [ idx] . 0 . line . get ( ) as u32
266+ } else {
267+ frame. code . first_line_number . map ( |n| n. get ( ) ) . unwrap_or ( 0 ) as u32
262268 }
269+ } ;
270+
271+ puts ( fd, " File \" " ) ;
272+ puts ( fd, filename) ;
273+ puts ( fd, "\" , line " ) ;
274+ dump_decimal ( fd, lineno as usize ) ;
275+ puts ( fd, " in " ) ;
276+ if funcname. len ( ) > MAX_FUNCTION_NAME_LEN {
277+ puts ( fd, & funcname[ ..MAX_FUNCTION_NAME_LEN ] ) ;
278+ puts ( fd, "..." ) ;
263279 } else {
264- puts ( fd, " <no Python frame> \n " ) ;
280+ puts ( fd, funcname ) ;
265281 }
282+ puts ( fd, "\n " ) ;
266283 }
267284
268285 // faulthandler_dump_traceback (signal-safe, for fatal errors)
@@ -281,16 +298,11 @@ mod decl {
281298 puts ( fd, "Stack (most recent call first):\n " ) ;
282299 }
283300
284- dump_snapshot_frames ( fd) ;
301+ dump_live_frames ( fd) ;
285302
286303 REENTRANT . store ( false , Ordering :: SeqCst ) ;
287304 }
288305
289- /// Snapshot current Python frames into signal-safe global storage.
290- fn snapshot_current_frames ( vm : & VirtualMachine ) {
291- crate :: vm:: frame_snapshot:: do_update_frame_snapshots ( & vm. frames . borrow ( ) ) ;
292- }
293-
294306 const MAX_FUNCTION_NAME_LEN : usize = 500 ;
295307
296308 /// Write a frame's info to an fd using signal-safe I/O.
@@ -444,10 +456,6 @@ mod decl {
444456 . all_threads
445457 . store ( args. all_threads , Ordering :: Relaxed ) ;
446458
447- // Enable frame snapshot updates and immediately snapshot existing frames
448- SNAPSHOT_ENABLED . store ( true , Ordering :: Relaxed ) ;
449- snapshot_current_frames ( vm) ;
450-
451459 // Install signal handlers
452460 if !faulthandler_enable_internal ( ) {
453461 return Err ( vm. new_runtime_error ( "Failed to enable faulthandler" . to_owned ( ) ) ) ;
@@ -753,29 +761,11 @@ mod decl {
753761 FATAL_ERROR . enabled . store ( false , Ordering :: Relaxed ) ;
754762 }
755763
756- // Check if any faulthandler feature needs snapshots
757- #[ cfg( unix) ]
758- fn any_user_signal_enabled ( ) -> bool {
759- for signum in 1 ..64 {
760- if user_signals:: is_enabled ( signum) {
761- return true ;
762- }
763- }
764- false
765- }
766-
767764 // faulthandler_disable_py
768765 #[ pyfunction]
769766 fn disable ( ) -> bool {
770767 let was_enabled = FATAL_ERROR . enabled . load ( Ordering :: Relaxed ) ;
771768 faulthandler_disable_internal ( ) ;
772- // Disable snapshots only if no user signals are registered
773- #[ cfg( unix) ]
774- if !any_user_signal_enabled ( ) {
775- SNAPSHOT_ENABLED . store ( false , Ordering :: Relaxed ) ;
776- }
777- #[ cfg( not( unix) ) ]
778- SNAPSHOT_ENABLED . store ( false , Ordering :: Relaxed ) ;
779769 was_enabled
780770 }
781771
@@ -874,7 +864,7 @@ mod decl {
874864 puts_bytes ( fd, header. as_bytes ( ) ) ;
875865
876866 // Use thread frame slots when threading is enabled (includes all threads).
877- // Fall back to frame snapshots for non-threaded builds.
867+ // Fall back to live frame walking for non-threaded builds.
878868 #[ cfg( feature = "threading" ) ]
879869 {
880870 for ( tid, slot) in & thread_frame_slots {
@@ -885,7 +875,7 @@ mod decl {
885875 #[ cfg( not( feature = "threading" ) ) ]
886876 {
887877 write_thread_id ( fd, false ) ;
888- dump_snapshot_frames ( fd) ;
878+ dump_live_frames ( fd) ;
889879 }
890880
891881 if exit {
@@ -930,10 +920,6 @@ mod decl {
930920
931921 let header = format_timeout ( timeout_us) ;
932922
933- // Enable frame snapshot updates so watchdog can read them
934- SNAPSHOT_ENABLED . store ( true , Ordering :: Relaxed ) ;
935- snapshot_current_frames ( vm) ;
936-
937923 // Snapshot thread frame slots so watchdog can dump tracebacks
938924 #[ cfg( feature = "threading" ) ]
939925 let thread_frame_slots: Vec < ( u64 , ThreadFrameSlot ) > = {
@@ -1076,8 +1062,8 @@ mod decl {
10761062 puts ( user. fd , "Stack (most recent call first):\n " ) ;
10771063 }
10781064
1079- // Dump frame snapshots (updated by main thread on every frame push/pop )
1080- dump_snapshot_frames ( user. fd ) ;
1065+ // Dump live frame chain (walks Frame.previous pointers )
1066+ dump_live_frames ( user. fd ) ;
10811067
10821068 // If chain is enabled, call the previous handler
10831069 if user. chain {
@@ -1140,10 +1126,6 @@ mod decl {
11401126
11411127 let signum = args. signum as usize ;
11421128
1143- // Enable frame snapshot updates and immediately snapshot existing frames
1144- SNAPSHOT_ENABLED . store ( true , Ordering :: Relaxed ) ;
1145- snapshot_current_frames ( vm) ;
1146-
11471129 // Get current handler to save as previous
11481130 let previous = if !user_signals:: is_enabled ( signum) {
11491131 // Install signal handler using sigaction with SA_NODEFER
@@ -1201,11 +1183,11 @@ mod decl {
12011183 // Test functions for faulthandler testing
12021184
12031185 #[ pyfunction]
1204- fn _read_null ( vm : & VirtualMachine ) {
1186+ fn _read_null ( _vm : & VirtualMachine ) {
12051187 #[ cfg( not( target_arch = "wasm32" ) ) ]
12061188 {
12071189 suppress_crash_report ( ) ;
1208- snapshot_current_frames ( vm ) ;
1190+
12091191 unsafe {
12101192 let ptr: * const i32 = core:: ptr:: null ( ) ;
12111193 core:: ptr:: read_volatile ( ptr) ;
@@ -1221,11 +1203,10 @@ mod decl {
12211203 }
12221204
12231205 #[ pyfunction]
1224- fn _sigsegv ( _args : SigsegvArgs , vm : & VirtualMachine ) {
1206+ fn _sigsegv ( _args : SigsegvArgs , _vm : & VirtualMachine ) {
12251207 #[ cfg( not( target_arch = "wasm32" ) ) ]
12261208 {
12271209 suppress_crash_report ( ) ;
1228- snapshot_current_frames ( vm) ;
12291210
12301211 // Write to NULL pointer to trigger a real hardware SIGSEGV,
12311212 // matching CPython's *((volatile int *)NULL) = 0;
@@ -1239,23 +1220,23 @@ mod decl {
12391220 }
12401221
12411222 #[ pyfunction]
1242- fn _sigabrt ( vm : & VirtualMachine ) {
1223+ fn _sigabrt ( _vm : & VirtualMachine ) {
12431224 #[ cfg( not( target_arch = "wasm32" ) ) ]
12441225 {
12451226 suppress_crash_report ( ) ;
1246- snapshot_current_frames ( vm ) ;
1227+
12471228 unsafe {
12481229 libc:: abort ( ) ;
12491230 }
12501231 }
12511232 }
12521233
12531234 #[ pyfunction]
1254- fn _sigfpe ( vm : & VirtualMachine ) {
1235+ fn _sigfpe ( _vm : & VirtualMachine ) {
12551236 #[ cfg( not( target_arch = "wasm32" ) ) ]
12561237 {
12571238 suppress_crash_report ( ) ;
1258- snapshot_current_frames ( vm ) ;
1239+
12591240 unsafe {
12601241 libc:: raise ( libc:: SIGFPE ) ;
12611242 }
0 commit comments