tmm1/ruby — pull request #1 by tmm1 (2013-06-02 01:58:58+00:00)
Title: CpuProfiler

WIP cpu profiler for ruby 2.1, inspired by perftools.rb.

A simple example:

``` ruby
require 'cpuprofiler'

class A
def initialize
pow
self.class.newobj
math
end

def pow
2 ** 100
end

def self.newobj
Object.new
end

def math
2.times do
2 + 3 * 4 ^ 5 / 6
end
end
end

CpuProfiler.run(1000) do
1000000.times do
A.new
end
end.tap do |profile|
overall_samples = profile.delete(:overall_samples)
profile = profile.sort_by{ |iseq, stats| -stats[:caller_samples] }

printf "% 10s (pct) % 10s (pct) ISEQ\n" % ["TOTAL", "CALLER"]
profile.each do |iseq, stats|
call, total = stats.values_at(:caller_samples, :total_samples)
stats[:name] = ''
stats[:name] << (iseq.class_path || '')
stats[:name] << iseq.label
printf "% 10d % 8s % 10d % 8s %s\n", total, "(%2.1f%%)" % (total*100.0/overall_samples), call, "(%2.1f%%)" % (call*100.0/overall_samples), stats[:name]
end

File.open('out.gv', 'w') do |f|
f.puts "digraph profile {"
profile.each do |iseq, stats|
call, total = stats.values_at(:caller_samples, :total_samples)
sample = ''
sample << "#{call} (%2.1f%%)\\rof " % (call*100.0/overall_samples) if call < total
sample << "#{total} (%2.1f%%)\\r" % (total*100.0/overall_samples)
size = (1.0 * call / overall_samples) * 28 + 10

f.puts " #{iseq.object_id} [size=#{size}] [fontsize=#{size}] [shape=box] [label=\"#{stats[:name]}\\n#{sample}\"];"
if edges = stats[:edges]
edges.each do |edge, weight|
size = (1.0 * weight / overall_samples) * 28
f.puts " #{iseq.object_id} -> #{edge.object_id} [label=\"#{weight}\"];"
end
end
end
f.puts "}"
end
end
```

The text output:

```
% ./ruby -I.:./.ext/x86_64-darwin12.3.0 profiletest.rb
TOTAL (pct) CALLER (pct) ISEQ
123 (46.1%) 123 (46.1%) A#pow
69 (25.8%) 69 (25.8%) block in math
57 (21.3%) 57 (21.3%) A.newobj
77 (28.8%) 8 (3.0%) A#math
267 (100.0%) 6 (2.2%) block (2 levels) in

261 (97.8%) 4 (1.5%) A#initialize
267 (100.0%) 0 (0.0%)

267 (100.0%) 0 (0.0%) block in

```

And the generated graphviz:

```
% cat out.gv && dot -Tpng out.gv -o out.png
digraph profile {
70231918211400 [size=22.89887640449438] [fontsize=22.89887640449438] [shape=box] [label="A#pow\n123 (46.1%)\r"];
70231918211100 [size=17.235955056179776] [fontsize=17.235955056179776] [shape=box] [label="block in math\n69 (25.8%)\r"];
70231918211300 [size=15.97752808988764] [fontsize=15.97752808988764] [shape=box] [label="A.newobj\n57 (21.3%)\r"];
70231918211200 [size=10.838951310861423] [fontsize=10.838951310861423] [shape=box] [label="A#math\n8 (3.0%)\rof 77 (28.8%)\r"];
70231918211200 -> 70231918211100 [label="69"];
70231918209720 [size=10.629213483146067] [fontsize=10.629213483146067] [shape=box] [label="block (2 levels) in

\n6 (2.2%)\rof 267 (100.0%)\r"];
70231918209720 -> 70231918211520 [label="261"];
70231918211520 [size=10.41947565543071] [fontsize=10.41947565543071] [shape=box] [label="A#initialize\n4 (1.5%)\rof 261 (97.8%)\r"];
70231918211520 -> 70231918211200 [label="77"];
70231918211520 -> 70231918211400 [label="123"];
70231918211520 -> 70231918211300 [label="57"];
70231918211700 [size=10.0] [fontsize=10.0] [shape=box] [label="

\n0 (0.0%)\rof 267 (100.0%)\r"];
70231918211700 -> 70231918209800 [label="267"];
70231918209800 [size=10.0] [fontsize=10.0] [shape=box] [label="block in

\n0 (0.0%)\rof 267 (100.0%)\r"];
70231918209800 -> 70231918209720 [label="267"];
}
```

![out](https://f.cloud.github.com/assets/2567/595770/dc0a9d22-cb27-11e2-9b8c-ac5f077c83a1.png)