Project

General

Profile

Actions

Bug #22176

open

[Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined

Bug #22176: [Feature] TracePoint#defined_box: get the Ruby::Box where a method is defined

Added by udzura (Uchio KONDO) 1 day ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25]
[ruby-core:125924]

Description

Summary

In TracePoint events such as :call / :c_call / :return / :c_return, I'd like to propose adding an attribute TracePoint#defined_box, which returns the Ruby::Box in which the called method was defined.

Currently, tp.defined_class correctly returns a distinct class/module object per Box, but there is no direct way to know which Box that object belongs to.

Background / Motivation

With Ruby::Box, the same class name and the same source file can be loaded independently in multiple Boxes.

In this situation, tools that observe and record method calls via TracePoint (profilers, security auditing tools, test support tools, etc.) run into a practical problem: they cannot distinguish "which Box the called method was defined in."

Reproduction

sample1.rb:

b = Ruby::Box.new
b.require_relative 'sample2'

require_relative 'sample2'

tp = TracePoint.new(:call) do |tp|
  if tp.method_id == :method1
    puts "Calling #{tp.defined_class}##{tp.method_id} from #{tp.path}:#{tp.lineno}"
  end
end

tp.enable

ins1 = Sample2.new
ins1.method1

ins2 = b::Sample2.new
ins2.method1

sample2.rb:

class Sample2
  def method1
    puts "This is method 1"
  end
end

Output:

$ RUBY_BOX=1 ./miniruby ./sample1.rb
./miniruby: warning: Ruby::Box is experimental, and the behavior may change in the future!
See https://docs.ruby-lang.org/en/master/Ruby/Box.html for known issues, etc.
Calling Sample2#method1 from /path/to/sample2.rb:2
This is method 1
Calling Sample2#method1 from /path/to/sample2.rb:2
This is method 1

There is no way to distinguish the two calls using tp.defined_class, tp.method_id, tp.path, or tp.lineno.

I have confirmed that the class returned by tp.defined_class does in fact have a different object id in each case, but there is no way to check which Box it is associated with.

Known workarounds and their issues

1. tp.binding.eval "Ruby::Box.current.inspect"

It's possible to obtain the Box indirectly by evaluating Ruby::Box.current through tp.binding. However, :c_call / :c_return (calls into methods implemented in C) have no corresponding Ruby-level binding, so tp.binding itself is unavailable, and this workaround cannot be used there.

2. tp.self.method(tp.method_id).box.inspect

Re-obtaining an Object#method reference does work, and this also works for C methods. However, it has the downside of re-constructing a Method object inside the TracePoint block every time, which adds overhead, and it isn't very intuitive.

Proposed API

Add TracePoint#defined_box, paired with tp.defined_class, returning the Ruby::Box instance in which the method/class was defined (or nil when RUBY_BOX is disabled).

tp = TracePoint.new(:call, :c_call) do |tp|
  puts "#{tp.defined_class}##{tp.method_id} defined in #{tp.defined_box.inspect}"
end

On naming

Candidate Verdict Reason
defined_box Proposed Symmetric with defined_class
box Under consideration Matches Method#box, but less specific

Possible implementation

I'm considering two directions and would appreciate feedback:

  • Go through rb_trace_arg_t * and use rb_callable_method_entry_without_refinements() to look up the method definition from the klass and called_id, then read the box off of it. This may add some overhead.
  • Add an argument carrying the defining-Box information to rb_exec_event_hook_orig(). Since this function is used very broadly, this would come with a higher cost of change.

Open questions

  1. For singleton methods, defined_class returns the singleton class per spec. Should defined_box likewise return the Box that singleton class belongs to?
  2. Is similar information also needed for the :script_compiled event, etc.?

Environment

$ ./miniruby --version
ruby 4.1.0dev (2026-07-04T01:14:12Z master 870c8d6a50) +PRISM [arm64-darwin25]

Use cases

Security auditing / debugging

When multiple Boxes coexist and an unexpected side effect occurs, there is likely demand for being able to trace "which Box the code that actually ran was defined in."

In vivarium, an auditing tool I'm developing, I'm also considering per-Box auditing, but currently the only options are going through tp.binding or re-fetching Method#box each time, both of which feel redundant.

Verifying dependency updates

One of the use cases for Box is running an old and a new version of a dependency in parallel, each in its own Box, and comparing the resulting responses. For this, a mechanism to visualize which Box's code path was actually executed is needed.

Application to multi-tenant systems, etc.

TracePoint would make it possible to tally how much code from which Box (i.e., which plugin or tenant) was invoked. For example, an APM/profiling tool could use this to visualize per-Box usage, which may be a real demand.

Since this seems like a generally useful attribute to trace with respect to Box in the first place, I expect more ideas for use cases to surface as well.

Related

  • [Feature #21311] Namespace on read (revised)

No data to display

Actions

Also available in: PDF Atom