2010-02-08 32 views
28

我已經繼承了大量的Ruby代碼,坦率地說,對於像我這樣的凡人來說,幾乎無法理解。它實際上是Rspec單元測試代碼,但是它的結構「很不尋常」可以很好地表達出來。如何記錄每個在Ruby程序中調用的方法?

想我能夠做的就是運行代碼,並已經某處記錄以下信息:

  • 每一個被調用,包括定義該方法的類的名稱的方法,和已定義調用方法的文件名(也就是說,我們已經在多個不同文件中定義了相同的類/方法,並且很難知道哪個被調用)
  • (可選)傳遞給每個方法的參數被調用

With那我可以開始嘗試重構它。沒有它,由於代碼庫的大小(20k +單元測試用例),將它變成一個非常困難的任務。

我不能進入並對正在運行的代碼執行批量編輯,因爲當您甚至使用粗糙的語言(即頻繁)時它會中斷。相反,我需要能夠在現有狀態下對代碼進行測試,或者對現有的代碼進行最小限度的更改。

有沒有一種方法可以記錄此級別的細節而不對代碼庫進行大規模更改?我看了一下Ruby探查器,看看它是否有幫助,可能會;我很好奇,如果有更好的方法(尤其是記錄包含被調用方法的文件名)。

在此先感謝

+0

你有沒有考慮一個靜態的分析,還是你只是尋找一個實際運行代碼的東西嗎? Doxygen輸出一些很好的調用者/被調用者圖形,並沒有看到它是否支持Ruby,但調用圖形可以證明對理解現有代碼非常有用。 – 2010-02-08 04:15:23

+0

我相當確信Doxygen不支持Ruby - 如果確實如此,它肯定對我有用,但我找不到任何有關支持Ruby的Doxygen的信息。 我寧願有一些實際運行代碼的東西,主要是因爲處理require需求的順序會影響給定方法的多個(不同的)定義中的哪一個將被使用。 正如我所說,這是一個醜陋的代碼庫... – monch1962 2010-02-08 04:24:23

回答

58

這絕對是可能的 - 事實上,甚至有一種方法!

set_trace_func proc { |event, file, line, id, binding, classname| 
    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname 
} 

你想要的祕密武器來自Kernel#set_trace_func,正如上面所指出的:就在你的代碼要開始記錄的東西點之前添加這個地方

  • set_trace_func( PROC)=> PROC
  • set_trace_func(無)=>無

proc設置爲跟蹤處理程序,如果參數爲nil,則禁用跟蹤。 proc最多需要六個參數:事件名稱,文件名,行號,對象ID,綁定和類名稱。每當發生事件時都會調用proc。事件是:c-call(調用C語言例程),c-return(從C語言例程返回),call(調用Ruby方法),class(啓動類或模塊定義),end(完成類或模塊定義),line(在新行上執行代碼),raise(引發異常)和return(從Ruby方法返回)。跟蹤在proc的上下文中被禁用。

這裏有一個簡便的事例:

class Test 
    def test 
    a = 1 
    b = 2 
    end 
end 

set_trace_func proc { |event, file, line, id, binding, classname| 
    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname 
} 

t = Test.new 
t.test 

(注意:除非你想文本的一個巨大的滾動屏幕不要嘗試這種在irb)輸出結果是:

line test.rb:11    false 
    c-call test.rb:11  new Class 
    c-call test.rb:11 initialize Object 
c-return test.rb:11 initialize Object 
c-return test.rb:11  new Class 
    line test.rb:12    false 
    call test.rb:2  test  Test 
    line test.rb:3  test  Test 
    line test.rb:4  test  Test 
    return test.rb:4  test  Test 

你可以使用上面的格式化字符串來獲得你想要記錄的結果(例如,聽起來你只對call事件感興趣)。希望有所幫助,並祝你能夠在所有單元測試中排序!

+0

哇 - 這正是我所希望的。謝謝約翰! – monch1962 2010-02-08 04:36:51

+0

沒問題。對於這種瑞士軍刀來說,Ruby變得相當方便。 – 2010-02-08 04:48:32

+1

+1爲IRB警告。 – 2010-12-06 23:29:01

4

我想包括秒,過去一分鐘的情況下,在happend以及多久每個功能

start = DateTime.now.strftime('%Q').to_i/1000.0 
set_trace_func proc { |event, file, line, id, binding, classname| 
    now_ms = DateTime.now.strftime('%Q').to_i/1000.0 
    duration = '%.3f' % (now_ms - start) 
    start = DateTime.now.strftime('%Q').to_i/1000.0 
    printf "%s %s %8s %s:%-2d %10s %8s\n", DateTime.now.strftime("%S.%L"), duration, event, file, line, id, classname 
} 

AdminUser.create(password: "password", password_confirmation: "password", email: email) 

set_trace_func nil 

我試圖調試爲什麼花了這麼長時間來創建用戶花在並登錄到ActiveAdmin。

05.761 0.000 c-return /Users/nperry/.rvm/gems/[email protected]/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51  to_s String 
05.761 0.000 c-call /Users/nperry/.rvm/gems/[email protected]/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine 
09.736 63.975 c-return /Users/nperry/.rvm/gems/[email protected]/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine 
09.736 0.000 return /Users/nperry/.rvm/gems/[email protected]/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:59 hash_secret BCrypt::Engine 
09.736 0.000 c-call /Users/nperry/.rvm/gems/[email protected]/gems/bcrypt-3.1.7/lib/bcrypt/password.rb:46  new Class 

從那我知道Ruby花了一分多鐘在__bc_crypt

+1

聽起來像你有一個相當高的Bcrypt「成本」。 T.J. Shuck有一個很好的視頻解釋了這個(http://rubyvideos.com/presenters/t-j-schuck)(前15分鐘左右)。 – jwadsack 2015-07-30 20:02:05

+0

我相信我們當時確實解決了這個問題,問題肯定是Bcrypt。 – Nate 2015-07-31 14:11:16

3

近日,set_trace_func已被否決:

注:此方法已過時,請使用跟蹤點來代替。

我們可以利用跟蹤點,它備份set_trace_func,而不是:

trace = TracePoint.new(:call) do |tp| 
    puts "#{tp.defined_class}##{tp.method_id} got called (#{tp.path}:#{tp.lineno})" 
end 

trace.enable 
# do stuff here 
trace.disable 

其實,這甚至比set_trace_func更強大,因爲您可以啓用和在您方便的禁用。你可以有選擇地掛接到以下事件::line, :class, :end, :call, :return, :c_call, :c_return, :raise, :b_call, :b_return, :thread_begin, :thread_end

下面一個完整的例子:

class MyClass 
    def initialize 
    end 
    def y 
    z 
    end 
    def z 
    1 + 1 
    end 
end 

trace = TracePoint.new(:call) do |tp| 
    puts "#{tp.defined_class}##{tp.method_id} got called (#{tp.path}:#{tp.lineno})" 
end 

trace.enable # note 
MyClass.new.y 
trace.disable 
    # MyClass#initialize got called (./trace.rb:4) 
    # MyClass#y got called (./trace.rb:7) 
    # MyClass#z got called (./trace.rb:10) 
相關問題