Как мне зарегистрировать каждый метод, который вызывается в Ruby-программе? - PullRequest
32 голосов
/ 08 февраля 2010

Я унаследовал большую кучу кода Ruby, который, честно говоря, почти невозможно понять для смертного, такого как я. На самом деле это код модульного теста Rspec, но структура «весьма необычна», чтобы выразить это красиво.

То, что я хотел бы сделать, - это запустить код и записать в нем следующую информацию:

  • каждый вызываемый метод, включая имя класса, который определяет метод, и имя файла, в котором был вызван вызываемый метод (да, у нас есть один и тот же класс / метод, определенный в нескольких разных файлах, и это трудно понять, что вызывается)
  • (необязательно) параметры, передаваемые каждому вызванному методу

С этим я мог бы начать пытаться его реорганизовать. Без этого будет очень трудно выправить его из-за размера базы кода (20k + модульные тесты).

Я не могу позволить себе пойти и выполнить массовое редактирование исполняемого кода, потому что он ломается, когда вы даже используете грубый язык вокруг него (то есть часто). Вместо этого мне нужно иметь возможность обрабатывать код в его существующем состоянии или с минимальными изменениями того, что существует сейчас.

Есть ли способ зарегистрировать этот уровень детализации без внесения оптовых изменений в базу кода? Я взглянул на профилировщик Ruby, чтобы увидеть, может ли он помочь, и, возможно, может; Мне любопытно, есть ли лучший способ (особенно регистрация имени файла, содержащего вызванный метод).

Заранее спасибо

Ответы [ 3 ]

59 голосов
/ 08 февраля 2010

Это определенно возможно - на самом деле, есть даже способ для этого! Просто добавьте это где-нибудь в своем коде до того момента, когда вы хотите начать регистрировать вещи:

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 (nil) => nil

Устанавливает proc в качестве обработчика для трассировки или отключает трассировку, если параметр nil. proc принимает до шести параметров: имя события, имя файла, номер строки, идентификатор объекта, привязку и имя класса. 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 события). Надеюсь, что это поможет, и удачи в прохождении всех этих юнит-тестов!

12 голосов
/ 19 декабря 2017

Недавно set_trace_func устарела:

Примечание: этот метод устарел, используйте вместо него TracePoint.

Мы можем использовать TracePoint, который поддерживает 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)
4 голосов
/ 27 августа 2014

Я хотел бы указать в секундах за минутой, когда происходило событие, а также сколько времени было потрачено в каждой функции

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/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51       to_s   String
05.761 0.000   c-call /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine
09.736 63.975 c-return /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:51 __bc_crypt BCrypt::Engine
09.736 0.000   return /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/engine.rb:59 hash_secret BCrypt::Engine
09.736 0.000   c-call /Users/nperry/.rvm/gems/ruby-2.1.2@rxair/gems/bcrypt-3.1.7/lib/bcrypt/password.rb:46        new    Class

И из этого я знаю, что Руби потратила больше минуты на __bc_crypt.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...