1+ //! Runtime tracing module backed by PyO3.
2+ //!
3+ //! Tracer implementations must return `CallbackResult` from every callback so they can
4+ //! signal when CPython should disable further monitoring for a location by propagating
5+ //! the `sys.monitoring.DISABLE` sentinel.
6+
17use std:: fs;
2- use std:: path:: { PathBuf , Path } ;
8+ use std:: path:: Path ;
39use std:: sync:: atomic:: { AtomicBool , Ordering } ;
410use std:: sync:: Once ;
511
612use pyo3:: exceptions:: PyRuntimeError ;
713use pyo3:: prelude:: * ;
8- use std:: fmt;
9-
1014pub mod code_object;
11- pub mod tracer;
1215mod runtime_tracer;
16+ pub mod tracer;
1317pub use crate :: code_object:: { CodeObjectRegistry , CodeObjectWrapper } ;
14- pub use crate :: tracer:: { install_tracer, uninstall_tracer, EventSet , Tracer } ;
18+ pub use crate :: tracer:: {
19+ install_tracer, uninstall_tracer, CallbackOutcome , CallbackResult , EventSet , Tracer ,
20+ } ;
1521
1622/// Global flag tracking whether tracing is active.
1723static ACTIVE : AtomicBool = AtomicBool :: new ( false ) ;
@@ -25,20 +31,14 @@ fn init_rust_logging_with_default(default_filter: &str) {
2531 let env = env_logger:: Env :: default ( ) . default_filter_or ( default_filter) ;
2632 // Use a compact format with timestamps and targets to aid debugging.
2733 let mut builder = env_logger:: Builder :: from_env ( env) ;
28- builder
29- . format_timestamp_micros ( )
30- . format_target ( true ) ;
34+ builder. format_timestamp_micros ( ) . format_target ( true ) ;
3135 let _ = builder. try_init ( ) ;
3236 } ) ;
3337}
3438
3539/// Start tracing using sys.monitoring and runtime_tracing writer.
3640#[ pyfunction]
37- fn start_tracing (
38- path : & str ,
39- format : & str ,
40- activation_path : Option < & str > ,
41- ) -> PyResult < ( ) > {
41+ fn start_tracing ( path : & str , format : & str , activation_path : Option < & str > ) -> PyResult < ( ) > {
4242 // Ensure logging is ready before any tracer logs might be emitted.
4343 // Default only our crate to debug to avoid excessive verbosity from deps.
4444 init_rust_logging_with_default ( "codetracer_python_recorder=debug" ) ;
@@ -49,26 +49,31 @@ fn start_tracing(
4949 // Interpret `path` as a directory where trace files will be written.
5050 let out_dir = Path :: new ( path) ;
5151 if out_dir. exists ( ) && !out_dir. is_dir ( ) {
52- return Err ( PyRuntimeError :: new_err ( "trace path exists and is not a directory" ) ) ;
52+ return Err ( PyRuntimeError :: new_err (
53+ "trace path exists and is not a directory" ,
54+ ) ) ;
5355 }
5456 if !out_dir. exists ( ) {
5557 // Best-effort create the directory tree
56- fs:: create_dir_all ( & out_dir)
57- . map_err ( |e| PyRuntimeError :: new_err ( format ! ( "failed to create trace directory: {}" , e) ) ) ?;
58+ fs:: create_dir_all ( & out_dir) . map_err ( |e| {
59+ PyRuntimeError :: new_err ( format ! ( "failed to create trace directory: {}" , e) )
60+ } ) ?;
5861 }
5962
6063 // Map format string to enum
6164 let fmt = match format. to_lowercase ( ) . as_str ( ) {
6265 "json" => runtime_tracing:: TraceEventsFileFormat :: Json ,
6366 // Use BinaryV0 for "binary" to avoid streaming writer here.
64- "binary" | "binaryv0" | "binary_v0" | "b0" => runtime_tracing:: TraceEventsFileFormat :: BinaryV0 ,
67+ "binary" | "binaryv0" | "binary_v0" | "b0" => {
68+ runtime_tracing:: TraceEventsFileFormat :: BinaryV0
69+ }
6570 //TODO AI! We need to assert! that the format is among the known values.
6671 other => {
6772 eprintln ! ( "Unknown format '{}', defaulting to binary (v0)" , other) ;
6873 runtime_tracing:: TraceEventsFileFormat :: BinaryV0
6974 }
7075 } ;
71-
76+
7277 // Build output file paths inside the directory.
7378 let ( events_path, meta_path, paths_path) = match fmt {
7479 runtime_tracing:: TraceEventsFileFormat :: Json => (
@@ -90,17 +95,10 @@ fn start_tracing(
9095 // Program and args: keep minimal; Python-side API stores full session info if needed
9196 let sys = py. import ( "sys" ) ?;
9297 let argv = sys. getattr ( "argv" ) ?;
93- let program: String = argv
94- . get_item ( 0 ) ?
95- . extract :: < String > ( ) ?;
98+ let program: String = argv. get_item ( 0 ) ?. extract :: < String > ( ) ?;
9699 //TODO: Error-handling. What to do if argv is empty? Does this ever happen?
97100
98- let mut tracer = runtime_tracer:: RuntimeTracer :: new (
99- & program,
100- & [ ] ,
101- fmt,
102- activation_path,
103- ) ;
101+ let mut tracer = runtime_tracer:: RuntimeTracer :: new ( & program, & [ ] , fmt, activation_path) ;
104102
105103 // Start location: prefer activation path, otherwise best-effort argv[0]
106104 let start_path: & Path = activation_path. unwrap_or ( Path :: new ( & program) ) ;
0 commit comments