vivarium 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be07034ab56d73e0aaa4fe9e124d67888ae2006cc5f6cb632cf4b93b20855422
4
- data.tar.gz: 5f95cdaa111a56415f4fed08da0c638a87f5c08f01e51db2b5b818c04c256bc4
3
+ metadata.gz: 0a0f8b6dfc29af71d39ff5fe1ebceca9040cf62a2c6147dba5c2a074b392cdc1
4
+ data.tar.gz: c9f10129e5b42fd51653dcda0a02ed0e7be8b4c2ad7daef4f48a9fa5c03dcf41
5
5
  SHA512:
6
- metadata.gz: 49e166adbaca6231263d10255779ce4ff16a98ba9905a4afc48da012382d21fe1635b4f3bfff3990125bcb9718f7e1e5f3c5168a3a7f39b7fd8981be312bc3a4
7
- data.tar.gz: a85783ba20a6eb866bd8215f55a4edb6890bef1b0b3e3e7af6d11bfcc0fda77adbfa2e7ea465e201c424d6fc9f883bcb20d7846e3c051c294560151ac0078d30
6
+ metadata.gz: 9530187d826e63976bd7e1c86872345f5cad281608b2ee06f0dbe20e6211455d8d050a18013822f6baecf4c0af18e9e01b58fec7fe13174d9631181df8660569
7
+ data.tar.gz: c3ee802cf168d5ca917c6d9de5cce25c9441ba46a35995bc5e5f78ce6c78055c1a4d8141bff7dd4e7763205d65c1a41344a789185dbea8fc65f4589ef8e90586
data/README.md CHANGED
@@ -155,6 +155,14 @@ bundle exec ruby examples/env_access_external_demo.rb
155
155
 
156
156
  This demo spawns an external process that directly calls libc `getenv`, `setenv`, `unsetenv`, `putenv`, and `clearenv`, intended to trigger `env_caccess` eBPF events.
157
157
 
158
+ 10) Box context demo:
159
+
160
+ ```bash
161
+ bundle exec ruby examples/box_demo.rb
162
+ ```
163
+
164
+ This demo shows how to use `Vivarium::Box` to isolate Ruby code evaluation and trace method calls within that isolated context.
165
+
158
166
  You can also start top-level observation without a block (it keeps observing until process exit):
159
167
 
160
168
  ```ruby
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "vivarium"
5
+
6
+ # Usage:
7
+ # 1) In another shell (root): sudo bundle exec vivariumd
8
+ # 2) Run this script: bundle exec ruby examples/box_demo.rb
9
+ #
10
+ # This demo demonstrates Vivarium::Box usage for automatically tracing
11
+ # method calls within an isolated eval context.
12
+
13
+ FILTER = {
14
+ include_events: %w[span_start span_stop]
15
+ }.freeze
16
+
17
+ # Create a box and define a class within it
18
+ box = Vivarium::Box.new
19
+
20
+ box.eval(<<~RUBY)
21
+ require "net/http"
22
+ class Calculator
23
+ def add(a, b)
24
+ a + b
25
+ end
26
+
27
+ def multiply(a, b)
28
+ a * b
29
+ end
30
+ end
31
+
32
+ class Greeter
33
+ def greet(name)
34
+ system "echo Hello \#{name}!"
35
+ end
36
+ end
37
+
38
+ system "ping -c 1 example.com"
39
+ RUBY
40
+
41
+ box.done_load!
42
+
43
+ # Enable observation - all Box method calls will be automatically traced
44
+ puts "[box-demo] calling box methods with automatic tracing"
45
+
46
+ # Access classes defined in the box through const_missing
47
+ calc = box::Calculator.new
48
+ result1 = calc.add(10, 20)
49
+ puts "[box-demo] calc.add(10, 20) = #{result1}"
50
+
51
+ result2 = calc.multiply(3, 4)
52
+ puts "[box-demo] calc.multiply(3, 4) = #{result2}"
53
+
54
+ greeter = box::Greeter.new
55
+ greeting = greeter.greet("World")
56
+ puts "[box-demo] greeter.greet('World') = #{greeting}"
57
+
58
+ puts "[box-demo] done"
59
+
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Vivarium
6
+ # Box provides an isolated execution context where method calls are automatically traced
7
+ # through Vivarium's observation system.
8
+ #
9
+ # Usage:
10
+ # box = Vivarium::Box.new
11
+ # box.eval('class MyClass; def foo; "result"; end; end')
12
+ # result = box::MyClass.new.foo # automatically traced if Vivarium.observe is active
13
+ #
14
+ class Box < Module
15
+ DEFAULT_FILTER = {
16
+ include_events: %w[
17
+ proc_fork proc_exec span_start span_stop
18
+ sock_connect dns_req odd_socket
19
+ ssl_write
20
+ dlopen mmap_exec
21
+ task_kill
22
+ setid_change capable_check bprm_creds
23
+ ]
24
+ }
25
+
26
+ def initialize(pin_dir: Vivarium.bpf_pin_dir, dest: $stdout, filter: DEFAULT_FILTER)
27
+ super()
28
+ @inner_box = Ruby::Box.new
29
+ @pin_dir = pin_dir
30
+ @dest = dest
31
+ @filter = filter
32
+ @session = nil
33
+
34
+ @tracing_level = [0]
35
+ # Set up TracePoint to automatically trace method calls within this box
36
+ @tracer = TracePoint.new(:call, :return) do |tp|
37
+ handle_trace_event(tp, @tracing_level, @inner_box)
38
+ end
39
+ end
40
+ attr_reader :inner_box, :tracer
41
+
42
+ # Evaluate code within the box context
43
+ def eval(code)
44
+ result = nil
45
+ Vivarium.observe(filter: @filter) do
46
+ result = @inner_box.eval(code)
47
+ end
48
+ result
49
+ end
50
+
51
+ # Require a file within the box context
52
+ # Automatically traced if Vivarium.observe is active
53
+ def require(path)
54
+ result = nil
55
+ Vivarium.observe(filter: @filter) do
56
+ result = @inner_box.require(path)
57
+ end
58
+ result
59
+ end
60
+
61
+ # Require a file relative to the current file within the box context
62
+ # Automatically traced if Vivarium.observe is active
63
+ def require_relative(path)
64
+ result = nil
65
+ Vivarium.observe(filter: @filter) do
66
+ result = @inner_box.require_relative(path)
67
+ end
68
+ result
69
+ end
70
+
71
+ # Load a file within the box context (executed every time, unlike require)
72
+ # Automatically traced if Vivarium.observe is active
73
+ def load(path, wrap = false)
74
+ result = nil
75
+ Vivarium.observe(filter: @filter) do
76
+ result = @inner_box.load(path, wrap)
77
+ end
78
+ result
79
+ end
80
+
81
+ # Intercept constant access to resolve from the box's evaluated context
82
+ def const_missing(name)
83
+ @inner_box.const_get(name)
84
+ rescue NameError => e
85
+ raise NameError, "#{name} not found in box: #{e.message}"
86
+ end
87
+
88
+ def done_load!
89
+ @tracer.enable
90
+ end
91
+
92
+ private
93
+
94
+ def handle_trace_event(tp, tracing_level, target_box)
95
+ begin
96
+ if should_trace_call?(tp, target_box)
97
+ case tp.event
98
+ when :call
99
+ if tracing_level[0].zero?
100
+ start_vivarium_observation
101
+ end
102
+ tracing_level[0] += 1
103
+ file_arg = Vivarium.tail_fit_string(tp.path, Vivarium::SPAN_FILE_ARG_MAX)
104
+ root = Ruby::Box.root
105
+ root::Vivarium::Usdt.start("#{tp.defined_class}", tp.method_id.to_s, file: file_arg, lineno: tp.lineno)
106
+ when :return
107
+ tracing_level[0] -= 1
108
+ file_arg = Vivarium.tail_fit_string(tp.path, Vivarium::SPAN_FILE_ARG_MAX)
109
+ root = Ruby::Box.root
110
+ root::Vivarium::Usdt.stop("#{tp.defined_class}", tp.method_id.to_s, file: file_arg, lineno: tp.lineno)
111
+ if tracing_level[0].zero?
112
+ stop_vivarium_observation
113
+ end
114
+ end
115
+ end
116
+ rescue StandardError
117
+ # Silently ignore tracing errors to avoid breaking user code
118
+ end
119
+ end
120
+
121
+ def should_trace_call?(tp, target_box)
122
+ tp.binding.eval("Ruby::Box.current") == target_box
123
+ end
124
+
125
+ def start_vivarium_observation
126
+ puts "[debug] Starting Vivarium observation for Box method calls"
127
+ @session = Vivarium.top_observe(pin_dir: @pin_dir, dest: @dest, filter: @filter)
128
+ end
129
+
130
+ def stop_vivarium_observation
131
+ puts "[debug] Stopping Vivarium observation for Box method calls"
132
+ @session&.stop
133
+ @session = nil
134
+ end
135
+ end
136
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vivarium
4
- VERSION = "0.4.1"
4
+ VERSION = "0.4.2"
5
5
  end
data/lib/vivarium.rb CHANGED
@@ -7,6 +7,12 @@ require "optparse"
7
7
  require "pathname"
8
8
  require "rbbcc"
9
9
  require "socket"
10
+ if defined?(Ruby) && defined?(Ruby::Box) && Ruby::Box.enabled?
11
+ Ruby::Box.root.require "vivarium_usdt"
12
+ else
13
+ require "vivarium_usdt"
14
+ end
15
+
10
16
  require_relative "vivarium/version"
11
17
  require_relative "vivarium/cli"
12
18
 
@@ -1694,7 +1700,6 @@ module Vivarium
1694
1700
  .gsub("__VIVARIUM_F_PATH_OFFSET__", f_path_offset.to_s)
1695
1701
  .gsub("__VIVARIUM_DENTRY_D_NAME_OFFSET__", d_name_offset.to_s)
1696
1702
 
1697
- require "vivarium_usdt"
1698
1703
  usdt_so_path = ENV.fetch("VIVARIUM_USDT_SO_PATH") { Vivarium.locate_vivarium_usdt_so }
1699
1704
  usdt = RbBCC::USDT.new(path: usdt_so_path)
1700
1705
  usdt.enable_probe(probe: "start_probe", fn_name: "on_span_start")
@@ -1969,8 +1974,6 @@ module Vivarium
1969
1974
  end
1970
1975
 
1971
1976
  def self.top_observe(pin_dir: bpf_pin_dir, dest: $stdout, filter: nil)
1972
- require "vivarium_usdt"
1973
-
1974
1977
  store = MapStore.new(pin_dir: pin_dir)
1975
1978
  pid = Process.pid
1976
1979
  store.register_pid(pid)
@@ -1997,8 +2000,6 @@ module Vivarium
1997
2000
  end
1998
2001
 
1999
2002
  def self.scoped_observe(pin_dir:, dest:, filter: nil)
2000
- require "vivarium_usdt"
2001
-
2002
2003
  store = MapStore.new(pin_dir: pin_dir)
2003
2004
  pid = Process.pid
2004
2005
  store.register_pid(pid)
@@ -2079,7 +2080,6 @@ module Vivarium
2079
2080
  end
2080
2081
 
2081
2082
  def self.locate_vivarium_usdt_so
2082
- require "vivarium_usdt/vivarium_usdt"
2083
2083
  so = $LOADED_FEATURES.find { |p| p =~ %r{vivarium_usdt/vivarium_usdt\.(so|bundle|dylib)\z} }
2084
2084
  raise Error, "vivarium_usdt native extension not found in $LOADED_FEATURES" unless so
2085
2085
 
@@ -2127,3 +2127,6 @@ end
2127
2127
  require_relative "vivarium/correlator"
2128
2128
  require_relative "vivarium/display_filter"
2129
2129
  require_relative "vivarium/tree_renderer"
2130
+ if defined?(Ruby) && defined?(Ruby::Box) && Ruby::Box.enabled?
2131
+ require_relative "vivarium/box"
2132
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vivarium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uchio Kondo
@@ -64,6 +64,7 @@ files:
64
64
  - CONTEXT.md
65
65
  - README.md
66
66
  - Rakefile
67
+ - examples/box_demo.rb
67
68
  - examples/dlopen_demo.rb
68
69
  - examples/drop_demo.rb
69
70
  - examples/env_access_external_demo.rb
@@ -80,6 +81,7 @@ files:
80
81
  - exe/vivariumd
81
82
  - image.png
82
83
  - lib/vivarium.rb
84
+ - lib/vivarium/box.rb
83
85
  - lib/vivarium/cli.rb
84
86
  - lib/vivarium/correlator.rb
85
87
  - lib/vivarium/display_filter.rb