post Image
Ruby の TracePoint について調べてみた

Ruby の TracePoint について調べてみました。

Ruby 2.0 から導入された機能

TracePoint は Ruby 2.0 から使えるようになった機能です。様々なイベントをフックすることが出来ます。

トレースの開始方法

トレースの開始するには 2通りの方法があります。

1. trace を利用して開始する

trace を使用した場合はトレースが即座に有効状態になります。

TracePoint.trace do |tp|
  p tp
end

返り値として TracePoint オブジェクトを返すので、トレースを無効にしたい場合は disable を実行します。

trace = TracePoint.trace  do |tp|
  p tp
end

trace.enabled? #=> true
trace.disable
trace.enabled? #=> false

2. new で TracePoint オブジェクトを作成して enable で開始する

もう一つは new で TracePoint オブジェクトを生成して enable を実行する方法です。

trace = TracePoint.new do |tp|
  p tp
end

trace.enable

disable を使用するとトレースが終了します。(トレースが有効かどうかを調べるには disabled? を使用する。

trace = TracePoint.new do |tp|
  p tp
end

trace.enabled? #=> false
trace.enable
trace.enabled? #=> true
trace.disable
trace.enabled? #=> false

トレースするイベントを指定

tracenew では引数を渡すことでトレースするイベントを指出することが出来ます。

何も指定しない場合は全てのイベントがトレースされるのですが、引数でトレースするイベントの種類を指定することで、そのイベントだけがトレースされるようになります。

TracePoint.trace(:call) do |tp|
  p [tp.self, tp.event, tp.method_id]
end

def say
  'hello'
end

say

実行結果

[main, :call, :say]

トレース出来るイベントの種類

TracePoint では Ruby の様々なイベントを指定することが可能です。

イベント 説明
:line 式の評価
:class クラス定義、特異クラス定義、モジュール定義への突入
:end クラス定義、特異クラス定義、モジュール定義の終了
:call Ruby で記述されたメソッドの呼び出し
:return Ruby で記述されたメソッド呼び出しからのリターン
:c_call Cで記述されたメソッドの呼び出し
:c_return Cで記述されたメソッド呼び出しからのリターン
:raise 例外の発生
:b_call ブロックの開始
:b_return ブロックの終了
:thread_begin スレッドの開始
:thread_end スレッドの終了
:fiber_switch コンテキストの切替時

line

式の評価毎に発生するイベント。

TracePoint.trace(:line) do |tp|
  puts "#{tp.lineno}: exec"
end

a = 1
b = 2
c = 3

実行結果

5: exec
6: exec
7: exec

class, end

class はクラスやモジュールの定義の開始、 end はクラスやモジュールの定義の終了の際に発生するイベントです。

TracePoint.trace(:class, :end) do |tp|
  p [tp.lineno, tp.self, tp.event]
end

class A
  def say
  end
end

実行結果

[5, A, :class]
[8, A, :end]

self を利用すると、どのクラス・モジュールが定義されたのかがわかります。

call, return

call は Ruby で記述されたメソッドの呼び出し、 return は Ruby で記述されたメソッドからのリターン時に呼び出されるイベントです。 method_id を使用することで、どのメソッドから呼び出されたのかがわかります。

TracePoint.trace(:call, :return) do |tp|
  p [tp.lineno, tp.event, tp.method_id]
end

def say
  'hello'
end

say

実行結果

[5, :call, :say]
[7, :return, :say]

c_call, c_return

c_call はC で記述されたメソッドの呼び出し、 c_return はC で記述されたメソッド呼び出しからのリターン時に呼び出されるイベントです。

TracePoint.trace(:c_call, :c_return) do |tp|
  p [tp.lineno, tp.event, tp.method_id]
end

'100'.to_i

実行結果

[1, :c_return, :trace]
[5, :c_call, :to_i]
[5, :c_return, :to_i]

raise

raise は例外発生時に呼び出されるイベントです。発生した例外は raised_exception で取得することが出来ます。

TracePoint.trace(:raise) do |tp|
  p [tp.lineno, tp.event, tp.raised_exception]
end

raise 'error'

実行結果

[5, :raise, #<RuntimeError: error>]
Traceback (most recent call last):
raise.rb:5:in `<main>': error (RuntimeError)

b_call, b_return

b_call はブロックの開始、 b_return はブロックの終了時に呼び出されるイベントです。

TracePoint.trace(:b_call, :b_return) do |tp|
  p [tp.lineno, tp.event]
end

[1, 2, 3].each do |n|
  p n
end

実行結果

[5, :b_call]
1
[7, :b_return]
[5, :b_call]
2
[7, :b_return]
[5, :b_call]
3
[7, :b_return]

thread_begin, thread_end

thread_begin はスレッドの開始、 thread_end はスレッドの終了時に呼び出されるイベントです。

TracePoint.trace(:thread_begin, :thread_end) do |tp|
  p tp.event
end

thr = Thread.new { puts 'Hello World!' }
thr.join

実行結果

:thread_begin
Hello World!
:thread_end

fiber_switch

fiber_switch はコンテキストの切り替えが発生したタイミングに発生するイベントです。

TracePoint.trace(:fiber_switch) do |tp|
  p [tp.lineno, tp.event]
end

f = Fiber.new do
  n = 0

  loop do
    Fiber.yield(n)
    n += 1
  end
end

3.times do
 p f.resume
end

実行結果

[0, :fiber_switch]
[15, :fiber_switch]
0
[9, :fiber_switch]
[15, :fiber_switch]
1
[9, :fiber_switch]
[15, :fiber_switch]
2

TracePoint で使用できるメソッド

TracePoint では様々なメソッドが使用できます

メソッド名 説明
binding binding オブジェクトを返す
defined_class メソッドを定義したクラスかモジュールを返す
disable トレースを無効にする
enable トレースを有効にする
enabled? トレースが有効かどうかを調べる
event 発生したイベントの種類を返す
inspect self の状態を人間が読みやすいものにしてくれる
lineno イベントが発生した行番号を返す
method_id イベントが発生したメソッド名を返す
path イベントが発生したファイルのパスを返す
raised_exception 発生した例外を返す
return_value メソッドやブロックの戻り値を返す
self イベントを発生させたオブジェクトを返す

binding

binding オブジェクトを取得することが出来ます。

binding でさらに便利になる TracePoint

Binding オブジェクトで利用できるメソッドを使用することで、TracePoint がさらに強力になります。

メソッド名 説明
eval self をコンテキストとして渡された文字列を評価しその結果を返す
local_variable_defined? 引数で渡された名前の変数が定義されているかどうかを判定する
local_variable_get 引数で渡された名前の変数の値を取得する
local_variable_set 第一引数で指定した名前の変数に第二引数で渡された値を設定する
local_variables ローカル変数の一覧を取得します
receiver 保持するコンテキスト内での self を返します

例) メソッドの引数の値をチェックする

TracePoint.trace(:call) do |tp|
  puts "#{tp.method_id} : arguments"

  tp.binding.local_variables.each do |name|
    puts "  #{name} = #{tp.binding.local_variable_get(name).inspect}"
  end
end

def say(message)
  puts message
end

say('hello')

実行結果

say : arguments
  message = "hello"
hello

defined_class

メソッドを定義したクラスかモジュールを返します。

TracePoint.trace(:call) do |tp|
  p tp.defined_class
end

class A
  def say
    'hi'
  end
end

A.new.say

実行結果

A

disable

トレースを無効にします。

trace = TracePoint.trace { |tp| }

trace.enabled? #=> true
trace.disable
trace.enabled? #=> false

disable にブロックを渡すことで、そのブロックの範囲だけトレースを無効にすることが出来ます。

trace = TracePoint.trace { |tp| }

trace.enabled? #=> true
trace.disable { }
trace.enabled? #=> true

enable

トレースを有効にします。

trace = TracePoint.new { |tp| }

trace.enabled? #=> false
trace.enable
trace.enabled? #=> true

enable にブロックを渡すことで、そのブロックの範囲だけトレースを有効にすることが出来ます。

trace = TracePoint.new { |tp| }

trace.enabled? #=> false
trace.enable { }
trace.enabled? #=> false

enabled?

トレースが有効かどうかを返します。

トレースが有効のままだと遅くなる?

トレースが有効な状態はトレースが無効な状態に比べて実行速度が遅くなってしまうので、使う時はなるべく必要な範囲だけを有効にしたほうが良さそうです。

require 'benchmark'

trace = TracePoint.new { |tp| }

result1 = Benchmark.measure do 
  1000000.times { 1 + 1 }
end

result2 = Benchmark.measure do 
  trace.enable { 1000000.times { 1 + 1 } }
end

puts Benchmark::CAPTION
puts result1
puts result2

結果

      user     system      total        real
  0.052800   0.000084   0.052884 (  0.053058)
  0.339369   0.000967   0.340336 (  0.341383)

event

発生したイベントの種類を返します。

inspect

self の状態を人間が読みやすいものにしてくれます。

TracePoint.trace do |tp|
  p tp
end

実行結果

#<TracePoint:c_return `trace'@inspect.rb:1>

lineno

イベントが発生した行番号を返します。 path と一緒に使用することでどこで発生したイベントなのかを判別することが出来ます。

TracePoint.trace(:line) do |tp|
  p tp.lineno
end

a = 1
b = 2
c = 3

実行結果

5
6
7

method_id

イベントが発生したメソッド名を返します。

TracePoint.trace(:call) do |tp|
  p tp.method_id
end

class A
  def say
    'hi'
  end
end

A.new.say

実行結果

:say

path

イベントが発生したファイルのパスを返します。

raised_exception

発生した例外を返します。 raise イベント以外で使用した場合は RuntimeError が発生します。

return_value

メソッドやブロックの返り値を取得することが出来ます。このイベントが利用出来るのは return, c_return, b_return の 3つで、これ以外のイベントで使用すると RuntimeError が発生します。

TracePoint.trace(:return) do |tp|
  p tp.return_value
end

def message
  'hi'
end

message

実行結果

"hi"

return_value を書き変える

この return_value の値を破壊的に変更することで、メソッドの返り値を変更することが出来ます。

TracePoint.trace(:c_return) do |tp|
  tp.return_value.downcase! if tp.method_id == :upcase
end

p 'Hello'.upcase

実行結果

"hello"

self

イベントを発生させたオブジェクトを返します。

TracePoint.trace(:call) do |tp|
  p tp.self
  p tp.binding.eval('self')
  p tp.binding.receiver
end

class A
  def say
  end
end

A.new.say

実行結果

#<A:0x00007fa099020198>
#<A:0x00007fa099020198>
#<A:0x00007fa099020198>

サンプルコード

TracePoint を使って色々コードを書いてみました。

参考サイト


『 Ruby 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。