yarv-prof 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/README.md +80 -0
- data/bin/yarv-prof +152 -0
- data/lib/yarv-prof/version.rb +3 -0
- data/lib/yarv-prof.rb +37 -0
- data/yarv-prof.gemspec +22 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c5d67e8ac138faae3a64a5dcfbf51e8884b7f2d1
|
4
|
+
data.tar.gz: ccb8273915b075b0b8dec96618438038d269d314
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a2c20570f872587c569853617f4f3400b6b4ca388996fe72182f2d3ee29504c7a679e5bc19bd1a539eac44a74eaf1d930278bc6ca6b403eef64413a33bbeccf
|
7
|
+
data.tar.gz: 0eaeaf451efb07b4363f612a94ff153fa17b6a7ee2cec5965c19493e58d226835d7eca6103a616031f7b93d8d0cb9e799682fc0c48a03966ab35fafce9f5637a
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
## Prerequisite
|
2
|
+
|
3
|
+
- DTrace must be installed on your System
|
4
|
+
- You need to edit MRI source code as follows(since pre-compiled MRI doesn't provide `insn` probe, you need to enable this manually, as of Ruby2.4.0)
|
5
|
+
|
6
|
+
```
|
7
|
+
$ git clone https://github.com/ruby/ruby.git
|
8
|
+
$ cd ruby && autoconf
|
9
|
+
$ ./configure
|
10
|
+
$ vi vm_opts.h # set the VM_COLLECT_USAGE_DETAILS flag to 1 manually
|
11
|
+
$ make && make install
|
12
|
+
$ ./ruby -v
|
13
|
+
```
|
14
|
+
|
15
|
+
## Getting Started
|
16
|
+
|
17
|
+
Since we use custom built MRI binary, the profiling command will look like following(unless you have installed custom built MRI and rubygems onto your system)
|
18
|
+
|
19
|
+
```
|
20
|
+
$ gem install yarv-prof
|
21
|
+
$ sudo ./ruby -I `gem env gemdir`/gems/yarv-prof*/lib/ -r yarv-prof -e "YarvProf.start; p :hello; YarvProf.end"
|
22
|
+
$ yarv-prof --load /tmp/yarv-prof/20161128_214131.dump
|
23
|
+
total number of instruction calls: 26
|
24
|
+
insn count total_walltime mean variance stdev
|
25
|
+
---------------------------------------------------------------------------------------------------
|
26
|
+
opt_send_without_block 4(15%) 298567(33%) 74642 2362231992 48603
|
27
|
+
trace 4(15%) 139888(15%) 34972 566184557 23795
|
28
|
+
getinlinecache 2(7%) 76616(8%) 38308 1093622912 33070
|
29
|
+
getconstant 2(7%) 65426(7%) 32713 674546450 25972
|
30
|
+
setinlinecache 2(7%) 51301(5%) 25650 218718612 14789
|
31
|
+
leave 2(7%) 46497(5%) 23248 54444612 7379
|
32
|
+
putobject 2(7%) 44293(4%) 22146 182576940 13512
|
33
|
+
getinstancevariable 2(7%) 42118(4%) 21059 94146642 9703
|
34
|
+
pop 2(7%) 41412(4%) 20706 2056392 1434
|
35
|
+
opt_not 1(3%) 30059(3%) 30059 NaN NaN
|
36
|
+
jump 1(3%) 27644(3%) 27644 NaN NaN
|
37
|
+
putself 1(3%) 23844(2%) 23844 NaN NaN
|
38
|
+
branchunless 1(3%) 15042(1%) 15042 NaN NaN
|
39
|
+
```
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
```
|
44
|
+
YarvProf.start(clock: :cpu, out:'~/log/')
|
45
|
+
p :hello
|
46
|
+
YarvProf.end
|
47
|
+
```
|
48
|
+
|
49
|
+
This is the sample usage of YarvProf in your code. YarvProf#start can take 2 keyword args, one is for the flag to switch measurement mode (:cpu or :wall), and the other one is to specify the target directory the dump file will be seved in.
|
50
|
+
|
51
|
+
```
|
52
|
+
$ yarvprof --load ./result.dump --insn getlocal_OP__WC__1
|
53
|
+
```
|
54
|
+
|
55
|
+
And this is the sample usage of yarv-prof CLI command, which is specifically designed to parse and view dumped data. yarv-prof command can take following options.
|
56
|
+
|
57
|
+
```
|
58
|
+
Usage: yarv-prof [options]
|
59
|
+
-v, --version Print version
|
60
|
+
--load=FILENAME Load .dump file
|
61
|
+
--csv Report with csv format
|
62
|
+
--raw Show raw log data with time series alignment
|
63
|
+
--insn=VALUE Show a specific instruction only
|
64
|
+
```
|
65
|
+
|
66
|
+
## TBD in future
|
67
|
+
|
68
|
+
- More detailed information regarding YARV instructions
|
69
|
+
- Improve performance
|
70
|
+
- System Tap support for linux environment(only if needed)
|
71
|
+
- Sampling profiler mode(if needed)
|
72
|
+
- Docker support etc
|
73
|
+
|
74
|
+
## Other resources
|
75
|
+
|
76
|
+
- You may want to refer [my presentation slide](TBD) at RubyConf Taiwan 2016
|
77
|
+
|
78
|
+
## License
|
79
|
+
|
80
|
+
MIT
|
data/bin/yarv-prof
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if !ARGV.index('-d') then
|
4
|
+
require 'rubygems'
|
5
|
+
else
|
6
|
+
Encoding.default_external = 'UTF-8'
|
7
|
+
['..', '../lib'].each do |path|
|
8
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), path))
|
9
|
+
end
|
10
|
+
ARGV.delete('-d')
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'yarv-prof'
|
14
|
+
require 'optparse'
|
15
|
+
require 'enumerable/statistics'
|
16
|
+
|
17
|
+
class DatasetBase
|
18
|
+
attr_reader :instructions, :total_time, :total_count, :measure_mode
|
19
|
+
def initialize
|
20
|
+
@total_time = 0
|
21
|
+
@total_count = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_entry(insn, elapsed_time)
|
25
|
+
@total_time = @total_time + elapsed_time
|
26
|
+
@total_count = @total_count + 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_measure_mode(label)
|
30
|
+
@measure_mode = label == "vtimestamp" ? "cputime" : "walltime"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class SummaryDataset < DatasetBase
|
35
|
+
def initialize
|
36
|
+
@instructions = {}
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_entry(insn, elapsed_time)
|
41
|
+
@instructions[insn] = [] if @instructions[insn].nil?
|
42
|
+
@instructions[insn] << elapsed_time
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class TimeSeriesDataset < DatasetBase
|
48
|
+
def initialize
|
49
|
+
@instructions = []
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_entry(insn, elapsed_time)
|
54
|
+
@instructions << [insn, elapsed_time]
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class DumpLogParser
|
60
|
+
attr_reader :dataset
|
61
|
+
def initialize(filename, dataset)
|
62
|
+
@filename = filename
|
63
|
+
@dataset = dataset
|
64
|
+
end
|
65
|
+
def parse(insn=nil)
|
66
|
+
prev = nil
|
67
|
+
File.read(@filename).each_line{|line|
|
68
|
+
line.chomp!
|
69
|
+
next if line.empty?
|
70
|
+
if line.split(",")[0] == "insn" then
|
71
|
+
@dataset.set_measure_mode line.split(",")[1]
|
72
|
+
else
|
73
|
+
if !prev.nil? && (insn.nil? || insn==prev[0])
|
74
|
+
@dataset.add_entry prev[0], (line.split(",")[1].to_i - prev[1].to_i)
|
75
|
+
end
|
76
|
+
prev = line.split(",")
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class Presentation
|
84
|
+
attr_accessor :filename, :csv, :raw, :insn
|
85
|
+
|
86
|
+
def make
|
87
|
+
@parser = DumpLogParser.new(@filename, @raw || @insn ? TimeSeriesDataset.new : SummaryDataset.new )
|
88
|
+
@parser.parse(@insn)
|
89
|
+
end
|
90
|
+
|
91
|
+
def display
|
92
|
+
ds = @parser.dataset
|
93
|
+
if ds.class == TimeSeriesDataset
|
94
|
+
if @csv
|
95
|
+
puts "insn,#{ds.measure_mode}"
|
96
|
+
ds.instructions.each{|e|
|
97
|
+
puts "%s,%d" % [e[0], e[1]]
|
98
|
+
}
|
99
|
+
else
|
100
|
+
puts "isns".ljust(30)+ds.measure_mode.rjust(15)
|
101
|
+
puts "-"*45
|
102
|
+
ds.instructions.each{|e|
|
103
|
+
puts ("%s" % e[0]).ljust(30) + ("%d" % e[1]).rjust(15)
|
104
|
+
}
|
105
|
+
end
|
106
|
+
else
|
107
|
+
if @csv
|
108
|
+
puts "insn,count,total_#{ds.measure_mode},mean,variance,stdev"
|
109
|
+
ds.instructions.each{|insn, values|
|
110
|
+
puts "%s,%d,%d,%.0f,%.0f,%.0f" % [insn, values.count, values.inject(:+), values.mean, values.variance, values.stdev]
|
111
|
+
}
|
112
|
+
else
|
113
|
+
puts "total number of instruction calls: #{ds.total_count}"
|
114
|
+
presentation = []
|
115
|
+
puts "insn".ljust(30) + "count".rjust(14) + "total_#{ds.measure_mode}".rjust(20) + "mean".rjust(10) + "variance".rjust(16) + "stdev".rjust(9)
|
116
|
+
puts "-"*99
|
117
|
+
ds.instructions.each{|insn, values|
|
118
|
+
#presentation << [values.inject(:+), "%s:\n count=%d(%d%s), total=%d(%d%s), mean=%.0f, variance=%.0f, stdev=%.0f\n" % [insn, values.count, (values.count*100/@parser.dataset.total_count), "%%", values.inject(:+), (values.inject(:+)*100/@parser.dataset.total_time), "%%", values.mean, values.variance, values.stdev] ]
|
119
|
+
presentation << [values.inject(:+), "%s%s%s%s%s%s\n" % [
|
120
|
+
insn.ljust(30),
|
121
|
+
("%d(%d%s)" % [values.count, (values.count*100/ds.total_count), "%%"]).rjust(15),
|
122
|
+
("%d(%d%s)" % [values.inject(:+), (values.inject(:+)*100/ds.total_time), "%%"]).rjust(21),
|
123
|
+
("%.0f" % values.mean).rjust(10),
|
124
|
+
("%.0f" % values.variance).rjust(16),
|
125
|
+
("%.0f" % values.stdev).rjust(9)
|
126
|
+
]]
|
127
|
+
}
|
128
|
+
presentation.sort{|a,b|
|
129
|
+
b[0] <=> a[0]
|
130
|
+
}.each{|e|
|
131
|
+
printf e[1]
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
if ARGV.empty?
|
139
|
+
raise "Use -h option to know what commands are available"
|
140
|
+
else
|
141
|
+
report = Presentation.new
|
142
|
+
Options = OptionParser.new do |opts|
|
143
|
+
opts.on("-v", "--version", "Print version") {|v| puts YarvProf::VERSION; exit 0}
|
144
|
+
opts.on("--load=FILENAME", "Load .dump file") {|filename| report.filename = filename}
|
145
|
+
opts.on("--csv", "Report with csv format") {report.csv = true}
|
146
|
+
opts.on("--raw", "Show raw log data with time series alignment") {report.raw = true}
|
147
|
+
opts.on("--insn=VALUE", "Show a specific instruction only") { |insn| report.insn = insn }
|
148
|
+
end
|
149
|
+
Options.parse!(ARGV)
|
150
|
+
report.make
|
151
|
+
report.display
|
152
|
+
end
|
data/lib/yarv-prof.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'yarv-prof/version'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class YarvProf
|
5
|
+
class << self
|
6
|
+
def start(clock: :wall, out:'/tmp/yarv-prof/')
|
7
|
+
at_exit do
|
8
|
+
Process.kill(:TERM, @pid) if !@pid.nil?
|
9
|
+
FileUtils.remove_entry @file.path if File.exists?(@file.path)
|
10
|
+
end
|
11
|
+
@measure_mode = clock == :cpu ? "vtimestamp" : "timestamp"
|
12
|
+
@dump_to = out
|
13
|
+
@file = Tempfile.new('.yarv-prof.d')
|
14
|
+
@file.puts <<EOS
|
15
|
+
dtrace:::BEGIN{
|
16
|
+
printf("insn,#{@measure_mode}\\n");
|
17
|
+
}
|
18
|
+
|
19
|
+
ruby#{Process.pid}:::insn{
|
20
|
+
printf("%s,%d\\n", copyinstr(arg0), #{@measure_mode});
|
21
|
+
}
|
22
|
+
EOS
|
23
|
+
@file.close
|
24
|
+
FileUtils.mkdir @dump_to if !File.directory?(@dump_to)
|
25
|
+
dumpfile = Time.now.strftime('%Y%m%d_%H%M%S.dump')
|
26
|
+
@pid = Process.spawn("dtrace -q -s '#{@file.path}'", :err => :out,:out => @dump_to + dumpfile)
|
27
|
+
#while File.read(DUMPTO+dumpfile).size < 10 do
|
28
|
+
# sleep 0.01
|
29
|
+
#end
|
30
|
+
sleep 0.5
|
31
|
+
end
|
32
|
+
|
33
|
+
def end
|
34
|
+
Process.kill(:TERM, @pid) if !@pid.nil?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/yarv-prof.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'yarv-prof/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'yarv-prof'
|
7
|
+
s.version = YarvProf::VERSION
|
8
|
+
s.homepage = 'https://github.com/remore/yarv-prof'
|
9
|
+
|
10
|
+
s.authors = 'Kei Sawada(@remore)'
|
11
|
+
s.email = 'k@swd.cc'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.bindir = 'bin'
|
15
|
+
s.executables = 'yarv-prof'
|
16
|
+
|
17
|
+
s.summary = 'A DTrace-based YARV profiler'
|
18
|
+
s.description = 'A DTrace-based YARV profiler'
|
19
|
+
s.license = 'MIT'
|
20
|
+
|
21
|
+
s.add_dependency "enumerable-statistics"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yarv-prof
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kei Sawada(@remore)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: enumerable-statistics
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: A DTrace-based YARV profiler
|
28
|
+
email: k@swd.cc
|
29
|
+
executables:
|
30
|
+
- yarv-prof
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- README.md
|
36
|
+
- bin/yarv-prof
|
37
|
+
- lib/yarv-prof.rb
|
38
|
+
- lib/yarv-prof/version.rb
|
39
|
+
- yarv-prof.gemspec
|
40
|
+
homepage: https://github.com/remore/yarv-prof
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.5.1
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: A DTrace-based YARV profiler
|
64
|
+
test_files: []
|