Skip to content

Commit 5dca110

Browse files
committed
feat(pure): expose RubyRecorder API
1 parent f10e5e4 commit 5dca110

File tree

4 files changed

+98
-30
lines changed

4 files changed

+98
-30
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ gem install codetracer_pure_ruby_recorder
2222
After installing, load the tracer:
2323

2424
```ruby
25-
require 'codetracer_ruby_recorder'
25+
require 'codetracer_ruby_recorder' # native implementation
26+
# require 'codetracer_pure_ruby_recorder' # pure Ruby implementation
2627

2728
recorder = RubyRecorder.new
2829
recorder.enable_tracing

examples/selective_tracing_pure.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env ruby
2+
3+
# Load the pure Ruby tracer library if RubyRecorder is not already defined
4+
unless defined?(RubyRecorder)
5+
lib_base = File.expand_path('../gems/pure-ruby-tracer/lib/codetracer_pure_ruby_recorder', __dir__)
6+
require lib_base
7+
end
8+
9+
recorder = RubyRecorder.new
10+
11+
puts 'start trace'
12+
recorder.disable_tracing
13+
puts 'this will not be traced'
14+
recorder.enable_tracing
15+
puts 'this will be traced'
16+
recorder.disable_tracing
17+
puts 'tracing disabled'
18+
recorder.flush_trace(Dir.pwd)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
require_relative 'trace'
2+
3+
# Ruby implementation of the RubyRecorder API used by the native tracer.
4+
# Provides basic tracing controls and serialization using the pure Ruby tracer.
5+
class RubyRecorder
6+
def initialize(debug: ENV['CODETRACER_RUBY_RECORDER_DEBUG'] == '1')
7+
@record = $codetracer_record
8+
@tracer = Tracer.new(@record, debug: debug)
9+
setup_defaults
10+
end
11+
12+
# Enable tracing of Ruby code execution.
13+
def enable_tracing
14+
@tracer.activate
15+
end
16+
17+
# Disable tracing without discarding collected data.
18+
def disable_tracing
19+
@tracer.deactivate
20+
end
21+
22+
# Serialize the trace to +out_dir+.
23+
def flush_trace(out_dir)
24+
@tracer.stop_tracing
25+
@record.serialize('', out_dir)
26+
end
27+
28+
# Record a custom event at +path+ and +line+ with +content+.
29+
def record_event(path, line, content)
30+
@tracer.record_event(["#{path}:#{line}"], content)
31+
end
32+
33+
private
34+
35+
def setup_defaults
36+
@record.register_call('', 1, '<top-level>', [])
37+
@tracer.ignore('lib/ruby')
38+
@tracer.ignore('trace.rb')
39+
@tracer.ignore('recorder.rb')
40+
@tracer.ignore('<internal:')
41+
@tracer.ignore('gems/')
42+
end
43+
end

gems/pure-ruby-tracer/lib/trace.rb

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,44 @@
1818
# however this seems as a risky solution, as it clears global gem state!
1919
# BE CAREFUL if you have other ruby projects/data there!
2020

21-
# override some of the IO methods to record them for event log
22-
module Kernel
23-
alias :old_p :p
24-
alias :old_puts :puts
25-
alias :old_print :print
26-
27-
def p(*args)
28-
if $tracer.tracing
29-
$tracer.deactivate
30-
$tracer.record_event(caller, args.join("\n"))
31-
$tracer.activate
32-
end
33-
old_p(*args)
34-
end
21+
# instrumentation helpers for recording IO calls
22+
module CodetracerKernelPatches
23+
def self.install(tracer)
24+
Kernel.module_eval do
25+
unless method_defined?(:old_p)
26+
alias :old_p :p
27+
alias :old_puts :puts
28+
alias :old_print :print
29+
end
3530

36-
def puts(*args)
37-
if $tracer.tracing
38-
$tracer.deactivate
39-
$tracer.record_event(caller, args.join("\n"))
40-
$tracer.activate
41-
end
42-
old_puts(*args)
43-
end
31+
define_method(:p) do |*args|
32+
if tracer.tracing
33+
tracer.deactivate
34+
tracer.record_event(caller, args.join("\n"))
35+
tracer.activate
36+
end
37+
old_p(*args)
38+
end
4439

45-
def print(*args)
46-
if $tracer.tracing
47-
$tracer.deactivate
48-
$tracer.record_event(caller, args.join("\n"))
49-
$tracer.activate
40+
define_method(:puts) do |*args|
41+
if tracer.tracing
42+
tracer.deactivate
43+
tracer.record_event(caller, args.join("\n"))
44+
tracer.activate
45+
end
46+
old_puts(*args)
47+
end
48+
49+
define_method(:print) do |*args|
50+
if tracer.tracing
51+
tracer.deactivate
52+
tracer.record_event(caller, args.join("\n"))
53+
tracer.activate
54+
end
55+
old_print(*args)
56+
end
5057
end
51-
old_print(*args)
5258
end
53-
5459
end
5560

5661
# class IO
@@ -252,6 +257,7 @@ def load_variables(binding)
252257

253258
if __FILE__ == $PROGRAM_NAME
254259
$tracer = Tracer.new($codetracer_record, debug: ENV['CODETRACER_RUBY_RECORDER_DEBUG'] == '1')
260+
CodetracerKernelPatches.install($tracer)
255261

256262
options = {}
257263
parser = OptionParser.new do |opts|

0 commit comments

Comments
 (0)