13

I would like to observe the Ruby bytecode executed by YARV, post mortem.

How can I get this ?

Someone else here told it was not possible. However, there is the hotruby framework that seems to execute ruby bytecode, so I am puzzled...

Thanks a lot!

Community
  • 1
  • 1
JCLL
  • 5,379
  • 5
  • 44
  • 64

3 Answers3

15

You can very well compile a chunk of Ruby code to the bytecode using the interpreter (of course, only the Ruby MRI 1.9 will work, as it is the only implementation which uses the YARV virtual machine), and get its Ruby-ish representation:

ruby-1.9.2-p180 :007 > require 'pp'
 => true
ruby-1.9.2-p180 :008 > pp RubyVM::InstructionSequence.compile('puts "hello world"').to_a
["YARVInstructionSequence/SimpleDataFormat",
 1,
 2,
 1,
 {:arg_size=>0, :local_size=>1, :stack_max=>2},
 "<compiled>",
 "<compiled>",
 nil,
 1,
 :top,
 [],
 0,
 [],
 [1,
  [:trace, 1],
  [:putnil],
  [:putstring, "hello world"],
  [:send, :puts, 1, nil, 8, 0],
  [:leave]]]

This is exactly what HotRuby does: it uses MRI as a parser and AST-to-YARV translator, and then just executes the code in JavaScript.

You can get the bytecode for an existing method with the RubyVM::InstructionSequence.disasm method. It expects a Proc as an argument, so first convert your method to a block by using object.method(:name).to_proc.

I'm not quite sure what do you mean by 'post-mortem'. In an exception handler? After Ruby has crashed with SEGV? The latter is hardly possible due to inability of damaged interpreter to run any Ruby code successfully. You'll need to make a C extension for that, and do quite a lot of dirty hacks. Using this trick in the exception handler is perfectly possible, through.

Catherine
  • 22,492
  • 3
  • 32
  • 47
  • I mentioned "post-mortem", because I though some decisions were not possible to translate, at compile time, into VM instructions. It seems that I am wrong. – JCLL Aug 01 '11 at 09:53
  • When you say "MRI", do you mean the CRuby implementation of Ruby 1.8? – Andrew Grimm Aug 01 '11 at 23:27
  • @Andrew, MRI stands for "Matz Reference Edition". It is a synonym for CRuby, both the 1.8 and 1.9 branches. (MRI has ~100 times more hits on Google. Also, CRuby reminds me of CPython, which I personally hate.) – Catherine Aug 02 '11 at 00:05
  • @whitequark: Sometimes people use "MRI" to refer only to the 1.8 CRuby implementation, and sometimes people use "MRI" to refer to the CRuby implementations of both 1.8 and 1.9. – Andrew Grimm Aug 02 '11 at 00:09
  • @Andrew, I've noticed that I never explicitly stated the Ruby version in my answer, probably because I thought that "YARV" in the title made it clear enough. I've updated the answer. Also, while there is no official statements on the "MRI" name and its meaning (and there will never be), I've never seen anyone to refer with MRI to just the 1.8 series. Even [Wikipedia](http://en.wikipedia.org/wiki/Ruby_MRI) talks about two branches. – Catherine Aug 02 '11 at 00:26
2

As far as I understand, RubyVM::InstructionSequence.compile is what you need.

Victor Deryagin
  • 11,895
  • 1
  • 29
  • 38
0

To get more readable output, you can use disasm after compile

[17] pry(main)> code = <<STR
[17] pry(main)* puts "Hello World"
[17] pry(main)* STR
=> "puts \"Hello World\"\n"
[19] pry(main)> puts RubyVM::InstructionSequence.compile(code).disasm
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putself
0003 putstring        "Hello World"
0005 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0007 leave
=> nil
[20] pry(main)>
MikeWu
  • 3,042
  • 2
  • 19
  • 27