Tool Update – ruby-trace: A Low-Level Tracer for Ruby
2022-1-1 01:59:0 Author: research.nccgroup.com(查看原文) 阅读量:17 收藏

We released ruby-trace back in August to coincide with my DEF CON 29 talk on it and parasitic tracing in general. Back then, it supported (c)Ruby 2.6 through 3.0. A few days ago, Ruby 3.1 was released. We have updated ruby-trace to add support for Ruby 3.1 and reorganized our test suite to validate our handling of 3.1 changes across Ruby 2.6 through 3.1.

Back in this post from late 2020, I discussed some of the problems with Ruby’s tracing APIs and tracing the lower-level behaviors of Ruby in general, especially when calls start going into native code and you need to capture arguments and return values. Ruby’s Kernel#set_trace_func and TracePoint API that wraps it are Ruby’s main interface to its built-in tracing capabilities. These are focused around “events” that are emitted by the tracing implementation kicked off by the internal vm_trace() function, which gets called by the trace_* variants of Ruby’s bytecode instructions that simply call it and then jump to the non-trace bytecode instruction handler. When Ruby’s tracing is enabled, it switches bytecode execution to use the trace_* instruction handler for every instruction, ensuring that vm_trace() is called. Additionally, there are a number of EXEC_EVENT_HOOK() macro calls strewn throughout the lower-level Ruby runtime code to emit events for native events such as :c_call/:c_return and thread operations.

However, these events leave much to be desired. Even though it could arguably be modified to provide information on the bytecode instructions being executed, it only provides higher-level information focused mostly on method, class, module, and block entry or returns and doesn’t provide any means to intercept method arguments, let alone native function parameters. And it also provides very limited information on Ruby-to-native transitions, mostly just the Ruby names of native methods. Arguably, this is due to the fact that YARV bytecode is an implementation detail of the current CRuby, but it seems odd that it would not provide an extension of the TracePoint API for additional implementation-specific information, especially given how much bytecode-related functionality is thrown into the CRuby-specific RubyVM module. So I wrote my own…

ruby-trace is a Frida-based tracer for CRuby, the main Ruby implementation, written in JavaScript. Targeting Ruby on Linux, it instruments Ruby/libruby to dump execution information from Ruby programs. It focuses on extracting all relevant execution information, including method, bytecode instruction, and native function arguments and return values, and additional metadata around control flow, such as branch choices and exception handling. In fact, the “simplest” part of ruby-trace is probably its general hook implementation for Ruby VM opcode handlers, for which the bytecode typically executes in a large while-goto state machine in normal builds. In addition to hooks on the Ruby send, opt_send_without_block, invokesuper, and invokebock opcodes that are primarily used to call things from Ruby code, it additionally hooks a number of other mechanisms internal to Ruby that are used to call Ruby methods and native functions, such as rb_vm_call_cfunc, vm_call_cfunc(_with_frame), rb_vm_call0, and rb_iterate0; and the various Ruby exception handling mechanisms and their native components.

To emit useful output, ruby-trace makes liberal use of the Ruby inspect method to stringify objects, with alternate handling for the critical regions in the VM where Ruby functions cannot safely be dispatched. It will additionally disassemble bytecode objects (e.g. instruction sequence (“ISEQ”)), like methods and blocks, when they are defined (and occasionally generated). It also provides useful human-readable representations for a number of internal values and flags.

Overall, ruby-trace, very deeply instruments the Ruby VM, covering every single one of the 110 Ruby VM opcodes between Ruby 2.6 and 3.1, including several that Ruby itself is incapable of emitting, including vestigial opcodes that likely never actually worked at all.

ruby-trace deeply integrates with Ruby/libruby, not only in the form of its hooks placed throughout, but also in its use of Ruby symbols and C API functions to work with, analyze, and invoke methods on Ruby objects. ruby-trace bundles a large number of Ruby struct definitions for each version and uses a shared interface to abstract them, with version-specific overrides where necessary, enabling it to directly access fields from internal structs and extract call and opcode metadata. The main benefit of this approach is that it is fairly simple to update ruby-trace to support additional versions of Ruby, and doing so does not require maintaining an out-of-tree patchset or unmaintainable fork of Ruby to gain advanced trace output.

Because YARV instruction handlers are not generally compiled as functions with proper preludes and returns, but instead are labeled goto blocks in a state machine, it is not really possible to hook their return, because they generally don’t return at all. Instead, to get opcode results, which are typically placed on the top of the Ruby VM stack, ruby-trace abuses the fact that it hooks all opcode handler entrypoints to unravel the state machine from the inside and place callback functions to pull the returned values before the next instruction is executed.

In general, other than its liberal use of inspect, ruby-trace attempts to avoid performing actions that will cause significant side-effects on execution. As it cannot always glean the full context of a given instruction being executed, ruby-trace often reimplements the logic within opcode handlers to precompute certain information about the instruction being executed based on its register and stack arguments. Additionally, due to the fact that ruby-trace must stringify Ruby objects during critical regions within the VM where Ruby’s method dispatch cannot be used, it implements its own form of Ruby method dispatch parallel to the one internal to Ruby by assembling maps for all inspect and to_s methods by hooking rb_define_method and rb_define_alias. Due to these techniques, ruby-trace, in some sense, acts like a second YARV VM injected into Ruby. Alas, much of the code within ruby-trace exists solely for these kinds of reasons, to prevent Ruby’s own code from crashing itself because Ruby does not like calling Ruby methods during arbitrary junctures within the VM’s execution; much of the work on ruby-trace is focused on squashing instances of memory corruption Ruby constantly does to itself. And trust me, it’s turtles all the way down and all the way up.

ruby-trace can be installed with sudo npm install -g ruby-trace. To use ruby-trace, add and enable a TracePoint object to a script to trace as shown below, and run the script with ruby-trace -- prepended to the ruby command used to run the script.

ruby-trace -- ruby demo.rb
# ruby-trace -- ruby demo.rb
[ruby-trace]{info} vm.ruby_version: 31
[ruby-trace]{info} OPTS: ["OPT_DIRECT_THREADED_CODE","OPT_OPERANDS_UNIFICATION","OPT_INLINE_METHOD_CACHE"]
[6236]{L} >> pop (false)
[6236]{L} >> invokeblock: yield [type:proc, flags:ARGS_SIMPLE] {#<Proc:0x00007f1d6d8fd798 test/demo.rb:34>}
[6236]{L} >> putself (main)
[6236]{L} >> putstring "hello"
[6236]{L} >> opt_send_without_block: (main).foo "hello" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> "hello"
[6236]{L} >> dup ("hello")
[6236]{L} >> dup -> TOPN(0): "hello"
[6236]{L} >>     -> TOPN(1): "hello"
[6236]{L} >> opt_case_dispatch key: "hello" (type:5), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: "hello" (0x94628585850776->0x94628585851376)
[6236]{L} >> pop ("hello")
[6236]{L} >> putstring "world"
[6236]{L} >> leave -> "world"
[6236]{L} >> putself (main)
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> opt_send_without_block: (main).foo 1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 1
[6236]{L} >> dup (1)
[6236]{L} >> dup -> TOPN(0): 1
[6236]{L} >>     -> TOPN(1): 1
[6236]{L} >> opt_case_dispatch key: 1 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: 1 (0x94628585850776->0x94628585851408)
[6236]{L} >> pop (1)
[6236]{L} >> putstring "num"
[6236]{L} >> leave -> "num"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (2.0)
[6236]{L} >> putobject (1.0)
[6236]{L} >> opt_plus: (1.0) + (2.0)
[6236]{L} >> opt_plus -> 3.0
[6236]{L} >> opt_send_without_block: (main).foo 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 3.0
[6236]{L} >> dup (3.0)
[6236]{L} >> dup -> TOPN(0): 3.0
[6236]{L} >>     -> TOPN(1): 3.0
[6236]{L} >> opt_case_dispatch key: 3.0 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: 3 (0x94628585850776->0x94628585851472)
[6236]{L} >> pop (3.0)
[6236]{L} >> putstring "float"
[6236]{L} >> leave -> "float"
[6236]{L} >> putself (main)
[6236]{L} >> putself (main)
[6236]{L} >> putstring "3.0"
[6236]{L} >> opt_send_without_block: (main).BigDecimal "3.0" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6f10, ec->cfp: 0x7f1d6dbe6f10
[6236]{L} >> vm_call_cfunc: (main).BigDecimal // cfunc.func: <unknown>[@0x7f1d6d868560](argc: 1, defined argc: -1); cfunc.invoker: ractor_safe_call_cfunc_m1[@0x7f1d772bba40]
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d868560](0x1, ["3.0"], main)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d868560](0x1, ["3.0"], main) -> 0.3e1
[6236]{L} >> opt_send_without_block: (main).foo 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 0.3e1
[6236]{L} >> dup (0.3e1)
[6236]{L} >> dup -> TOPN(0): 0.3e1
[6236]{L} >>     -> TOPN(1): 0.3e1
[6236]{L} >> opt_case_dispatch key: 0.3e1 (type:12), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: ("hello").=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 0.3e1)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 0.3e1) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (1).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184263540)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184263540) -> 69882184263540
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 1)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 1) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 0.3e1) -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (2147483648).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}}=>true}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}}=>true}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184263540)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184263540) -> 69882184263540
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 2147483648)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 2147483648) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 0.3e1) -> false
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { false }; jump: not taken
[6236]{L} >> putobject (3.0)
[6236]{L} >> topn n: 1 [ 3.0, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (3.0).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (3.0).=== // cfunc.func: rb_float_equal[@0x7f1d771acff0](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184263540)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184263540) -> 69882184263540
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 3.0)
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d89cee8:type_guess=unknown_const>, 3.0) -> true
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 0.3e1) -> true
[6236]{L} >> branchif +53 (0x56106f3ec898->0x56106f3eca50) { true }; jump: taken
[6236]{L} >> pop (0.3e1)
[6236]{L} >> putstring "float"
[6236]{L} >> leave -> "float"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (:foo)
[6236]{L} >> opt_send_without_block: (main).foo :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> :foo
[6236]{L} >> dup (:foo)
[6236]{L} >> dup -> TOPN(0): :foo
[6236]{L} >>     -> TOPN(1): :foo
[6236]{L} >> opt_case_dispatch key: :foo (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: :foo (0x94628585850776->0x94628585851600)
[6236]{L} >> pop (:foo)
[6236]{L} >> putstring "symbol"
[6236]{L} >> leave -> "symbol"
[6236]{L} >> putself (main)
[6236]{L} >> putstring "foo"
[6236]{L} >> opt_send_without_block: (main).foo "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> "foo"
[6236]{L} >> dup ("foo")
[6236]{L} >> dup -> TOPN(0): "foo"
[6236]{L} >>     -> TOPN(1): "foo"
[6236]{L} >> opt_case_dispatch key: "foo" (type:5), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: "foo" (0x94628585850776->0x94628585851568)
[6236]{L} >> pop ("foo")
[6236]{L} >> putstring "string"
[6236]{L} >> leave -> "string"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (2147483648)
[6236]{L} >> opt_send_without_block: (main).foo 2147483648 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 2147483648
[6236]{L} >> dup (2147483648)
[6236]{L} >> dup -> TOPN(0): 2147483648
[6236]{L} >>     -> TOPN(1): 2147483648
[6236]{L} >> opt_case_dispatch key: 2147483648 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: 2147483648 (0x94628585850776->0x94628585851440)
[6236]{L} >> pop (2147483648)
[6236]{L} >> putstring "bignum"
[6236]{L} >> leave -> "bignum"
[6236]{L} >> newarray num: 7 [ "world", "num", "float", "float", "symbol", "string", "bignum" ]
[6236]{L} >> newarray -> ["world", "num", "float", "float", "symbol", "string", "bignum"]
[6236]{L} >> setlocal_WC_0 [email protected]:0(raw:4)[1/2], level:0, val: (["world", "num", "float", "float", "symbol", "string", "bignum"])
[6236]{L} >> putspecialobject 3 (VM_SPECIAL_OBJECT_CONST_BASE) {Object}
[6236]{L} >> putnil
[6236]{L} >> defineclass id: :Symbol, class_iseq: 0x7f1d6d8e5d50, flags: 0 (VM_DEFINECLASS_TYPE_CLASS), cbase: Object, super: nil
[6236]{L} == disasm: #<ISeq:<class:Symbol>@test/demo.rb:37 (37,2)-(42,5)> (catch: FALSE)
[6236]{L}    0000 putspecialobject                       1                         (  38)[LiCl]
[6236]{L}    0002 putspecialobject                       2
[6236]{L}    0004 putobject                              :===
[6236]{L}    0006 opt_send_without_block                 <calldata!mid:core#undef_method, argc:2, ARGS_SIMPLE>
[6236]{L}    0008 pop
[6236]{L}    0009 definemethod                           :===, ===                 (  39)[Li]
[6236]{L}    0012 putobject                              :===
[6236]{L}    0014 leave                                                            (  42)[En]
[6236]{L} 
[6236]{L}    == disasm: #<ISeq:[email protected]/demo.rb:39 (39,4)-(41,7)> (catch: FALSE)
[6236]{L}    local table (size: 1, argc: 0 [opts: 0, rest: 0, post: 0, block: -1, kw: [email protected], kwrest: -1])
[6236]{L}    [ 1] [email protected]<Rest>
[6236]{L}    0000 putobject                              true                      (  40)[LiCa]
[6236]{L}    0002 leave                                                            (  41)[Re]
[6236]{L} >> putspecialobject 1 (VM_SPECIAL_OBJECT_VMCORE) {BasicObject}
[6236]{L} >> putspecialobject 2 (VM_SPECIAL_OBJECT_CBASE) {Symbol}
[6236]{L} >> putobject (:===)
[6236]{L} >> opt_send_without_block: (BasicObject).core#undef_method(Symbol, :===) [flags:ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (BasicObject).core#undef_method // cfunc.func: m_core_undef_method[@0x7f1d772dd9a0](argc: 2); cfunc.invoker: ractor_safe_call_cfunc_2[@0x7f1d772bba70]
[6236]{L} >> cfunc: m_core_undef_method[@0x7f1d772dd9a0](<uninspectable:0x7f1d6dab18a0:type_guess=unknown_const>, Symbol, :===)
[6236]{L} >> cfunc: m_core_undef_method[@0x7f1d772dd9a0](<uninspectable:0x7f1d6dab18a0:type_guess=unknown_const>, Symbol, :===) -> nil
[6236]{L} >> pop (nil)
[6236]{L} >> definemethod id: :===, iseq: 0x7f1d6d8e5d00
[6236]{L} == disasm: #<ISeq:[email protected]/demo.rb:39 (39,4)-(41,7)> (catch: FALSE)
[6236]{L}    local table (size: 1, argc: 0 [opts: 0, rest: 0, post: 0, block: -1, kw: [email protected], kwrest: -1])
[6236]{L}    [ 1] [email protected]<Rest>
[6236]{L}    0000 putobject                              true                      (  40)[LiCa]
[6236]{L}    0002 leave                                                            (  41)[Re]
[6236]{L} >> putobject (:===)
[6236]{L} >> leave -> :===
[6236]{L} >> pop (:===)
[6236]{L} >> putself (main)
[6236]{L} >> putstring "hello"
[6236]{L} >> opt_send_without_block: (main).foo "hello" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> "hello"
[6236]{L} >> dup ("hello")
[6236]{L} >> dup -> TOPN(0): "hello"
[6236]{L} >>     -> TOPN(1): "hello"
[6236]{L} >> opt_case_dispatch key: "hello" (type:5), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", "hello" ] (top->bottom)
[6236]{L} >> topn -> "hello"
[6236]{L} >> opt_send_without_block: ("hello").=== "hello" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", "hello")
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", "hello") -> true
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { true }; jump: taken
[6236]{L} >> pop ("hello")
[6236]{L} >> putstring "world"
[6236]{L} >> leave -> "world"
[6236]{L} >> putself (main)
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> opt_send_without_block: (main).foo 1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 1
[6236]{L} >> dup (1)
[6236]{L} >> dup -> TOPN(0): 1
[6236]{L} >>     -> TOPN(1): 1
[6236]{L} >> opt_case_dispatch key: 1 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", 1 ] (top->bottom)
[6236]{L} >> topn -> 1
[6236]{L} >> opt_send_without_block: ("hello").=== 1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 1)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 1) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, 1 ] (top->bottom)
[6236]{L} >> topn -> 1
[6236]{L} >> opt_send_without_block: (1).=== 1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 1)
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 1) -> true
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { true }; jump: taken
[6236]{L} >> pop (1)
[6236]{L} >> putstring "num"
[6236]{L} >> leave -> "num"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (2.0)
[6236]{L} >> putobject (1.0)
[6236]{L} >> opt_plus: (1.0) + (2.0)
[6236]{L} >> opt_plus -> 3.0
[6236]{L} >> opt_send_without_block: (main).foo 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 3.0
[6236]{L} >> dup (3.0)
[6236]{L} >> dup -> TOPN(0): 3.0
[6236]{L} >>     -> TOPN(1): 3.0
[6236]{L} >> opt_case_dispatch key: 3.0 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", 3.0 ] (top->bottom)
[6236]{L} >> topn -> 3.0
[6236]{L} >> opt_send_without_block: ("hello").=== 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 3.0)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 3.0) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, 3.0 ] (top->bottom)
[6236]{L} >> topn -> 3.0
[6236]{L} >> opt_send_without_block: (1).=== 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 3.0)
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 3.0) -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, 3.0 ] (top->bottom)
[6236]{L} >> topn -> 3.0
[6236]{L} >> opt_send_without_block: (2147483648).=== 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 3.0)
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 3.0) -> false
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { false }; jump: not taken
[6236]{L} >> putobject (3.0)
[6236]{L} >> topn n: 1 [ 3.0, 3.0 ] (top->bottom)
[6236]{L} >> topn -> 3.0
[6236]{L} >> opt_send_without_block: (3.0).=== 3.0 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (3.0).=== // cfunc.func: rb_float_equal[@0x7f1d771acff0](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 3.0)
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 3.0) -> true
[6236]{L} >> branchif +53 (0x56106f3ec898->0x56106f3eca50) { true }; jump: taken
[6236]{L} >> pop (3.0)
[6236]{L} >> putstring "float"
[6236]{L} >> leave -> "float"
[6236]{L} >> putself (main)
[6236]{L} >> putself (main)
[6236]{L} >> putstring "3.0"
[6236]{L} >> opt_send_without_block: (main).BigDecimal "3.0" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6f10, ec->cfp: 0x7f1d6dbe6f10
[6236]{L} >> vm_call_cfunc: (main).BigDecimal // cfunc.func: <unknown>[@0x7f1d6d868560](argc: 1, defined argc: -1); cfunc.invoker: ractor_safe_call_cfunc_m1[@0x7f1d772bba40]
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d868560](0x1, ["3.0"], main)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d868560](0x1, ["3.0"], main) -> 0.3e1
[6236]{L} >> opt_send_without_block: (main).foo 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 0.3e1
[6236]{L} >> dup (0.3e1)
[6236]{L} >> dup -> TOPN(0): 0.3e1
[6236]{L} >>     -> TOPN(1): 0.3e1
[6236]{L} >> opt_case_dispatch key: 0.3e1 (type:12), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: ("hello").=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 0.3e1)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 0.3e1) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (1).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184280480)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184280480) -> 69882184280480
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 1)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 1) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 0.3e1) -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (2147483648).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184280480)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184280480) -> 69882184280480
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 2147483648)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>)
[6236]{L} >> cfunc: thread_s_current[@0x7f1d7728dc00](<uninspectable:thread_s_current() arg>) -> #<Thread:0x00007f1d6dab7160 run>
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 2147483648) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 0.3e1) -> false
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { false }; jump: not taken
[6236]{L} >> putobject (3.0)
[6236]{L} >> topn n: 1 [ 3.0, 0.3e1 ] (top->bottom)
[6236]{L} >> topn -> 0.3e1
[6236]{L} >> opt_send_without_block: (3.0).=== 0.3e1 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (3.0).=== // cfunc.func: rb_float_equal[@0x7f1d771acff0](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 0.3e1)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184280480)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184280480) -> 69882184280480
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 3.0)
[6236]{L} >> cfunc: <unknown>[@0x7f1d6d86b320](<uninspectable:0x7f1d6d8a5340:type_guess=unknown_const>, 3.0) -> true
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, 0.3e1) -> true
[6236]{L} >> branchif +53 (0x56106f3ec898->0x56106f3eca50) { true }; jump: taken
[6236]{L} >> pop (0.3e1)
[6236]{L} >> putstring "float"
[6236]{L} >> leave -> "float"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (:foo)
[6236]{L} >> opt_send_without_block: (main).foo :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> :foo
[6236]{L} >> dup (:foo)
[6236]{L} >> dup -> TOPN(0): :foo
[6236]{L} >>     -> TOPN(1): :foo
[6236]{L} >> opt_case_dispatch key: :foo (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: ("hello").=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", :foo)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", :foo) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (1).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, :foo)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 1127388)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 1127388) -> 1127388
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 1)
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 1) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, :foo) -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (2147483648).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, :foo)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 1127388)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 1127388) -> 1127388
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 2147483648)
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 2147483648) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, :foo) -> false
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { false }; jump: not taken
[6236]{L} >> putobject (3.0)
[6236]{L} >> topn n: 1 [ 3.0, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (3.0).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (3.0).=== // cfunc.func: rb_float_equal[@0x7f1d771acff0](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, :foo)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 1127388)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 1127388) -> 1127388
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 3.0)
[6236]{L} >> cfunc: rb_obj_equal[@0x7f1d771b7d40](#<Symbol:0x0000000000dc310c>, 3.0) -> false
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, :foo) -> false
[6236]{L} >> branchif +53 (0x56106f3ec898->0x56106f3eca50) { false }; jump: not taken
[6236]{L} >> putobject (true)
[6236]{L} >> topn n: 1 [ true, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (true).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (true).=== // cfunc.func: rb_equal[@0x7f1d771b7e50](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](true, :foo)
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](true, :foo) -> false
[6236]{L} >> branchif +49 (0x56106f3ec8d8->0x56106f3eca70) { false }; jump: not taken
[6236]{L} >> putnil
[6236]{L} >> topn n: 1 [ nil, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (nil).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (nil).=== // cfunc.func: rb_equal[@0x7f1d771b7e50](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](nil, :foo)
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](nil, :foo) -> false
[6236]{L} >> branchif +46 (0x56106f3ec910->0x56106f3eca90) { false }; jump: not taken
[6236]{L} >> putobject ("foo")
[6236]{L} >> topn n: 1 [ "foo", :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: ("foo").=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("foo").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", :foo)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", :foo) -> false
[6236]{L} >> branchif +42 (0x56106f3ec950->0x56106f3ecab0) { false }; jump: not taken
[6236]{L} >> putobject (:foo)
[6236]{L} >> topn n: 1 [ :foo, :foo ] (top->bottom)
[6236]{L} >> topn -> :foo
[6236]{L} >> opt_send_without_block: (:foo).=== :foo [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> putobject (true)
[6236]{L} >> leave -> true
[6236]{L} >> branchif +38 (0x56106f3ec990->0x56106f3ecad0) { true }; jump: taken
[6236]{L} >> pop (:foo)
[6236]{L} >> putstring "symbol"
[6236]{L} >> leave -> "symbol"
[6236]{L} >> putself (main)
[6236]{L} >> putstring "foo"
[6236]{L} >> opt_send_without_block: (main).foo "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> "foo"
[6236]{L} >> dup ("foo")
[6236]{L} >> dup -> TOPN(0): "foo"
[6236]{L} >>     -> TOPN(1): "foo"
[6236]{L} >> opt_case_dispatch key: "foo" (type:5), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: ("hello").=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", "foo")
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", "foo") -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: (1).=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, "foo")
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184349060)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 1, 69882184349060) -> 69882184349060
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 1)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 1) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, "foo") -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: (2147483648).=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, "foo")
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184349060)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 2147483648, 69882184349060) -> 69882184349060
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 2147483648)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 2147483648) -> false
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, "foo") -> false
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { false }; jump: not taken
[6236]{L} >> putobject (3.0)
[6236]{L} >> topn n: 1 [ 3.0, "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: (3.0).=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (3.0).=== // cfunc.func: rb_float_equal[@0x7f1d771acff0](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, "foo")
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{{:===>{}, :join=>{}, :foo=>{}, :====>{...}, :inspect=>{{...}=>true, {...}=>true, {...}=>true}}=>true}, :inspect=>{}}, :===) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184349060)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, 3.0, 69882184349060) -> 69882184349060
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 3.0)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", 3.0) -> false
[6236]{L} >> cfunc: rb_float_equal[@0x7f1d771acff0](#<Float:0x0040000000000002>, "foo") -> false
[6236]{L} >> branchif +53 (0x56106f3ec898->0x56106f3eca50) { false }; jump: not taken
[6236]{L} >> putobject (true)
[6236]{L} >> topn n: 1 [ true, "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: (true).=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (true).=== // cfunc.func: rb_equal[@0x7f1d771b7e50](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](true, "foo")
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](true, "foo") -> false
[6236]{L} >> branchif +49 (0x56106f3ec8d8->0x56106f3eca70) { false }; jump: not taken
[6236]{L} >> putnil
[6236]{L} >> topn n: 1 [ nil, "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: (nil).=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (nil).=== // cfunc.func: rb_equal[@0x7f1d771b7e50](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](nil, "foo")
[6236]{L} >> cfunc: rb_equal[@0x7f1d771b7e50](nil, "foo") -> false
[6236]{L} >> branchif +46 (0x56106f3ec910->0x56106f3eca90) { false }; jump: not taken
[6236]{L} >> putobject ("foo")
[6236]{L} >> topn n: 1 [ "foo", "foo" ] (top->bottom)
[6236]{L} >> topn -> "foo"
[6236]{L} >> opt_send_without_block: ("foo").=== "foo" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("foo").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", "foo")
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("foo", "foo") -> true
[6236]{L} >> branchif +42 (0x56106f3ec950->0x56106f3ecab0) { true }; jump: taken
[6236]{L} >> pop ("foo")
[6236]{L} >> putstring "string"
[6236]{L} >> leave -> "string"
[6236]{L} >> putself (main)
[6236]{L} >> putobject (2147483648)
[6236]{L} >> opt_send_without_block: (main).foo 2147483648 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> 2147483648
[6236]{L} >> dup (2147483648)
[6236]{L} >> dup -> TOPN(0): 2147483648
[6236]{L} >>     -> TOPN(1): 2147483648
[6236]{L} >> opt_case_dispatch key: 2147483648 (type:-1), hash: {"hello"=>72, 1=>76, 2147483648=>80, 3=>84, true=>88, nil=>92, "foo"=>96, :foo=>100}; path taken: fall through (:=== redefined)
[6236]{L} >> putobject ("hello")
[6236]{L} >> topn n: 1 [ "hello", 2147483648 ] (top->bottom)
[6236]{L} >> topn -> 2147483648
[6236]{L} >> opt_send_without_block: ("hello").=== 2147483648 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: ("hello").=== // cfunc.func: rb_str_equal[@0x7f1d77277400](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 2147483648)
[6236]{L} >> cfunc: rb_str_equal[@0x7f1d77277400]("hello", 2147483648) -> false
[6236]{L} >> branchif +64 (0x56106f3ec7e0->0x56106f3ec9f0) { false }; jump: not taken
[6236]{L} >> putobject_INT2FIX_1_ [putobject (1)]
[6236]{L} >> topn n: 1 [ 1, 2147483648 ] (top->bottom)
[6236]{L} >> topn -> 2147483648
[6236]{L} >> opt_send_without_block: (1).=== 2147483648 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (1).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 2147483648)
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](1, 2147483648) -> false
[6236]{L} >> branchif +61 (0x56106f3ec818->0x56106f3eca10) { false }; jump: not taken
[6236]{L} >> putobject (2147483648)
[6236]{L} >> topn n: 1 [ 2147483648, 2147483648 ] (top->bottom)
[6236]{L} >> topn -> 2147483648
[6236]{L} >> opt_send_without_block: (2147483648).=== 2147483648 [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_sendish [inlined]: inline method cache hit, cd->cc->call_: vm_call_cfunc_with_frame[@0x7f1d772c35e0]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6ed0, ec->cfp: 0x7f1d6dbe6ed0
[6236]{L} >> vm_call_cfunc: (2147483648).=== // cfunc.func: rb_int_equal[@0x7f1d771ae510](argc: 1); cfunc.invoker: ractor_safe_call_cfunc_1[@0x7f1d772bba60]
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 2147483648)
[6236]{L} >> cfunc: rb_int_equal[@0x7f1d771ae510](2147483648, 2147483648) -> true
[6236]{L} >> branchif +57 (0x56106f3ec858->0x56106f3eca30) { true }; jump: taken
[6236]{L} >> pop (2147483648)
[6236]{L} >> putstring "bignum"
[6236]{L} >> leave -> "bignum"
[6236]{L} >> newarray num: 7 [ "world", "num", "float", "float", "symbol", "string", "bignum" ]
[6236]{L} >> newarray -> ["world", "num", "float", "float", "symbol", "string", "bignum"]
[6236]{L} >> setlocal_WC_0 [email protected]:1(raw:3)[2/2], level:0, val: (["world", "num", "float", "float", "symbol", "string", "bignum"])
[6236]{L} >> putself (main)
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:4)[1/2], level:0
[6236]{L} >> getlocal_WC_0 -> ["world", "num", "float", "float", "symbol", "string", "bignum"]
[6236]{L} >> getlocal_WC_0 [email protected]:1(raw:3)[2/2], level:0
[6236]{L} >> getlocal_WC_0 -> ["world", "num", "float", "float", "symbol", "string", "bignum"]
[6236]{L} >> newarray num: 2 [ ["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"] ]
[6236]{L} >> newarray -> [["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]]
[6236]{L} >> opt_send_without_block: ([["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]]).inspect [flags:ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6f10, ec->cfp: 0x7f1d6dbe6f10
[6236]{L} >> vm_call_cfunc: ([["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]]).inspect // cfunc.func: rb_ary_inspect[@0x7f1d77075ec0](argc: 0); cfunc.invoker: ractor_safe_call_cfunc_0[@0x7f1d772bba50]
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0]([["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]])
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{{...}=>true, {...}=>true}}, :inspect)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{{...}=>true, {...}=>true}}, :inspect) -> {}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, [["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]], true)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({}, [["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]], true) -> true
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0](["world", "num", "float", "float", "symbol", "string", "bignum"])
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{[...]=>true, {...}=>true, {...}=>true}}, :inspect)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{[...]=>true, {...}=>true, {...}=>true}}, :inspect) -> {[...]=>true, {...}=>true}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({[...]=>true, {...}=>true}, ["world", "num", "float", "float", "symbol", "string", "bignum"], true)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({[...]=>true, {...}=>true}, ["world", "num", "float", "float", "symbol", "string", "bignum"], true) -> true
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("world")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("world") -> "\"world\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("num")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("num") -> "\"num\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float") -> "\"float\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float") -> "\"float\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("symbol")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("symbol") -> "\"symbol\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("string")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("string") -> "\"string\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("bignum")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("bignum") -> "\"bignum\""
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0](["world", "num", "float", "float", "symbol", "string", "bignum"]) -> "[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]"
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0](["world", "num", "float", "float", "symbol", "string", "bignum"])
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{[...]=>true, {...}=>true, {...}=>true}}, :inspect)
[6236]{L} >> cfunc: rb_hash_aref[@0x7f1d7713e270]({:===>{}, :join=>{}, :foo=>{}, :====>{}, :inspect=>{[...]=>true, {...}=>true, {...}=>true}}, :inspect) -> {[...]=>true, {...}=>true}
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({[...]=>true, {...}=>true}, ["world", "num", "float", "float", "symbol", "string", "bignum"], true)
[6236]{L} >> cfunc: rb_hash_aset[@0x7f1d7713a880]({[...]=>true, {...}=>true}, ["world", "num", "float", "float", "symbol", "string", "bignum"], true) -> true
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("world")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("world") -> "\"world\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("num")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("num") -> "\"num\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float") -> "\"float\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("float") -> "\"float\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("symbol")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("symbol") -> "\"symbol\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("string")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("string") -> "\"string\""
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("bignum")
[6236]{L} >> cfunc: rb_str_inspect[@0x7f1d77279b70]("bignum") -> "\"bignum\""
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0](["world", "num", "float", "float", "symbol", "string", "bignum"]) -> "[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]"
[6236]{L} >> cfunc: rb_ary_inspect[@0x7f1d77075ec0]([["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]]) -> "[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]"
[6236]{L} >> opt_send_without_block: (main).puts "[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]" [flags:FCALL|ARGS_SIMPLE]
[6236]{L} >> vm_call_cfunc(_with_frame): reg_cfp: 0x7f1d6dbe6f10, ec->cfp: 0x7f1d6dbe6f10
[6236]{L} >> vm_call_cfunc: (main).puts // cfunc.func: rb_f_puts[@0x7f1d77155730](argc: 1, defined argc: -1); cfunc.invoker: ractor_safe_call_cfunc_m1[@0x7f1d772bba40]
[6236]{L} >> cfunc: rb_f_puts[@0x7f1d77155730](0x1, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]"], main)
[6236]{L} >> cfunc: rb_io_puts[@0x7f1d771554d0](0x1, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]"], #<IO:<STDOUT>>)
[6236]{L} >> cfunc: io_write_m[@0x7f1d77151e00](0x2, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]", "\n"], #<IO:<STDOUT>>)
[6236]{L} >> cfunc: io_write_m[@0x7f1d77151e00](0x2, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]", "\n"], #<IO:<STDOUT>>) -> 133
[6236]{L} >> cfunc: rb_io_puts[@0x7f1d771554d0](0x1, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]"], #<IO:<STDOUT>>) -> nil
[6236]{L} >> cfunc: rb_f_puts[@0x7f1d77155730](0x1, ["[[\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"], [\"world\", \"num\", \"float\", \"float\", \"symbol\", \"string\", \"bignum\"]]"], main) -> nil
[6236]{L} >> leave -> nil
[6236]{L} >> getlocal_WC_0 [email protected]:0(raw:3)[1/1], level:0
[6236]{L} >> getlocal_WC_0 -> #<TracePoint:enabled>
[6236]{L} >> opt_send_without_block: (#<TracePoint:enabled>).disable [flags:ARGS_SIMPLE]
[6236]{L} >> putnil
[6236]{L} >> leave -> nil
[6236]{L} >> opt_invokebuiltin_delegate "tracepoint_disable_m" (tracepoint_disable_m[@0x7f1d772ea2e0]), index: 0x0
[6236]{1} [["world", "num", "float", "float", "symbol", "string", "bignum"], ["world", "num", "float", "float", "symbol", "string", "bignum"]]
[6236]{1}
[6236]{2 -> EOF}
[6236]{1 -> EOF}
[*] onChildDetached(reason='process-terminated')

In addition to support for Ruby 3.1, this new release of ruby-trace also includes a number of other improvements.

While it was already the case that ruby-trace had a better and more complete set of test cases for emitting opcodes and covering edge cases in the Ruby VM than Ruby itself, this release includes an overhauled test infrastructure to better ensure correctness and test coverage. This release also includes several fixes to edge cases caught by the new test harness, including modifications to test cases that ensure emission of specific opcodes across applicable Ruby versions as the Ruby bytecode compiler will often change what sorts of opcodes get emitted for certain code constructs.

ruby-trace uses the TracePoint API infrastructure to ensure that tracing can reliably be enabled and, more importantly, disabled, regardless of if the code being traced raises exceptions or performs other weird control flow changes that might otherwise evade a disable tracing call. The current version of ruby-trace now exposes a custom Ruby-facing interface to enable and disable tracing without hitching a ride on the Ruby tracing API, and a separate CLI feature to enable tracing whenever a specific native function is executed. These make it possible to perform more fine-grained tracing without also running Ruby’s own tracing at the same time.

ruby-trace doesn’t yet fully support the use of multiple Ruby execution contexts, and therefore cannot easily distinguish between ractors in Ruby 3.0+. Part of this is due to caching of rb_execution_context_t values and part of this is due to needing to carefully track the creation and use of them in the first place, with the limited number of non-elided symbols available. However, adding support for ractors is on the todo list, so look forward to it.

In very early versions of ruby-trace before its tracing on-off switches were anchored to the TracePoint API, it would instrument from the earliest moments of the start of the Ruby VM in rb_call_inits, including the construction of the BasicObject class, which happens fairly late in the grand scheme of things. While very cool, this is also a very difficult context to work within as even the infrastructure for symbols and IDs don’t really exist until just before the logic to create the class hierarchy and Object and BasicObject, let alone any operations that require the VM to be functional to execute opcodes. As a result, stringification for trace output had to be very simple so as not to crash the process due to lower level support functions not being runnable so early in VM bringup. So ruby-trace was modified to use Init_Object as its main anchor to safely apply its other hooks, including the hooks to enable the tracing hooks when the TracePoint API was invoked.

While it “should”™️ be a simple enough affair to enable a CLI flag to simply begin tracing after rb_call_inits returns, it would be nice to try to begin tracing from the point that Init_Object returns, and ideally, from the start of Init_BareVM or even ruby_setup/ruby_init. However, this is likely going to require some work, and may require parallel hooking infrastructure(s) tailored to the lack of a runtime substrate in the different phases of Ruby bringup.

Right now, any library code that interacts with Ruby’s own tracing APIs could very easily accidentally disable ruby-trace’s tracing. Going forward, I’ll likely add some features to better control what can and cannot enable and disable its tracing. Additionally, it would be useful to provide CLI-based class/module method filters to enable tracing either until the method returns, or simply until disabled, obviating the need to write any Ruby to trace components of existing Ruby applications. I would also like to make the hooks configurable to, for example, trace only certain opcodes, but not native calls, etc., or to disable tracing entirely for certain methods as large quantities of trace output can be daunting to work with.

ruby-trace has been a fun hobby project to work on, if only for the work in reversing Ruby and triaging some truly deranged bugs. I’m not sure if it could ever be considered “stable,” but it mostly exists to prove a point. Overall, I think the Ruby developers should just add a proper tracing implementation, even if it’s not terribly performant. It would certainly make my life easier.


文章来源: https://research.nccgroup.com/2021/12/31/tool-update-ruby-trace-a-low-level-tracer-for-ruby/
如有侵权请联系:admin#unsafe.sh