Skip to content

Commit 5504cfe

Browse files
committed
feat: add binary trace support
upgrade runtime_tracing to 0.12 and allow flushing traces as json or binary; benchmark script now measures native binary format
1 parent 9d1e952 commit 5504cfe

File tree

9 files changed

+173
-48
lines changed

9 files changed

+173
-48
lines changed

.agents/codex-setup

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ AGENTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
44
cd $AGENTS_DIR
55

66
sudo apt-get update
7-
sudo apt-get install -y --no-install-recommends just ruby-full libclang-dev
7+
sudo apt-get install -y --no-install-recommends \
8+
just \
9+
ruby-full \
10+
libclang-dev \
11+
capnproto libcapnp-dev
812

913
pushd ..
1014
just build-extension
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
There is a new version of the runtime_tracing create used by the `gems/codetracer-ruby-recorder/ext/native_tracer/src/lib.rs` module.
2+
3+
The primary new feature is that the create support a new binary format for the trace files and requires the user to choose between json and binary when flushing the trace to a file.
4+
5+
Please migrate the project to the new version of runtime_tracing and enhance the benchmark setup to compare the pure ruby implementation vs json in the native implementation vs the new binary format in the native implementation.

flake.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
description = "Development environment for codetracer-ruby-recorder";
33

4-
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
4+
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
55

66
outputs = {
77
self,
@@ -33,6 +33,8 @@
3333
# For build automation
3434
just
3535
git-lfs
36+
37+
capnproto # Required for the native tracer's Cap'n Proto serialization
3638
] ++ pkgs.lib.optionals isLinux [
3739
# C standard library headers required for Ruby C extension compilation on Linux
3840
# Without this, build fails with "stdarg.h file not found" error

gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.lock

Lines changed: 36 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
1111

1212
[dependencies]
1313
rb-sys = "0.9"
14-
runtime_tracing = "0.10.0"
14+
runtime_tracing = "0.12.1"
1515

1616
[build-dependencies]
1717
rb-sys-env = "0.2"

gems/codetracer-ruby-recorder/ext/native_tracer/src/lib.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn value_type_id(val: &ValueRecord) -> runtime_tracing::TypeId {
133133
| Reference { type_id, .. }
134134
| Raw { type_id, .. }
135135
| Error { type_id, .. }
136+
| BigInt { type_id, .. }
136137
| None { type_id } => *type_id,
137138
Cell { .. } => runtime_tracing::NONE_TYPE_ID,
138139
}
@@ -266,12 +267,15 @@ unsafe extern "C" fn disable_tracing(self_val: VALUE) -> VALUE {
266267
Qnil.into()
267268
}
268269

269-
fn flush_to_dir(tracer: &Tracer, dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
270+
fn flush_to_dir(tracer: &Tracer, dir: &Path, format: runtime_tracing::TraceEventsFileFormat) -> Result<(), Box<dyn std::error::Error>> {
270271
std::fs::create_dir_all(dir)?;
271-
let events = dir.join("trace.json");
272+
let events = match format {
273+
runtime_tracing::TraceEventsFileFormat::Json => dir.join("trace.json"),
274+
runtime_tracing::TraceEventsFileFormat::Binary => dir.join("trace.bin"),
275+
};
272276
let metadata = dir.join("trace_metadata.json");
273277
let paths = dir.join("trace_paths.json");
274-
tracer.store_trace_events(&events)?;
278+
tracer.store_trace_events(&events, format)?;
275279
tracer.store_trace_metadata(&metadata)?;
276280
tracer.store_trace_paths(&paths)?;
277281
Ok(())
@@ -551,16 +555,32 @@ unsafe fn record_event(tracer: &mut Tracer, path: &str, line: i64, content: &str
551555
}));
552556
}
553557

554-
unsafe extern "C" fn flush_trace(self_val: VALUE, out_dir: VALUE) -> VALUE {
558+
unsafe extern "C" fn flush_trace(self_val: VALUE, out_dir: VALUE, format: VALUE) -> VALUE {
555559
let recorder_ptr = get_recorder(self_val);
556560
let recorder = &mut *recorder_ptr;
557561
let ptr = RSTRING_PTR(out_dir) as *const u8;
558562
let len = RSTRING_LEN(out_dir) as usize;
559563
let slice = std::slice::from_raw_parts(ptr, len);
560564

565+
let fmt = if NIL_P(format) {
566+
runtime_tracing::TraceEventsFileFormat::Json
567+
} else if RB_SYMBOL_P(format) {
568+
let id = rb_sym2id(format);
569+
match CStr::from_ptr(rb_id2name(id)).to_str().unwrap_or("") {
570+
"binary" | "bin" => runtime_tracing::TraceEventsFileFormat::Binary,
571+
"json" => runtime_tracing::TraceEventsFileFormat::Json,
572+
_ => {
573+
rb_raise(rb_eIOError, b"Unknown format\0".as_ptr() as *const c_char);
574+
runtime_tracing::TraceEventsFileFormat::Json
575+
}
576+
}
577+
} else {
578+
runtime_tracing::TraceEventsFileFormat::Json
579+
};
580+
561581
match std::str::from_utf8(slice) {
562582
Ok(path_str) => {
563-
if let Err(e) = flush_to_dir(&recorder.tracer, Path::new(path_str)) {
583+
if let Err(e) = flush_to_dir(&recorder.tracer, Path::new(path_str), fmt) {
564584
rb_raise(rb_eIOError, b"Failed to flush trace: %s\0".as_ptr() as *const c_char, e.to_string().as_ptr() as *const c_char);
565585
}
566586
}
@@ -704,10 +724,10 @@ pub extern "C" fn Init_codetracer_ruby_recorder() {
704724
0
705725
);
706726
rb_define_method(
707-
class,
708-
b"flush_trace\0".as_ptr() as *const c_char,
709-
Some(std::mem::transmute(flush_trace as *const ())),
710-
1
727+
class,
728+
b"flush_trace\0".as_ptr() as *const c_char,
729+
Some(std::mem::transmute(flush_trace as *const ())),
730+
2
711731
);
712732
rb_define_method(
713733
class,

gems/codetracer-ruby-recorder/lib/codetracer_ruby_recorder.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ def self.parse_argv_and_trace_ruby_file(argv)
1515
opts.on('-o DIR', '--out-dir DIR', 'Directory to write trace files') do |dir|
1616
options[:out_dir] = dir
1717
end
18+
opts.on('-f FORMAT', '--format FORMAT', 'trace format: json or binary') do |fmt|
19+
options[:format] = fmt
20+
end
1821
opts.on('-h', '--help', 'Print this help') do
1922
puts opts
2023
exit
@@ -32,11 +35,12 @@ def self.parse_argv_and_trace_ruby_file(argv)
3235
program_args = argv.dup
3336

3437
out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
35-
trace_ruby_file(program, out_dir, program_args)
38+
format = (options[:format] || 'json').to_sym
39+
trace_ruby_file(program, out_dir, program_args, format)
3640
0
3741
end
3842

39-
def self.trace_ruby_file(program, out_dir, program_args = [])
43+
def self.trace_ruby_file(program, out_dir, program_args = [], format = :json)
4044
recorder = RubyRecorder.new
4145
return 1 unless recorder.available?
4246

@@ -56,7 +60,7 @@ def self.trace_ruby_file(program, out_dir, program_args = [])
5660
ARGV.concat(original_argv)
5761

5862
recorder.stop
59-
recorder.flush_trace(out_dir)
63+
recorder.flush_trace(out_dir, format)
6064
end
6165
0
6266
end
@@ -96,8 +100,8 @@ def record_event(path, line, content)
96100
end
97101

98102
# Flush trace to output directory
99-
def flush_trace(out_dir)
100-
@recorder.flush_trace(out_dir) if @recorder
103+
def flush_trace(out_dir, format = :json)
104+
@recorder.flush_trace(out_dir, format) if @recorder
101105
end
102106

103107
# Check if recorder is available

0 commit comments

Comments
 (0)