@@ -127,12 +127,6 @@ pub(crate) fn emit_crashreport(
127127 }
128128 }
129129
130- // Emit other threads (Phase 1: name/state; Phase 2: also stack if context available).
131- #[ cfg( target_os = "linux" ) ]
132- if config. collect_all_threads ( ) {
133- let _ = emit_all_threads ( pipe, config, ppid, crashing_tid) ;
134- }
135-
136130 writeln ! ( pipe, "{DD_CRASHTRACK_DONE}" ) ?;
137131 pipe. flush ( ) ?;
138132 Ok ( ( ) )
@@ -486,134 +480,6 @@ fn emit_thread_block(
486480 Ok ( ( ) )
487481}
488482
489- /// Read a thread's name from /proc/<pid>/task/<tid>/comm.
490- #[ cfg( target_os = "linux" ) ]
491- fn read_thread_name ( pid : libc:: pid_t , tid : libc:: pid_t ) -> Option < String > {
492- let path = format ! ( "/proc/{pid}/task/{tid}/comm" ) ;
493- std:: fs:: read_to_string ( path)
494- . ok ( )
495- . map ( |s| s. trim_end_matches ( '\n' ) . to_string ( ) )
496- }
497-
498- /// Read a thread's scheduler state letter from /proc/<pid>/task/<tid>/status.
499- /// Returns the single-letter state ("S", "R", "D") or None on failure.
500- #[ cfg( target_os = "linux" ) ]
501- fn read_thread_state ( pid : libc:: pid_t , tid : libc:: pid_t ) -> Option < String > {
502- let path = format ! ( "/proc/{pid}/task/{tid}/status" ) ;
503- let file = std:: fs:: File :: open ( path) . ok ( ) ?;
504- let reader = std:: io:: BufReader :: new ( file) ;
505- for line in reader. lines ( ) {
506- let line = line. ok ( ) ?;
507- if let Some ( rest) = line. strip_prefix ( "State:" ) {
508- return Some ( rest. trim ( ) . to_string ( ) ) ;
509- }
510- }
511- None
512- }
513-
514- /// Enumerate all live thread IDs under /proc/<pid>/task/ using std::fs (safe to
515- /// call from the collector child, where there are no async-signal-safety constraints).
516- #[ cfg( target_os = "linux" ) ]
517- fn enumerate_task_tids ( pid : libc:: pid_t ) -> Vec < libc:: pid_t > {
518- let path = format ! ( "/proc/{pid}/task" ) ;
519- let Ok ( entries) = std:: fs:: read_dir ( & path) else {
520- return vec ! [ ] ;
521- } ;
522- entries
523- . filter_map ( |e| e. ok ( ) )
524- . filter_map ( |e| {
525- e. file_name ( )
526- . to_str ( )
527- . and_then ( |s| s. parse :: < libc:: pid_t > ( ) . ok ( ) )
528- } )
529- . collect ( )
530- }
531-
532- /// Emit thread blocks for all threads other than the crashing thread.
533- ///
534- /// Called from the collector child (after fork), so std::fs and ptrace are safe to use.
535- /// Uses a streaming approach to avoid allocating vectors or hashmaps.
536- /// For each thread:
537- /// - Uses ptrace to capture thread context (registers + stack)
538- /// - Reads name and state from /proc/<ppid>/task/<tid>/
539- /// - Immediately emits the thread block without intermediate storage
540- #[ cfg( target_os = "linux" ) ]
541- fn emit_all_threads (
542- w : & mut impl Write ,
543- config : & CrashtrackerConfiguration ,
544- ppid : libc:: pid_t ,
545- crashing_tid : libc:: pid_t ,
546- ) -> Result < ( ) , EmitterError > {
547- use crate :: collector:: ptrace_collector:: stream_thread_contexts;
548- use std:: time:: Duration ;
549-
550- // Calculate timeout for ptrace operations
551- let context_timeout = Duration :: from_millis ( ( config. timeout ( ) . as_millis ( ) / 2 ) . min ( 200 ) as u64 ) ;
552-
553- let result = stream_thread_contexts (
554- ppid,
555- crashing_tid,
556- config. max_threads ( ) ,
557- context_timeout,
558- |tid, captured_context| {
559- // Read thread metadata from /proc
560- let name = read_thread_name ( ppid, tid) . unwrap_or_else ( || tid. to_string ( ) ) ;
561- let state = read_thread_state ( ppid, tid) ;
562-
563- // Get ucontext pointer if we captured context for this thread
564- let ucontext = captured_context. map ( |ctx| & ctx. ucontext as * const _ ) ;
565-
566- // Immediately emit the thread block
567- match emit_thread_block (
568- w,
569- tid,
570- false ,
571- & name,
572- state. as_deref ( ) ,
573- config. resolve_frames ( ) ,
574- ucontext,
575- ) {
576- Ok ( ( ) ) => true , // Continue with next thread
577- Err ( _) => false , // Stop iteration on write error
578- }
579- } ,
580- ) ;
581-
582- // Handle the case where ptrace setup fails entirely
583- if result. is_err ( ) {
584- // Fall back to thread enumeration without context capture
585- // This provides basic thread information even when ptrace fails
586- let tids = enumerate_task_tids ( ppid) ;
587- let max = config. max_threads ( ) ;
588- let mut emitted = 0 ;
589-
590- for tid in tids {
591- if tid == crashing_tid {
592- continue ;
593- }
594- if emitted >= max {
595- break ;
596- }
597-
598- let name = read_thread_name ( ppid, tid) . unwrap_or_else ( || tid. to_string ( ) ) ;
599- let state = read_thread_state ( ppid, tid) ;
600-
601- emit_thread_block (
602- w,
603- tid,
604- false ,
605- & name,
606- state. as_deref ( ) ,
607- config. resolve_frames ( ) ,
608- None , // No context available
609- ) ?;
610- emitted += 1 ;
611- }
612- }
613-
614- Ok ( ( ) )
615- }
616-
617483fn emit_config ( w : & mut impl Write , config_str : & str ) -> Result < ( ) , EmitterError > {
618484 writeln ! ( w, "{DD_CRASHTRACK_BEGIN_CONFIG}" ) ?;
619485 writeln ! ( w, "{config_str}" ) ?;
0 commit comments