Skip to content
Closed
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ recorder.flush_trace(Dir.pwd)
you can currently use it directly with

```bash
ruby gems/codetracer-pure-ruby-recorder/lib/trace.rb [--out-dir DIR] <path to ruby file>
ruby gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder [--out-dir DIR] <path to ruby file>
# produces several trace json files in DIR,
# or in `$CODETRACER_RUBY_RECORDER_OUT_DIR` if DIR is not provided.
# Defaults to the current directory.
Expand All @@ -47,7 +47,7 @@ You can also invoke a lightweight CLI that loads the native tracer extension
directly:

```bash
ruby gems/codetracer-ruby-recorder/lib/native_trace.rb [--out-dir DIR] <path to ruby file>
ruby gems/codetracer-ruby-recorder/bin/codetracer-ruby-recorder [--out-dir DIR] <path to ruby file>
# Uses DIR or `$CODETRACER_RUBY_RECORDER_OUT_DIR` to choose where traces are saved.
```

Expand Down
54 changes: 51 additions & 3 deletions gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,52 @@
#!/usr/bin/env ruby
require 'rbconfig'
script = File.expand_path('../lib/trace.rb', __dir__)
exec RbConfig.ruby, script, *ARGV
# SPDX-License-Identifier: MIT
# CLI for the pure Ruby tracer

require 'optparse'
lib_dir = File.expand_path('../lib', __dir__)
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
require 'codetracer_pure_ruby_recorder'

$tracer = Tracer.new($codetracer_record, debug: ENV['CODETRACER_RUBY_RECORDER_DEBUG'] == '1')
CodetracerKernelPatches.install($tracer)

options = {}
parser = OptionParser.new do |opts|
opts.banner = "usage: codetracer-pure-ruby-recorder [options] <program> [args]"
opts.on('-o DIR', '--out-dir DIR', 'Directory to write trace files') { |dir| options[:out_dir] = dir }
opts.on('-h', '--help', 'Print this help') { puts opts; exit }
end
parser.order!

program = ARGV.shift
if program.nil?
$stderr.puts parser
exit 1
end

$tracer.record.register_call('', 1, '<top-level>', [])
$tracer.ignore('lib/ruby')
$tracer.ignore('trace.rb')
$tracer.ignore('recorder.rb')
$tracer.ignore('<internal:')
$tracer.ignore('gems/')

$tracer.activate
begin
Kernel.load(program)
rescue Exception => e
if $tracer.debug
old_puts ''
old_puts '==== trace.rb error while tracing program ==='
old_puts 'ERROR'
old_puts e
old_puts e.backtrace
old_puts '====================='
old_puts ''
end
end

$tracer.stop_tracing

out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
$tracer.record.serialize(program, out_dir)
7 changes: 4 additions & 3 deletions gems/codetracer-pure-ruby-recorder/lib/trace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def initialize(record, debug: false)
@debug = debug
@record.debug = debug if @record.respond_to?(:debug=)
setup_tracepoints
::Codetracer::KernelPatches.install(self)
end

def stop_tracing
Expand Down Expand Up @@ -229,10 +230,10 @@ def load_variables(binding)
end
end

<<<<<<< HEAD
if __FILE__ == $PROGRAM_NAME
$tracer = Tracer.new($codetracer_record, debug: ENV['CODETRACER_RUBY_RECORDER_DEBUG'] == '1')
::Codetracer::KernelPatches.install($tracer)


options = {}
parser = OptionParser.new do |opts|
opts.banner = "usage: ruby trace.rb [options] <program> [args]"
Expand Down Expand Up @@ -281,4 +282,4 @@ def load_variables(binding)

out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
$tracer.record.serialize(program, out_dir)
end
end
9 changes: 6 additions & 3 deletions gems/codetracer-ruby-recorder/bin/codetracer-ruby-recorder
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env ruby
require 'rbconfig'
script = File.expand_path('../lib/native_trace.rb', __dir__)
exec RbConfig.ruby, script, *ARGV
# SPDX-License-Identifier: MIT
# CLI wrapper for the native tracer

lib_dir = File.expand_path('../lib', __dir__)
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
require 'native_trace'
exit NativeTrace.execute(ARGV)
136 changes: 83 additions & 53 deletions gems/codetracer-ruby-recorder/lib/native_trace.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,98 @@
#!/usr/bin/env ruby
# SPDX-License-Identifier: MIT
# Simple utility loading the native tracer extension and executing a program.
# Library providing a helper method to execute the native tracer.

require 'optparse'
require 'fileutils'
require 'rbconfig'
require_relative 'codetracer/kernel_patches'

options = {}
parser = OptionParser.new do |opts|
opts.banner = "usage: ruby native_trace.rb [options] <program> [args]"
opts.on('-o DIR', '--out-dir DIR', 'Directory to write trace files') do |dir|
options[:out_dir] = dir
end
opts.on('-h', '--help', 'Print this help') do
puts opts
exit
end
end
parser.order!
module NativeTrace
# Execute the native tracer CLI logic with the provided +argv+.
def self.execute(argv)
options = {}
parser = OptionParser.new do |opts|
opts.banner = 'usage: codetracer-ruby-recorder [options] <program> [args]'
opts.on('-o DIR', '--out-dir DIR', 'Directory to write trace files') do |dir|
options[:out_dir] = dir
end
opts.on('-h', '--help', 'Print this help') do
puts opts
return 0
end
end
parser.order!(argv)

if ARGV.empty?
$stderr.puts parser
exit 1
end
if argv.empty?
$stderr.puts parser
return 1
end

out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] = out_dir
out_dir = options[:out_dir] || ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] || Dir.pwd
ENV['CODETRACER_RUBY_RECORDER_OUT_DIR'] = out_dir

# Path to the compiled native extension
ext_dir = File.expand_path('../ext/native_tracer/target/release', __dir__)
dlext = RbConfig::CONFIG['DLEXT']
target_path = File.join(ext_dir, "codetracer_ruby_recorder.#{dlext}")
unless File.exist?(target_path)
extensions = %w[so bundle dylib dll]
alt_path = extensions
.map { |ext| File.join(ext_dir, "libcodetracer_ruby_recorder.#{ext}") }
.find { |path| File.exist?(path) }
if alt_path
begin
File.symlink(alt_path, target_path)
rescue StandardError
FileUtils.cp(alt_path, target_path)
ext_dir = File.expand_path('../ext/native_tracer/target/release', __dir__)
dlext = RbConfig::CONFIG['DLEXT']
target_path = File.join(ext_dir, "codetracer_ruby_recorder.#{dlext}")
unless File.exist?(target_path)
extensions = %w[so bundle dylib dll]
alt_path = extensions
.map { |ext| File.join(ext_dir, "libcodetracer_ruby_recorder.#{ext}") }
.find { |path| File.exist?(path) }
if alt_path
begin
File.symlink(alt_path, target_path)
rescue StandardError
FileUtils.cp(alt_path, target_path)
end
end
end
end
end

recorder = nil
begin
require target_path
recorder = RubyRecorder.new
$recorder = recorder
::Codetracer::KernelPatches.install(recorder)
recorder = nil
begin
require target_path
recorder = RubyRecorder.new
$recorder = recorder

rescue Exception => e
warn "native tracer unavailable: #{e}"
end
Kernel.module_eval do
alias :old_p :p
alias :old_puts :puts
alias :old_print :print

program = ARGV.shift
recorder.enable_tracing if recorder
load program
if recorder
recorder.disable_tracing
::Codetracer::KernelPatches.uninstall(recorder)
recorder.flush_trace(out_dir)
end
define_method(:p) do |*args|
if $recorder
loc = caller_locations(1,1).first
$recorder.record_event(loc.path, loc.lineno, args.join("\n"))
end
old_p(*args)
end

define_method(:puts) do |*args|
if $recorder
loc = caller_locations(1,1).first
$recorder.record_event(loc.path, loc.lineno, args.join("\n"))
end
old_puts(*args)
end

define_method(:print) do |*args|
if $recorder
loc = caller_locations(1,1).first
$recorder.record_event(loc.path, loc.lineno, args.join("\n"))
end
old_print(*args)
end
end
rescue Exception => e
warn "native tracer unavailable: #{e}"
end

program = argv.shift
recorder.enable_tracing if recorder
load program
if recorder
recorder.disable_tracing
recorder.flush_trace(out_dir)
end
0
end
end
4 changes: 2 additions & 2 deletions test/benchmarks/run_benchmarks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def run_benchmark(name)
native_dir = File.join(TMP_DIR, name, 'native')
FileUtils.mkdir_p(native_dir)
elapsed = Benchmark.realtime do
system(RbConfig.ruby, File.expand_path('../../gems/codetracer-ruby-recorder/lib/native_trace.rb', __dir__),
system(RbConfig.ruby, File.expand_path('../../gems/codetracer-ruby-recorder/bin/codetracer-ruby-recorder', __dir__),
'--out-dir', native_dir, program)
raise 'Native trace failed' unless $?.success?
end
Expand All @@ -64,7 +64,7 @@ def run_benchmark(name)
pure_dir = File.join(TMP_DIR, name, 'pure')
FileUtils.mkdir_p(pure_dir)
elapsed = Benchmark.realtime do
system(RbConfig.ruby, File.expand_path('../../gems/codetracer-pure-ruby-recorder/lib/trace.rb', __dir__),
system(RbConfig.ruby, File.expand_path('../../gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder', __dir__),
'--out-dir', pure_dir, program)
raise 'Pure trace failed' unless $?.success?
end
Expand Down
6 changes: 3 additions & 3 deletions test/test_tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def program_args(base)
Dir.glob(File.join(FIXTURE_DIR, '*_trace.json')).each do |fixture|
base = File.basename(fixture, '_trace.json')
define_method("test_#{base}") do
pure_trace, pure_out = run_trace('gems/codetracer-pure-ruby-recorder/lib/trace.rb', "#{base}.rb", *program_args(base))
native_trace, native_out = run_trace('gems/codetracer-ruby-recorder/lib/native_trace.rb', "#{base}.rb", *program_args(base))
pure_trace, pure_out = run_trace('gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder', "#{base}.rb", *program_args(base))
native_trace, native_out = run_trace('gems/codetracer-ruby-recorder/bin/codetracer-ruby-recorder', "#{base}.rb", *program_args(base))

expected = expected_trace("#{base}.rb")
assert_equal expected, pure_trace
Expand Down Expand Up @@ -133,7 +133,7 @@ def test_pure_debug_smoke
env = { 'CODETRACER_RUBY_RECORDER_DEBUG' => '1' }
out_dir = File.join('test', 'tmp', 'debug_smoke')
FileUtils.rm_rf(out_dir)
stdout, stderr, status = Open3.capture3(env, RbConfig.ruby, 'gems/codetracer-pure-ruby-recorder/lib/trace.rb', '--out-dir', out_dir, File.join('test', 'programs', 'addition.rb'))
stdout, stderr, status = Open3.capture3(env, RbConfig.ruby, 'gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder', '--out-dir', out_dir, File.join('test', 'programs', 'addition.rb'))
raise "trace failed: #{stderr}" unless status.success?

lines = stdout.lines.map(&:chomp)
Expand Down
Loading