jkr 0.0.1 → 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.
@@ -0,0 +1,40 @@
1
+
2
+ module Jkr
3
+ class PlanFinder
4
+ def initialize(jkr_env)
5
+ @jkr_env = jkr_env
6
+ end
7
+
8
+ def find_by_name(name, options = {})
9
+ options[:plan_search_path] ||= [@jkr_env.jkr_plan_dir]
10
+
11
+ options[:plan_search_path].each do |dir|
12
+ Dir.glob("#{dir}/*.plan").each do |path|
13
+ if File.basename(path, ".plan") == name
14
+ return path
15
+ end
16
+ end
17
+ end
18
+
19
+ nil
20
+ end
21
+
22
+ def find_by_result_id(ret_id)
23
+ ret_dir = Dir[sprintf("#{@jkr_env.jkr_result_dir}/%05d*", ret_id)].first
24
+
25
+ unless ret_dir
26
+ raise ArgumentError.new("Result not found: id=#{ret_id}")
27
+ end
28
+
29
+ plan_files = Dir["#{ret_dir}/*.plan"]
30
+
31
+ if plan_files.size < 1
32
+ raise RuntimeError.new("No plan file found: #{File.basename(ret_dir)}")
33
+ elsif plan_files.size > 1
34
+ raise RuntimeError.new("Multiple plan files found: #{File.basename(ret_dir)}")
35
+ end
36
+
37
+ plan_files.first
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,626 @@
1
+
2
+ require 'pathname'
3
+
4
+ def rel_path(basefile, path)
5
+ base = Pathname.new(File.dirname(basefile))
6
+ path = Pathname.new(path)
7
+ path.relative_path_from(base).to_s
8
+ end
9
+
10
+ def gnuplot_label_escape(str)
11
+ ret = str.gsub(/_/, "\\\\\\_").gsub(/\{/, "\\{").gsub(/\}/, "\\}")
12
+ ret
13
+ end
14
+
15
+ def plot_distribution(config)
16
+ dataset = config[:dataset]
17
+ xrange_max = config[:xrange_max]
18
+ xrange_min = config[:xrange_min] || 0
19
+ title = config[:title]
20
+ xlabel = config[:xlabel]
21
+ eps_file = config[:output]
22
+ datafile = if config[:datafile]
23
+ File.open(config[:datafile], "w")
24
+ else
25
+ Tempfile.new("dist")
26
+ end
27
+ if config[:gpfile]
28
+ gpfile = File.open(config[:gpfile], "w")
29
+ else
30
+ gpfile = Tempfile.new("plot_dist")
31
+ end
32
+ datafile_path = rel_path(gpfile.path, datafile)
33
+
34
+ stepnum = 500
35
+
36
+ plot_stmt = []
37
+ data_idx = 0
38
+ xrange_max_local = 0
39
+ histset = []
40
+ dataset.each do |tx_type,data|
41
+ next if data.empty?
42
+ hist = Array.new
43
+ avg = data.inject(&:+) / data.size.to_f
44
+ sterr = data.sterr
45
+ stdev = data.stdev
46
+ n90th_idx = (data.size * 0.9).to_i
47
+ n90percent = data.sort[n90th_idx]
48
+ xrange_max_local = [xrange_max_local, xrange_max || n90percent * 4].max
49
+ step = xrange_max_local / stepnum
50
+ maxidx = 0
51
+ data.each_with_index do |num, data_idx|
52
+ idx = (num / step).to_i
53
+ maxidx = [idx, maxidx].max
54
+ hist[idx] = (hist[idx] || 0) + 1
55
+ end
56
+
57
+ 0.upto(maxidx) do |idx|
58
+ unless hist[idx]
59
+ hist[idx] = 0
60
+ end
61
+ end
62
+
63
+ histset.push({:tx_type => tx_type, :step => step, :hist => hist, :max => hist.max})
64
+
65
+ histmax = hist.max.to_f
66
+ datafile.puts "##{tx_type}"
67
+ 0.upto(stepnum - 1) do |i|
68
+ x = (i + 0.5) * step
69
+ y = hist[i] || 0
70
+ if config[:normalize]
71
+ y = y / histmax
72
+ end
73
+ datafile.puts "#{x}\t#{y}"
74
+ end
75
+ datafile.puts
76
+ datafile.puts
77
+
78
+ # index data_idx+1
79
+ datafile.puts "#{avg}\t0"
80
+ datafile.puts "#{avg}\t#{hist.max * 1.2}"
81
+ datafile.puts
82
+ datafile.puts
83
+
84
+ # index data_idx+2
85
+ datafile.puts "#{n90percent}\t0"
86
+ datafile.puts "#{n90percent}\t#{hist.max * 1.2}"
87
+ datafile.puts
88
+ datafile.puts
89
+
90
+ # index data_idx+3
91
+ datafile.puts "#{avg-sterr * 3}\t0"
92
+ datafile.puts "#{avg-sterr * 3}\t#{hist.max * 1.2}"
93
+ datafile.puts
94
+ datafile.puts
95
+ # index data_idx+4
96
+ datafile.puts "#{avg+sterr * 3}\t0"
97
+ datafile.puts "#{avg+sterr * 3}\t#{hist.max * 1.2}"
98
+ datafile.puts
99
+
100
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with linespoints title '%s'",
101
+ datafile_path, data_idx, data_idx, tx_type))
102
+ if config[:show_avgline]
103
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 1 lt 2 lw 3 title '%s(avg)'",
104
+ datafile_path, data_idx+1, data_idx+1, tx_type))
105
+ end
106
+ if config[:show_90pline]
107
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 2 lt 2 lw 3 title '%s(90%%-th)'",
108
+ datafile_path, data_idx+2, data_idx+2, tx_type))
109
+ end
110
+ if config[:show_confinterval]
111
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 3 lt 3 lw 3 title '%s(std err)'",
112
+ datafile_path, data_idx+3, data_idx+3, tx_type))
113
+ plot_stmt.push(sprintf("'%s' index %d:%d using 1:2 with lines lc 3 lt 3 lw 3 title '%s(std err)'",
114
+ datafile_path, data_idx+4, data_idx+4, tx_type))
115
+ end
116
+ data_idx += 5
117
+ end
118
+
119
+ datafile.fsync
120
+ plot_stmt = plot_stmt.flatten.join(", \\\n ")
121
+ plot_stmt = "plot #{plot_stmt}"
122
+
123
+ script = <<EOS
124
+ set term postscript enhanced color
125
+ set output "#{rel_path(gpfile.path, eps_file)}"
126
+ set size 0.9,0.6
127
+ set title "#{gnuplot_label_escape(title)}"
128
+ set ylabel "Frequency"
129
+ set xlabel "#{gnuplot_label_escape(xlabel)}"
130
+ set xrange [#{xrange_min}:#{xrange_max_local}]
131
+ #{config[:other_options]}
132
+ #{plot_stmt}
133
+ EOS
134
+ gpfile.puts script
135
+ gpfile.fsync
136
+ Dir.chdir(File.dirname(gpfile.path)) do
137
+ system_("gnuplot #{gpfile.path}")
138
+ end
139
+ gpfile.close
140
+ end
141
+
142
+ def plot_timeseries(config)
143
+ [:plot_data, :output, :title, :xlabel, :ylabel].each do |key|
144
+ unless config[key]
145
+ raise ArgumentError.new("key '#{key.to_s}' is required for time-series graph")
146
+ return
147
+ end
148
+ end
149
+
150
+ xrange = if config[:xrange]
151
+ "set xrange #{config[:xrange]}"
152
+ else
153
+ ""
154
+ end
155
+ yrange = if config[:yrange]
156
+ "set yrange #{config[:yrange]}"
157
+ else
158
+ ""
159
+ end
160
+
161
+ if config[:gpfile]
162
+ gpfile = File.open(config[:gpfile], "w")
163
+ else
164
+ gpfile = Tempfile.new("plot_timeseries")
165
+ end
166
+
167
+ plot_stmt = "plot " + config[:plot_data].map {|plot_datum|
168
+ [:datafile, :using, :with, :title].each do |key|
169
+ unless plot_datum[key]
170
+ raise ArgumentError.new("key '#{key.to_s}' is required for a plot_datum of time-series graph")
171
+ return
172
+ end
173
+ end
174
+ index = ""
175
+ if plot_datum[:index]
176
+ index = " index #{plot_datum[:index]} "
177
+ end
178
+ "'#{rel_path(gpfile.path, plot_datum[:datafile])}' #{index} using #{plot_datum[:using]} with #{plot_datum[:with]}"+
179
+ " title '#{gnuplot_label_escape(plot_datum[:title])}' " + plot_datum[:other_options].to_s
180
+ }.join(", \\\n ")
181
+
182
+ script = <<EOS
183
+ set term postscript enhanced color
184
+ set output "#{rel_path(gpfile.path, config[:output])}"
185
+ set size 0.9,0.6
186
+ set title "#{gnuplot_label_escape(config[:title])}"
187
+ set ylabel "#{config[:ylabel]}"
188
+ set xlabel "#{config[:xlabel]}"
189
+ set rmargin 3
190
+ set lmargin 10
191
+ #{xrange}
192
+ #{yrange}
193
+ set grid
194
+ #{config[:other_options]}
195
+ #{plot_stmt}
196
+ EOS
197
+ gpfile.puts script
198
+ gpfile.fsync
199
+ Dir.chdir(File.dirname(gpfile.path)) do
200
+ system_("gnuplot #{gpfile.path}")
201
+ end
202
+ gpfile.close
203
+ end
204
+
205
+ def plot_bar(config)
206
+ [:output, :title, :series_labels, :item_labels, :data].each do |key|
207
+ unless config[key]
208
+ raise StandardError.new("key '#{key.to_s}' is required for bar graph")
209
+ end
210
+ end
211
+
212
+ config[:size] ||= "0.9,0.6"
213
+
214
+ data = config[:data]
215
+
216
+ if ! data.all?{|series| series.all?{|datum| datum[:value].is_a? Numeric}}
217
+ raise StandardError.new("All datum must have :value key")
218
+ end
219
+
220
+ if config[:datafile]
221
+ datafile = File.open(config[:datafile], "w")
222
+ else
223
+ datafile = Tempfile.new("plot_bar")
224
+ end
225
+ if config[:gpfile]
226
+ gpfile = File.open(config[:gpfile], "w")
227
+ else
228
+ gpfile = Tempfile.new("plot_bar")
229
+ end
230
+ datafile_path = rel_path(gpfile.path, datafile.path)
231
+
232
+ config[:item_label_angle] ||= -30
233
+
234
+ plot_data = []
235
+ plot_stdev_data = []
236
+
237
+ if data.all?{|series| series.all?{|datum| datum[:stdev].is_a? Numeric}}
238
+ draw_stdev = true
239
+ else
240
+ draw_stdev = false
241
+ end
242
+
243
+ item_barwidth = 0.8 # 2.0 / 3.0
244
+ num_serieses = data.size
245
+ data_idx = 0
246
+ config[:data].each_with_index do |series, series_idx|
247
+ series.each_with_index do |datum, item_idx|
248
+ xpos = item_idx - item_barwidth / 2.0 + item_barwidth / num_serieses.to_f / 2.0 + item_barwidth / num_serieses.to_f * series_idx
249
+ barwidth = item_barwidth / num_serieses.to_f
250
+ datafile.puts([xpos, datum[:value], barwidth, (datum[:stdev] || "")].join("\t"))
251
+ end
252
+
253
+ plot_data.push({
254
+ :title => config[:series_labels][series_idx],
255
+ :using => "1:2:3",
256
+ :index => "#{data_idx}:#{data_idx}",
257
+ :with => "with boxes fs solid 0.7",
258
+ })
259
+
260
+ if series.every?{|datum| datum[:stdev]}
261
+ plot_data.push({
262
+ :title => nil,
263
+ :using => "1:2:4",
264
+ :index => "#{data_idx}:#{data_idx}",
265
+ :with => "with yerrorbars lc 1 lt 1 pt 0",
266
+ })
267
+ end
268
+
269
+ datafile.puts("\n\n")
270
+ data_idx += 1
271
+ end
272
+ datafile.fsync
273
+
274
+ ylabel = if config[:ylabel]
275
+ "set ylabel '#{config[:ylabel]}'"
276
+ else
277
+ ""
278
+ end
279
+
280
+ yrange = if config[:yrange]
281
+ "set yrange #{config[:yrange]}"
282
+ else
283
+ ""
284
+ end
285
+
286
+ plot_stmt = "plot " + plot_data.map do |plot_datum|
287
+ [:using].each do |key|
288
+ unless plot_datum[key]
289
+ raise StandardError.new("key '#{key.to_s}' is required for a plot_datum of bar graph")
290
+ return
291
+ end
292
+ end
293
+ index = ""
294
+ if plot_datum[:index]
295
+ index = " index #{plot_datum[:index]} "
296
+ end
297
+ if plot_datum[:title]
298
+ title = "title '#{gnuplot_label_escape(plot_datum[:title])}'"
299
+ else
300
+ title = "notitle"
301
+ end
302
+ "'#{rel_path(gpfile.path, datafile.path)}' #{index} using #{plot_datum[:using]}"+
303
+ " #{title} #{plot_datum[:with]} "
304
+ end.join(", \\\n ")
305
+
306
+ xpos = -1
307
+ xtics = config[:item_labels].map do |label|
308
+ xpos += 1
309
+ "\"#{label}\" #{xpos}"
310
+ end.join(",")
311
+ xtics = "(#{xtics})"
312
+ xrange = "[-0.5:#{config[:item_labels].size - 0.5}]"
313
+
314
+ script = <<EOS
315
+ set term postscript enhanced color
316
+ set output "#{rel_path(gpfile.path, config[:output])}"
317
+ set size #{config[:size]}
318
+ set title "#{gnuplot_label_escape(config[:title])}"
319
+ #{ylabel}
320
+ #{yrange}
321
+ set xrange #{xrange}
322
+ set xtic rotate by #{config[:item_label_angle]} scale 0
323
+ set xtics #{xtics}
324
+ #{config[:other_options]}
325
+ #{plot_stmt}
326
+ EOS
327
+ gpfile.puts script
328
+ gpfile.fsync
329
+ Dir.chdir(File.dirname(gpfile.path)) do
330
+ system_("gnuplot #{gpfile.path}")
331
+ end
332
+ gpfile.close
333
+ end
334
+
335
+ def plot_scatter(config)
336
+ [:plot_data, :output, :xlabel, :ylabel].each do |key|
337
+ unless config.keys.include?(key)
338
+ raise ArgumentError.new("key '#{key.to_s}' is required for scatter graph")
339
+ end
340
+ end
341
+
342
+ xrange = if config[:xrange]
343
+ "set xrange #{config[:xrange]}"
344
+ else
345
+ ""
346
+ end
347
+ yrange = if config[:yrange]
348
+ "set yrange #{config[:yrange]}"
349
+ else
350
+ ""
351
+ end
352
+ if config[:gpfile]
353
+ gpfile = File.open(config[:gpfile], "w")
354
+ else
355
+ gpfile = Tempfile.new("gnuplot")
356
+ end
357
+
358
+ plot_stmt = "plot " + config[:plot_data].map {|plot_datum|
359
+ unless plot_datum[:expression] ||
360
+ [:datafile, :using].every?{|key| plot_datum[key]}
361
+ raise ArgumentError.new("key ('datafile', 'using', 'with') or 'expression' is required for a plot_datum")
362
+ end
363
+
364
+ plot_target = nil
365
+ if plot_datum[:expression]
366
+ plot_target = plot_datum[:expression]
367
+ else
368
+ index = ""
369
+ if plot_datum[:index]
370
+ index = " index #{plot_datum[:index]} "
371
+ end
372
+
373
+ plot_target = "'#{rel_path(gpfile.path, plot_datum[:datafile])}' #{index} using #{plot_datum[:using]}"
374
+ end
375
+
376
+ unless plot_datum[:title]
377
+ title = "notitle"
378
+ else
379
+ title = "title '#{gnuplot_label_escape(plot_datum[:title])}'"
380
+ end
381
+
382
+ if plot_datum[:with]
383
+ with = "with #{plot_datum[:with]}"
384
+ else
385
+ with = ""
386
+ end
387
+ "#{plot_target} #{with} #{title} " + plot_datum[:other_options].to_s
388
+ }.join(", \\\n ")
389
+
390
+ title_stmt = "unset title"
391
+ if config[:title]
392
+ title_stmt = "set title \"#{gnuplot_label_escape(config[:title])}\""
393
+ end
394
+
395
+ case config[:output]
396
+ when /\.svg\Z/
397
+ terminal = 'svg'
398
+ else
399
+ terminal = 'postscript enhanced color'
400
+ end
401
+
402
+ script = <<EOS
403
+ set term #{terminal}
404
+ set output "#{rel_path(gpfile.path, config[:output])}"
405
+ #{if config[:size]; then "set size " + config[:size]; else; ""; end}
406
+ #{title_stmt}
407
+ set ylabel "#{gnuplot_label_escape(config[:ylabel])}"
408
+ set xlabel "#{gnuplot_label_escape(config[:xlabel])}"
409
+ set rmargin 10
410
+ set lmargin 10
411
+ #{xrange}
412
+ #{yrange}
413
+ set grid
414
+ #{config[:other_options]}
415
+ #{plot_stmt}
416
+ EOS
417
+ gpfile.puts script
418
+ gpfile.fsync
419
+ Dir.chdir(File.dirname(gpfile.path)) do
420
+ system_("gnuplot #{gpfile.path}")
421
+ end
422
+ gpfile.close
423
+ end
424
+
425
+ def plot_mpstat_data(mpstat_data, option)
426
+ option[:dir] ||= "mpstat"
427
+ option[:start_time] ||= mpstat_data.first[:time]
428
+ option[:end_time] ||= mpstat_data[-1][:time]
429
+
430
+ if ! Dir.exists?(option[:dir])
431
+ FileUtils.mkdir_p(option[:dir])
432
+ end
433
+
434
+ datafile = File.open(File.join(option[:dir],
435
+ "mpstat.tsv"),
436
+ "w")
437
+
438
+ start_time = option[:start_time]
439
+ end_time = option[:end_time]
440
+
441
+ data_idx = 0
442
+ nr_cpu = mpstat_data.first[:data].size - 1
443
+ (nr_cpu + 1).times do |cpu_idx|
444
+ scale = 1.0
445
+ if cpu_idx < nr_cpu
446
+ label = cpu_idx.to_s
447
+ else
448
+ label = "ALL"
449
+ scale = nr_cpu.to_f
450
+ end
451
+
452
+ datafile.puts("# cpu#{label}")
453
+ mpstat_data.each do |rec|
454
+ t = rec[:time] - start_time
455
+
456
+ if cpu_idx < nr_cpu
457
+ data = rec[:data].find{|x| x[:cpu] == cpu_idx}
458
+ else
459
+ data = rec[:data].find{|x| x[:cpu] == "all"}
460
+ end
461
+ datafile.puts([t,
462
+ data[:usr] * scale,
463
+ data[:nice] * scale,
464
+ data[:sys] * scale,
465
+ data[:iowait] * scale,
466
+ data[:irq] * scale,
467
+ data[:soft] * scale,
468
+ data[:steal] * scale,
469
+ data[:idle] * scale].map(&:to_s).join("\t"))
470
+ end
471
+ datafile.puts("\n\n")
472
+ datafile.fsync
473
+
474
+ plot_scatter(:output => File.join(option[:dir],
475
+ "cpu#{label}.eps"),
476
+ :gpfile => File.join(option[:dir],
477
+ "gp_cpu#{label}.gp"),
478
+ :title => nil,
479
+ :xlabel => "elapsed time [sec]",
480
+ :ylabel => "CPU usage [%]",
481
+ :xrange => "[0:#{end_time - start_time}]",
482
+ :yrange => "[0:#{105 * scale}]",
483
+ :plot_data => [{
484
+ :title => "%usr",
485
+ :using => "1:2",
486
+ :index => "#{data_idx}:#{data_idx}",
487
+ :with => "filledcurve x1",
488
+ :datafile => datafile.path
489
+ },
490
+ {
491
+ :title => "%nice",
492
+ :using => "1:($2+$3)",
493
+ :index => "#{data_idx}:#{data_idx}",
494
+ :with => "filledcurve x1",
495
+ :datafile => datafile.path
496
+ },
497
+ {
498
+ :title => "%sys",
499
+ :using => "1:($2+$3+$4)",
500
+ :index => "#{data_idx}:#{data_idx}",
501
+ :with => "filledcurve x1",
502
+ :datafile => datafile.path
503
+ },
504
+ {
505
+ :title => "%iowait",
506
+ :using => "1:($2+$3+$4+$5)",
507
+ :index => "#{data_idx}:#{data_idx}",
508
+ :with => "filledcurve x1",
509
+ :datafile => datafile.path
510
+ },
511
+ {
512
+ :title => "%irq",
513
+ :using => "1:($2+$3+$4+$5+$6)",
514
+ :index => "#{data_idx}:#{data_idx}",
515
+ :with => "filledcurve x1",
516
+ :datafile => datafile.path
517
+ },
518
+ {
519
+ :title => "%soft",
520
+ :using => "1:($2+$3+$4+$5+$6+$7)",
521
+ :index => "#{data_idx}:#{data_idx}",
522
+ :with => "filledcurve x1",
523
+ :datafile => datafile.path
524
+ },
525
+ {
526
+ :title => "%steal",
527
+ :using => "1:($2+$3+$4+$5+$6+$7+$8)",
528
+ :index => "#{data_idx}:#{data_idx}",
529
+ :with => "filledcurve x1",
530
+ :datafile => datafile.path
531
+ }].reverse,
532
+ :size => "0.9,0.9",
533
+ :other_options => <<EOS
534
+ set rmargin 3
535
+ set lmargin 5
536
+ set key below
537
+ EOS
538
+ )
539
+
540
+ data_idx += 1
541
+ end
542
+ end
543
+
544
+ def plot_io_pgr_data(pgr_data, option)
545
+ option[:dir] ||= "io_pgr"
546
+ option[:start_time] ||= pgr_data.first["time"]
547
+ option[:end_time] ||= pgr_data[-1]["time"]
548
+
549
+ if ! Dir.exists?(option[:dir])
550
+ FileUtils.mkdir_p(option[:dir])
551
+ end
552
+
553
+ start_time = option[:start_time]
554
+ end_time = option[:end_time]
555
+
556
+ datafile = File.open(File.join(option[:dir],
557
+ "io_pgr.tsv"),
558
+ "w")
559
+ devices = pgr_data.first["ioinfo"]["devices"]
560
+
561
+ idx = 0
562
+ devices.each do |device|
563
+ do_read = false
564
+ do_write = false
565
+ unless pgr_data.all?{|rec| rec["ioinfo"][device]['r/s'] < 0.1}
566
+ do_read = true
567
+ end
568
+ unless pgr_data.all?{|rec| rec["ioinfo"][device]['w/s'] < 0.1}
569
+ do_read = true
570
+ end
571
+
572
+ if do_read == false && do_write == false
573
+ $stderr.puts("#{device} performs no I/O")
574
+ next
575
+ end
576
+
577
+ datafile.puts("# #{device}")
578
+ pgr_data.each do |rec|
579
+ datafile.puts([rec["time"] - start_time,
580
+ rec["ioinfo"][device]['r/s'],
581
+ rec["ioinfo"][device]['w/s'],
582
+ ].map(&:to_s).join("\t"))
583
+ end
584
+ datafile.puts("\n\n")
585
+ datafile.fsync
586
+
587
+ plot_data = []
588
+ if do_read
589
+ plot_data.push({
590
+ :title => "read",
591
+ :datafile => datafile.path,
592
+ :using => "1:2",
593
+ :index => "#{idx}:#{idx}",
594
+ :with => "lines"
595
+ })
596
+ end
597
+ if do_write
598
+ plot_data.push({
599
+ :title => "write",
600
+ :datafile => datafile.path,
601
+ :using => "1:3",
602
+ :index => "#{idx}:#{idx}",
603
+ :with => "lines"
604
+ })
605
+ end
606
+
607
+ idx += 1
608
+
609
+ plot_scatter(:output => File.join(option[:dir],
610
+ device + ".eps"),
611
+ :gpfile => File.join(option[:dir],
612
+ "gp_" + device + ".gp"),
613
+ :xlabel => "elapsed time [sec]",
614
+ :ylabel => "IOPS",
615
+ :xrange => "[0:#{end_time - start_time}]",
616
+ :yrange => "[0:]",
617
+ :title => "IO performance",
618
+ :plot_data => plot_data,
619
+ :other_options => <<EOS
620
+ set rmargin 3
621
+ set lmargin 10
622
+ set key below
623
+ EOS
624
+ )
625
+ end
626
+ end