Skip to content

Commit ad87413

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 4af63b5 commit ad87413

File tree

10 files changed

+213
-47
lines changed

10 files changed

+213
-47
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.

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,38 @@ jobs:
1616
- uses: actions/checkout@v4
1717
with:
1818
lfs: true
19+
- name: Install system dependencies
20+
shell: bash
21+
run: |
22+
if [[ "$RUNNER_OS" == "Linux" ]]; then
23+
sudo apt-get update
24+
sudo apt-get install -y --no-install-recommends libclang-dev capnproto libcapnp-dev pkg-config
25+
# Set environment variables for Rust/bindgen
26+
LLVM_VERSION=$(ls /usr/lib/ | grep llvm | sort -V | tail -1)
27+
echo "LIBCLANG_PATH=/usr/lib/$LLVM_VERSION/lib" >> $GITHUB_ENV
28+
echo "CLANG_PATH=/usr/bin/clang" >> $GITHUB_ENV
29+
elif [[ "$RUNNER_OS" == "macOS" ]]; then
30+
brew install capnp pkg-config
31+
elif [[ "$RUNNER_OS" == "Windows" ]]; then
32+
# Install LLVM/Clang for Windows
33+
choco install llvm -y
34+
# Install vcpkg for Cap'n Proto on Windows
35+
git clone https://github.com/Microsoft/vcpkg.git
36+
cd vcpkg
37+
./bootstrap-vcpkg.bat
38+
./vcpkg install capnproto:x64-windows
39+
echo "VCPKG_ROOT=$(pwd)" >> $GITHUB_ENV
40+
echo "CMAKE_TOOLCHAIN_FILE=$(pwd)/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV
41+
cd ..
42+
fi
1943
- name: Setup Ruby
2044
uses: ruby/setup-ruby@v1
2145
with:
2246
ruby-version: '3.2'
47+
- name: Setup Rust
48+
uses: dtolnay/rust-toolchain@stable
49+
- name: Install Ruby dependencies
50+
run: bundle install
2351
- name: Setup just
2452
uses: extractions/setup-just@v1
2553
- name: Build extension
@@ -33,10 +61,23 @@ jobs:
3361
- uses: actions/checkout@v4
3462
with:
3563
lfs: true
64+
- name: Install system dependencies
65+
shell: bash
66+
run: |
67+
sudo apt-get update
68+
sudo apt-get install -y --no-install-recommends libclang-dev capnproto libcapnp-dev pkg-config
69+
# Set environment variables for Rust/bindgen
70+
LLVM_VERSION=$(ls /usr/lib/ | grep llvm | sort -V | tail -1)
71+
echo "LIBCLANG_PATH=/usr/lib/$LLVM_VERSION/lib" >> $GITHUB_ENV
72+
echo "CLANG_PATH=/usr/bin/clang" >> $GITHUB_ENV
3673
- name: Setup Ruby
3774
uses: ruby/setup-ruby@v1
3875
with:
3976
ruby-version: '3.2'
77+
- name: Setup Rust
78+
uses: dtolnay/rust-toolchain@stable
79+
- name: Install Ruby dependencies
80+
run: bundle install
4081
- name: Setup just
4182
uses: extractions/setup-just@v1
4283
- name: Build extension

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
@@ -68,6 +68,7 @@ fn value_type_id(val: &ValueRecord) -> runtime_tracing::TypeId {
6868
| Reference { type_id, .. }
6969
| Raw { type_id, .. }
7070
| Error { type_id, .. }
71+
| BigInt { type_id, .. }
7172
| None { type_id } => *type_id,
7273
Cell { .. } => runtime_tracing::NONE_TYPE_ID,
7374
}
@@ -201,12 +202,15 @@ unsafe extern "C" fn disable_tracing(self_val: VALUE) -> VALUE {
201202
Qnil.into()
202203
}
203204

204-
fn flush_to_dir(tracer: &Tracer, dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
205+
fn flush_to_dir(tracer: &Tracer, dir: &Path, format: runtime_tracing::TraceEventsFileFormat) -> Result<(), Box<dyn std::error::Error>> {
205206
std::fs::create_dir_all(dir)?;
206-
let events = dir.join("trace.json");
207+
let events = match format {
208+
runtime_tracing::TraceEventsFileFormat::Json => dir.join("trace.json"),
209+
runtime_tracing::TraceEventsFileFormat::Binary => dir.join("trace.bin"),
210+
};
207211
let metadata = dir.join("trace_metadata.json");
208212
let paths = dir.join("trace_paths.json");
209-
tracer.store_trace_events(&events)?;
213+
tracer.store_trace_events(&events, format)?;
210214
tracer.store_trace_metadata(&metadata)?;
211215
tracer.store_trace_paths(&paths)?;
212216
Ok(())
@@ -486,16 +490,32 @@ unsafe fn record_event(tracer: &mut Tracer, path: &str, line: i64, content: &str
486490
}));
487491
}
488492

489-
unsafe extern "C" fn flush_trace(self_val: VALUE, out_dir: VALUE) -> VALUE {
493+
unsafe extern "C" fn flush_trace(self_val: VALUE, out_dir: VALUE, format: VALUE) -> VALUE {
490494
let recorder_ptr = get_recorder(self_val);
491495
let recorder = &mut *recorder_ptr;
492496
let ptr = RSTRING_PTR(out_dir) as *const u8;
493497
let len = RSTRING_LEN(out_dir) as usize;
494498
let slice = std::slice::from_raw_parts(ptr, len);
495499

500+
let fmt = if NIL_P(format) {
501+
runtime_tracing::TraceEventsFileFormat::Json
502+
} else if RB_SYMBOL_P(format) {
503+
let id = rb_sym2id(format);
504+
match CStr::from_ptr(rb_id2name(id)).to_str().unwrap_or("") {
505+
"binary" | "bin" => runtime_tracing::TraceEventsFileFormat::Binary,
506+
"json" => runtime_tracing::TraceEventsFileFormat::Json,
507+
_ => {
508+
rb_raise(rb_eIOError, b"Unknown format\0".as_ptr() as *const c_char);
509+
runtime_tracing::TraceEventsFileFormat::Json
510+
}
511+
}
512+
} else {
513+
runtime_tracing::TraceEventsFileFormat::Json
514+
};
515+
496516
match std::str::from_utf8(slice) {
497517
Ok(path_str) => {
498-
if let Err(e) = flush_to_dir(&recorder.tracer, Path::new(path_str)) {
518+
if let Err(e) = flush_to_dir(&recorder.tracer, Path::new(path_str), fmt) {
499519
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);
500520
}
501521
}
@@ -639,10 +659,10 @@ pub extern "C" fn Init_codetracer_ruby_recorder() {
639659
0
640660
);
641661
rb_define_method(
642-
class,
643-
b"flush_trace\0".as_ptr() as *const c_char,
644-
Some(std::mem::transmute(flush_trace as *const ())),
645-
1
662+
class,
663+
b"flush_trace\0".as_ptr() as *const c_char,
664+
Some(std::mem::transmute(flush_trace as *const ())),
665+
2
646666
);
647667
rb_define_method(
648668
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)