perfmonger 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.dir-locals.el +2 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +12 -0
- data/COPYING +674 -0
- data/Gemfile +5 -0
- data/HOWTO.md +15 -0
- data/NEWS +115 -0
- data/README.md +61 -0
- data/Rakefile +8 -0
- data/bin/perfmonger +6 -0
- data/data/NOTICE +8 -0
- data/data/Twitter_Bootstrap_LICENSE.txt +176 -0
- data/data/assets/css/bootstrap-responsive.css +1109 -0
- data/data/assets/css/bootstrap.css +6167 -0
- data/data/assets/css/perfmonger.css +17 -0
- data/data/assets/dashboard.erb +319 -0
- data/data/assets/img/glyphicons-halflings-white.png +0 -0
- data/data/assets/img/glyphicons-halflings.png +0 -0
- data/data/assets/js/bootstrap.js +2280 -0
- data/data/assets/js/bootstrap.min.js +6 -0
- data/data/assets/js/canvasjs.js +9042 -0
- data/data/assets/js/canvasjs.min.js +271 -0
- data/data/sysstat.ioconf +268 -0
- data/ext/perfmonger/extconf.rb +19 -0
- data/ext/perfmonger/perfmonger.h +58 -0
- data/ext/perfmonger/perfmonger_record.c +754 -0
- data/ext/perfmonger/sysstat/common.c +627 -0
- data/ext/perfmonger/sysstat/common.h +207 -0
- data/ext/perfmonger/sysstat/ioconf.c +515 -0
- data/ext/perfmonger/sysstat/ioconf.h +84 -0
- data/ext/perfmonger/sysstat/iostat.c +1100 -0
- data/ext/perfmonger/sysstat/iostat.h +121 -0
- data/ext/perfmonger/sysstat/libsysstat.h +19 -0
- data/ext/perfmonger/sysstat/mpstat.c +953 -0
- data/ext/perfmonger/sysstat/mpstat.h +79 -0
- data/ext/perfmonger/sysstat/rd_stats.c +2388 -0
- data/ext/perfmonger/sysstat/rd_stats.h +651 -0
- data/ext/perfmonger/sysstat/sysconfig.h +13 -0
- data/lib/perfmonger/cli.rb +115 -0
- data/lib/perfmonger/command/base_command.rb +39 -0
- data/lib/perfmonger/command/fingerprint.rb +453 -0
- data/lib/perfmonger/command/plot.rb +429 -0
- data/lib/perfmonger/command/record.rb +32 -0
- data/lib/perfmonger/command/record_option.rb +149 -0
- data/lib/perfmonger/command/server.rb +294 -0
- data/lib/perfmonger/command/stat.rb +60 -0
- data/lib/perfmonger/command/stat_option.rb +29 -0
- data/lib/perfmonger/command/summary.rb +402 -0
- data/lib/perfmonger/config.rb +6 -0
- data/lib/perfmonger/version.rb +5 -0
- data/lib/perfmonger.rb +12 -0
- data/misc/release-howto.txt +17 -0
- data/misc/sample-cpu.png +0 -0
- data/misc/sample-read-iops.png +0 -0
- data/perfmonger.gemspec +44 -0
- data/test/run-test.sh +39 -0
- data/test/spec/bin_spec.rb +37 -0
- data/test/spec/data/2devices.expected +42 -0
- data/test/spec/data/2devices.output +42 -0
- data/test/spec/spec_helper.rb +20 -0
- data/test/spec/summary_spec.rb +193 -0
- data/test/test-perfmonger.c +145 -0
- data/test/test.h +9 -0
- metadata +154 -0
@@ -0,0 +1,402 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module PerfMonger
|
6
|
+
module Command
|
7
|
+
|
8
|
+
class SummaryCommand < BaseCommand
|
9
|
+
register_command 'summary', "Show a summary of a perfmonger log file"
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@parser = OptionParser.new
|
13
|
+
@parser.banner = <<EOS
|
14
|
+
Usage: perfmonger summary [options] LOG_FILE
|
15
|
+
|
16
|
+
Options:
|
17
|
+
EOS
|
18
|
+
|
19
|
+
@json = false
|
20
|
+
@pager = nil
|
21
|
+
|
22
|
+
@parser.on('--json', "Output summary in JSON") do
|
23
|
+
@json = true
|
24
|
+
end
|
25
|
+
|
26
|
+
@parser.on('-p', '--pager [PAGER]', "Use pager to see summary output.") do |pager|
|
27
|
+
if pager.nil?
|
28
|
+
if ENV['PAGER'].nil?
|
29
|
+
puts("ERROR: No pager is available.")
|
30
|
+
puts("ERROR: Please set PAGER or give pager name to --pager option.")
|
31
|
+
puts(@parser.help)
|
32
|
+
exit(false)
|
33
|
+
else
|
34
|
+
@pager = ENV['PAGER']
|
35
|
+
end
|
36
|
+
else
|
37
|
+
@pager = pager
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_args(argv)
|
43
|
+
@parser.parse!(argv)
|
44
|
+
|
45
|
+
if argv.size == 0
|
46
|
+
puts("ERROR: PerfMonger log file is required")
|
47
|
+
puts(@parser.help)
|
48
|
+
exit(false)
|
49
|
+
end
|
50
|
+
|
51
|
+
@logfile = argv.shift
|
52
|
+
if ! File.exists?(@logfile)
|
53
|
+
puts("ERROR: No such file: #{@logfile}")
|
54
|
+
puts(@parser.help)
|
55
|
+
exit(false)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def run(argv, summary_title = nil)
|
60
|
+
parse_args(argv)
|
61
|
+
|
62
|
+
summary_title ||= @logfile
|
63
|
+
|
64
|
+
show_summary(@logfile, summary_title)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def read_logfile(logfile)
|
69
|
+
File.read(logfile).lines.map do |line|
|
70
|
+
begin
|
71
|
+
JSON.parse(line)
|
72
|
+
rescue JSON::ParserError => err
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end.compact
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_accumulation(records)
|
79
|
+
unless records.all?{|record| record.has_key?("ioinfo")}
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
unless records.size > 1
|
83
|
+
return nil
|
84
|
+
end
|
85
|
+
|
86
|
+
accum = Hash.new
|
87
|
+
|
88
|
+
devices = records.first["ioinfo"]["devices"]
|
89
|
+
accum["ioinfo"] = Hash.new
|
90
|
+
|
91
|
+
devices.each do |device|
|
92
|
+
read_requests = 0
|
93
|
+
read_bytes = 0
|
94
|
+
write_requests = 0
|
95
|
+
write_bytes = 0
|
96
|
+
|
97
|
+
(1..(records.size - 1)).each do |idx|
|
98
|
+
last_record = records[idx - 1]
|
99
|
+
record = records[idx]
|
100
|
+
dt = record["time"] - last_record["time"]
|
101
|
+
|
102
|
+
read_requests += record["ioinfo"][device]["riops"] * dt
|
103
|
+
write_requests += record["ioinfo"][device]["wiops"] * dt
|
104
|
+
read_bytes += record["ioinfo"][device]["rsecps"] * 512 * dt
|
105
|
+
write_bytes += record["ioinfo"][device]["wsecps"] * 512 * dt
|
106
|
+
end
|
107
|
+
|
108
|
+
accum["ioinfo"][device] = Hash.new
|
109
|
+
accum["ioinfo"][device]["read_requests"] = read_requests
|
110
|
+
accum["ioinfo"][device]["read_bytes"] = read_bytes
|
111
|
+
accum["ioinfo"][device]["write_requests"] = write_requests
|
112
|
+
accum["ioinfo"][device]["write_bytes"] = write_bytes
|
113
|
+
end
|
114
|
+
|
115
|
+
accum
|
116
|
+
end
|
117
|
+
|
118
|
+
def make_summary(records)
|
119
|
+
if records.empty?
|
120
|
+
return nil
|
121
|
+
elsif records.size == 1
|
122
|
+
return records.first
|
123
|
+
end
|
124
|
+
|
125
|
+
# setup getters and setters all attributes for avg. calculation
|
126
|
+
# getter.call(record) returns value
|
127
|
+
# setter.call(record, value) set value
|
128
|
+
# getters and setters include attribute info as a closure
|
129
|
+
getters = []
|
130
|
+
setters = []
|
131
|
+
|
132
|
+
if records.first.include?("ioinfo")
|
133
|
+
records.first["ioinfo"]["devices"].each do |device|
|
134
|
+
records.first["ioinfo"][device].keys.each do |attr|
|
135
|
+
getters << lambda do |record|
|
136
|
+
record["ioinfo"][device][attr]
|
137
|
+
end
|
138
|
+
setters << lambda do |record, value|
|
139
|
+
record["ioinfo"][device][attr] = value
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
records.first["ioinfo"]["total"].keys.each do |attr|
|
145
|
+
getters << lambda do |record|
|
146
|
+
record["ioinfo"]["total"][attr]
|
147
|
+
end
|
148
|
+
setters << lambda do |record, value|
|
149
|
+
record["ioinfo"]["total"][attr] = value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
if records.first.include?("cpuinfo")
|
155
|
+
records.first["cpuinfo"]["all"].keys.each do |attr|
|
156
|
+
getters << lambda do |record|
|
157
|
+
record["cpuinfo"]["all"][attr]
|
158
|
+
end
|
159
|
+
setters << lambda do |record, value|
|
160
|
+
record["cpuinfo"]["all"][attr] = value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
records.first["cpuinfo"]["nr_cpu"].times do |cpu_idx|
|
165
|
+
records.first["cpuinfo"]["cpus"][cpu_idx].keys.each do |attr|
|
166
|
+
getters << lambda do |record|
|
167
|
+
record["cpuinfo"]["cpus"][cpu_idx][attr]
|
168
|
+
end
|
169
|
+
setters << lambda do |record, value|
|
170
|
+
record["cpuinfo"]["cpus"][cpu_idx][attr] = value
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
avg_record = Marshal.load(Marshal.dump(records.first)); # deep copy
|
177
|
+
|
178
|
+
setters.each do |setter|
|
179
|
+
setter.call(avg_record, 0.0)
|
180
|
+
end
|
181
|
+
|
182
|
+
(1..(records.size - 1)).each do |idx|
|
183
|
+
record = records[idx]
|
184
|
+
|
185
|
+
last_t = records[idx - 1]["time"]
|
186
|
+
t = record["time"]
|
187
|
+
|
188
|
+
getters.size.times do |_etters_idx|
|
189
|
+
getter = getters[_etters_idx]
|
190
|
+
setter = setters[_etters_idx]
|
191
|
+
|
192
|
+
setter.call(avg_record,
|
193
|
+
getter.call(avg_record) + getter.call(record) * (t - last_t))
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
getters.size.times do |_etters_idx|
|
198
|
+
getter = getters[_etters_idx]
|
199
|
+
setter = setters[_etters_idx]
|
200
|
+
|
201
|
+
setter.call(avg_record,
|
202
|
+
getter.call(avg_record) / (records[-1]["time"] - records[0]["time"]))
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
# r_await/w_await need special handling
|
207
|
+
if records.first.include?("ioinfo")
|
208
|
+
records.first["ioinfo"]["devices"].each do |device|
|
209
|
+
accum_r_io_time = 0.0
|
210
|
+
accum_w_io_time = 0.0
|
211
|
+
r_io_count = 0
|
212
|
+
w_io_count = 0
|
213
|
+
|
214
|
+
(records.size - 1).times do |idx|
|
215
|
+
rec0 = records[idx]
|
216
|
+
rec1 = records[idx + 1]
|
217
|
+
dev_ioinfo = rec1["ioinfo"][device]
|
218
|
+
dt = rec1["time"] - rec0["time"]
|
219
|
+
|
220
|
+
accum_r_io_time += dev_ioinfo["r_await"] * dev_ioinfo["riops"] * dt
|
221
|
+
accum_w_io_time += dev_ioinfo["w_await"] * dev_ioinfo["wiops"] * dt
|
222
|
+
r_io_count += dev_ioinfo["riops"] * dt
|
223
|
+
w_io_count += dev_ioinfo["wiops"] * dt
|
224
|
+
end
|
225
|
+
|
226
|
+
if r_io_count > 0
|
227
|
+
avg_record["ioinfo"][device]["r_await"] = accum_r_io_time / r_io_count
|
228
|
+
else
|
229
|
+
avg_record["ioinfo"][device]["r_await"] = 0.0
|
230
|
+
end
|
231
|
+
|
232
|
+
if w_io_count > 0
|
233
|
+
avg_record["ioinfo"][device]["w_await"] = accum_w_io_time / w_io_count
|
234
|
+
else
|
235
|
+
avg_record["ioinfo"][device]["w_await"] = 0.0
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
avg_record
|
241
|
+
end
|
242
|
+
|
243
|
+
def show_summary(logfile, summary_title)
|
244
|
+
records = read_logfile(logfile)
|
245
|
+
summary = make_summary(records)
|
246
|
+
accum = make_accumulation(records)
|
247
|
+
|
248
|
+
duration = records.last["time"] - records.first["time"]
|
249
|
+
|
250
|
+
if @json
|
251
|
+
output = Hash.new
|
252
|
+
|
253
|
+
output["duration"] = duration
|
254
|
+
if summary
|
255
|
+
if summary["cpuinfo"]
|
256
|
+
output["cpuinfo"] = summary["cpuinfo"]
|
257
|
+
end
|
258
|
+
|
259
|
+
if summary['ioinfo']
|
260
|
+
output["ioinfo"] = summary["ioinfo"]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
puts output.to_json
|
265
|
+
else
|
266
|
+
if $stdout.tty? && @pager
|
267
|
+
output_file = IO.popen(@pager, "w")
|
268
|
+
else
|
269
|
+
output_file = $stdout
|
270
|
+
end
|
271
|
+
|
272
|
+
output_file.puts("")
|
273
|
+
output_file.puts("== Performance summary of '#{summary_title}' ==")
|
274
|
+
output_file.puts("")
|
275
|
+
duration_str = sprintf("%.2f", duration)
|
276
|
+
output_file.puts("record duration: #{duration_str} sec")
|
277
|
+
|
278
|
+
if summary.nil?
|
279
|
+
output_file.puts("")
|
280
|
+
output_file.puts("No performance info was collected.")
|
281
|
+
output_file.puts("This is because command execution time was too short, or something went wrong.")
|
282
|
+
end
|
283
|
+
|
284
|
+
if summary && summary["cpuinfo"]
|
285
|
+
nr_cpu = records.first["cpuinfo"]["nr_cpu"]
|
286
|
+
|
287
|
+
usr, sys, iowait, irq, soft, idle =
|
288
|
+
[summary['cpuinfo']['all']['usr'] + summary['cpuinfo']['all']['nice'],
|
289
|
+
summary['cpuinfo']['all']['sys'],
|
290
|
+
summary['cpuinfo']['all']['iowait'],
|
291
|
+
summary['cpuinfo']['all']['irq'],
|
292
|
+
summary['cpuinfo']['all']['soft'],
|
293
|
+
summary['cpuinfo']['all']['idle']].map do |val|
|
294
|
+
val * nr_cpu
|
295
|
+
end
|
296
|
+
|
297
|
+
other = [100.0 - (usr + sys + iowait + irq + soft + idle), 0.0].max * nr_cpu
|
298
|
+
|
299
|
+
usr_str, sys_str, iowait_str, irq_str, soft_str, other_str, idle_str =
|
300
|
+
[usr, sys, iowait, irq, soft, other, idle].map do |value|
|
301
|
+
sprintf("%.2f", value)
|
302
|
+
end
|
303
|
+
|
304
|
+
total_non_idle_str = sprintf("%.2f", usr + sys + irq + soft + other)
|
305
|
+
total_idle_str = sprintf("%.2f", iowait + idle)
|
306
|
+
|
307
|
+
output_file.puts("")
|
308
|
+
output_file.puts <<EOS
|
309
|
+
* Average CPU usage (MAX: #{100 * nr_cpu} %)
|
310
|
+
* Non idle portion: #{total_non_idle_str}
|
311
|
+
%usr: #{usr_str}
|
312
|
+
%sys: #{sys_str}
|
313
|
+
%irq: #{irq_str}
|
314
|
+
%soft: #{soft_str}
|
315
|
+
%other: #{other_str}
|
316
|
+
* Idle portion: #{total_idle_str}
|
317
|
+
%iowait: #{iowait_str}
|
318
|
+
%idle: #{idle_str}
|
319
|
+
EOS
|
320
|
+
end
|
321
|
+
|
322
|
+
if summary && summary['ioinfo']
|
323
|
+
total_r_iops, total_w_iops, total_r_sec, total_w_sec = [0.0] * 4
|
324
|
+
|
325
|
+
summary['ioinfo']['devices'].each do |device|
|
326
|
+
r_iops, w_iops, r_sec, w_sec, r_await, w_await =
|
327
|
+
[summary['ioinfo'][device]['riops'],
|
328
|
+
summary['ioinfo'][device]['wiops'],
|
329
|
+
summary['ioinfo'][device]['rsecps'] * 512 / 1024.0 / 1024.0,
|
330
|
+
summary['ioinfo'][device]['wsecps'] * 512 / 1024.0 / 1024.0,
|
331
|
+
summary['ioinfo'][device]['r_await'],
|
332
|
+
summary['ioinfo'][device]['w_await']]
|
333
|
+
|
334
|
+
total_r_iops += r_iops
|
335
|
+
total_w_iops += w_iops
|
336
|
+
total_r_sec += r_sec
|
337
|
+
total_w_sec += w_sec
|
338
|
+
|
339
|
+
r_iops_str, w_iops_str, r_sec_str, w_sec_str = [r_iops, w_iops, r_sec, w_sec].map do |value|
|
340
|
+
sprintf("%.2f", value)
|
341
|
+
end
|
342
|
+
|
343
|
+
r_await_str, w_await_str = [r_await, w_await].map do |await|
|
344
|
+
if await < 1.0
|
345
|
+
sprintf("%.1f usec", await * 1000)
|
346
|
+
else
|
347
|
+
sprintf("%.2f msec", await)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
total_r_bytes_str, total_w_bytes_str = ["read_bytes", "write_bytes"].map do |key|
|
352
|
+
bytes = accum["ioinfo"][device][key]
|
353
|
+
if bytes > 2**30
|
354
|
+
sprintf("%.2f GB", bytes / 2**30)
|
355
|
+
elsif bytes > 2**20
|
356
|
+
sprintf("%.2f MB", bytes / 2**20)
|
357
|
+
elsif bytes > 2**10
|
358
|
+
sprintf("%.2f KB", bytes / 2**10)
|
359
|
+
else
|
360
|
+
sprintf("%.2f bytes", bytes)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
output_file.puts("")
|
366
|
+
output_file.puts("* Average DEVICE usage: #{device}")
|
367
|
+
output_file.puts(" read IOPS: #{r_iops_str}")
|
368
|
+
output_file.puts(" write IOPS: #{w_iops_str}")
|
369
|
+
output_file.puts(" read throughput: #{r_sec_str} MB/s")
|
370
|
+
output_file.puts(" write throughput: #{w_sec_str} MB/s")
|
371
|
+
output_file.puts(" read latency: #{r_await_str}")
|
372
|
+
output_file.puts(" write latency: #{w_await_str}")
|
373
|
+
output_file.puts(" read amount: #{total_r_bytes_str}")
|
374
|
+
output_file.puts(" write amount: #{total_w_bytes_str}")
|
375
|
+
end
|
376
|
+
|
377
|
+
if summary['ioinfo']['devices'].size > 1
|
378
|
+
total_r_iops_str, total_w_iops_str, total_r_sec_str, total_w_sec_str =
|
379
|
+
[total_r_iops, total_w_iops, total_r_sec, total_w_sec].map do |value|
|
380
|
+
sprintf("%.2f", value)
|
381
|
+
end
|
382
|
+
|
383
|
+
output_file.puts("")
|
384
|
+
output_file.puts("* TOTAL DEVICE usage: #{summary['ioinfo']['devices'].join(', ')}")
|
385
|
+
output_file.puts(" read IOPS: #{total_r_iops_str}")
|
386
|
+
output_file.puts(" write IOPS: #{total_w_iops_str}")
|
387
|
+
output_file.puts(" read throughput: #{total_r_sec_str} MB/s")
|
388
|
+
output_file.puts(" write throughput: #{total_w_sec_str} MB/s")
|
389
|
+
end
|
390
|
+
|
391
|
+
output_file.puts("")
|
392
|
+
end
|
393
|
+
|
394
|
+
if output_file != $stdout
|
395
|
+
output_file.close
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
end # module Command
|
402
|
+
end # module PerfMonger
|
data/lib/perfmonger.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'perfmonger/version.rb'
|
2
|
+
require 'perfmonger/config.rb'
|
3
|
+
require 'perfmonger/cli.rb'
|
4
|
+
require 'perfmonger/command/base_command.rb'
|
5
|
+
require 'perfmonger/command/record.rb'
|
6
|
+
require 'perfmonger/command/record_option.rb'
|
7
|
+
require 'perfmonger/command/stat.rb'
|
8
|
+
require 'perfmonger/command/stat_option.rb'
|
9
|
+
require 'perfmonger/command/summary.rb'
|
10
|
+
require 'perfmonger/command/plot.rb'
|
11
|
+
require 'perfmonger/command/server.rb'
|
12
|
+
require 'perfmonger/command/fingerprint.rb'
|
data/misc/sample-cpu.png
ADDED
Binary file
|
Binary file
|
data/perfmonger.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
require 'perfmonger'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'perfmonger'
|
8
|
+
s.version = PerfMonger::VERSION
|
9
|
+
s.date = '2015-01-19'
|
10
|
+
s.summary = "yet anothor performance measurement/monitoring tool"
|
11
|
+
s.description = "yet anothor performance measurement/monitoring tool"
|
12
|
+
s.authors = ["Yuto HAYAMIZU"]
|
13
|
+
s.email = 'y.hayamizu@gmail.com'
|
14
|
+
s.homepage = 'http://github.com/hayamiz/perfmonger/'
|
15
|
+
s.license = 'GPL-2'
|
16
|
+
|
17
|
+
s.required_ruby_version = '>= 1.9.0'
|
18
|
+
|
19
|
+
s.add_development_dependency "rake"
|
20
|
+
s.add_development_dependency "rspec"
|
21
|
+
s.add_development_dependency "rake-compiler"
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
26
|
+
|
27
|
+
s.extensions << 'ext/perfmonger/extconf.rb'
|
28
|
+
|
29
|
+
s.post_install_message = <<EOS
|
30
|
+
|
31
|
+
============================================================
|
32
|
+
|
33
|
+
Thank you for installing perfmonger.
|
34
|
+
Try to start performance monitoring with:
|
35
|
+
|
36
|
+
perfmonger record
|
37
|
+
|
38
|
+
Enjoy.
|
39
|
+
|
40
|
+
============================================================
|
41
|
+
|
42
|
+
EOS
|
43
|
+
|
44
|
+
end
|
data/test/run-test.sh
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
export BASE_DIR="`dirname $0`"
|
4
|
+
export TEST_DIR="$(readlink -f $(dirname $0))"
|
5
|
+
top_dir="$BASE_DIR/.."
|
6
|
+
|
7
|
+
if test -z "$NO_MAKE"; then
|
8
|
+
make -C $top_dir > /dev/null || exit 1
|
9
|
+
fi
|
10
|
+
|
11
|
+
if test -z "$CUTTER"; then
|
12
|
+
CUTTER="`make -s -C $BASE_DIR echo-cutter`"
|
13
|
+
fi
|
14
|
+
|
15
|
+
if ! test -z "$DEBUG"; then
|
16
|
+
WRAPPER="gdb --args"
|
17
|
+
elif ! test -z "$MEMCHECK"; then
|
18
|
+
VGLOG=`mktemp`
|
19
|
+
WRAPPER="valgrind --leak-check=summary --track-origins=yes --log-file=${VGLOG}"
|
20
|
+
fi
|
21
|
+
|
22
|
+
# MOCK_SO=$(readlink -f $(find -name mock.so | head -n1))
|
23
|
+
# if ! test -z "$MOCK_SO"; then
|
24
|
+
# export LD_PRELOAD="$MOCK_SO"
|
25
|
+
# fi
|
26
|
+
|
27
|
+
$WRAPPER $CUTTER --keep-opening-modules -s $BASE_DIR "$@" $BASE_DIR
|
28
|
+
RET=$?
|
29
|
+
|
30
|
+
# show log file of valgrind
|
31
|
+
if ! test -z "$MEMCHECK"; then
|
32
|
+
if ! test -z "$PAGER"; then
|
33
|
+
$PAGER $VGLOG
|
34
|
+
else
|
35
|
+
cat $VGLOG
|
36
|
+
fi
|
37
|
+
fi
|
38
|
+
|
39
|
+
exit $RET
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe "PerfmongerCommand" do
|
5
|
+
before(:each) do
|
6
|
+
@perfmonger_command = File.expand_path('../../src/perfmonger', __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be an executable" do
|
10
|
+
File.executable?(@perfmonger_command).should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should print version number if --version specified' do
|
14
|
+
`#{@perfmonger_command} --version`.should include(PerfMonger::VERSION)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'stat subcommand' do
|
18
|
+
it 'should print "Execution time: XXX.XXX"' do
|
19
|
+
if File.exists?("/proc/diskstats")
|
20
|
+
`#{@perfmonger_command} stat -- sleep 1`.should match(/^Execution time: (\d+)\.(\d+)$/)
|
21
|
+
else
|
22
|
+
# do nothing
|
23
|
+
true.should be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'summary subcommand' do
|
29
|
+
it 'should print expected output with 2devices.log' do
|
30
|
+
File.open(data_file('2devices.output'), "w") do |f|
|
31
|
+
f.print(`#{@perfmonger_command} summary #{data_file('2devices.log')}`)
|
32
|
+
end
|
33
|
+
|
34
|
+
system("diff -u #{data_file('2devices.expected')} #{data_file('2devices.output')}").should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
== Performance summary of 'spec/data/2devices.log' ==
|
3
|
+
|
4
|
+
record duration: 1.00 sec
|
5
|
+
|
6
|
+
* Average CPU usage (MAX: 200 %)
|
7
|
+
* Non idle portion: 200.00
|
8
|
+
%usr: 0.00
|
9
|
+
%sys: 200.00
|
10
|
+
%irq: 0.00
|
11
|
+
%soft: 0.00
|
12
|
+
%other: 0.00
|
13
|
+
* Idle portion: 0.00
|
14
|
+
%iowait: 0.00
|
15
|
+
%idle: 0.00
|
16
|
+
|
17
|
+
* Average DEVICE usage: sda
|
18
|
+
read IOPS: 1.00
|
19
|
+
write IOPS: 0.00
|
20
|
+
read throughput: 0.00 MB/s
|
21
|
+
write throughput: 0.00 MB/s
|
22
|
+
read latency: 53.00 msec
|
23
|
+
write latency: 0.0 usec
|
24
|
+
read amount: 512.00 bytes
|
25
|
+
write amount: 0.00 bytes
|
26
|
+
|
27
|
+
* Average DEVICE usage: sdb
|
28
|
+
read IOPS: 0.00
|
29
|
+
write IOPS: 0.00
|
30
|
+
read throughput: 0.00 MB/s
|
31
|
+
write throughput: 0.00 MB/s
|
32
|
+
read latency: 0.0 usec
|
33
|
+
write latency: 0.0 usec
|
34
|
+
read amount: 0.00 bytes
|
35
|
+
write amount: 0.00 bytes
|
36
|
+
|
37
|
+
* TOTAL DEVICE usage: sda, sdb
|
38
|
+
read IOPS: 1.00
|
39
|
+
write IOPS: 0.00
|
40
|
+
read throughput: 0.00 MB/s
|
41
|
+
write throughput: 0.00 MB/s
|
42
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
== Performance summary of 'spec/data/2devices.log' ==
|
3
|
+
|
4
|
+
record duration: 1.00 sec
|
5
|
+
|
6
|
+
* Average CPU usage (MAX: 200 %)
|
7
|
+
* Non idle portion: 200.00
|
8
|
+
%usr: 0.00
|
9
|
+
%sys: 200.00
|
10
|
+
%irq: 0.00
|
11
|
+
%soft: 0.00
|
12
|
+
%other: 0.00
|
13
|
+
* Idle portion: 0.00
|
14
|
+
%iowait: 0.00
|
15
|
+
%idle: 0.00
|
16
|
+
|
17
|
+
* Average DEVICE usage: sda
|
18
|
+
read IOPS: 1.00
|
19
|
+
write IOPS: 0.00
|
20
|
+
read throughput: 0.00 MB/s
|
21
|
+
write throughput: 0.00 MB/s
|
22
|
+
read latency: 53.00 msec
|
23
|
+
write latency: 0.0 usec
|
24
|
+
read amount: 512.00 bytes
|
25
|
+
write amount: 0.00 bytes
|
26
|
+
|
27
|
+
* Average DEVICE usage: sdb
|
28
|
+
read IOPS: 0.00
|
29
|
+
write IOPS: 0.00
|
30
|
+
read throughput: 0.00 MB/s
|
31
|
+
write throughput: 0.00 MB/s
|
32
|
+
read latency: 0.0 usec
|
33
|
+
write latency: 0.0 usec
|
34
|
+
read amount: 0.00 bytes
|
35
|
+
write amount: 0.00 bytes
|
36
|
+
|
37
|
+
* TOTAL DEVICE usage: sda, sdb
|
38
|
+
read IOPS: 1.00
|
39
|
+
write IOPS: 0.00
|
40
|
+
read throughput: 0.00 MB/s
|
41
|
+
write throughput: 0.00 MB/s
|
42
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path('../../src/ruby', __FILE__)
|
4
|
+
|
5
|
+
TEST_DATA_DIR = File.expand_path('../data', __FILE__)
|
6
|
+
|
7
|
+
require 'perfmonger'
|
8
|
+
require 'tempfile'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
def data_file(rel_path)
|
12
|
+
from = Pathname.new(Dir.pwd)
|
13
|
+
path = Pathname.new(File.expand_path(rel_path, TEST_DATA_DIR))
|
14
|
+
|
15
|
+
path.relative_path_from(from).to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
# RSpec config here
|
20
|
+
end
|