perfmonger 0.6.1 → 0.7.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.
Files changed (87) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +6 -0
  3. data/.tachikoma.yml +1 -0
  4. data/.travis.yml +18 -6
  5. data/Gemfile +1 -3
  6. data/Guardfile +26 -0
  7. data/NEWS +21 -0
  8. data/README.md +8 -9
  9. data/Rakefile +33 -1
  10. data/core/Makefile +23 -0
  11. data/core/build.sh +48 -0
  12. data/core/perfmonger-player.go +165 -0
  13. data/core/perfmonger-recorder.go +296 -0
  14. data/core/perfmonger-summarizer.go +207 -0
  15. data/core/subsystem/Makefile +3 -0
  16. data/core/subsystem/perfmonger.go +60 -0
  17. data/core/subsystem/perfmonger_darwin.go +22 -0
  18. data/core/subsystem/perfmonger_linux.go +292 -0
  19. data/core/subsystem/perfmonger_linux_test.go +73 -0
  20. data/core/subsystem/stat.go +214 -0
  21. data/core/subsystem/stat_test.go +281 -0
  22. data/core/subsystem/usage.go +410 -0
  23. data/core/subsystem/usage_test.go +496 -0
  24. data/lib/exec/operationBinding.rb.svn-base +59 -0
  25. data/lib/exec/perfmonger-player_darwin_amd64 +0 -0
  26. data/lib/exec/perfmonger-player_linux_386 +0 -0
  27. data/lib/exec/perfmonger-player_linux_amd64 +0 -0
  28. data/lib/exec/perfmonger-recorder_darwin_amd64 +0 -0
  29. data/lib/exec/perfmonger-recorder_linux_386 +0 -0
  30. data/lib/exec/perfmonger-recorder_linux_amd64 +0 -0
  31. data/lib/exec/perfmonger-summarizer_darwin_amd64 +0 -0
  32. data/lib/exec/perfmonger-summarizer_linux_386 +0 -0
  33. data/lib/exec/perfmonger-summarizer_linux_amd64 +0 -0
  34. data/lib/exec/perfmonger-summary_linux_386 +0 -0
  35. data/lib/exec/perfmonger-summary_linux_amd64 +0 -0
  36. data/lib/perfmonger/cli.rb +8 -3
  37. data/lib/perfmonger/command/core.rb +62 -0
  38. data/lib/perfmonger/command/live.rb +39 -0
  39. data/lib/perfmonger/command/play.rb +56 -0
  40. data/lib/perfmonger/command/plot.rb +30 -22
  41. data/lib/perfmonger/command/record.rb +3 -2
  42. data/lib/perfmonger/command/record_option.rb +40 -59
  43. data/lib/perfmonger/command/server.rb +7 -2
  44. data/lib/perfmonger/command/stat.rb +2 -2
  45. data/lib/perfmonger/command/stat_option.rb +1 -1
  46. data/lib/perfmonger/command/summary.rb +11 -326
  47. data/lib/perfmonger/version.rb +1 -3
  48. data/lib/perfmonger.rb +3 -0
  49. data/misc/_perfmonger +128 -0
  50. data/misc/perfmonger-completion.bash +49 -0
  51. data/perfmonger.gemspec +6 -5
  52. data/spec/data/busy100.pgr +0 -0
  53. data/spec/fingerprint_spec.rb +35 -0
  54. data/spec/live_spec.rb +25 -0
  55. data/spec/perfmonger_spec.rb +37 -0
  56. data/spec/play_spec.rb +21 -0
  57. data/spec/plot_spec.rb +42 -0
  58. data/spec/record_spec.rb +15 -0
  59. data/spec/spec_helper.rb +33 -0
  60. data/spec/stat_spec.rb +15 -0
  61. data/spec/summary_spec.rb +51 -0
  62. data/spec/support/aruba.rb +11 -0
  63. data/wercker.yml +59 -0
  64. metadata +117 -45
  65. data/ext/perfmonger/extconf.rb +0 -19
  66. data/ext/perfmonger/perfmonger.h +0 -58
  67. data/ext/perfmonger/perfmonger_record.c +0 -754
  68. data/ext/perfmonger/sysstat/common.c +0 -627
  69. data/ext/perfmonger/sysstat/common.h +0 -207
  70. data/ext/perfmonger/sysstat/ioconf.c +0 -515
  71. data/ext/perfmonger/sysstat/ioconf.h +0 -84
  72. data/ext/perfmonger/sysstat/iostat.c +0 -1100
  73. data/ext/perfmonger/sysstat/iostat.h +0 -121
  74. data/ext/perfmonger/sysstat/libsysstat.h +0 -19
  75. data/ext/perfmonger/sysstat/mpstat.c +0 -953
  76. data/ext/perfmonger/sysstat/mpstat.h +0 -79
  77. data/ext/perfmonger/sysstat/rd_stats.c +0 -2388
  78. data/ext/perfmonger/sysstat/rd_stats.h +0 -651
  79. data/ext/perfmonger/sysstat/sysconfig.h +0 -13
  80. data/test/run-test.sh +0 -39
  81. data/test/spec/bin_spec.rb +0 -37
  82. data/test/spec/data/2devices.expected +0 -42
  83. data/test/spec/data/2devices.output +0 -42
  84. data/test/spec/spec_helper.rb +0 -20
  85. data/test/spec/summary_spec.rb +0 -193
  86. data/test/test-perfmonger.c +0 -145
  87. data/test/test.h +0 -9
@@ -0,0 +1,56 @@
1
+
2
+ require 'optparse'
3
+ require 'json'
4
+
5
+ module PerfMonger
6
+ module Command
7
+
8
+ class PlayCommand < BaseCommand
9
+ register_command 'play', "Play a perfmonger log file in JSON"
10
+
11
+ def initialize
12
+ @parser = OptionParser.new
13
+ @parser.banner = <<EOS
14
+ Usage: perfmonger play [options] LOG_FILE
15
+
16
+ Options:
17
+ EOS
18
+
19
+ end
20
+
21
+ def parse_args(argv)
22
+ @parser.parse!(argv)
23
+
24
+ if argv.size == 0
25
+ puts("ERROR: PerfMonger log file is required")
26
+ puts(@parser.help)
27
+ exit(false)
28
+ end
29
+
30
+ @logfile = argv.shift
31
+ if ! File.exists?(@logfile)
32
+ puts("ERROR: No such file: #{@logfile}")
33
+ puts(@parser.help)
34
+ exit(false)
35
+ end
36
+ end
37
+
38
+ def run(argv)
39
+ parse_args(argv)
40
+
41
+ @player_bin = ::PerfMonger::Command::CoreFinder.player()
42
+
43
+ if ! @player_bin
44
+ puts("[ERROR] no executable binary found.")
45
+ exit(false)
46
+ end
47
+
48
+ cmd = [@player_bin]
49
+ cmd << @logfile
50
+
51
+ Process.exec(*cmd)
52
+ end
53
+ end
54
+
55
+ end # module Command
56
+ end # module PerfMonger
@@ -93,19 +93,27 @@ EOS
93
93
  exit(false)
94
94
  end
95
95
 
96
- unless system('gnuplot -e "set terminal"|grep pdfcairo >/dev/null 2>&1')
96
+ unless system('gnuplot -e "set terminal" < /dev/null 2>&1 | grep pdfcairo >/dev/null 2>&1')
97
97
  puts("ERROR: pdfcairo is not supported by installed gnuplot")
98
98
  puts("ERROR: PerfMonger requires pdfcairo-supported gnuplot")
99
99
  puts(@parser.help)
100
100
  exit(false)
101
101
  end
102
102
 
103
- plot_ioinfo()
104
- plot_cpuinfo()
103
+ player_bin = ::PerfMonger::Command::CoreFinder.player()
104
+
105
+ tmpfile = Tempfile.new("jsondata")
106
+ IO.popen([player_bin, @data_file], "r").each_line do |line|
107
+ tmpfile.print(line)
108
+ end
109
+ tmpfile.flush()
110
+
111
+ plot_ioinfo(tmpfile.path)
112
+ plot_cpuinfo(tmpfile.path)
105
113
  end
106
114
 
107
115
  private
108
- def plot_ioinfo()
116
+ def plot_ioinfo(json_file)
109
117
  iops_pdf_filename = @output_prefix + 'iops.pdf'
110
118
  transfer_pdf_filename = @output_prefix + 'transfer.pdf'
111
119
  gp_filename = @output_prefix + 'io.gp'
@@ -126,20 +134,20 @@ EOS
126
134
  start_time = nil
127
135
  devices = nil
128
136
 
129
- File.open(@data_file).each_line do |line|
137
+ File.open(json_file).each_line do |line|
130
138
  record = JSON.parse(line)
131
139
  time = record["time"]
132
- ioinfo = record["ioinfo"]
133
- return unless ioinfo
140
+ diskinfo = record["disk"]
141
+ return unless diskinfo
134
142
 
135
143
  start_time ||= time
136
- devices ||= ioinfo["devices"]
144
+ devices ||= diskinfo["devices"]
137
145
 
138
146
  datafile.puts([time - start_time,
139
147
  devices.map{|device|
140
- [ioinfo[device]["riops"], ioinfo[device]["wiops"],
141
- ioinfo[device]["rsecps"] * 512 / 1024 / 1024, # in MB/s
142
- ioinfo[device]["wsecps"] * 512 / 1024 / 1024, # in MB/s
148
+ [diskinfo[device]["riops"], diskinfo[device]["wiops"],
149
+ diskinfo[device]["rkbyteps"] * 512 / 1024 / 1024, # in MB/s
150
+ diskinfo[device]["wkbyteps"] * 512 / 1024 / 1024, # in MB/s
143
151
  ]
144
152
  }].flatten.map(&:to_s).join("\t"))
145
153
  end
@@ -214,7 +222,7 @@ EOS
214
222
  end # mktempdir
215
223
  end # def
216
224
 
217
- def plot_cpuinfo()
225
+ def plot_cpuinfo(json_file)
218
226
  pdf_filename = @output_prefix + 'cpu.pdf'
219
227
  gp_filename = @output_prefix + 'cpu.gp'
220
228
  dat_filename = @output_prefix + 'cpu.dat'
@@ -243,21 +251,21 @@ EOS
243
251
  devices = nil
244
252
  nr_cpu = nil
245
253
 
246
- File.open(@data_file).each_line do |line|
254
+ File.open(json_file).each_line do |line|
247
255
  record = JSON.parse(line)
248
256
 
249
257
  time = record["time"]
250
- cpuinfo = record["cpuinfo"]
258
+ cpuinfo = record["cpu"]
251
259
  return unless cpuinfo
252
- nr_cpu = cpuinfo['nr_cpu']
260
+ nr_cpu = cpuinfo['num_core']
253
261
 
254
- cores = cpuinfo['cpus']
262
+ cores = cpuinfo['cores']
255
263
 
256
264
  start_time ||= time
257
265
  end_time = [end_time, time].max
258
266
 
259
267
  datafile.puts([time - start_time,
260
- %w|usr nice sys iowait irq soft steal guest idle|.map do |key|
268
+ %w|usr nice sys iowait hardirq softirq steal guest idle|.map do |key|
261
269
  cores.map{|core| core[key]}.inject(&:+)
262
270
  end].flatten.map(&:to_s).join("\t"))
263
271
  end
@@ -266,7 +274,7 @@ EOS
266
274
  col_idx = 2
267
275
  columns = []
268
276
  plot_stmt_list = []
269
- %w|%usr %nice %sys %iowait %irq %soft %steal %guest|.each do |key|
277
+ %w|%usr %nice %sys %iowait %hardirq %softirq %steal %guest|.each do |key|
270
278
  columns << col_idx
271
279
  plot_stmt = "\"#{datafile.path}\" usi 1:(#{columns.map{|i| "$#{i}"}.join("+")}) with filledcurve x1 lw 0 lc #{col_idx - 1} title \"#{key}\""
272
280
  plot_stmt_list << plot_stmt
@@ -321,15 +329,15 @@ EOS
321
329
  legend_height = 0.04
322
330
  nr_cpu.times do |cpu_idx|
323
331
  all_datafile.puts("# cpu #{cpu_idx}")
324
- File.open(@data_file).each_line do |line|
332
+ File.open(json_file).each_line do |line|
325
333
  record = JSON.parse(line)
326
334
  time = record["time"]
327
- cpurec = record["cpuinfo"]["cpus"][cpu_idx]
335
+ cpurec = record["cpu"]["cores"][cpu_idx]
328
336
  all_datafile.puts([time - start_time,
329
337
  cpurec["usr"] + cpurec["nice"],
330
338
  cpurec["sys"],
331
- cpurec["irq"],
332
- cpurec["soft"],
339
+ cpurec["hardirq"],
340
+ cpurec["softirq"],
333
341
  cpurec["steal"] + cpurec["guest"],
334
342
  cpurec["iowait"]].map(&:to_s).join("\t"))
335
343
  end
@@ -1,8 +1,7 @@
1
-
2
1
  require 'optparse'
3
- require 'json'
4
2
  require 'tempfile'
5
3
  require 'tmpdir'
4
+ require 'json'
6
5
 
7
6
  module PerfMonger
8
7
  module Command
@@ -24,6 +23,8 @@ private
24
23
  def exec_record_cmd()
25
24
  cmd = @option.make_command
26
25
 
26
+ $stdout.puts("[recording to #{@option.logfile}]")
27
+
27
28
  Process.exec(*cmd)
28
29
  end
29
30
  end
@@ -7,11 +7,11 @@ class RecordOption
7
7
  attr_reader :interval
8
8
  attr_reader :verbose
9
9
  attr_reader :report_cpu
10
- attr_reader :report_io
11
- attr_reader :report_ctx_switch
10
+ attr_reader :no_disk
12
11
  attr_reader :logfile
13
12
 
14
13
  attr_reader :parser
14
+ attr_accessor :record_bin
15
15
 
16
16
  def self.parse(argv)
17
17
  option = self.new
@@ -23,88 +23,73 @@ class RecordOption
23
23
  def parse(argv)
24
24
  argv = @parser.parse(argv)
25
25
 
26
- if ! @report_io && ! @report_ctx_switch && ! @report_cpu
27
- @report_cpu = true
28
- @report_io = true
29
- @all_devices = true
30
- end
26
+ @no_cpu = false
27
+ @no_disk = false
28
+ @all_devices = true
31
29
 
32
30
  argv
33
31
  end
34
32
 
35
33
  def make_command
36
- # try to search perfmonger-record in build environment
37
- # then search installed directory
38
- record_bin = [File.expand_path("../../perfmonger_record", __FILE__),
39
- File.expand_path("lib/perfmonger/perfmonger_record", PerfMonger::ROOTDIR),
40
- File.expand_path("ext/perfmonger/perfmonger_record", PerfMonger::ROOTDIR)].find do |bin|
41
- File.executable?(bin)
42
- end
34
+ @recorder_bin = ::PerfMonger::Command::CoreFinder.recorder()
43
35
 
44
- if record_bin == nil || ! File.executable?(record_bin)
45
- puts("ERROR: perfmonger-record(1) not found!")
36
+ if ! @recorder_bin
37
+ puts("ERROR: no executable binaries")
46
38
  exit(false)
47
39
  end
48
40
 
49
- cmd = [record_bin]
50
- cmd << '-i'
51
- cmd << @interval.to_s
52
- if @interval_backoff
53
- cmd << '-b'
41
+ cmd = [@recorder_bin]
42
+ cmd << sprintf("-interval=%.1fms", @interval * 1000)
43
+ if ! @interval_backoff
44
+ cmd << "-no-interval-backoff"
54
45
  end
55
46
  if @start_delay > 0
56
- cmd << '-s'
57
- cmd << @start_delay.to_s
47
+ cmd << "-start-delay"
48
+ cmd << "#{@start_delay*1000}ms"
58
49
  end
59
50
  if @timeout
60
- cmd << '-t'
61
- cmd << @timeout.to_s
51
+ cmd << "-timeout"
52
+ cmd << "#{@timeout*1000}ms"
53
+ end
54
+ if @no_cpu
55
+ cmd << "-no-cpu"
56
+ end
57
+ if @no_disk
58
+ cmd << "-no-disk"
62
59
  end
63
- cmd << '-C' if @report_cpu
64
- cmd << '-S' if @report_ctx_switch
65
- cmd << '-l' if @logfile != STDOUT
66
- cmd << @logfile if @logfile != STDOUT
67
- if @report_io
68
- if @all_devices
69
- cmd << '-D'
70
- else
71
- @devices.each do |device|
72
- cmd << '-d'
73
- cmd << device
74
- end
75
- end
60
+ if @devices.size > 0
61
+ cmd << "-disks"
62
+ cmd << @devices.join(",")
76
63
  end
77
- cmd << '-v' if @verbose
64
+
65
+ # TODO: implement device filter
66
+
67
+ cmd << "-output"
68
+ cmd << @logfile
69
+
70
+ raise NotImplementedError if @verbose
78
71
 
79
72
  cmd
80
73
  end
81
74
 
82
75
  private
83
76
  def initialize
84
- @devices = []
85
- @all_devices = false
86
77
  @interval = 1.0 # in second
87
78
  @interval_backoff = true
88
79
  @start_delay = 0.0 # in second
89
80
  @timeout = nil # in second, or nil (= no timeout)
90
81
  @verbose = false
91
- @report_cpu = false
92
- @report_io = false
93
- @report_ctx_switch = false
94
- @logfile = STDOUT
82
+ @no_cpu = false
83
+ @no_disk = false
84
+ @devices = []
85
+ @logfile = "perfmonger.pgr"
95
86
 
96
87
  @parser = OptionParser.new
97
88
 
98
- @parser.on('-d', '--device DEVICE',
89
+ @parser.on('-d', '--disk DEVICE',
99
90
  'Device name to be monitored (e.g. sda, sdb, md0, dm-1).') do |device|
100
91
  @devices.push(device)
101
- @report_io = true
102
- end
103
-
104
- @parser.on('-D', '--all-devices',
105
- 'Monitor all block devices.') do
106
- @all_devices = true
107
- @report_io = true
92
+ @no_disk = false
108
93
  end
109
94
 
110
95
  @parser.on('-i', '--interval SEC',
@@ -127,12 +112,8 @@ class RecordOption
127
112
  @timeout = Float(timeout)
128
113
  end
129
114
 
130
- @parser.on('-C', '--cpu', 'Report CPU usage.') do
131
- @report_cpu = true
132
- end
133
-
134
- @parser.on('-S', '--context-switch', 'Report context switches per sec.') do
135
- @report_ctx_switch = true
115
+ @parser.on('--no-cpu', 'Suppress recording CPU usage.') do
116
+ @no_cpu = true
136
117
  end
137
118
 
138
119
  @parser.on('-l', '--logfile FILE') do |file|
@@ -70,12 +70,12 @@ EOS
70
70
  @hostname = hostname
71
71
  end
72
72
 
73
- @parser.on('--http-hostname NAME',
73
+ @parser.on('--http-host NAME',
74
74
  "Host name for HTTP server URL. If not specified, value of '--hostname' option is used.") do |hostname|
75
75
  @http_hostname = hostname
76
76
  end
77
77
 
78
- @parser.on('--http-port PORT', 'HTTP server port to listen.') do |port|
78
+ @parser.on('--port PORT', 'HTTP server port to listen.') do |port|
79
79
  if ! port =~ /\A\d+\Z/
80
80
  puts("ERROR: invalid port number value: #{port}")
81
81
  puts(@parser.help)
@@ -84,6 +84,11 @@ EOS
84
84
  @http_port = port.to_i
85
85
  end
86
86
 
87
+ @parser.on('-h', '--help', 'Show this help.') do
88
+ puts @parser.help
89
+ exit(false)
90
+ end
91
+
87
92
  # @parser.on('--tcp-port PORT', 'TCP data server port to listen.') do |port|
88
93
  # if ! port =~ /\A\d+\Z/
89
94
  # puts("ERROR: invalid port number value: #{port}")
@@ -43,15 +43,15 @@ class StatCommand < BaseCommand
43
43
  system(*@argv)
44
44
  end
45
45
  end
46
+
46
47
  Process.wait(command_pid)
48
+
47
49
  ensure
48
50
  @end_time = Time.now
49
51
  Process.kill(:INT, record_pid)
50
52
  Process.wait(record_pid)
51
53
  end
52
54
 
53
- puts("")
54
- printf("Execution time: %.4f\n", @end_time - @start_time)
55
55
  summary_command = SummaryCommand.new.run([@option.logfile], @argv.join(" "))
56
56
  end
57
57
  end
@@ -16,7 +16,7 @@ Run a command and gather performance information during its execution.
16
16
  Options:
17
17
  EOS
18
18
 
19
- @logfile = './perfmonger.log'
19
+ @logfile = './perfmonger.pgr'
20
20
  @json = false
21
21
 
22
22
  @parser.on('--json', "Output summary in JSON") do