Ruby có hệ thống theo dõi tích hợp sẵn mà bạn có thể truy cập thông qua lớp TracePoint. Một số thứ bạn có thể theo dõi bao gồm gọi phương thức, luồng mới và ngoại lệ.
Tại sao bạn muốn dùng tính năng này?
Nó có thể hữu ích nếu bạn muốn theo dõi việc thực thi của một phương thức cụ thể. Bạn sẽ thấy được các phương thức khác đang được gọi và giá trị trả về của chúng.
Hãy cùng xem một vài ví dụ!
Theo dõi gọi phương thức
Phần lớn thời gian bạn sẽ muốn TracePoint theo dõi mã ứng dụng thay vì các phương thức tích hợp sẵn (như puts, size, v.v.).
Bạn có thể làm điều này bằng sự kiện call.
Ví dụ:
def the_method; other_method; end
def other_method; end
def start_trace
trace = TracePoint.new(:call) { |tp| p [tp.path, tp.lineno, tp.event, tp.method_id] }
trace.enable
yield
trace.disable
end
start_trace { the_method }
Đoạn mã này in ra đường dẫn tệp, số dòng, tên sự kiện và tên phương thức.
["test.rb", 1, :call, :the_method] ["test.rb", 2, :call, :other_method]
Nếu bạn không chỉ định sự kiện nào, Ruby sẽ gọi block của bạn cho tất cả các sự kiện, dẫn đến nhiều đầu ra hơn. Vì vậy, tôi khuyên bạn nên tập trung vào các sự kiện cụ thể để tìm thấy thứ bạn cần nhanh hơn 🙂
Dưới đây là bảng các sự kiện của TracePoint:
| Tên sự kiện | Mô tả |
|---|---|
| call | Phương thức ứng dụng |
| c_call | Phương thức cấp C (như puts) |
| return | Trả về phương thức (để theo dõi giá trị trả về và độ sâu gọi) |
| b_call | Gọi block |
| b_return | Trả về block |
| raise | Ngoại lệ được ném ra |
| thread_begin | Luồng mới |
| thread_end | Luồng kết thúc |
TracePoint + Graphviz
Nhiều phương thức sẽ thực hiện nhiều hơn 3 lần gọi phương thức, đặc biệt trong mã framework, nên đầu ra từ TracePoint có thể khó hình dung.
Vì vậy tôi tạo了一個 gem cho phép bạn tạo biểu đồ gọi trực quan như thế này:
require 'visual_call_graph'
VisualCallGraph.trace { "Your method call here..." }
Điều này tạo ra tệp call_graph.png với kết quả.

Hãy nhớ rằng đây không phải là phân tích tĩnh, nó sẽ thực sự gọi phương thức!
Hiển thị đường dẫn tệp
Bạn có muốn biết các phương thức này được định nghĩa ở đâu không?
Đừng lo, tôi đã lo cho bạn! Tôi đã thêm một tùy chọn bạn có thể bật để hiển thị đường dẫn tệp cho mỗi lần gọi phương thức.
VisualCallGraph.trace(show_path: true) { Foo.aaa }
Kết quả là:

Nếu bạn muốn xem các biểu đồ gọi khổng lồ, chỉ cần theo dõi một số phương thức Rails 😉
Giá trị trả về
Trong phần mở đầu tôi đã nhắc đến việc bạn cũng có thể lấy giá trị trả về...
Để làm điều này, bạn sẽ cần theo dõi sự kiện return và sử dụng phương thức return_value.
Ví dụ:
def the_method; "A" * 10; end
trace = TracePoint.new(:return) { |tp| puts "Return value for #{tp.method_id} is #{tp.return_value}." }
trace.enable
the_method
trace.disable
Điều này sẽ in ra:
Return value for the_method is AAAAAAAAAA.
Sự kiện xảy ra trước
Có ai đó hỏi trên Reddit làm thế nào để tránh in từ "bar" khi gọi phương thức foo trong mã sau:
class Thing
def foo
puts "foo"
bar
end
def bar
puts "bar"
end
end
# your code here
t = Thing.new
t.foo
Có nhiều cách để đạt được điều này, như prepend một module, chuyển hướng $stdout hoặc định nghĩa lại phương thức bar.
Nếu bạn cảm thấy sáng tạo, hãy bình luận vào bài viết này với ý tưởng của bạn!
Nhưng tôi thấy một câu trả lời đặc biệt thú vị vì nó sử dụng lớp TracePoint.
Đây là nó:
TracePoint.trace(:call) { |tp| exit if tp.method_id == :bar }
Đoạn mã này sẽ gọi exit khi phương thức bar được gọi, ngăn chặn việc in chuỗi bằng cách kết thúc chương trình.
Có lẽ không phải điều bạn muốn dùng trong mã thực tế, nhưng nó chứng minh một điều về TracePoint: Sự kiện được kích hoạt trước khi chúng xảy ra.
Điều cần nhớ nếu bạn định xây dựng某种工具 dựa trên này 🙂
Tóm tắt
Trong bài viết này, bạn đã học về lớp TracePoint, cho phép bạn theo dõi một số sự kiện như gọi phương thức hoặc luồng mới. Điều này có thể hữu ích như công cụ gỡ lỗi hoặc khám phá mã.
Hãy nhớ chia sẻ bài viết này để nhiều người hơn có thể tận hưởng nó 🙂