Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 47 additions & 33 deletions gems/native-tracer/ext/native_tracer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ extern "C" {
struct Recorder {
tracer: Tracer,
active: bool,
to_s_id: ID,
locals_id: ID,
local_get_id: ID,
inst_meth_id: ID,
parameters_id: ID,
class_id: ID,
}

unsafe extern "C" fn recorder_free(ptr: *mut c_void) {
Expand Down Expand Up @@ -101,7 +107,22 @@ unsafe extern "C" fn ruby_recorder_alloc(klass: VALUE) -> VALUE {
let path = Path::new("");
let func_id = tracer.ensure_function_id("<top-level>", path, Line(1));
tracer.events.push(TraceLowLevelEvent::Call(CallRecord { function_id: func_id, args: vec![] }));
let recorder = Box::new(Recorder { tracer, active: false });
let to_s_id = rb_intern(b"to_s\0".as_ptr() as *const c_char);
let locals_id = rb_intern(b"local_variables\0".as_ptr() as *const c_char);
let local_get_id = rb_intern(b"local_variable_get\0".as_ptr() as *const c_char);
let inst_meth_id = rb_intern(b"instance_method\0".as_ptr() as *const c_char);
let parameters_id = rb_intern(b"parameters\0".as_ptr() as *const c_char);
let class_id = rb_intern(b"class\0".as_ptr() as *const c_char);
let recorder = Box::new(Recorder {
tracer,
active: false,
to_s_id,
locals_id,
local_get_id,
inst_meth_id,
parameters_id,
class_id,
});
let ty = std::ptr::addr_of!(RECORDER_TYPE) as *const rb_data_type_t;
rb_data_typed_object_wrap(klass, Box::into_raw(recorder) as *mut c_void, ty)
}
Expand Down Expand Up @@ -151,22 +172,21 @@ unsafe fn cstr_to_string(ptr: *const c_char) -> Option<String> {
CStr::from_ptr(ptr).to_str().ok().map(|s| s.to_string())
}

unsafe fn value_to_string(val: VALUE) -> Option<String> {
unsafe fn value_to_string(val: VALUE, to_s_id: ID) -> Option<String> {
if RB_TYPE_P(val, rb_sys::ruby_value_type::RUBY_T_STRING) {
let ptr = RSTRING_PTR(val);
let len = RSTRING_LEN(val) as usize;
let slice = std::slice::from_raw_parts(ptr as *const u8, len);
return Some(String::from_utf8_lossy(slice).to_string());
}
let to_s_id = rb_intern(b"to_s\0".as_ptr() as *const c_char);
let str_val = rb_funcall(val, to_s_id, 0);
let ptr = RSTRING_PTR(str_val);
let len = RSTRING_LEN(str_val) as usize;
let slice = std::slice::from_raw_parts(ptr as *const u8, len);
Some(String::from_utf8_lossy(slice).to_string())
}

unsafe fn to_value(tracer: &mut Tracer, val: VALUE, depth: usize) -> ValueRecord {
unsafe fn to_value(tracer: &mut Tracer, val: VALUE, depth: usize, to_s_id: ID) -> ValueRecord {
if depth == 0 {
let type_id = tracer.ensure_type_id(TypeKind::Error, "No type");
return ValueRecord::None { type_id };
Expand Down Expand Up @@ -203,45 +223,40 @@ unsafe fn to_value(tracer: &mut Tracer, val: VALUE, depth: usize) -> ValueRecord
let ptr = RARRAY_CONST_PTR(val);
for i in 0..len {
let elem = *ptr.add(i);
elements.push(to_value(tracer, elem, depth - 1));
elements.push(to_value(tracer, elem, depth - 1, to_s_id));
}
let type_id = tracer.ensure_type_id(TypeKind::Seq, "Array");
return ValueRecord::Sequence { elements, is_slice: false, type_id };
}
let class_name = cstr_to_string(rb_obj_classname(val)).unwrap_or_else(|| "Object".to_string());
let text = value_to_string(val).unwrap_or_default();
let text = value_to_string(val, to_s_id).unwrap_or_default();
let type_id = tracer.ensure_type_id(TypeKind::Raw, &class_name);
ValueRecord::Raw { r: text, type_id }
}

unsafe fn record_variables(tracer: &mut Tracer, binding: VALUE) -> Vec<FullValueRecord> {
unsafe fn record_variables(recorder: &mut Recorder, binding: VALUE) -> Vec<FullValueRecord> {
let mut result = Vec::new();
let locals_id = rb_intern(b"local_variables\0".as_ptr() as *const c_char);
let get_id = rb_intern(b"local_variable_get\0".as_ptr() as *const c_char);
let vars = rb_funcall(binding, locals_id, 0);
let vars = rb_funcall(binding, recorder.locals_id, 0);
let len = RARRAY_LEN(vars) as usize;
let ptr = RARRAY_CONST_PTR(vars);
for i in 0..len {
let sym = *ptr.add(i);
let id = rb_sym2id(sym);
let name = CStr::from_ptr(rb_id2name(id)).to_str().unwrap_or("");
let value = rb_funcall(binding, get_id, 1, sym);
let val_rec = to_value(tracer, value, 10);
tracer.register_variable_with_full_value(name, val_rec.clone());
let var_id = tracer.ensure_variable_id(name);
let value = rb_funcall(binding, recorder.local_get_id, 1, sym);
let val_rec = to_value(&mut recorder.tracer, value, 10, recorder.to_s_id);
recorder.tracer.register_variable_with_full_value(name, val_rec.clone());
let var_id = recorder.tracer.ensure_variable_id(name);
result.push(FullValueRecord { variable_id: var_id, value: val_rec });
}
result
}

unsafe fn record_parameters(tracer: &mut Tracer, binding: VALUE, defined_class: VALUE, mid: ID, register: bool) -> Vec<FullValueRecord> {
unsafe fn record_parameters(recorder: &mut Recorder, binding: VALUE, defined_class: VALUE, mid: ID, register: bool) -> Vec<FullValueRecord> {
let mut result = Vec::new();
let inst_meth_id = rb_intern(b"instance_method\0".as_ptr() as *const c_char);
let parameters_id = rb_intern(b"parameters\0".as_ptr() as *const c_char);
let local_get_id = rb_intern(b"local_variable_get\0".as_ptr() as *const c_char);
let method_sym = rb_id2sym(mid);
let method_obj = rb_funcall(defined_class, inst_meth_id, 1, method_sym);
let params_ary = rb_funcall(method_obj, parameters_id, 0);
let method_obj = rb_funcall(defined_class, recorder.inst_meth_id, 1, method_sym);
let params_ary = rb_funcall(method_obj, recorder.parameters_id, 0);
let params_len = RARRAY_LEN(params_ary) as usize;
let params_ptr = RARRAY_CONST_PTR(params_ary);
for i in 0..params_len {
Expand All @@ -260,11 +275,11 @@ unsafe fn record_parameters(tracer: &mut Tracer, binding: VALUE, defined_class:
continue;
}
let name = CStr::from_ptr(name_c).to_str().unwrap_or("");
let value = rb_funcall(binding, local_get_id, 1, name_sym);
let val_rec = to_value(tracer, value, 10);
let value = rb_funcall(binding, recorder.local_get_id, 1, name_sym);
let val_rec = to_value(&mut recorder.tracer, value, 10, recorder.to_s_id);
if register {
tracer.register_variable_with_full_value(name, val_rec.clone());
let var_id = tracer.ensure_variable_id(name);
recorder.tracer.register_variable_with_full_value(name, val_rec.clone());
let var_id = recorder.tracer.ensure_variable_id(name);
result.push(FullValueRecord { variable_id: var_id, value: val_rec });
}
}
Expand Down Expand Up @@ -309,7 +324,7 @@ unsafe extern "C" fn record_event_api(self_val: VALUE, path: VALUE, line: VALUE,
String::from_utf8_lossy(std::slice::from_raw_parts(ptr as *const u8, len)).to_string()
};
let line_num = rb_num2long(line) as i64;
let content_str = value_to_string(content).unwrap_or_default();
let content_str = value_to_string(content, recorder.to_s_id).unwrap_or_default();
record_event(&mut recorder.tracer, &path_str, line_num, &content_str);
rb_sys::Qnil.into()
}
Expand Down Expand Up @@ -350,29 +365,28 @@ unsafe extern "C" fn event_hook_raw(data: VALUE, arg: *mut rb_trace_arg_t) {
let binding = rb_tracearg_binding(arg);
recorder.tracer.register_step(Path::new(&path), Line(line));
if !NIL_P(binding) {
record_variables(&mut recorder.tracer, binding);
record_variables(recorder, binding);
}
} else if (ev & RUBY_EVENT_CALL) != 0 {
let binding = rb_tracearg_binding(arg);

let self_val = rb_tracearg_self(arg);
let mid_sym = rb_tracearg_callee_id(arg);
let mid = rb_sym2id(mid_sym);
let class_id = rb_intern(b"class\0".as_ptr() as *const c_char);
let defined_class = rb_funcall(self_val, class_id, 0);
let defined_class = rb_funcall(self_val, recorder.class_id, 0);
if !NIL_P(binding) {
// register parameter types first
let _ = record_parameters(&mut recorder.tracer, binding, defined_class, mid, false);
let _ = record_parameters(recorder, binding, defined_class, mid, false);
}
let self_rec = to_value(&mut recorder.tracer, self_val, 10);
let self_rec = to_value(&mut recorder.tracer, self_val, 10, recorder.to_s_id);
recorder
.tracer
.register_variable_with_full_value("self", self_rec.clone());

let mut args = if NIL_P(binding) {
vec![]
} else {
record_parameters(&mut recorder.tracer, binding, defined_class, mid, true)
record_parameters(recorder, binding, defined_class, mid, true)
};
args.insert(0, recorder.tracer.arg("self", self_rec));
recorder.tracer.register_step(Path::new(&path), Line(line));
Expand All @@ -391,12 +405,12 @@ unsafe extern "C" fn event_hook_raw(data: VALUE, arg: *mut rb_trace_arg_t) {
} else if (ev & RUBY_EVENT_RETURN) != 0 {
recorder.tracer.register_step(Path::new(&path), Line(line));
let ret = rb_tracearg_return_value(arg);
let val_rec = to_value(&mut recorder.tracer, ret, 10);
let val_rec = to_value(&mut recorder.tracer, ret, 10, recorder.to_s_id);
recorder.tracer.register_variable_with_full_value("<return_value>", val_rec.clone());
recorder.tracer.events.push(TraceLowLevelEvent::Return(ReturnRecord { return_value: val_rec }));
} else if (ev & RUBY_EVENT_RAISE) != 0 {
let exc = rb_tracearg_raised_exception(arg);
if let Some(msg) = value_to_string(exc) {
if let Some(msg) = value_to_string(exc, recorder.to_s_id) {
recorder.tracer.events.push(TraceLowLevelEvent::Event(RecordEvent {
kind: EventLogKind::Error,
metadata: String::new(),
Expand Down
Loading