Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ติดตามเมธอด Ruby ด้วย TracePoint: คู่มือปฏิบัติจริง

Ruby มีระบบติดตาม (tracing) ในตัวที่คุณสามารถเข้าถึงได้ผ่านคลาส TracePoint สิ่งที่คุณติดตามได้รวมถึงการเรียกเมธอด การสร้างเธรดใหม่ และข้อยกเว้น (exceptions)

ทำไมต้องใช้ TracePoint?

มันมีประโยชน์ถ้าคุณอยากติดตามการทำงานของเมธอดใดเมธอดหนึ่ง คุณจะเห็นว่าเมธอดอื่นๆ ถูกเรียกอย่างไร และค่าที่คืนกลับมาคืออะไร

มาเริ่มดูตัวอย่างกัน!

การติดตามการเรียกเมธอด

ส่วนใหญ่คุณจะอยากให้ TracePoint ติดตามโค้ดแอปพลิเคชัน ไม่ใช่เมธอดในตัว (built-in methods) เช่น puts, size ฯลฯ

คุณทำได้โดยใช้ event call

ตัวอย่าง:

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 }

โค้ดนี้จะพิมพ์พาธไฟล์ หมายเลขบรรทัด ชื่อ event และชื่อเมธอด

["test.rb", 1, :call, :the_method]
["test.rb", 2, :call, :other_method]

ถ้าคุณไม่ระบุ event Ruby จะเรียกบล็อกของคุณสำหรับทุก event ทำให้เอาต์พุตเยอะขึ้น แนะนำให้โฟกัสที่ event เฉพาะเจาะจงเพื่อหาข้อมูลที่ต้องการเร็วขึ้น 🙂

นี่คือตาราง event ของ TracePoint:

Event name คำอธิบาย
call เมธอดระดับแอปพลิเคชัน
c_call เมธอดระดับ C (อย่าง puts)
return การคืนค่าของเมธอด (สำหรับติดตามค่าที่คืนและความลึกของการเรียก)
b_call การเรียกบล็อก
b_return การคืนค่าของบล็อก
raise มีข้อยกเว้นเกิดขึ้น
thread_begin มีเธรดใหม่สร้างขึ้น
thread_end เธรดสิ้นสุด

TracePoint + Graphviz

เมธอดหลายตัวจะเรียกเมธอดอื่นมากกว่าแค่ 3 ครั้ง โดยเฉพาะในโค้ด framework ทำให้เอาต์พุตจาก TracePoint ยากต่อการจินตนาการ

เลยทำ gem ขึ้นมาหนึ่งตัวที่ช่วยสร้างกราฟการเรียกแบบภาพได้:

require 'visual_call_graph'
VisualCallGraph.trace { "Your method call here..." }

จะสร้างไฟล์ call_graph.png ไปพร้อมกับผลลัพธ์

ติดตามเมธอด Ruby ด้วย TracePoint: คู่มือปฏิบัติจริง

จำไว้ว่า นี่ไม่ใช่ static analysis มันจะเรียกเมธอดจริงๆ!

การแสดงพาธไฟล์

อยากรู้ว่าเมธอดเหล่านี้ถูกกำหนดที่ไหน?

ไม่ต้องกังวล ผมเตรียมไว้ให้แล้ว! เพิ่มออปชันให้เปิดใช้งานเพื่อแสดงพาธไฟล์สำหรับแต่ละการเรียกเมธอด

VisualCallGraph.trace(show_path: true) { Foo.aaa }

ผลลัพธ์:

ติดตามเมธอด Ruby ด้วย TracePoint: คู่มือปฏิบัติจริง

ถ้าอยากดู call graph ใหญ่ๆ ลองติดตามเมธอดของ Rails ดู 😉

ค่าที่คืนกลับ (Return Values)

ใน مقدمة กล่าวไปว่าสามารถดึงค่าที่คืนกลับได้...

สำหรับนี้ ต้องติดตาม event return และใช้เมธอด return_value

ตัวอย่าง:

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

จะพิมพ์:

Return value for the_method is AAAAAAAAAA.

Event เกิดขึ้นก่อนการทำงานจริง

มีคนถามบน Reddit ว่าจะกันไม่ให้คำว่า "bar" ถูกพิมพ์เมื่อเรียกเมธอด foo ในโค้ดนี้:

class Thing
  def foo
    puts "foo"
    bar
  end

  def bar
    puts "bar"
  end
end

# your code here
t = Thing.new
t.foo

มีหลายวิธีทำได้ เช่น prepend module, redirect $stdout หรือ redefine เมธอด bar

ถ้าคุณรู้สึกสร้างสรรค์ ลองคอมเมนต์ไอเดียของคุณด้านล่างดูสิ!

แต่วิธีตอบหนึ่งน่าสนใจมาก เพราะใช้คลาส TracePoint

นี่คือคำตอบ:

TracePoint.trace(:call) { |tp| exit if tp.method_id == :bar }

โค้ดนี้จะเรียก exit เมื่อเมธอด bar ถูกเรียก ทำให้โปรแกรมหยุดทำงานก่อนพิมพ์สตริง

อาจไม่ใช่สิ่งที่ควรใช้ในโค้ดจริง แต่พิสูจน์ได้ว่าสำหรับ TracePoint: Event ถูกกระตุ้นก่อนที่การทำงานจริงจะเกิดขึ้น

ควรจำไว้ถ้าจะสร้างเครื่องมือรอบๆ TracePoint 🙂

สรุป

ในบทความนี้ คุณเรียนรู้เกี่ยวกับคลาส TracePoint ซึ่งช่วยให้ติดตาม event ต่างๆ เช่น การเรียกเมธอด หรือการสร้างเธรดใหม่ สามารถนำไปใช้เป็นเครื่องมือ debug หรือสำรวจโค้ดได้

จำไว้ แชร์บทความนี้ ให้เพื่อนๆ ได้อ่านด้วยนะครับ 🙂