TracePoint

TracePoint (코드 실행 추적)

TracePointRuby 코드의 실행 지점(이벤트)을 추적하는 기능이에요. 특정 이벤트(메서드 호출, 라인 실행, 클래스 정의, 예외 발생 등)가 발생할 때마다 콜백을 받아볼 수 있어요. 디버깅이나 프로파일링, 동적 분석에 유용해요. trace_point를 require 해서 사용해요.

출처: Ruby 3.3 API

본문

TracePoint.new(*events) { |obj| block } → obj

기본적으로 활성화되지 않은 새 TracePoint 객체를 돌려줘요. 다음으로 트레이스를 활성화하려면 TracePoint#enable을 사용해야 해요.

trace = TracePoint.new(:call) do |tp|
  p [tp.lineno, tp.defined_class, tp.method_id, tp.event]
end
#=> #<TracePoint:...>

trace.enable   #=> false
puts "Hello, TracePoint!"
# ...
# [48, IRB::Notifier::AbstractNotifier, :printf, :call]
# ...

# trace를 비활성화하려면 TracePoint#disable
trace.disable

블록은 반드시 주어야 하며, 주지 않으면 ArgumentError가 발생해요. 트레이스할 메서드가 주어진 이벤트 필터에 포함되지 않으면 RuntimeError가 발생해요.

TracePoint.trace(*events) { |obj| block } → obj

TracePoint.new의 편의 메서드로, 트레이스를 자동으로 활성화해요.

trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
#=> #<TracePoint:...>
trace.enabled?  #=> true

TracePoint.stat → obj

TracePoint 내부 정보를 돌려줘요. 반환값의 내용은 구현 세부 사항이라 미래에 바뀔 수 있어요. 이 메서드는 TracePoint 자체를 디버깅할 때만 사용하는 메서드예요.

allow_reentry { block }

일반적으로 TracePoint 콜백이 실행되는 동안 재진입(reentrance)으로 인한 혼란을 피하려고 다른 등록된 콜백은 호출되지 않아요. 이 메서드는 주어진 블록 안에서 재진입을 허용해요. 조심해서 사용해야 해요. 그렇지 않으면 콜백이 쉽게 무한히 호출될 수 있어요. 이미 재진입이 허용된 상태에서 이 메서드를 호출하면 RuntimeError가 발생해요.

allow_reentryTracePoint 클래스 자체(싱글턴) 메서드로도, 인스턴스의 참조를 통해 (TracePoint.allow_reentry { ... } 형태로 블록) 사용할 수 있어요.

enable(target: nil, target_line: nil, target_thread: nil) → true or false

트레이스를 활성화해요. 활성화돼 있으면 true, 아니면 false를 돌려줘요. 블록을 주면 그 블록 호출 동안에만 짧게 트레이스가 활성화돼요. targettarget_line이 둘 다 nil인데 블록이 주어지면, target_thread는 기본값으로 현재 스레드가 돼요.

disable → true or false · disable { block } → obj

트레이스를 비활성화해요. 트레이스가 활성화돼 있었으면 true, 아니었으면 false를 돌려줘요. 블록을 주면 블록의 스코프 안에서만 트레이스가 비활성화돼요. 주의: 블록 안에서는 이벤트 훅에 접근할 수 없어요. (예: trace.disable { p tp.lineno }RuntimeError: access from outside)

enabled? → true or false

트레이스의 현재 상태를 돌려줘요.

이벤트 접근자(인스턴스 메서드)

  • event() - 이벤트 타입.
  • lineno() - 이벤트의 라인 번호.
  • path() - 실행 중인 파일의 경로.
  • method_id() - 호출되는 메서드의 정의 이름.
  • callee_id() - 호출되는 메서드의 호출 이름.
  • defined_class() - 호출되는 메서드가 정의된 클래스 또는 모듈.
  • parameters() - 현재 훅이 속한 메서드/블록의 파라미터 정의. 형식은 Method#parameters와 같아요.
  • binding() - 이벤트에서 생성된 binding 객체. :c_call:c_return 이벤트에서는 C 메서드에는 binding이 없으므로 nil을 돌려줘요.
  • self() - 이벤트 동안의 트레이스 객체. :c_call/:c_return에 대해 올바른 객체(메서드 수신자)를 돌려주는 점을 빼면 trace.binding.eval('self')와 같아요.
  • return_value() - :return, :c_return, :b_return 이벤트의 반환값.
  • raised_exception() - :raise 이벤트에서 던져지거나 :rescue 이벤트에서 구출된 예외.
  • eval_script() - :script_compiled 이벤트에서 *eval 메서드의 컴파일된 소스 코드(String). 파일에서 로드했다면 nil을 돌려줘요.
  • instruction_sequence() - :script_compiled 이벤트에서 RubyVM::InstructionSequence 인스턴스로 표현된 컴파일된 명령 시퀀스. 이 메서드는 MRI 특정(구현 의존)이에요.
  • inspect → string - 사람이 읽을 수 있는 TracePoint 상태 문자열을 돌려줘요.

defined_class 예시

class C; def foo; end; end
trace = TracePoint.new(:call) do |tp|
  p tp.defined_class  #=> C
end.enable do
  C.new.foo
end

메서드가 모듈로 정의되면 그 모듈이 돌려줘요.

module M; def foo; end; end
class C; include M; end
trace = TracePoint.new(:call) do |tp|
  p tp.defined_class  #=> M
end.enable do
  C.new.foo
end

defined_class는 싱글턴 클래스를 돌려준다는 점에 주의하세요. Kernel#set_trace_func의 6번째 블록 파라미터는 싱글턴 클래스에 붙은 원래 클래스를 전달하는데, 이게 Kernel#set_trace_funcTracePoint의 차이예요.