yarv-prof 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|