perfmonger 0.6.1
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 +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
|