检查 Ruby 脚本为什么会停滞不前

8
有时候我的规格说明会卡住,我不得不杀掉相应的ruby进程。当我运行使用capybara和webkit驱动编写的集成规格说明时,这种情况很常见。
有没有可能检查给定的ruby进程并查看它在哪里卡住了?包括方法、操作、文件、行号等信息。

2
你能从终端运行并使用Control-C杀死吗?这应该会给你一个Ruby跟踪。 - morri
有时我无法用这种方式杀死进程。特别是当 capybara-webkit 卡住时。 - luacassus
你能够附加gdb吗?那可能有所帮助...另一种选择可能是使用“-rtracer”运行它,并将输出到文件中,它会显示最后一个进入的方法... - rogerdpack
2个回答

12

简介

使用 gdb(例如Linux):

  • echo 'call (void)rb_backtrace()' | gdb -p $(pgrep -f ruby)

或者使用 lldb(例如OS X):

  • echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -f ruby)

你可以通过使用调试库来调试Ruby脚本。

如果脚本是从shell中执行的,可以通过将脚本的第一行(shebang)更改为以下内容实现:

#!/usr/bin/env ruby -rdebug

或者作为以下方式运行:

ruby -rdebug my_script.rb

一旦调试器加载完毕,您可以设置一些断点或只需输入 c 继续执行应用程序。

然后调试器会在任何异常(例如 Ctrl+C)或断点(例如包含 debugger 的行)时自动中断。

每次显示调试器控制台时,您可以选择以下选项:

  • c 继续(到下一个异常、断点或包含 debugger 的行),
  • n 下一行,
  • w/where 显示框架/调用堆栈,
  • l 显示当前代码,
  • cat 显示捕获点。
  • h 获取更多帮助。

另请参阅:使用 ruby-debug 进行调试ruby-debug gem 的键盘快捷键

这种方法的缺点是没有像按一个魔法按钮一样随时引发调试器的功能,唯一的方法是在脚本内部引发异常,这将展示不同的代码块而不是挂起的代码块。

以下是一些其他想法:

  • 在您的代码中添加 debugger 语句,引发调试器并逐步执行。
  • 使用 Pry 调试器(参见:GitHub)。

    通过以下命令进行安装: gem install pry,运行方式为:pry 或添加 require 'pry'

  • 尝试 lldb 调试器(旨在替换 gdb),它可以附加到当前运行的进程。

    示例(将 PID 替换为您的进程 id):

    $ lldb -p PID
    (lldb) bt all
    * thread #1: tid = 0x11d68a, 0x00007fff86c71716 libsystem_kernel.dylib`__psynch_cvwait + 10
      * frame #0: 0x00007fff86c71716 libsystem_kernel.dylib`__psynch_cvwait + 10
        frame #1: 0x00007fff838a9c3b libsystem_pthread.dylib`_pthread_cond_wait + 727
        frame #2: 0x0000000100241aad libruby.2.0.0.dylib`native_cond_wait + 29
    

    另一个示例,展示了正在运行的ruby脚本(在其tty上)的回溯信息:

    echo 'call (void)rb_backtrace()' | lldb -p $(pgrep -f ruby)
    
  • 或者使用 gdb(您可以通过扩展gdb.rb 来查看 Ruby 对象)。

    1. 通过以下方式安装:sudo apt-get install gdb python-dev ncurses-dev && gem install gdb.rb
    2. 在 Unix/OS X 上,按下挂起进程上的 Ctrl+T 键以检查 PID 和正在执行的操作(或通过 ps wuax | grep ruby 检查)。
    3. 通过以下命令附加到进程:gdb -p PID

    另请参阅:使用 gdb 检查已挂起的 Ruby 进程Ruby 的 GDB 包装器 以及 检查运行中的 Ruby 进程

  • 其他可帮助的库/工具包括:debuggercrash-watchmemprofrack-perftools_profiler

如果没有帮助,您可以尝试使用以下语法:strace(Linux)/dtruss(OS X):

sudo strace -fp <PID>
sudo dtruss -fp <PID>
或者使用可以跟踪库调用的ltrace,与strace系统调用不同。
如果您认为这是网络问题,请使用tcpdump
请参见:

1
这是一个很棒的答案。应该被接受。为对不同技术进行全面评估表示赞扬和+1。 - Jesse Sanford

1
我也遇到了这个问题,并将其追踪到特定页面上的ShareThis JavaScript小部件。您可能正在使用它,但真正的问题可能是由于页面上的某些内容导致外部请求永远无法完成而挂起。Capybara-webkit将知道原始请求,但如果此代码本身发出请求,则capybara-webkit将永远不会知道它,如果最后一个请求挂起,例如等待响应,那么capybara-webkit也将挂起...
对于您来说,使用webkit-debug运行测试并查看最后一个请求。对我来说,我看到了以下内容:
    1 requests remaining 
    Page finished with true 
    Received 200 from "http://w.sharethis.com/share4x/js/st.60709d5fdf0c137e879e64f41b8a6606.js" 
    0 requests remaining 
    Started request to "http://w.sharethis.com/share4x/css/share.470030190b6a6bdc89365fcc74d3bf55.css" 
    Received 200 from "http://w.sharethis.com/share4x/css/share.470030190b6a6bdc89365fcc74d3bf55.css" 
    0 requests remaining 

这启示我搜索我的代码库以查找ShareThis。我在该代码周围放置了一个if(Rails.env.test?)块,然后,瞧,我开始工作了。不得不在您的代码库中为测试环境放置条件语句是一个糟糕的解决方法...但它让我从这个愚蠢的问题中前进了...

希望这可以帮助到您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接