Skip to content

Commit 0829ac6

Browse files
committed
docs: add initial inspection listing
Introduce listing-001.md summarizing environment, dependencies, build tasks, and the RubyRecorder implementation for review.
1 parent a8fea2b commit 0829ac6

File tree

1 file changed

+330
-0
lines changed

1 file changed

+330
-0
lines changed

listing-001.md

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
# Listing 001
2+
3+
This listing introduces the development environment for **codetracer-ruby-recorder**. We review documentation for installation and environment variables (`README.md`), project dependencies (`Gemfile`), build and test tasks (`Rakefile` and `Justfile`), and then walk through the primary `RubyRecorder` class that powers the native tracer.
4+
5+
**Project heading states this gem records Ruby programs to produce CodeTracer traces.**
6+
```markdown
7+
## codetracer-ruby-recorder
8+
9+
A recorder of Ruby programs that produces [CodeTracer](https://github.com/metacraft-labs/CodeTracer) traces.
10+
```
11+
12+
**Installation instructions show gem installation and fallback to pure Ruby version.**
13+
```bash
14+
gem install codetracer-ruby-recorder
15+
gem install codetracer-pure-ruby-recorder
16+
```
17+
18+
**Environment variables toggle debug logging and specify trace output directory.**
19+
```markdown
20+
* if you pass `CODETRACER_RUBY_RECORDER_DEBUG=1`, you enable some additional debug-related logging
21+
* `CODETRACER_RUBY_RECORDER_OUT_DIR` can be used to specify the directory for trace files
22+
```
23+
24+
**Development setup suggests installing debugging gems and running tests manually.**
25+
```bash
26+
gem install debug pry
27+
ruby -I lib -I test test/test_tracer.rb
28+
```
29+
30+
**Gemfile sets source and references both native and pure-Ruby recorder gems locally.**
31+
```ruby
32+
# frozen_string_literal: true
33+
source "https://rubygems.org"
34+
35+
gem "codetracer-ruby-recorder", path: "gems/codetracer-ruby-recorder"
36+
gem "codetracer-pure-ruby-recorder", path: "gems/codetracer-pure-ruby-recorder"
37+
```
38+
39+
**Optional development gems for debugging are commented out; rubocop is included for development.**
40+
```ruby
41+
# gem "debug", "~> 1.7" # Ruby debugging with rdbg
42+
# gem "pry", "~> 0.14" # Interactive debugging and REPL
43+
gem "rubocop", "~> 1.77", :group => :development
44+
```
45+
46+
**Rakefile loads rb_sys extension task.**
47+
```ruby
48+
require 'rb_sys/extensiontask'
49+
```
50+
51+
**Extension task configuration specifies build and library directories.**
52+
```ruby
53+
RbSys::ExtensionTask.new('codetracer_ruby_recorder') do |ext|
54+
ext.ext_dir = 'gems/codetracer-ruby-recorder/ext/native_tracer'
55+
ext.lib_dir = 'gems/codetracer-ruby-recorder/lib'
56+
ext.gem_spec = Gem::Specification.load('gems/codetracer-ruby-recorder/codetracer-ruby-recorder.gemspec')
57+
end
58+
```
59+
60+
**Alias for running tests and the test command executes installation checks and unit tests.**
61+
```make
62+
alias t := test
63+
test:
64+
ruby -Itest test/gem_installation.rb
65+
ruby -Itest -e 'Dir["test/test_*.rb"].each { |f| require File.expand_path(f) }'
66+
```
67+
68+
**Benchmark task runs benchmarks with pattern and report options.**
69+
```make
70+
bench pattern="*" write_report="console":
71+
ruby test/benchmarks/run_benchmarks.rb '{{pattern}}' --write-report={{write_report}}
72+
```
73+
74+
**Build native extension via Cargo.**
75+
```make
76+
build-extension:
77+
cargo build --release --manifest-path gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.toml
78+
```
79+
80+
**Formatting tasks for Rust, Nix, and Ruby.**
81+
```make
82+
format-rust:
83+
cargo fmt --manifest-path gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.toml
84+
85+
format-nix:
86+
if command -v nixfmt >/dev/null; then find . -name '*.nix' -print0 | xargs -0 nixfmt; fi
87+
88+
format-ruby:
89+
if command -v bundle >/dev/null && bundle exec rubocop -v >/dev/null 2>&1; then bundle exec rubocop -A; else echo "Ruby formatter not available; skipping"; fi
90+
```
91+
92+
**Aggregate formatting and linting tasks with an alias.**
93+
```make
94+
format:
95+
just format-rust
96+
just format-ruby
97+
just format-nix
98+
99+
lint-rust:
100+
cargo fmt --check --manifest-path gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.toml
101+
102+
lint-nix:
103+
if command -v nixfmt >/dev/null; then find . -name '*.nix' -print0 | xargs -0 nixfmt --check; fi
104+
105+
lint-ruby:
106+
if command -v bundle >/dev/null && bundle exec rubocop -v >/dev/null 2>&1; then bundle exec rubocop; else echo "rubocop not available; skipping"; fi
107+
108+
lint:
109+
just lint-rust
110+
just lint-ruby
111+
just lint-nix
112+
113+
alias fmt := format
114+
```
115+
116+
**Header comments declare license and purpose.**
117+
```ruby
118+
# SPDX-License-Identifier: MIT
119+
# Library providing a helper method to execute the native tracer.
120+
```
121+
122+
**Load option parsing, file utilities, configuration, and kernel patches.**
123+
```ruby
124+
require 'optparse'
125+
require 'fileutils'
126+
require 'rbconfig'
127+
require_relative 'codetracer/kernel_patches'
128+
```
129+
130+
**Define RubyRecorder inside CodeTracer module.**
131+
```ruby
132+
module CodeTracer
133+
class RubyRecorder
134+
```
135+
136+
**Begin parsing CLI arguments and set up OptionParser.**
137+
```ruby
138+
def self.parse_argv_and_trace_ruby_file(argv)
139+
options = {}
140+
parser = OptionParser.new do |opts|
141+
opts.banner = 'usage: codetracer-ruby-recorder [options] <program> [args]'
142+
```
143+
144+
**Accept output directory and format options.**
145+
```ruby
146+
opts.on('-o DIR', '--out-dir DIR', 'Directory to write trace files') do |dir|
147+
options[:out_dir] = dir
148+
end
149+
opts.on('-f FORMAT', '--format FORMAT', 'trace format: json or binary') do |fmt|
150+
options[:format] = fmt
151+
end
152+
```
153+
154+
**Provide help flag and finalize option parsing.**
155+
```ruby
156+
opts.on('-h', '--help', 'Print this help') do
157+
puts opts
158+
exit
159+
end
160+
end
161+
parser.order!(argv)
162+
```
163+
164+
**Extract program argument and handle missing program.**
165+
```ruby
166+
program = argv.shift
167+
if program.nil?
168+
$stderr.puts parser
169+
exit 1
170+
end
171+
```
172+
173+
**Capture remaining program arguments and determine output directory and format.**
174+
```ruby
175+
# Remaining arguments after the program name are passed to the traced program
176+
program_args = argv.dup
177+
178+
out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
179+
format = (options[:format] || 'json').to_sym
180+
trace_ruby_file(program, out_dir, program_args, format)
181+
0
182+
end
183+
```
184+
185+
**Trace specified Ruby file with selected options.**
186+
```ruby
187+
def self.trace_ruby_file(program, out_dir, program_args = [], format = :json)
188+
recorder = RubyRecorder.new(out_dir, format)
189+
return 1 unless recorder.available?
190+
191+
ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] = out_dir
192+
```
193+
194+
**Execute program under recorder, adjusting ARGV temporarily.**
195+
```ruby
196+
recorder.start
197+
begin
198+
# Set ARGV to contain the program arguments
199+
original_argv = ARGV.dup
200+
ARGV.clear
201+
ARGV.concat(program_args)
202+
203+
load program
204+
ensure
205+
# Restore original ARGV
206+
ARGV.clear
207+
ARGV.concat(original_argv)
208+
209+
recorder.stop
210+
recorder.flush_trace
211+
end
212+
0
213+
end
214+
```
215+
216+
**Entry point to run CLI logic.**
217+
```ruby
218+
# Execute the native tracer CLI logic with the provided +argv+.
219+
def self.execute(argv)
220+
parse_argv_and_trace_ruby_file(argv)
221+
end
222+
```
223+
224+
**Initialize recorder and load native implementation.**
225+
```ruby
226+
def initialize(out_dir, format = :json)
227+
@recorder = nil
228+
@active = false
229+
load_native_recorder(out_dir, format)
230+
end
231+
```
232+
233+
**Start recording and apply kernel patches if not already active.**
234+
```ruby
235+
# Start the recorder and install kernel patches
236+
def start
237+
return if @active || @recorder.nil?
238+
239+
@recorder.enable_tracing
240+
CodeTracer::KernelPatches.install(self)
241+
@active = true
242+
end
243+
```
244+
245+
**Stop recording and remove patches.**
246+
```ruby
247+
# Stop the recorder and remove kernel patches
248+
def stop
249+
return unless @active
250+
251+
CodeTracer::KernelPatches.uninstall(self)
252+
@recorder.disable_tracing if @recorder
253+
@active = false
254+
end
255+
```
256+
257+
**Delegate recording events to native recorder.**
258+
```ruby
259+
# Record event for kernel patches integration
260+
def record_event(path, line, content)
261+
@recorder.record_event(path, line, content) if @recorder
262+
end
263+
```
264+
265+
**Flush trace data and report availability.**
266+
```ruby
267+
# Flush trace to output directory
268+
def flush_trace
269+
@recorder.flush_trace if @recorder
270+
end
271+
272+
# Check if recorder is available
273+
def available?
274+
!@recorder.nil?
275+
end
276+
```
277+
278+
**Mark following methods as private and begin loading native recorder.**
279+
```ruby
280+
private
281+
282+
def load_native_recorder(out_dir, format = :json)
283+
begin
284+
# Load native extension at module level
285+
```
286+
287+
**Resolve extension directory and target library path based on platform.**
288+
```ruby
289+
ext_dir = File.expand_path('../ext/native_tracer/target/release', __dir__)
290+
dlext = RbConfig::CONFIG['DLEXT']
291+
target_path = File.join(ext_dir, "codetracer_ruby_recorder.#{dlext}")
292+
unless File.exist?(target_path)
293+
extensions = %w[so bundle dylib dll]
294+
```
295+
296+
**Search for alternative library names and create symlink or copy as needed.**
297+
```ruby
298+
alt_path = extensions
299+
.map { |ext| File.join(ext_dir, "libcodetracer_ruby_recorder.#{ext}") }
300+
.find { |path| File.exist?(path) }
301+
if alt_path
302+
begin
303+
File.symlink(alt_path, target_path)
304+
rescue StandardError
305+
FileUtils.cp(alt_path, target_path)
306+
end
307+
end
308+
end
309+
```
310+
311+
**Load library and build recorder instance.**
312+
```ruby
313+
require target_path
314+
@recorder = CodeTracerNativeRecorder.new(out_dir, format)
315+
```
316+
317+
**On errors, emit warning and fall back to nil recorder.**
318+
```ruby
319+
rescue Exception => e
320+
warn "native tracer unavailable: #{e}"
321+
@recorder = nil
322+
end
323+
end
324+
```
325+
326+
**Terminate the RubyRecorder class and CodeTracer module.**
327+
```ruby
328+
end
329+
end
330+
```

0 commit comments

Comments
 (0)