ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

Linux上のRubyのUSDT

Rubyは 2.0 から --enable-dtrace オプションでコンパイルが可能になったんですが、

magazine.rubyist.net

一方で現代のLinuxとしてはこれらのトレースポイントは USDT としてトレースできます。今日はその手順など。

--enable-dtrace で作ったRubyコンパイル

rbenv なら CONFIGURE_OPTS='--enable-dtrace' という環境変数で渡せばOK。

$ CONFIGURE_OPTS='--enable-dtrace' rbenv install 2.6.4
...
$ ruby -r rbconfig -e 'puts RbConfig::CONFIG["configure_args"]'
 '--prefix=/home/vagrant/.rbenv/versions/2.6.4' '--enable-dtrace' 'LDFLAGS=-L/home/vagrant/.rbenv/versions/2.6.4/lib ' 'CPPFLAGS=-I/home/vagrant/.rbenv/versions/2.6.4/include '

プローブの一覧

bpftrace を使うので snap install bpftrace などしてください。

$ ruby -e 'loop { File.read("/etc/hosts"); sleep 1 }' &
[1] 14665
$ sudo bpftrace -p 14665 -l 'usdt:*ruby*'
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:raise
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:gc__mark__end
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:gc__mark__begin
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:gc__sweep__end
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:gc__sweep__begin
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:hash__create
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:load__entry
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:load__return
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:find__require__return
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:require__entry
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:find__require__entry
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:require__return
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:object__create
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:parse__begin
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:parse__end
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:string__create
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:symbol__create
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:method__cache__clear
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:cmethod__entry
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:cmethod__return
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:method__return
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:method__entry
usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:array__create

るびまの解説記事と同じようなポイントが定義されている。

$ sudo bpftrace -p 14665 -e 'usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:cmethod__entry { printf("cmethod__entry: %s#%s\n", str(arg0), str(arg1)); }'
Attaching 1 probe...
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep
cmethod__entry: IO#read
cmethod__entry: Kernel#sleep

IO#readもKernel#sleep も cmethod__entry を通る。

$ ruby -e 'def foobar; Object.new; end; loop { foobar; sleep 1 }' &                                                              
[1] 15744
$ sudo bpftrace -p 15744 -e 'usdt:/home/vagrant/.rbenv/versions/2.6.4/bin/ruby:ruby:object__create{ printf("object class: %s\n", str(arg0)); }'
Attaching 1 probe...
object class: Object
object class: Object
object class: Object
object class: Object
object class: Object
object class: Object
object class: Object

オブジェクトの生成を検知したり、などなど。

動いているプロセスにアタッチできたりもするのと、他のkprobe/uprobeと組み合わせるなどして、何かしら使えるかもしれませんね。