kamal-railsbench 0.9.9.pre

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.
Files changed (54) hide show
  1. data/BUGS +2 -0
  2. data/CHANGELOG +2124 -0
  3. data/GCPATCH +73 -0
  4. data/INSTALL +75 -0
  5. data/LICENSE +222 -0
  6. data/Manifest.txt +53 -0
  7. data/PROBLEMS +56 -0
  8. data/README +337 -0
  9. data/Rakefile +51 -0
  10. data/bin/railsbench +80 -0
  11. data/config/benchmarking.rb +21 -0
  12. data/config/benchmarks.rb +21 -0
  13. data/config/benchmarks.yml +2 -0
  14. data/images/empty.png +0 -0
  15. data/images/minus.png +0 -0
  16. data/images/plus.png +0 -0
  17. data/install.rb +70 -0
  18. data/latest_changes.txt +18 -0
  19. data/lib/benchmark.rb +576 -0
  20. data/lib/railsbench/benchmark.rb +576 -0
  21. data/lib/railsbench/benchmark_specs.rb +63 -0
  22. data/lib/railsbench/gc_info.rb +158 -0
  23. data/lib/railsbench/perf_info.rb +146 -0
  24. data/lib/railsbench/perf_utils.rb +202 -0
  25. data/lib/railsbench/railsbenchmark.rb +640 -0
  26. data/lib/railsbench/version.rb +9 -0
  27. data/lib/railsbench/write_headers_only.rb +15 -0
  28. data/postinstall.rb +12 -0
  29. data/ruby184gc.patch +516 -0
  30. data/ruby185gc.patch +562 -0
  31. data/ruby186gc.patch +564 -0
  32. data/ruby19gc.patch +2425 -0
  33. data/script/convert_raw_data_files +49 -0
  34. data/script/generate_benchmarks +171 -0
  35. data/script/perf_bench +74 -0
  36. data/script/perf_comp +151 -0
  37. data/script/perf_comp_gc +113 -0
  38. data/script/perf_diff +48 -0
  39. data/script/perf_diff_gc +53 -0
  40. data/script/perf_html +103 -0
  41. data/script/perf_plot +225 -0
  42. data/script/perf_plot_gc +254 -0
  43. data/script/perf_prof +87 -0
  44. data/script/perf_run +39 -0
  45. data/script/perf_run_gc +40 -0
  46. data/script/perf_table +104 -0
  47. data/script/perf_tex +58 -0
  48. data/script/perf_times +66 -0
  49. data/script/perf_times_gc +94 -0
  50. data/script/run_urls +57 -0
  51. data/setup.rb +1585 -0
  52. data/test/railsbench_test.rb +11 -0
  53. data/test/test_helper.rb +2 -0
  54. metadata +133 -0
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.length < 4 || ARGV.first == 'help'
4
+ $stderr.puts 'usage: perf_diff iterations common-options options1 options4 [conf-name1] [conf-name2]'
5
+ $stderr.puts 'example: perf_diff 100 "-bm=all" "-log" "-nocache" c1 c2'
6
+ exit 1
7
+ end
8
+
9
+ bindir = File.dirname(__FILE__)
10
+ require "#{bindir}/../lib/railsbench/perf_utils"
11
+
12
+ determine_rails_root_or_die!
13
+
14
+ iterations = ARGV[0]
15
+ common = ARGV[1]
16
+ options1 = ARGV[2]
17
+ options2 = ARGV[3]
18
+
19
+ benchmark = "default"
20
+ benchmark = $1 if common =~ /-bm=([^ ]+)/
21
+
22
+ file1 = benchmark_file_name(benchmark, ARGV[4], 1)
23
+ perf_run("perf_diff", iterations, "#{common} #{options1}", file1)
24
+ puts;puts
25
+
26
+ file2 = benchmark_file_name(benchmark, ARGV[5], 2)
27
+ perf_run("perf_diff", iterations, "#{common} #{options2}", file2)
28
+ puts;puts
29
+
30
+ system("ruby #{bindir}/perf_comp #{file1} #{file2}")
31
+
32
+ __END__
33
+
34
+ # Copyright (C) 2005-2008 Stefan Kaes
35
+ #
36
+ # This program is free software; you can redistribute it and/or modify
37
+ # it under the terms of the GNU General Public License as published by
38
+ # the Free Software Foundation; either version 2 of the License, or
39
+ # (at your option) any later version.
40
+ #
41
+ # This program is distributed in the hope that it will be useful,
42
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
43
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44
+ # GNU General Public License for more details.
45
+ #
46
+ # You should have received a copy of the GNU General Public License
47
+ # along with this program; if not, write to the Free Software
48
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.length < 4 || ARGV.first == 'help'
4
+ $stderr.puts 'usage: perf_diff_gc iterations options1 options2 [conf-name1] [conf-name2]'
5
+ $stderr.puts 'example: perf_diff_gc 100 "-bm=default" "-log" "-nocache" c1 c2'
6
+ exit 1
7
+ end
8
+
9
+ bindir = File.dirname(__FILE__)
10
+ require "#{bindir}/../lib/railsbench/perf_utils"
11
+
12
+ determine_rails_root_or_die!
13
+
14
+ iterations = ARGV[0]
15
+ common = ARGV[1]
16
+ options1 = ARGV[2]
17
+ options2 = ARGV[3]
18
+
19
+ benchmark = "default"
20
+ benchmark = $1 if common =~ /-bm=([^ ]+)/
21
+
22
+ file1 = benchmark_file_name(benchmark, ARGV[4], 1, :gc)
23
+ perf_run_gc("perf_run_gc", iterations, "#{common} #{options1}", file1)
24
+
25
+ puts;puts
26
+
27
+ file2 = benchmark_file_name(benchmark, ARGV[5], 2, :gc)
28
+ perf_run_gc("perf_run_gc", iterations, "#{common} #{options2}", file2)
29
+
30
+ puts;puts
31
+
32
+ puts "benchmark comparison data"
33
+ puts
34
+
35
+ system("ruby #{bindir}/perf_comp_gc #{file1} #{file2}")
36
+
37
+ __END__
38
+
39
+ # Copyright (C) 2005-2008 Stefan Kaes
40
+ #
41
+ # This program is free software; you can redistribute it and/or modify
42
+ # it under the terms of the GNU General Public License as published by
43
+ # the Free Software Foundation; either version 2 of the License, or
44
+ # (at your option) any later version.
45
+ #
46
+ # This program is distributed in the hope that it will be useful,
47
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
48
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49
+ # GNU General Public License for more details.
50
+ #
51
+ # You should have received a copy of the GNU General Public License
52
+ # along with this program; if not, write to the Free Software
53
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless ARGV.include?('-nocss')
4
+ puts <<'END'
5
+ <style type="text/css">
6
+ <!--
7
+ .perf_header, .perf_data, .perf_name { font-size:70%; padding-left:5px; padding-right:5px; }
8
+ .perf_header { text-align:center; vertical-align:top; font-weight:bold;}
9
+ .i { font-style:italic; }
10
+ .b { font-weight:bold; }
11
+ .c1 { background:#fff3df; }
12
+ .c2 { background:#dfedff; }
13
+ .factor { background:#efe0ef; }
14
+ .factor_red_alert { background:#CC303B; }
15
+ .factor_red { background:#FF6060; }
16
+ .factor_reddish { background:#FFA766; }
17
+ .factor_green_alert { background:#0B8900; }
18
+ .factor_green { background:#6AB788; }
19
+ .factor_greenish { background:#7FFFB0; }
20
+ .name { background:#dfdfdf; }
21
+ .perf_name { text-align:left; }
22
+ .perf_data { text-align:right; }
23
+ -->
24
+ </style>
25
+ END
26
+ end
27
+
28
+ def factor_class(v)
29
+ return 'factor' unless ARGV.include?('-colorize')
30
+ if v <= 0.85
31
+ 'factor_red_alert'
32
+ elsif v <= 0.90
33
+ 'factor_red'
34
+ elsif v <= 0.95
35
+ 'factor_reddish'
36
+ elsif v >= 1.15
37
+ 'factor_green_alert'
38
+ elsif v >= 1.10
39
+ 'factor_green'
40
+ elsif v >= 1.05
41
+ 'factor_greenish'
42
+ else
43
+ 'factor'
44
+ end
45
+ end
46
+
47
+ unless ARGV.include?('-notable')
48
+ puts "<table cellspacing=1px>"
49
+ $stdin.each_line do |l|
50
+ case l
51
+ when /^garbage collection/
52
+ unless ARGV.include?('-gc')
53
+ puts "</table>"
54
+ exit
55
+ end
56
+ puts "<tr></tr><tr></tr>"
57
+ puts "<tr>"
58
+ puts "<th class='perf_header name' style='text-align:left'>GC statistics</th>"
59
+ puts "<th class='perf_header c1'>c1 total</th><th class='perf_header c2'>c2 total</th>"
60
+ puts "<th class='perf_header c1'>c1 #gc</th><th class='perf_header c2'>c2 #gc</th>"
61
+ puts "<th class='perf_header c1'>c1 gc%</th><th class='perf_header c2'>c2 #gc%</th>"
62
+ puts "<th class='perf_header factor'>c1/c2</th>"
63
+ puts "</tr>"
64
+ when /^page/
65
+ puts "<tr>"
66
+ puts "<th class='perf_header name' style='text-align:left;'>page</th>"
67
+ puts "<th class='perf_header c1'>c1 total</th><th class='perf_header c2'>c2 total</th>"
68
+ puts "<th class='perf_header c1'>c1 r/s</th><th class='perf_header c2'>c2 r/s</th>"
69
+ puts "<th class='perf_header c1'>c1 ms/r</th><th class='perf_header c2'>c2 ms/r</th>"
70
+ puts "<th class='perf_header factor'>c1/c2</th>"
71
+ puts "</tr>"
72
+ end
73
+ case l
74
+ when %r{^([A-Za-z0-9./?=_ ]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+$}
75
+ puts "<tr>"
76
+ puts "<td class='perf_name name#{" i" if $1.strip == "all requests"}'>#{$1}</td>"
77
+ puts "<td class='perf_data c1'>#{$2}</td><td class='perf_data c2'>#{$3}</td>"
78
+ puts "<td class='perf_data c1'>#{$4}</td><td class='perf_data c2'>#{$5}</td>"
79
+ puts "<td class='perf_data c1'>#{$6}</td><td class='perf_data c2'>#{$7}</td>"
80
+ puts "<td class='perf_data #{factor_class($8.to_f)}'>#{$8}</td>"
81
+ puts "</tr>"
82
+ end
83
+ end
84
+ puts "</table>"
85
+ end
86
+
87
+ __END__
88
+
89
+ # Copyright (C) 2005-2008 Stefan Kaes
90
+ #
91
+ # This program is free software; you can redistribute it and/or modify
92
+ # it under the terms of the GNU General Public License as published by
93
+ # the Free Software Foundation; either version 2 of the License, or
94
+ # (at your option) any later version.
95
+ #
96
+ # This program is distributed in the hope that it will be useful,
97
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
98
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99
+ # GNU General Public License for more details.
100
+ #
101
+ # You should have received a copy of the GNU General Public License
102
+ # along with this program; if not, write to the Free Software
103
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require 'optparse'
6
+ require 'ostruct'
7
+
8
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
9
+ require 'railsbench/perf_info'
10
+
11
+ # parse options
12
+ o = OpenStruct.new
13
+ o.selection = []
14
+ o.title = "Performance Graph"
15
+ o.files = []
16
+ o.names = []
17
+ o.labels = []
18
+ o.graph_type = :line
19
+ o.graph_width = '800x600'
20
+ o.font_size = 14
21
+ o.output_file = nil
22
+ o.colors = nil
23
+ o.theme = nil
24
+ o.engine = :gruff
25
+
26
+ parser = OptionParser.new do |opts|
27
+ opts.banner = "Usage: perf_plot [options] file1 file2 ..."
28
+
29
+ opts.separator ""
30
+ opts.separator "Options:"
31
+
32
+ opts.on("-t", "--title T",
33
+ "Specify the title for your plot") do |t|
34
+ o.title = t
35
+ end
36
+
37
+ opts.on("--only LIST", Array,
38
+ "Restrict plot to a subset of the benchmarks") do |t|
39
+ o.selection = t.map{|s| s.to_i}
40
+ end
41
+
42
+ opts.on("--line",
43
+ "Plot a line graph") do |t|
44
+ o.graph_type = :line
45
+ end
46
+
47
+ opts.on("--bar",
48
+ "Plot a bar graph") do |t|
49
+ o.graph_type = :bar
50
+ end
51
+
52
+ opts.on("-w", "--width W", Integer,
53
+ "Width of the plot (pixels)") do |w|
54
+ o.graph_width = w
55
+ end
56
+
57
+ opts.on("-g", "--geometry WxH", /\d+x\d+/,
58
+ "Specify plot dimensions (pixels)") do |d|
59
+ o.graph_width = d
60
+ end
61
+
62
+ opts.on("-c", "--colors LIST", Array,
63
+ "Use specified colors for lines/bars") do |t|
64
+ o.colors = t
65
+ end
66
+
67
+ opts.on("--theme NAME",
68
+ "Use specified theme") do |t|
69
+ o.theme = t
70
+ end
71
+
72
+ opts.on("-e", "--engine ENGINE", [:gruff, :gnuplot],
73
+ "Select plotting engine: (gruff, gnuplot)") do |e|
74
+ o.engine = e
75
+ end
76
+
77
+ opts.on("-n", "--names LIST", Array,
78
+ "Use specified names for the legend") do |t|
79
+ o.names = t
80
+ end
81
+
82
+ opts.on("-l", "--labels LIST", Array,
83
+ "Use specified labels instead of benchmark names") do |t|
84
+ o.labels = t
85
+ end
86
+
87
+ opts.on("-o", "--out FILE",
88
+ "Specify output file") do |f|
89
+ o.output_file = f
90
+ end
91
+
92
+ opts.on("-f", "--font-size N", Integer,
93
+ "Overall font size to use in the plot (points)") do |n|
94
+ o.font_size = n
95
+ end
96
+
97
+ opts.on_tail("-h", "--help", "Show this message") do
98
+ puts opts
99
+ exit
100
+ end
101
+ end
102
+
103
+ # option compatibility with older versions
104
+ args=[]
105
+ ARGV.each do |arg|
106
+ arg = arg.sub("font_size", "font-size") if arg =~ /^-font_size/
107
+ arg = "-" + arg if arg =~ /^-(title|width|out|geometry|font-size|names|labels|colors|line|bar|only)/
108
+ args << arg
109
+ end
110
+
111
+ parser.parse!(args)
112
+
113
+ o.output_file ||= o.engine == :gruff ? "graph.png" : "graph.pdf"
114
+
115
+ args.each do |arg|
116
+ o.files << File.open_or_die(arg)
117
+ o.names[o.files.length-1] ||= File.basename(arg).sub(/\.txt$/, '').sub(/^\d\d-\d\d\.[^\.]+./, '')
118
+ end
119
+
120
+ o.files.length > 0 or die(parser.banner)
121
+
122
+ class Plotter
123
+ attr_reader :o
124
+ def initialize(options)
125
+ @o = options
126
+ setup
127
+ end
128
+
129
+ def setup
130
+ pi = nil
131
+ @perf_data = []
132
+ o.files.each do |file|
133
+ pi = PerfInfo.new(file)
134
+ iter = pi.iterations
135
+ urls = pi.requests_per_key
136
+ @perf_data << pi.keys.map{ |key| iter*urls/pi.timings_mean(key) }
137
+ file.close
138
+ end
139
+ o.selection = (1..(@perf_data.last.length)).to_a if o.selection.empty?
140
+ if o.labels.empty?
141
+ o.labels = pi.keys.restrict_to(o.selection.map{|i| i-1})
142
+ end
143
+ @perf_data = @perf_data.map{|d| d.restrict_to(o.selection.map{|i| i-1})}
144
+ end
145
+
146
+ def plot_with_gruff
147
+ require 'gruff'
148
+
149
+ g = o.graph_type == :line ? Gruff::Line.new(o.graph_width) : Gruff::Bar.new(o.graph_width)
150
+ g.send "theme_#{o.theme}" if o.theme
151
+
152
+ # on OS X, ImagMagick can't find it's default font (arial) sometimes, so specify some font
153
+ #g.font = 'Arial-Normal' if RUBY_PLATFORM =~ /darwin/
154
+ g.font = 'Helvetica' if RUBY_PLATFORM =~ /darwin/
155
+
156
+ g.replace_colors(o.colors) if o.colors
157
+ g.title = o.title
158
+ g.sort = false
159
+ g.legend_font_size = o.font_size
160
+ g.legend_box_size = o.font_size
161
+ g.marker_font_size = o.font_size
162
+ g.minimum_value = 0
163
+ g.maximum_value = @perf_data.flatten.max.ceil
164
+ g.labels = o.labels.index_map
165
+ @perf_data.each_with_index{|d,i| g.data(o.names[i], d)}
166
+ g.write(o.output_file)
167
+ end
168
+
169
+ def plot_with_gnuplot
170
+ require 'gnuplot'
171
+ Gnuplot.open(false) do |gnuplot|
172
+ Gnuplot::Plot.new(gnuplot) do |plot|
173
+ plot.terminal "pdf noenhanced color fname 'Helvetica' fsize 5"
174
+ plot.output o.output_file
175
+ plot.xlabel "Benchmarks"
176
+ plot.ylabel "Requests per second"
177
+ plot.yrange "[0:*]"
178
+ plot.title o.title
179
+ plot.style "fill solid 1.0 noborder"
180
+ plot.style "data #{o.graph_type == :line ? "linespoints" : "histogram"}"
181
+ plot.style "histogram cluster gap 1"
182
+ plot.xtics "nomirror rotate by -45"
183
+ label_specs = []
184
+ o.labels.each_with_index{ |l,i| label_specs << "\"#{l}\" #{i}" }
185
+ plot.xtics "(#{label_specs.join(', ')})"
186
+ plot.key "outside reverse"
187
+ #plot.boxwidth "0.95"
188
+ plot.grid "nopolar"
189
+ plot.grid "noxtics nomxtics ytics nomytics noztics nomztics nox2tics nomx2tics noy2tics nomy2tics nocbtics nomcbtics"
190
+ plot.grid "back linetype 0 linewidth 0.7"
191
+
192
+ @perf_data.each_with_index do |d,i|
193
+ plot.data << Gnuplot::DataSet.new([[o.names[i]] + d]) do |ds|
194
+ ds.using = "1 title 1"
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ def plot
202
+ send "plot_with_#{o.engine}"
203
+ end
204
+ end
205
+
206
+ Plotter.new(o).plot
207
+
208
+
209
+ __END__
210
+
211
+ # Copyright (C) 2005-2008 Stefan Kaes
212
+ #
213
+ # This program is free software; you can redistribute it and/or modify
214
+ # it under the terms of the GNU General Public License as published by
215
+ # the Free Software Foundation; either version 2 of the License, or
216
+ # (at your option) any later version.
217
+ #
218
+ # This program is distributed in the hope that it will be useful,
219
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
220
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
221
+ # GNU General Public License for more details.
222
+ #
223
+ # You should have received a copy of the GNU General Public License
224
+ # along with this program; if not, write to the Free Software
225
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+
7
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
8
+ require 'railsbench/gc_info'
9
+
10
+ # parse options
11
+ o = OpenStruct.new
12
+ o.title = "GC Data Plot"
13
+ o.graph_width = '1400x1050'
14
+ o.font_size = 10
15
+ o.ignored_object_types = []
16
+ o.output_file = nil
17
+ o.plot_live = true
18
+ o.plot_freed = true
19
+ o.plot_type = :both
20
+ o.engine = :gruff
21
+
22
+ parser = OptionParser.new do |opts|
23
+ opts.banner = "Usage: perf_plot_gc [options] file1 file2 ..."
24
+
25
+ opts.separator ""
26
+ opts.separator "Options:"
27
+
28
+ opts.on("-t", "--title T",
29
+ "Specify the title for your plot") do |t|
30
+ o.title = t
31
+ end
32
+
33
+ opts.on("-i", "--ignore LIST", Array,
34
+ "Specify the object types to ignore") do |i|
35
+ o.ignored_object_types = i.map{|t| t.upcase}
36
+ end
37
+
38
+ opts.on("-d", "--type TYPE", [:freed, :live, :both],
39
+ "Select data points to plot: (live, freed, both)") do |dp|
40
+ o.plot_type = dp
41
+ o.plot_freed = o.plot_live = false
42
+ case dp
43
+ when :live then o.plot_live = true
44
+ when :freed then o.plot_freed = true
45
+ when :both then o.plot_freed = true; o.plot_live = true
46
+ end
47
+ end
48
+
49
+ opts.on("-e", "--engine ENGINE", [:gruff, :gnuplot],
50
+ "Select plotting engine: (gruff, gnuplot)") do |e|
51
+ o.engine = e
52
+ end
53
+
54
+ opts.on("-f", "--font-size N", Integer,
55
+ "Overall font size to use in the plot (points)") do |n|
56
+ o.font_size = n
57
+ end
58
+
59
+ opts.on("-w", "--width W", Integer,
60
+ "Width of the plot (pixels)") do |w|
61
+ o.graph_width = w
62
+ end
63
+
64
+ opts.on("-g", "--geometry WxH", /\d+x\d+/,
65
+ "Specify plot dimensions (pixels)") do |d|
66
+ o.graph_width = d
67
+ end
68
+
69
+ opts.on("-o", "--out FILE",
70
+ "Specify output file") do |f|
71
+ o.output_file = f
72
+ end
73
+
74
+ opts.on_tail("-h", "--help", "Show this message") do
75
+ puts opts
76
+ exit
77
+ end
78
+ end
79
+
80
+ # option compatibility with older versions
81
+ args=[]
82
+ ARGV.each do |arg|
83
+ arg = arg.sub("font_size", "font-size") if arg =~ /^-font_size/
84
+ arg = "-" + arg if arg =~ /^-(title|width|out|geometry|font-size|ignore)/
85
+ args << arg
86
+ end
87
+
88
+ parser.parse!(args)
89
+
90
+ o.output_file ||= if o.engine == :gruff
91
+ "graph.png"
92
+ elsif RUBY_PLATFORM =~ /darwin/
93
+ "graph.pdf"
94
+ else
95
+ "graph.ps"
96
+ end
97
+ o.files = []
98
+ o.names = []
99
+ args.each do |arg|
100
+ o.files << File.open_or_die(arg)
101
+ o.names[o.files.length-1] ||= File.basename(arg)
102
+ end
103
+
104
+ o.files.length > 0 or die(parser.banner)
105
+
106
+ o.gcis = []
107
+ o.files.each do |file|
108
+ o.gcis << GCInfo.new(file)
109
+ file.close
110
+ end
111
+
112
+ # o.object_types = %w(NODE STRING ARRAY HASH SCOPE VARMAP CLASS ICLASS REGEXP FLOAT MATCH FILE DATA MODULE OBJECT)
113
+ o.colors = %w(ff0000 00c000 0080ff c000ff 00eeee c04000 ee0000 2020c0 ffc020 008040 a080ff 804000 ff80ff 00c060 006080 c06080 008000 40ff80 306080 806000).map{|c| "#" + c}
114
+ o.object_types = GCInfo.object_types(o.gcis)
115
+ o.gc_count_max = o.gcis.map{|gci| gci.collections}.max
116
+ o.gc_max_processed = o.gcis.map{|gci| gci.processed_max}.max
117
+ o.gc_max_freed = o.gcis.map{|gci| gci.freed_max}.max
118
+ o.gc_max_live = o.gcis.map{|gci| gci.live_max}.max
119
+
120
+ o.title << " ["
121
+ o.title << "freed" if o.plot_freed
122
+ o.title << "," if o.plot_freed && o.plot_live
123
+ o.title << "live" if o.plot_live
124
+ o.title << "]"
125
+ o.title << " (ignoring #{o.ignored_object_types.join(', ')})" unless o.ignored_object_types.empty?
126
+
127
+ # for very large logs, we need to ignore some entries
128
+ N = o.gc_count_max < 100 ? 1 : o.gc_count_max / 99
129
+
130
+ class Plotter
131
+ attr_reader :o
132
+ def initialize(options)
133
+ @o = options
134
+ end
135
+
136
+ def plot_with_gruff
137
+ require 'gruff'
138
+
139
+ g = Gruff::StackedBar.new(o.graph_width)
140
+
141
+ # on OS X, ImageMagick can't find it's default font (arial) sometimes, so specify some font
142
+ # g.font = 'Arial-Normal' if RUBY_PLATFORM =~ /darwin/
143
+ g.font = 'Helvetica-Narrow' if RUBY_PLATFORM =~ /darwin/
144
+
145
+ %w(#FF0000 #00FF00 #0000FF #D2FF77 #FF68C0 #D1FDFF #FFF0BD #15FFDC
146
+ ).each do |color|
147
+ g.add_color(color)
148
+ end
149
+
150
+ g.title = o.title
151
+ g.sort = false
152
+ g.title_font_size = o.font_size+2
153
+ g.legend_font_size = o.font_size-2
154
+ g.legend_box_size = o.font_size-2
155
+ g.marker_font_size = o.font_size-2
156
+ if o.ignored_object_types.empty?
157
+ g.minimum_value = 0
158
+ maximums = []
159
+ maximums << o.gc_max_live if o.plot_live
160
+ maximums << o.gc_max_freed if o.plot_freed
161
+ g.maximum_value = maximums.max
162
+ end
163
+ label_step = 0
164
+ label_step += 1 if o.plot_live
165
+ label_step += 1 if o.plot_freed
166
+ g.labels = Hash[* (0...o.gc_count_max).map{|i| [label_step*i*o.files.length, i.to_s]}.flatten ]
167
+ # puts g.labels.inspect
168
+ # puts object_types.inspect
169
+ puts "ignoring #{o.ignored_object_types.join(', ')}" unless o.ignored_object_types.empty?
170
+
171
+ (o.object_types + %w(FREELIST)).each do |ot|
172
+ data = prepare_data(ot)
173
+ # puts "#{ot}: #{data.inspect}"
174
+ g.data(ot, data)
175
+ end
176
+
177
+ g.write(o.output_file)
178
+ end
179
+
180
+ def prepare_data(ot)
181
+ return if o.ignored_object_types.include?(ot)
182
+ data = []
183
+ o.gc_count_max.times do |gc_index|
184
+ next unless 0 == gc_index.modulo(N)
185
+ for gci in o.gcis
186
+ map_at_this_gc = gci.freed_objects[gc_index].merge('FREELIST' => gci.freelist[gc_index])
187
+ data << ((map_at_this_gc && map_at_this_gc[ot]) || 0) if o.plot_freed
188
+ map_at_this_gc = gci.live_objects[gc_index]
189
+ data << ((map_at_this_gc && map_at_this_gc[ot]) || 0) if o.plot_live
190
+ end
191
+ end
192
+ data
193
+ end
194
+
195
+ def plot_with_gnuplot
196
+ require 'gnuplot'
197
+ # there's a separate gnuplot binary which can read from stdin, but the gem doesn't know this
198
+ ENV['RB_GNUPLOT'] ||= 'pgnuplot.exe' if RUBY_PLATFORM =~ /mswin/
199
+ plot = Gnuplot::Plot.new
200
+ if o.output_file =~ /\.pdf$/
201
+ plot.terminal "pdf enhanced color font 'Helvetica,4'"
202
+ else
203
+ plot.terminal "postscript enhanced color font 'Helvetica,4'"
204
+ end
205
+ plot.output o.output_file
206
+ plot.xlabel "Collections"
207
+ plot.ylabel "Objects"
208
+ plot.title o.title
209
+ plot.style "fill solid 1.0 noborder"
210
+ plot.style "data histogram"
211
+ plot.style "histogram rowstacked"
212
+ plot.xtics "out nomirror"
213
+ plot.xrange "[-1:#{(o.gc_count_max/N)*((o.plot_type == :both) ? 2 : 1)}]"
214
+ plot.key "outside invert reverse"
215
+ plot.boxwidth "0.8"
216
+ plot.grid "nopolar"
217
+ plot.grid "noxtics nomxtics ytics nomytics noztics nomztics nox2tics nomx2tics noy2tics nomy2tics nocbtics nomcbtics"
218
+ plot.grid "back linetype 0 linewidth 0.7"
219
+
220
+ (o.object_types + %w(FREELIST)).each_with_index do |ot, i|
221
+ next unless data = prepare_data(ot)
222
+ plot.data << Gnuplot::DataSet.new([[ot.downcase] + data]) do |ds|
223
+ ds.using = "1 title 1 lc rgb '#{ot == 'FREELIST' ? '#666666' : o.colors[i]}'"
224
+ end
225
+ end
226
+
227
+ cmds = plot.to_gplot # puts cmds
228
+ Gnuplot.open(false){|gnuplot| gnuplot << cmds}
229
+ end
230
+
231
+ def plot
232
+ send "plot_with_#{o.engine}"
233
+ end
234
+ end
235
+
236
+ Plotter.new(o).plot
237
+
238
+ __END__
239
+
240
+ # Copyright (C) 2005-2008 Stefan Kaes
241
+ #
242
+ # This program is free software; you can redistribute it and/or modify
243
+ # it under the terms of the GNU General Public License as published by
244
+ # the Free Software Foundation; either version 2 of the License, or
245
+ # (at your option) any later version.
246
+ #
247
+ # This program is distributed in the hope that it will be useful,
248
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
249
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
250
+ # GNU General Public License for more details.
251
+ #
252
+ # You should have received a copy of the GNU General Public License
253
+ # along with this program; if not, write to the Free Software
254
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA