2010-02-08 40 views
28

Tôi đã thừa hưởng một đống mã Ruby lớn, thật lòng, gần như không thể hiểu được một người chết như chính tôi. Nó thực sự là mã kiểm thử đơn vị Rspec, nhưng cấu trúc là "rất bất thường" để đặt nó độc đáo.Làm cách nào để đăng nhập mọi phương thức được gọi trong chương trình Ruby?

Những gì tôi muốn để có thể làm được điều hành mã, và có các thông tin sau đăng nhập ở đâu đó:

  • mọi phương pháp đó được gọi, trong đó có tên của lớp đó xác định phương pháp, và tên tệp nơi phương thức được gọi đã được định nghĩa (yep, chúng ta có cùng một lớp/phương thức được định nghĩa trong nhiều tệp khác nhau và thật khó để biết được hàm nào đang được gọi)
  • (tùy chọn) các tham số được truyền cho từng phương thức được gọi

Với rằng, tôi có thể bắt đầu cố gắng tái cấu trúc nó. Nếu không có nó, nó sẽ là một nhiệm vụ rất khó để làm cho nó thẳng ra, do kích thước của cơ sở mã (20k + đơn vị kiểm tra trường hợp).

Tôi không thể đi vào và thực hiện các chỉnh sửa bán buôn cho mã đang chạy, vì nó bị hỏng khi bạn sử dụng ngôn ngữ khắc nghiệt xung quanh nó (tức là thường xuyên). Thay vào đó, tôi cần để có thể thiết lập mã trong trạng thái hiện tại của nó, hoặc với những thay đổi tối thiểu cho những gì tồn tại ngay bây giờ.

Có cách ghi nhật ký chi tiết này mà không thực hiện thay đổi bán buôn cho cơ sở mã không? Tôi đã xem xét hồ sơ Ruby để xem liệu nó có thể giúp được không, và có lẽ nó có thể; Tôi tò mò nếu có một cách tốt hơn (đặc biệt là đăng nhập tên tập tin có chứa các phương pháp gọi).

Cảm ơn trước

+0

Bạn đã được coi là một phân tích tĩnh hoặc là bạn chỉ tìm kiếm cái gì đó thực sự chạy mã? Doxygen xuất ra một số biểu đồ người gọi/callee tốt đẹp, chưa xem xét liệu nó có hỗ trợ Ruby hay không nhưng đồ thị gọi có thể tỏ ra rất hữu ích cho việc hiểu mã hiện có. –

+0

Tôi khá chắc chắn Doxygen không hỗ trợ Ruby - nếu có, nó chắc chắn sẽ hữu ích cho tôi, nhưng tôi không thể tìm thấy bất kỳ thông tin nào về Doxygen hỗ trợ Ruby. Tôi muốn có cái gì đó thực sự chạy mã, chủ yếu là do thứ tự trong đó yêu cầu được xử lý sẽ ảnh hưởng đến định nghĩa nhiều (không giống hệt) của một phương thức đã cho sẽ được sử dụng. Như tôi đã nói, đó là một cơ sở mã xấu xí ... – monch1962

Trả lời

58

Điều này chắc chắn có thể - trên thực tế, thậm chí còn có một phương pháp cho nó! Chỉ cần thêm một nơi nào đó trong mã của bạn trước khi các điểm mà bạn muốn bắt đầu khai thác gỗ điều:

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

Các nước sốt bí mật mà bạn muốn xuất phát từ Kernel#set_trace_func, như đã nói ở trên:

  • set_trace_func (proc) => proc
  • set_trace_func (nil) => nil

Thiết lập proc làm trình xử lý để truy tìm hoặc vô hiệu hóa truy tìm nếu thông số là nil. proc có tối đa sáu tham số: tên sự kiện, tên tệp, số dòng, id đối tượng, ràng buộc và tên của lớp. proc được gọi khi có sự kiện xảy ra. Sự kiện là: c-call (gọi một thói quen ngôn ngữ C), c-return (trở về từ thường lệ ngôn ngữ C), call (gọi một phương thức Ruby), class (bắt đầu một lớp hoặc định nghĩa mô-đun), end (kết thúc định nghĩa lớp hoặc mô-đun), line (thực thi mã trên một dòng mới), raise (nêu một ngoại lệ) và return (trả về từ phương thức Ruby). Truy tìm bị tắt trong ngữ cảnh của proc.

Dưới đây là một ví dụ tiện dụng:

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 

(Lưu ý:. Đừng cố này trong irb trừ khi bạn muốn có một màn hình di chuyển khổng lồ của văn bản) Các kết quả đầu ra là:

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 

Bạn có thể chơi xung quanh với chuỗi định dạng ở trên để chỉ nhận được kết quả bạn muốn đăng nhập (ví dụ: có vẻ như bạn chỉ quan tâm đến các sự kiện call). Hy vọng rằng sẽ giúp, và may mắn với phân loại thông qua tất cả các bài kiểm tra đơn vị!

+0

Wow - đó là ... chính xác những gì tôi đã hy vọng. Cảm ơn John! – monch1962

+0

Không vấn đề gì. Ruby hóa ra khá tiện dụng với loại dao của quân đội Thụy Sĩ này. –

+1

+1 cho cảnh báo IRB. –

4

tôi muốn bao gồm giây-qua-the-phút sự kiện happend tại cũng như bao lâu đã được chi tiêu trong mỗi chức năng

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 

Tôi đã cố gắng để gỡ lỗi tại sao phải mất quá lâu để tạo người dùng và đăng nhập vào 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 

Và từ đó tôi biết Ruby đã dành hơn một phút trong __bc_crypt.

+1

Có vẻ như bạn có một chi phí Bcrypt khá cao. T.J. Shuck có [một video hay giải thích điều này] (http://rubyvideos.com/presenters/t-j-schuck) (15 phút đầu tiên hoặc lâu hơn). – jwadsack

+0

Tôi tin rằng chúng tôi đã giải quyết vấn đề này vào thời điểm đó và vấn đề chắc chắn là Bcrypt. – Nate

3

Gần đây, set_trace_func được chấp nhận:

Lưu ý: phương pháp này là lỗi thời, hãy sử dụng TracePoint để thay thế.

Chúng ta có thể sử dụng TracePoint, mà sao set_trace_func, thay vì:

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 

Đây là thực sự mạnh mẽ hơn set_trace_func vì bạn có thể kích hoạt và vô hiệu hóa một cách thuận tiện. Bạn có chọn lọc có thể móc vào các sự kiện sau đây: :line, :class, :end, :call, :return, :c_call, :c_return, :raise, :b_call, :b_return, :thread_begin, :thread_end

Dưới đây là một ví dụ đầy đủ:

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) 
Các vấn đề liên quan