|
1 | | -use std::ffi::CStr; |
2 | | -use std::os::raw::{c_char, c_int}; |
3 | | -use rb_sys::{VALUE, ID, rb_add_event_hook2, rb_event_flag_t, RUBY_EVENT_LINE, rb_sourcefile, rb_sourceline, RUBY_EVENT_HOOK_FLAG_RAW_ARG}; |
4 | | -use runtime_tracing::{TraceWriter, StepRecord}; |
5 | | - |
6 | | -static mut WRITER: Option<TraceWriter<std::fs::File>> = None; |
7 | | - |
8 | | -extern "C" fn event_hook(ev: rb_event_flag_t, _data: VALUE, _self: VALUE, _mid: ID, _klass: VALUE) { |
9 | | - if ev & RUBY_EVENT_LINE as rb_event_flag_t != 0 { |
10 | | - unsafe { |
11 | | - if let Some(writer) = WRITER.as_mut() { |
12 | | - let line: u32 = rb_sourceline() as u32; |
13 | | - let file_ptr: *const c_char = rb_sourcefile(); |
14 | | - if !file_ptr.is_null() { |
15 | | - if let Ok(path) = CStr::from_ptr(file_ptr).to_str() { |
16 | | - let rec = StepRecord { path: path.into(), line }; |
17 | | - let _ = writer.write_step(&rec); |
18 | | - } |
19 | | - } |
20 | | - } |
| 1 | +#![allow(clippy::missing_safety_doc)] |
| 2 | + |
| 3 | +use std::{ffi::CStr, mem::transmute, os::raw::c_char}; |
| 4 | + |
| 5 | +use rb_sys::{ |
| 6 | + // frequently used public items |
| 7 | + rb_add_event_hook2, rb_event_flag_t, |
| 8 | + rb_event_hook_flag_t::RUBY_EVENT_HOOK_FLAG_RAW_ARG, |
| 9 | + ID, VALUE, RUBY_EVENT_LINE, |
| 10 | + |
| 11 | + // the raw-trace-API symbols live in the generated `bindings` module |
| 12 | + bindings::{ |
| 13 | + rb_trace_arg_t, // struct rb_trace_arg |
| 14 | + rb_tracearg_event_flag, // event kind helpers |
| 15 | + rb_tracearg_lineno, |
| 16 | + rb_tracearg_path, |
| 17 | + }, |
| 18 | +}; |
| 19 | + |
| 20 | +/// Raw-argument callback (Ruby will call it when we set |
| 21 | +/// `RUBY_EVENT_HOOK_FLAG_RAW_ARG`). |
| 22 | +/// |
| 23 | +/// C prototype: |
| 24 | +/// ```c |
| 25 | +/// void (*)(VALUE data, rb_trace_arg_t *arg); |
| 26 | +/// ``` |
| 27 | +unsafe extern "C" fn event_hook_raw(_data: VALUE, arg: *mut rb_trace_arg_t) { |
| 28 | + if arg.is_null() { |
| 29 | + return; |
| 30 | + } |
| 31 | + |
| 32 | + let ev: rb_event_flag_t = rb_tracearg_event_flag(arg); |
| 33 | + if (ev & RUBY_EVENT_LINE) == 0 { |
| 34 | + return; |
| 35 | + } |
| 36 | + |
| 37 | + let path_ptr = rb_tracearg_path(arg) as *const c_char; |
| 38 | + let line = rb_tracearg_lineno(arg) as u32; |
| 39 | + |
| 40 | + if !path_ptr.is_null() { |
| 41 | + if let Ok(path) = CStr::from_ptr(path_ptr).to_str() { |
| 42 | + println!("Path: {path}, Line: {line}"); |
21 | 43 | } |
22 | 44 | } |
23 | 45 | } |
24 | 46 |
|
25 | 47 | #[no_mangle] |
26 | 48 | pub extern "C" fn Init_codetracer_ruby_recorder() { |
27 | 49 | unsafe { |
28 | | - let out = std::env::var("CODETRACER_DB_TRACE_PATH").unwrap_or_else(|_| "trace.json".to_string()); |
29 | | - let file = std::fs::File::create(out).expect("failed to create trace output"); |
30 | | - WRITER = Some(TraceWriter::new(file)); |
31 | | - rb_add_event_hook2(Some(event_hook), RUBY_EVENT_LINE as rb_event_flag_t, 0 as VALUE, RUBY_EVENT_HOOK_FLAG_RAW_ARG as i32); |
| 50 | + // rb_add_event_hook2’s first parameter is a function pointer with the |
| 51 | + // classic five-argument signature. We cast our raw callback to that |
| 52 | + // type via an intermediate variable so the sizes match. |
| 53 | + let raw_cb: unsafe extern "C" fn(VALUE, *mut rb_trace_arg_t) = event_hook_raw; |
| 54 | + let cb: unsafe extern "C" fn(rb_event_flag_t, VALUE, VALUE, ID, VALUE) = |
| 55 | + transmute(raw_cb); |
| 56 | + |
| 57 | + rb_add_event_hook2( |
| 58 | + Some(cb), // callback (now cast) |
| 59 | + RUBY_EVENT_LINE, // which events |
| 60 | + 0, // user data |
| 61 | + RUBY_EVENT_HOOK_FLAG_RAW_ARG, |
| 62 | + ); |
32 | 63 | } |
33 | 64 | } |
0 commit comments