とりあえずまとめておく。コレも便利だぜ、とかあったら教えてください

[C] gdb

まずはなにはともあれ gdb。例えば gdb を使って指定スレッドのCレベルでのスタックトレースを表示させるにはこんなかんじ。

$ sudo gdb -p <pid>
(gdb) info thread
(gdb) thread 14
(gdb) bt

core を吐かせておいてから追う場合はこう

$ sudo gcore <pid>
$ gdb /usr/lib64/fluent/ruby/bin/ruby core.22111
(gdb) info thread
(gdb) thread 14
(gdb) bt

bt の出力はこんなかんじ https://gist.github.com/sonots/10803036

[C] gstack

gdb で全スレッドのスタックレースをダンプさせようと思うとめんどくさいが、gstack を使うとできる。

$ sudo gstack <pid>

ただし、core ファイルに対しては適用できない。 pstack というツールもあるらしいが、私の環境だと全く同じものだった(gstack へのシンボリックリンクだった)。

出力はこんなかんじ https://gist.github.com/sonots/f58f632c9e7a5786aa05

[C] strace

実行中プロセスのシステムコールを追い続けるには strace を使おう。

$ strace -s 1024 -f -p <pid>

スレッドをおいたい場合は -f をつける。処理がすすめば出力は更新される。

出力はこんなかんじ https://gist.github.com/sonots/8020037

strace については以前書いたやつもあるのでそちらも見てもらえると良い=> http://blog.livedoor.jp/sonots/archives/18193659.html

[Ruby] sigdump

https://github.com/frsyuki/sigdump

ruby レベルでの全スレッドのスタックトレースをダンプすることができる。gstack の ruby レベル版と言えるか。ついでに、ObjectSpace の状態(どのクラスのインスタンスが何個存在しているかなど)もダンプできる。

require 'sigdump/setup'

しておき、kill -CONT すると、/tmp/sigdump-.log のようなファイルに出力される。

出力はこんなかんじ https://gist.github.com/sonots/e05d8fbcd7ab39e4a5f8

コードに仕込んで好きなところでダンプしたい場合は

Sigdump.dump

を呼び出せばできる。=> sigdump.rb#L13

特にコードを遅くするようなこともないので、とりあえず require しておくのが吉。

[Ruby] rbtrace

https://github.com/tmm1/rbtrace

rbtrace は ruby レベルでの strace のようなもの、とのこと。strace 同様、処理が進めば更新される。

trace 対象のアプリで require しておき、

require 'rbtrace'
$ rbtrace -p <pid> --firehose

とかやると追える。※ require すると外から rbtrace で attach できるようになるようだ。内部的には POSIX message queue を使っているっぽい。

firehose は全出力のオプションで以下のようになる。

Kernel#proc <0.000006>

Proc#call
  Dir.chdir
    Dir.pwd <0.000004>
    Process.pid <0.000003>
    String#multiply_vowels
      String#gsub
        String#* <0.000192>
        String#* <0.000003>
      String#gsub <0.000585>
    String#multiply_vowels <0.000593>
    Kernel#rand <0.000003>
    Kernel#sleep <0.239254>
  Dir.chdir <0.239933>
Proc#call <0.239947> 

-m で特定メソッドにしぼったり、--slow で遅いやつだけ出力したりもできる。=> 詳細は README 読んでくれ https://github.com/tmm1/rbtrace

[Ruby] profile

どのメソッドを何回呼び出したのか、平均で何秒かかったのかプロファイリングするには、標準ライブラリの profile を利用できる。

普通に require すると、その瞬間にスタートして、終了時に print する動きをするので、任意の期間に仕込みたい場合は、 profile ではなく profiler を require して以下のようにする。

require 'profiler'
RubyVM::InstructionSequence.compile_option = {
  :trace_instruction => true,
  :specialized_instruction => false
}

# 好きなタイミングで start する
Profiler__::start_profile

# print して終了する
Profiler__::print_profile(STDERR)
Profiler__::stop_profile

出力はこんなかんじ

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
100.00     0.01      0.01        2     5.00     5.00  Hoge.fuga
  0.00     0.01      0.00        2     0.00     0.00  IO#puts
  0.00     0.01      0.00        1     0.00     0.00  TracePoint#enable
  0.00     0.01      0.00        4     0.00     0.00  IO#write
  0.00     0.01      0.00        2     0.00     0.00  Fixnum#to_s
  0.00     0.01      0.00        2     0.00     0.00  Kernel#puts
  0.00     0.01      0.00        4     0.00     2.50  nil#
  0.00     0.01      0.00        1     0.00    10.00  Range#each
  0.00     0.01      0.00        1     0.00     0.00  TracePoint#disable
  0.00     0.01      0.00        1     0.00    10.00  #toplevel

[Ruby] tracer

on してから off するまでの ruby レベルの実行をすべてトレース出力するには、 標準ライブラリ tracerを利用できる。

require 'tracer'

# 好きなタイミングでスタートする
Tracer.on

# 好きなタイミングで終了する
Tracer.off

処理が進むたびに逐次出力される。

試しに require 'uri' をトレースした出力はこんなかんじ https://gist.github.com/sonots/551689f166e9b3cf4aa4

[Ruby] stackprof

https://github.com/tmm1/stackprof

これも Ruby レベルのプロファイラ。Ruby 2.1 時代のプロファイラらしい。

たとえば rack middleware としてこんなかんじで仕込んでおいて、1s ごとにプロファイリングしたりできるようだ。 もちろん普通に ruby コードとしてプロファイリングする期間を指定して出力を得ることもできる。

# config.ru
use StackProf::Middleware, enabled: true,
                           mode: :cpu,
                           interval: 1000,
                           save_every: 5

メソッドを指定して、そのメソッドのみのプロファイリング、そのメソッドを呼んでいるやつなどを特定できる。呼んでいるやつがわかるの便利。

$ stackprof tmp/stackprof-cpu-*.dump --method 'String#blank?'
  String#blank? (gems/activesupport-2.3.14.github30/lib/active_support/core_ext/object/blank.rb:80)
    samples:  1595 self (2.6%)  /   1660 total (2.7%)
    callers:
       373  (   41.0%)  ApplicationHelper#current_user
       192  (   21.1%)  ApplicationHelper#current_repository
    callers:
       803  (   48.4%)  Object#present?
    code:
                                    |    80  |   def blank?
   1225    (2.0%) /  1225   (2.0%)  |    81  |     self !~ /[^[:space:]]/
                                    |    82  |   end

作者のこちらのブログが非常にわかりやすいので一読をすすめる => http://tmm1.net/ruby21-profiling/