@@ -36,11 +36,8 @@ static DUMP_TRACEBACK_INIT: std::sync::Once = std::sync::Once::new();
3636extern "C" {
3737 fn pipe ( pipefd : * mut [ c_int ; 2 ] ) -> c_int ;
3838 fn read ( fd : c_int , buf : * mut c_void , count : usize ) -> isize ;
39- fn write ( fd : c_int , buf : * const c_void , count : usize ) -> isize ;
4039 fn close ( fd : c_int ) -> c_int ;
4140 fn fcntl ( fd : c_int , cmd : c_int , arg : c_int ) -> c_int ;
42- #[ cfg( unix) ]
43- fn PyThreadState_Next ( prev : * mut pyo3_ffi:: PyThreadState ) -> * mut pyo3_ffi:: PyThreadState ;
4441}
4542
4643pub trait RustWrapper {
@@ -358,18 +355,13 @@ const MAX_TRACEBACK_SIZE: usize = 8 * 1024; // 8KB
358355const FRAME_FUNCTION_CAP : usize = 256 ;
359356#[ cfg( unix) ]
360357const FRAME_FILE_CAP : usize = 512 ;
361- #[ cfg( unix) ]
362- const FRAME_TYPE_CAP : usize = 256 ;
363358
364359#[ cfg( unix) ]
365360static FRAME_COLLECTION_GUARD : AtomicBool = AtomicBool :: new ( false ) ;
366361
367362#[ cfg( unix) ]
368363unsafe fn capture_frames_via_python ( emit_frame : unsafe extern "C" fn ( & RuntimeStackFrame ) ) {
369- let mut emitted = false ;
370-
371364 let current = pyo3_ffi:: PyThreadState_Get ( ) ;
372-
373365 if !current. is_null ( ) {
374366 let _ = collect_and_emit_frames_for_thread ( current, emit_frame) ;
375367 }
@@ -430,60 +422,86 @@ unsafe fn emit_python_frame(
430422 return false ;
431423 }
432424
433- let mut file = get_code_attr_utf8 ( frame, b"co_filename\0 " ) ;
434- if file. len ( ) > FRAME_FILE_CAP {
435- file. truncate ( FRAME_FILE_CAP ) ;
436- }
437-
438- let mut function = get_code_attr_utf8 ( frame, b"co_name\0 " ) ;
439- if function. len ( ) > FRAME_FUNCTION_CAP {
440- function. truncate ( FRAME_FUNCTION_CAP ) ;
441- }
425+ let file_view = get_code_attr_utf8_view ( frame, b"co_filename\0 " ) ;
426+ let function_view = get_code_attr_utf8_view ( frame, b"co_name\0 " ) ;
442427 let line_number = pyo3_ffi:: PyFrame_GetLineNumber ( frame) ;
443428
429+ let file_slice = file_view
430+ . as_ref ( )
431+ . map ( |view| view. truncated ( FRAME_FILE_CAP ) )
432+ . unwrap_or ( & [ ] ) ;
433+ let function_slice = function_view
434+ . as_ref ( )
435+ . map ( |view| view. truncated ( FRAME_FUNCTION_CAP ) )
436+ . unwrap_or ( & [ ] ) ;
437+
444438 let runtime_frame = RuntimeStackFrame {
445439 line : if line_number < 0 {
446440 0
447441 } else {
448442 line_number as u32
449443 } ,
450444 column : 0 ,
451- function : function . as_slice ( ) ,
452- file : file . as_slice ( ) ,
445+ function : function_slice ,
446+ file : file_slice ,
453447 type_name : & [ ] ,
454448 } ;
455449
456450 emit_frame ( & runtime_frame) ;
451+
452+ if let Some ( view) = file_view {
453+ pyo3_ffi:: Py_DecRef ( view. obj ) ;
454+ }
455+ if let Some ( view) = function_view {
456+ pyo3_ffi:: Py_DecRef ( view. obj ) ;
457+ }
457458 true
458459}
459460
461+ struct Utf8View {
462+ ptr : * const u8 ,
463+ len : usize ,
464+ obj : * mut pyo3_ffi:: PyObject ,
465+ }
466+
467+ impl Utf8View {
468+ /// Safety: caller must ensure the underlying PyObject outlives the returned slice.
469+ /// This should be safe since no garbage collection should be happening while suspended in
470+ /// crash context
471+ fn truncated ( & self , cap : usize ) -> & [ u8 ] {
472+ let len = cmp:: min ( self . len , cap) ;
473+ unsafe { slice:: from_raw_parts ( self . ptr , len) }
474+ }
475+ }
476+
477+ /// Returns a view into a PyUnicode attribute without allocating; caller must Py_DecRef `obj`.
460478#[ cfg( unix) ]
461- unsafe fn get_code_attr_utf8 ( frame : * mut pyo3_ffi:: PyFrameObject , attr : & [ u8 ] ) -> Vec < u8 > {
479+ unsafe fn get_code_attr_utf8_view (
480+ frame : * mut pyo3_ffi:: PyFrameObject ,
481+ attr : & [ u8 ] ,
482+ ) -> Option < Utf8View > {
462483 let code_obj = pyo3_ffi:: PyFrame_GetCode ( frame) as * mut pyo3_ffi:: PyObject ;
463484 if code_obj. is_null ( ) {
464- return Vec :: new ( ) ;
485+ return None ;
465486 }
466487 let attr_obj = pyo3_ffi:: PyObject_GetAttrString ( code_obj, attr. as_ptr ( ) as * const c_char ) ;
467488 pyo3_ffi:: Py_DecRef ( code_obj) ;
468489 if attr_obj. is_null ( ) {
469- return Vec :: new ( ) ;
490+ return None ;
470491 }
471- let data = py_unicode_to_vec ( attr_obj) ;
472- pyo3_ffi:: Py_DecRef ( attr_obj) ;
473- data
474- }
475492
476- #[ cfg( unix) ]
477- unsafe fn py_unicode_to_vec ( obj : * mut pyo3_ffi:: PyObject ) -> Vec < u8 > {
478- if obj. is_null ( ) {
479- return Vec :: new ( ) ;
480- }
481493 let mut size: pyo3_ffi:: Py_ssize_t = 0 ;
482- let data = pyo3_ffi:: PyUnicode_AsUTF8AndSize ( obj , & mut size) ;
494+ let data = pyo3_ffi:: PyUnicode_AsUTF8AndSize ( attr_obj , & mut size) ;
483495 if data. is_null ( ) || size <= 0 {
484- return Vec :: new ( ) ;
496+ pyo3_ffi:: Py_DecRef ( attr_obj) ;
497+ return None ;
485498 }
486- slice:: from_raw_parts ( data as * const u8 , size as usize ) . to_vec ( )
499+
500+ Some ( Utf8View {
501+ ptr : data as * const u8 ,
502+ len : size as usize ,
503+ obj : attr_obj,
504+ } )
487505}
488506
489507// Attempt to resolve _Py_DumpTracebackThreads at runtime
0 commit comments