Skip to content

Commit 414a5ea

Browse files
committed
docs: add native tracer listing
Add listing-003.md detailing build scripts and initial Rust recorder implementation for Fagan inspection.
1 parent cdab341 commit 414a5ea

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

listing-003.md

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Listing 003
2+
3+
This listing examines the build pipeline and initial Rust implementation of the native tracer. We review the `extconf.rb` script, `build.rs`, the crate's `Cargo.toml`, and the opening portions of `src/lib.rs` that set up symbol lookups, recorder state, and early helper functions.
4+
5+
**Invoke mkmf and rb_sys to generate a Makefile for the Rust extension.**
6+
```ruby
7+
require 'mkmf'
8+
require 'rb_sys/mkmf'
9+
10+
create_rust_makefile('codetracer_ruby_recorder')
11+
```
12+
13+
**Activate rb_sys environment variables during Cargo build.**
14+
```rust
15+
fn main() -> Result<(), Box<dyn std::error::Error>> {
16+
rb_sys_env::activate()?;
17+
Ok(())
18+
}
19+
```
20+
21+
**Define package metadata, library type, dependencies, and build helpers.**
22+
```toml
23+
[package]
24+
name = "codetracer_ruby_recorder"
25+
description = "Native Ruby module for generating CodeTracer trace files"
26+
version = "0.1.0"
27+
edition = "2021"
28+
build = "build.rs"
29+
30+
[lib]
31+
crate-type = ["cdylib"]
32+
33+
[dependencies]
34+
rb-sys = "0.9"
35+
runtime_tracing = "0.14.0"
36+
37+
[build-dependencies]
38+
rb-sys-env = "0.2"
39+
40+
[profile.release]
41+
codegen-units = 1
42+
lto = "thin"
43+
opt-level = 3
44+
```
45+
46+
**Allow missing safety docs and import standard, Ruby, and tracing crates.**
47+
```rust
48+
#![allow(clippy::missing_safety_doc)]
49+
50+
use std::{
51+
collections::HashMap,
52+
ffi::CStr,
53+
mem::transmute,
54+
os::raw::{c_char, c_int, c_void},
55+
path::Path,
56+
ptr,
57+
};
58+
59+
use rb_sys::{
60+
rb_cObject, rb_define_alloc_func, rb_define_class, rb_define_method, rb_eIOError,
61+
rb_event_flag_t, rb_funcall, rb_id2name, rb_id2sym, rb_intern, rb_num2long, rb_obj_classname,
62+
rb_raise, rb_sym2id, ID, RUBY_EVENT_CALL, RUBY_EVENT_LINE, RUBY_EVENT_RAISE, RUBY_EVENT_RETURN,
63+
VALUE,
64+
};
65+
use rb_sys::{
66+
rb_protect, NIL_P, RARRAY_CONST_PTR, RARRAY_LEN, RB_FLOAT_TYPE_P, RB_INTEGER_TYPE_P,
67+
RB_SYMBOL_P, RB_TYPE_P, RSTRING_LEN, RSTRING_PTR,
68+
};
69+
use rb_sys::{Qfalse, Qnil, Qtrue};
70+
use runtime_tracing::{
71+
create_trace_writer, CallRecord, EventLogKind, FieldTypeRecord, FullValueRecord, Line,
72+
TraceEventsFileFormat, TraceLowLevelEvent, TraceWriter, TypeKind, TypeRecord, TypeSpecificInfo,
73+
ValueRecord,
74+
};
75+
```
76+
77+
**Declare event hook type and import flag enum and additional binding functions.**
78+
```rust
79+
// Event hook function type from Ruby debug.h
80+
type rb_event_hook_func_t = Option<unsafe extern "C" fn(rb_event_flag_t, VALUE, VALUE, ID, VALUE)>;
81+
82+
// Use event hook flags enum from rb_sys
83+
use rb_sys::rb_event_hook_flag_t;
84+
85+
// Types from rb_sys bindings
86+
use rb_sys::{
87+
rb_add_event_hook2, rb_cRange, rb_cRegexp, rb_cStruct, rb_cTime, rb_check_typeddata,
88+
rb_const_defined, rb_const_get, rb_data_type_struct__bindgen_ty_1, rb_data_type_t,
89+
rb_data_typed_object_wrap, rb_method_boundp, rb_num2dbl, rb_obj_is_kind_of,
90+
rb_remove_event_hook_with_data, rb_trace_arg_t, rb_tracearg_binding, rb_tracearg_callee_id,
91+
rb_tracearg_event_flag, rb_tracearg_lineno, rb_tracearg_path, rb_tracearg_raised_exception,
92+
rb_tracearg_return_value, rb_tracearg_self,
93+
};
94+
```
95+
96+
**Collect frequently used Ruby method identifiers for efficient lookup.**
97+
```rust
98+
struct InternedSymbols {
99+
to_s: ID,
100+
local_variables: ID,
101+
local_variable_get: ID,
102+
instance_method: ID,
103+
parameters: ID,
104+
class: ID,
105+
to_a: ID,
106+
begin: ID,
107+
end: ID,
108+
to_i: ID,
109+
nsec: ID,
110+
source: ID,
111+
options: ID,
112+
members: ID,
113+
values: ID,
114+
to_h: ID,
115+
instance_variables: ID,
116+
instance_variable_get: ID,
117+
set_const: ID,
118+
open_struct_const: ID,
119+
}
120+
```
121+
122+
**Construct the symbol table by interning method names.**
123+
```rust
124+
impl InternedSymbols {
125+
unsafe fn new() -> InternedSymbols {
126+
InternedSymbols {
127+
to_s: rb_intern!("to_s"),
128+
local_variables: rb_intern!("local_variables"),
129+
local_variable_get: rb_intern!("local_variable_get"),
130+
instance_method: rb_intern!("instance_method"),
131+
parameters: rb_intern!("parameters"),
132+
class: rb_intern!("class"),
133+
to_a: rb_intern!("to_a"),
134+
begin: rb_intern!("begin"),
135+
end: rb_intern!("end"),
136+
to_i: rb_intern!("to_i"),
137+
nsec: rb_intern!("nsec"),
138+
source: rb_intern!("source"),
139+
options: rb_intern!("options"),
140+
members: rb_intern!("members"),
141+
values: rb_intern!("values"),
142+
to_h: rb_intern!("to_h"),
143+
instance_variables: rb_intern!("instance_variables"),
144+
instance_variable_get: rb_intern!("instance_variable_get"),
145+
set_const: rb_intern!("Set"),
146+
open_struct_const: rb_intern!("OpenStruct"),
147+
}
148+
}
149+
}
150+
```
151+
152+
**Define the recorder state with tracing backend, flags, and cached type IDs.**
153+
```rust
154+
struct Recorder {
155+
tracer: Box<dyn TraceWriter>,
156+
active: bool,
157+
id: InternedSymbols,
158+
set_class: VALUE,
159+
open_struct_class: VALUE,
160+
struct_type_versions: HashMap<String, usize>,
161+
int_type_id: runtime_tracing::TypeId,
162+
float_type_id: runtime_tracing::TypeId,
163+
bool_type_id: runtime_tracing::TypeId,
164+
string_type_id: runtime_tracing::TypeId,
165+
symbol_type_id: runtime_tracing::TypeId,
166+
error_type_id: runtime_tracing::TypeId,
167+
}
168+
```
169+
170+
**Skip instrumentation for internal or library paths to reduce noise.**
171+
```rust
172+
fn should_ignore_path(path: &str) -> bool {
173+
const PATTERNS: [&str; 5] = [
174+
"codetracer_ruby_recorder.rb",
175+
"lib/ruby",
176+
"recorder.rb",
177+
"codetracer_pure_ruby_recorder.rb",
178+
"gems/",
179+
];
180+
if path.starts_with("<internal:") {
181+
return true;
182+
}
183+
PATTERNS.iter().any(|p| path.contains(p))
184+
}
185+
```
186+
187+
**Retrieve the type ID embedded within a `ValueRecord` variant.**
188+
```rust
189+
fn value_type_id(val: &ValueRecord) -> runtime_tracing::TypeId {
190+
use ValueRecord::*;
191+
match val {
192+
Int { type_id, .. }
193+
| Float { type_id, .. }
194+
| Bool { type_id, .. }
195+
| String { type_id, .. }
196+
| Sequence { type_id, .. }
197+
| Tuple { type_id, .. }
198+
| Struct { type_id, .. }
199+
| Variant { type_id, .. }
200+
| Reference { type_id, .. }
201+
| Raw { type_id, .. }
202+
| Error { type_id, .. }
203+
| BigInt { type_id, .. }
204+
| None { type_id } => *type_id,
205+
Cell { .. } => runtime_tracing::NONE_TYPE_ID,
206+
}
207+
}
208+
```

0 commit comments

Comments
 (0)