@@ -15,7 +15,7 @@ mod trace_mapper;
1515
1616use std:: ffi:: { c_char, c_int, c_void} ;
1717
18- // ── C ABI types (must match liblogjet.h exactly) ────────────────────────────
18+ // C ABI types (must match liblogjet.h exactly)
1919
2020#[ repr( C ) ]
2121pub struct LjAttribute {
@@ -66,7 +66,7 @@ pub struct LjIngestDescriptorV1 {
6666 reserved : [ u64 ; 8 ] ,
6767}
6868
69- // ── Signal constants ────────────────────────────────────────────────────────
69+ // Signal constants
7070
7171const LJ_INGEST_SIGNAL_LOGS : u32 = 1 << 0 ;
7272const LJ_INGEST_SIGNAL_METRICS : u32 = 1 << 1 ;
@@ -82,7 +82,7 @@ const LJ_INGEST_RECORD_TYPE_TRACES: u32 = 3;
8282#[ allow( dead_code) ]
8383const LJ_INGEST_RECORD_TYPE_EVENTS : u32 = 4 ;
8484
85- // ── Descriptor ──────────────────────────────────────────────────────────────
85+ // Descriptor
8686
8787struct IngestDescriptor ( LjIngestDescriptorV1 ) ;
8888
@@ -107,16 +107,17 @@ pub extern "C" fn lj_ingest_descriptor_v1() -> *const LjIngestDescriptorV1 {
107107 & PERFETTO_INGEST_DESCRIPTOR . 0
108108}
109109
110- // ── Plugin context ──────────────────────────────────────────────────────────
110+ // Plugin context
111111
112112pub struct PerfettoPlugin {
113113 pub ( crate ) legacy_callback : Option < RecordCallback > ,
114114 pub ( crate ) legacy_user : * mut c_void ,
115115 pub ( crate ) generic_callback : Option < GenericRecordCallback > ,
116116 pub ( crate ) generic_user : * mut c_void ,
117+ last_error : Option < String > ,
117118}
118119
119- // ── Exported C ABI ──────────────────────────────────────────────────────────
120+ // Exported C ABI
120121
121122#[ unsafe( no_mangle) ]
122123pub extern "C" fn lj_ingest_create ( ) -> * mut PerfettoPlugin {
@@ -125,6 +126,7 @@ pub extern "C" fn lj_ingest_create() -> *mut PerfettoPlugin {
125126 legacy_user : std:: ptr:: null_mut ( ) ,
126127 generic_callback : None ,
127128 generic_user : std:: ptr:: null_mut ( ) ,
129+ last_error : None ,
128130 } ) )
129131}
130132
@@ -164,6 +166,23 @@ pub unsafe extern "C" fn lj_ingest_feed(_ctx: *mut PerfettoPlugin, _data: *const
164166 0
165167}
166168
169+ /// Returns the last error message, or NULL if none.
170+ ///
171+ /// # Safety
172+ ///
173+ /// `ctx` must be a valid pointer from `lj_ingest_create`.
174+ #[ unsafe( no_mangle) ]
175+ pub unsafe extern "C" fn lj_ingest_last_error ( ctx : * mut PerfettoPlugin ) -> * const c_char {
176+ if ctx. is_null ( ) {
177+ return std:: ptr:: null ( ) ;
178+ }
179+ let ctx = unsafe { & * ctx } ;
180+ match & ctx. last_error {
181+ Some ( msg) => msg. as_ptr ( ) . cast :: < c_char > ( ) ,
182+ None => std:: ptr:: null ( ) ,
183+ }
184+ }
185+
167186/// Active source: reads a `.pftrace` file, invokes trace_processor, maps
168187/// results to OTel, and streams records through the generic callback.
169188///
@@ -176,31 +195,32 @@ pub unsafe extern "C" fn lj_ingest_fetch(ctx: *mut PerfettoPlugin) -> c_int {
176195 eprintln ! ( "perfetto-ingest: lj_ingest_fetch called with null context" ) ;
177196 return -1 ;
178197 }
179- let ctx = unsafe { & * ctx } ;
198+ let ctx = unsafe { & mut * ctx } ;
180199
181200 let trace_file = match std:: env:: var ( "LJD_PERFETTO_TRACE_FILE" ) {
182201 Ok ( path) => std:: path:: PathBuf :: from ( path) ,
183202 Err ( _) => {
184- eprintln ! ( "perfetto-ingest: LJD_PERFETTO_TRACE_FILE is not set") ;
203+ ctx . last_error = Some ( " LJD_PERFETTO_TRACE_FILE is not set". to_string ( ) ) ;
185204 return -2 ;
186205 }
187206 } ;
188207
189208 if !trace_file. is_file ( ) {
190- eprintln ! ( "perfetto-ingest: trace file not found: {}" , trace_file. display( ) ) ;
209+ ctx . last_error = Some ( format ! ( "trace file not found: {}" , trace_file. display( ) ) ) ;
191210 return -3 ;
192211 }
193212
194213 match run_pipeline ( ctx, & trace_file) {
195214 Ok ( ( ) ) => 0 ,
196215 Err ( err) => {
216+ ctx. last_error = Some ( err. to_string ( ) ) ;
197217 eprintln ! ( "perfetto-ingest: {err}" ) ;
198218 -4
199219 }
200220 }
201221}
202222
203- fn run_pipeline ( plugin : & PerfettoPlugin , trace_file : & std:: path:: Path ) -> Result < ( ) , String > {
223+ fn run_pipeline ( plugin : & mut PerfettoPlugin , trace_file : & std:: path:: Path ) -> Result < ( ) , String > {
204224 let tp_path = perfetto_invoke:: find_trace_processor ( )
205225 . map_err ( |err| format ! ( "trace_processor not found: {err}" ) ) ?;
206226
@@ -270,7 +290,7 @@ pub unsafe extern "C" fn lj_ingest_free(ctx: *mut PerfettoPlugin) {
270290 let _ = unsafe { Box :: from_raw ( ctx) } ;
271291}
272292
273- // ── Record emission helpers ─────────────────────────────────────────────────
293+ // Record emission helpers
274294
275295/// Calls the generic callback with a pre-encoded OTLP payload.
276296///
0 commit comments