perfmonger 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/NEWS +10 -2
- data/core/Makefile +14 -2
- data/core/build.sh +1 -1
- data/core/perfmonger-plot-formatter.go +325 -0
- data/core/subsystem/usage.go +13 -1
- data/lib/perfmonger.rb +1 -0
- data/lib/perfmonger/command/core.rb +4 -0
- data/lib/perfmonger/command/fingerprint.rb +48 -0
- data/lib/perfmonger/command/init-shell.rb +51 -0
- data/lib/perfmonger/command/plot.rb +241 -195
- data/lib/perfmonger/command/summary.rb +1 -1
- data/lib/perfmonger/version.rb +1 -1
- data/misc/{perfmonger-completion.bash → perfmonger.bash} +0 -0
- data/misc/{_perfmonger → perfmonger.zsh} +24 -8
- data/perfmonger.gemspec +0 -1
- data/spec/fingerprint_spec.rb +7 -7
- data/spec/live_spec.rb +5 -4
- data/spec/perfmonger_spec.rb +7 -7
- data/spec/play_spec.rb +4 -3
- data/spec/plot_spec.rb +18 -17
- data/spec/record_spec.rb +3 -3
- data/spec/stat_spec.rb +2 -2
- data/spec/summary_spec.rb +4 -4
- data/spec/support/aruba.rb +1 -2
- metadata +9 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6c4a036c08d2f75fc25b25da1b773e025461880
|
4
|
+
data.tar.gz: 87182b819268cdcd289ea1cfb89f8672cc307947
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcb1dd5f6086bff622f66dc7eb15a835604d0ce63fb2ea7b3d6ab022d3b5724384ca54ec38525c31984bc06dc08c87ca89cb21da7a62696bfaf665df6474d8a8
|
7
|
+
data.tar.gz: cb33cd2fe403d279687a6ad680fbda8af96c778b65b485f6f4b6024eac8e80c8fc6efd79e4e225f1a02fcbfa2f79cfe5061857716ff62fc3b7c4a1a828ca2568
|
data/.gitignore
CHANGED
data/NEWS
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
-
##
|
2
|
-
*
|
1
|
+
## 2016-12-12: PerfMonger 0.9.0
|
2
|
+
* New features
|
3
|
+
* [init-shell] subcommand: Added for loading shell completion function from rcfile
|
4
|
+
* zsh support
|
5
|
+
* bash support
|
6
|
+
* [fingerprint]
|
7
|
+
* log LVM, fdisk, and lsblk info
|
8
|
+
* [plot]
|
9
|
+
* Fast plot data formatting with new core implementation
|
10
|
+
* Improved shell completion
|
3
11
|
|
4
12
|
## 2016-11-22: PerfMonger 0.8.2
|
5
13
|
* Bug fixes
|
data/core/Makefile
CHANGED
@@ -20,6 +20,10 @@ all: build
|
|
20
20
|
go build -o $@ perfmonger-summarizer.go
|
21
21
|
|
22
22
|
|
23
|
+
../lib/exec/perfmonger-plot-formatter_linux_386: perfmonger-plot-formatter.go $(GOSRC)
|
24
|
+
go build -o $@ perfmonger-plot-formatter.go
|
25
|
+
|
26
|
+
|
23
27
|
../lib/exec/perfmonger-recorder_linux_amd64: perfmonger-recorder.go $(GOSRC)
|
24
28
|
go build -o $@ perfmonger-recorder.go
|
25
29
|
|
@@ -32,6 +36,10 @@ all: build
|
|
32
36
|
go build -o $@ perfmonger-summarizer.go
|
33
37
|
|
34
38
|
|
39
|
+
../lib/exec/perfmonger-plot-formatter_linux_amd64: perfmonger-plot-formatter.go $(GOSRC)
|
40
|
+
go build -o $@ perfmonger-plot-formatter.go
|
41
|
+
|
42
|
+
|
35
43
|
../lib/exec/perfmonger-recorder_darwin_amd64: perfmonger-recorder.go $(GOSRC)
|
36
44
|
go build -o $@ perfmonger-recorder.go
|
37
45
|
|
@@ -44,8 +52,12 @@ all: build
|
|
44
52
|
go build -o $@ perfmonger-summarizer.go
|
45
53
|
|
46
54
|
|
47
|
-
|
55
|
+
../lib/exec/perfmonger-plot-formatter_darwin_amd64: perfmonger-plot-formatter.go $(GOSRC)
|
56
|
+
go build -o $@ perfmonger-plot-formatter.go
|
57
|
+
|
58
|
+
|
59
|
+
build: ../lib/exec/perfmonger-recorder_linux_386 ../lib/exec/perfmonger-player_linux_386 ../lib/exec/perfmonger-summarizer_linux_386 ../lib/exec/perfmonger-plot-formatter_linux_386 ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
|
48
60
|
|
49
61
|
clean:
|
50
|
-
rm -f ../lib/exec/perfmonger-recorder_linux_386 ../lib/exec/perfmonger-player_linux_386 ../lib/exec/perfmonger-summarizer_linux_386 ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64
|
62
|
+
rm -f ../lib/exec/perfmonger-recorder_linux_386 ../lib/exec/perfmonger-player_linux_386 ../lib/exec/perfmonger-summarizer_linux_386 ../lib/exec/perfmonger-plot-formatter_linux_386 ../lib/exec/perfmonger-recorder_linux_amd64 ../lib/exec/perfmonger-player_linux_amd64 ../lib/exec/perfmonger-summarizer_linux_amd64 ../lib/exec/perfmonger-plot-formatter_linux_amd64 ../lib/exec/perfmonger-recorder_darwin_amd64 ../lib/exec/perfmonger-player_darwin_amd64 ../lib/exec/perfmonger-summarizer_darwin_amd64 ../lib/exec/perfmonger-plot-formatter_darwin_amd64
|
51
63
|
|
data/core/build.sh
CHANGED
@@ -54,7 +54,7 @@ for idx in $(seq 0 $((${#TARGET[@]}-1))); do
|
|
54
54
|
export GOOS=$1
|
55
55
|
export GOARCH=$2
|
56
56
|
|
57
|
-
for subcmd in recorder player summarizer; do
|
57
|
+
for subcmd in recorder player summarizer plot-formatter; do
|
58
58
|
TARGETS+=(../lib/exec/perfmonger-${subcmd}_${GOOS}_${GOARCH})
|
59
59
|
|
60
60
|
cat <<EOF >> Makefile
|
@@ -0,0 +1,325 @@
|
|
1
|
+
//usr/bin/env go run $0 $@ ; exit
|
2
|
+
|
3
|
+
package main
|
4
|
+
|
5
|
+
import (
|
6
|
+
"bufio"
|
7
|
+
"encoding/gob"
|
8
|
+
"encoding/json"
|
9
|
+
"flag"
|
10
|
+
"fmt"
|
11
|
+
"io"
|
12
|
+
"io/ioutil"
|
13
|
+
"os"
|
14
|
+
"sort"
|
15
|
+
|
16
|
+
ss "github.com/hayamiz/perfmonger/core/subsystem"
|
17
|
+
)
|
18
|
+
|
19
|
+
type CmdOption struct {
|
20
|
+
DiskFile string
|
21
|
+
CpuFile string
|
22
|
+
PerfmongerFile string
|
23
|
+
}
|
24
|
+
|
25
|
+
func parseArgs() *CmdOption {
|
26
|
+
opt := new(CmdOption)
|
27
|
+
|
28
|
+
flag.StringVar(&opt.DiskFile, "diskfile", "./disk.dat", "Disk performance data file")
|
29
|
+
flag.StringVar(&opt.CpuFile, "cpufile", "./cpu.dat", "CPU performance data file")
|
30
|
+
flag.StringVar(&opt.PerfmongerFile, "perfmonger", "", "Perfmonger log file")
|
31
|
+
|
32
|
+
flag.Parse()
|
33
|
+
|
34
|
+
if opt.PerfmongerFile == "" {
|
35
|
+
os.Stderr.WriteString("[ERROR] perfmonger log file is required.\n")
|
36
|
+
os.Exit(1)
|
37
|
+
}
|
38
|
+
|
39
|
+
return opt
|
40
|
+
}
|
41
|
+
|
42
|
+
type DiskMetaEntry struct {
|
43
|
+
Name string `json:"name"`
|
44
|
+
Idx int `json:"idx"`
|
45
|
+
}
|
46
|
+
|
47
|
+
type DiskMeta struct {
|
48
|
+
Devices []DiskMetaEntry `json:"devices"`
|
49
|
+
}
|
50
|
+
|
51
|
+
type CpuMeta struct {
|
52
|
+
NumCore int `json:"num_core"`
|
53
|
+
}
|
54
|
+
|
55
|
+
type PlotMeta struct {
|
56
|
+
Disk DiskMeta `json:"disk"`
|
57
|
+
Cpu CpuMeta `json:"cpu"`
|
58
|
+
StartTime float64 `json:"start_time"`
|
59
|
+
EndTime float64 `json:"end_time"`
|
60
|
+
}
|
61
|
+
|
62
|
+
type DiskDatTmpFile struct {
|
63
|
+
Name string
|
64
|
+
Path string
|
65
|
+
File *os.File
|
66
|
+
Writer *bufio.Writer
|
67
|
+
Idx int
|
68
|
+
}
|
69
|
+
|
70
|
+
type CpuDatTmpFile struct {
|
71
|
+
CoreId int
|
72
|
+
Path string
|
73
|
+
File *os.File
|
74
|
+
Writer *bufio.Writer
|
75
|
+
}
|
76
|
+
|
77
|
+
func makeDiskDatTmpFile(dname string, idx int) *DiskDatTmpFile {
|
78
|
+
ret := new(DiskDatTmpFile)
|
79
|
+
ret.Name = dname
|
80
|
+
ret.Idx = idx
|
81
|
+
|
82
|
+
f, err := ioutil.TempFile("", "perfmonger-"+dname)
|
83
|
+
if err != nil {
|
84
|
+
panic(err)
|
85
|
+
}
|
86
|
+
ret.File = f
|
87
|
+
ret.Path = f.Name()
|
88
|
+
ret.Writer = bufio.NewWriter(f)
|
89
|
+
|
90
|
+
return ret
|
91
|
+
}
|
92
|
+
|
93
|
+
func makeCpuDatTmpFile(coreid int) *CpuDatTmpFile {
|
94
|
+
ret := new(CpuDatTmpFile)
|
95
|
+
ret.CoreId = coreid
|
96
|
+
|
97
|
+
f, err := ioutil.TempFile("", fmt.Sprintf("perfmonger-core%d-", coreid))
|
98
|
+
if err != nil {
|
99
|
+
panic(err)
|
100
|
+
}
|
101
|
+
ret.File = f
|
102
|
+
ret.Path = f.Name()
|
103
|
+
ret.Writer = bufio.NewWriter(f)
|
104
|
+
|
105
|
+
return ret
|
106
|
+
}
|
107
|
+
|
108
|
+
func printCoreUsage(writer *bufio.Writer, elapsed_time float64, coreusage *ss.CpuCoreUsage) {
|
109
|
+
writer.WriteString(
|
110
|
+
fmt.Sprintf("%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
|
111
|
+
elapsed_time,
|
112
|
+
coreusage.User,
|
113
|
+
coreusage.Nice,
|
114
|
+
coreusage.Sys,
|
115
|
+
coreusage.Iowait,
|
116
|
+
coreusage.Hardirq,
|
117
|
+
coreusage.Softirq,
|
118
|
+
coreusage.Steal,
|
119
|
+
coreusage.Guest,
|
120
|
+
coreusage.Idle))
|
121
|
+
}
|
122
|
+
|
123
|
+
func main() {
|
124
|
+
opt := parseArgs()
|
125
|
+
|
126
|
+
var in *os.File
|
127
|
+
|
128
|
+
f, err := os.Open(opt.PerfmongerFile)
|
129
|
+
if err != nil {
|
130
|
+
panic(err)
|
131
|
+
}
|
132
|
+
in = f
|
133
|
+
defer f.Close()
|
134
|
+
|
135
|
+
dec := gob.NewDecoder(bufio.NewReader(in))
|
136
|
+
|
137
|
+
var cheader ss.CommonHeader
|
138
|
+
var pheader ss.PlatformHeader
|
139
|
+
var records = make([]ss.StatRecord, 2)
|
140
|
+
curr := 0
|
141
|
+
|
142
|
+
err = dec.Decode(&cheader)
|
143
|
+
if err == io.EOF {
|
144
|
+
return
|
145
|
+
}
|
146
|
+
if err != nil {
|
147
|
+
panic(err)
|
148
|
+
}
|
149
|
+
err = dec.Decode(&pheader)
|
150
|
+
if err == io.EOF {
|
151
|
+
return
|
152
|
+
}
|
153
|
+
if err != nil {
|
154
|
+
panic(err)
|
155
|
+
}
|
156
|
+
|
157
|
+
// read first record
|
158
|
+
err = dec.Decode(&records[curr])
|
159
|
+
if err == io.EOF {
|
160
|
+
return
|
161
|
+
} else if err != nil {
|
162
|
+
panic(err)
|
163
|
+
}
|
164
|
+
t0 := records[curr].Time
|
165
|
+
curr ^= 1
|
166
|
+
|
167
|
+
meta_set := false
|
168
|
+
meta := PlotMeta{}
|
169
|
+
meta.StartTime = float64(records[0].Time.UnixNano()) / 1.0e9
|
170
|
+
|
171
|
+
disk_dat_files := map[string]*DiskDatTmpFile{}
|
172
|
+
cpu_dat_files := make([]*CpuDatTmpFile, records[0].Cpu.NumCore)
|
173
|
+
meta.Cpu.NumCore = records[0].Cpu.NumCore
|
174
|
+
|
175
|
+
f, err = os.Create(opt.CpuFile)
|
176
|
+
if err != nil {
|
177
|
+
panic(err)
|
178
|
+
}
|
179
|
+
defer f.Close()
|
180
|
+
cpu_writer := bufio.NewWriter(f)
|
181
|
+
|
182
|
+
cpu_writer.WriteString("# All cpu usage\n")
|
183
|
+
cpu_writer.WriteString("# elapsed_time %usr %nice %sys %iowait %hardirq %softirq %steal %guest %idle\n")
|
184
|
+
|
185
|
+
for {
|
186
|
+
prev_rec := &records[curr^1]
|
187
|
+
cur_rec := &records[curr]
|
188
|
+
|
189
|
+
err := dec.Decode(cur_rec)
|
190
|
+
if err == io.EOF {
|
191
|
+
break
|
192
|
+
} else if err != nil {
|
193
|
+
panic(err)
|
194
|
+
}
|
195
|
+
|
196
|
+
// Disk usage
|
197
|
+
dusage, err := ss.GetDiskUsage(prev_rec.Time, prev_rec.Disk, cur_rec.Time, cur_rec.Disk)
|
198
|
+
if err != nil {
|
199
|
+
panic(err)
|
200
|
+
}
|
201
|
+
didx := 0
|
202
|
+
|
203
|
+
var dnames []string
|
204
|
+
for dname, _ := range *dusage {
|
205
|
+
if dname != "total" {
|
206
|
+
dnames = append(dnames, dname)
|
207
|
+
}
|
208
|
+
}
|
209
|
+
sort.Strings(dnames)
|
210
|
+
dnames = append(dnames, "total")
|
211
|
+
|
212
|
+
// for dname, dusage_entry := range *dusage {
|
213
|
+
for _, dname := range dnames {
|
214
|
+
dusage_entry, ok := (*dusage)[dname]
|
215
|
+
if !ok {
|
216
|
+
panic("device '" + dname + "' not found")
|
217
|
+
}
|
218
|
+
|
219
|
+
if !meta_set {
|
220
|
+
meta.Disk.Devices =
|
221
|
+
append(meta.Disk.Devices,
|
222
|
+
DiskMetaEntry{Name: dname, Idx: didx})
|
223
|
+
}
|
224
|
+
|
225
|
+
disk_dat, ok := disk_dat_files[dname]
|
226
|
+
if !ok {
|
227
|
+
disk_dat = makeDiskDatTmpFile(dname, didx)
|
228
|
+
disk_dat_files[dname] = disk_dat
|
229
|
+
defer disk_dat.File.Close()
|
230
|
+
|
231
|
+
disk_dat.Writer.WriteString("\n\n\n")
|
232
|
+
disk_dat.Writer.WriteString("# device: " + disk_dat.Name + "\n")
|
233
|
+
disk_dat.Writer.WriteString(fmt.Sprintln(
|
234
|
+
"# elapsed_time r_iops w_iops r_MB/s w_MB/s r_latency w_latency r_avgsz w_avgsz qdepth"))
|
235
|
+
}
|
236
|
+
|
237
|
+
elapsed_time := prev_rec.Time.Sub(t0).Seconds()
|
238
|
+
disk_dat.Writer.WriteString(
|
239
|
+
fmt.Sprintf("%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
|
240
|
+
elapsed_time,
|
241
|
+
dusage_entry.RdIops,
|
242
|
+
dusage_entry.WrIops,
|
243
|
+
dusage_entry.RdSecps*512.0/1024.0/1024.0,
|
244
|
+
dusage_entry.WrSecps*512.0/1024.0/1024.0,
|
245
|
+
dusage_entry.RdLatency,
|
246
|
+
dusage_entry.WrLatency,
|
247
|
+
dusage_entry.AvgRdSize,
|
248
|
+
dusage_entry.AvgWrSize,
|
249
|
+
dusage_entry.ReqQlen))
|
250
|
+
|
251
|
+
didx += 1
|
252
|
+
}
|
253
|
+
|
254
|
+
// Cpu usage
|
255
|
+
cusage, err := ss.GetCpuUsage(prev_rec.Cpu, cur_rec.Cpu)
|
256
|
+
if err != nil {
|
257
|
+
panic(err)
|
258
|
+
}
|
259
|
+
for coreid, coreusage := range cusage.CoreUsages {
|
260
|
+
cpu_dat := cpu_dat_files[coreid]
|
261
|
+
if cpu_dat == nil {
|
262
|
+
cpu_dat = makeCpuDatTmpFile(coreid)
|
263
|
+
cpu_dat_files[coreid] = cpu_dat
|
264
|
+
defer cpu_dat.File.Close()
|
265
|
+
|
266
|
+
cpu_dat.Writer.WriteString(fmt.Sprintf("\n\n\n# core: %d\n", coreid))
|
267
|
+
cpu_dat.Writer.WriteString("# elapsed_time %usr %nice %sys %iowait %hardirq %softirq %steal %guest %idle\n")
|
268
|
+
}
|
269
|
+
|
270
|
+
printCoreUsage(cpu_dat.Writer, prev_rec.Time.Sub(t0).Seconds(), coreusage)
|
271
|
+
}
|
272
|
+
printCoreUsage(cpu_writer, prev_rec.Time.Sub(t0).Seconds(), cusage.All)
|
273
|
+
|
274
|
+
curr ^= 1
|
275
|
+
meta_set = true
|
276
|
+
}
|
277
|
+
|
278
|
+
meta.EndTime = float64(records[curr^1].Time.UnixNano()) / 1.0e9
|
279
|
+
|
280
|
+
for _, disk_dat := range disk_dat_files {
|
281
|
+
disk_dat.Writer.Flush()
|
282
|
+
disk_dat.File.Close()
|
283
|
+
}
|
284
|
+
|
285
|
+
f, err = os.Create(opt.DiskFile)
|
286
|
+
if err != nil {
|
287
|
+
panic(err)
|
288
|
+
}
|
289
|
+
defer f.Close()
|
290
|
+
df_writer := bufio.NewWriter(f)
|
291
|
+
|
292
|
+
for _, dev := range meta.Disk.Devices {
|
293
|
+
disk_dat, ok := disk_dat_files[dev.Name]
|
294
|
+
if !ok {
|
295
|
+
panic(dev.Name)
|
296
|
+
}
|
297
|
+
|
298
|
+
content, err := ioutil.ReadFile(disk_dat.Path)
|
299
|
+
if err != nil {
|
300
|
+
panic(err)
|
301
|
+
}
|
302
|
+
|
303
|
+
df_writer.Write(content)
|
304
|
+
|
305
|
+
os.Remove(disk_dat.Path)
|
306
|
+
}
|
307
|
+
df_writer.Flush()
|
308
|
+
|
309
|
+
for _, cpu_dat := range cpu_dat_files {
|
310
|
+
cpu_dat.Writer.Flush()
|
311
|
+
cpu_dat.File.Close()
|
312
|
+
|
313
|
+
content, err := ioutil.ReadFile(cpu_dat.Path)
|
314
|
+
if err != nil {
|
315
|
+
panic(err)
|
316
|
+
}
|
317
|
+
|
318
|
+
cpu_writer.Write(content)
|
319
|
+
os.Remove(cpu_dat.Path)
|
320
|
+
}
|
321
|
+
cpu_writer.Flush()
|
322
|
+
|
323
|
+
json_enc := json.NewEncoder(os.Stdout)
|
324
|
+
json_enc.Encode(meta)
|
325
|
+
}
|
data/core/subsystem/usage.go
CHANGED
@@ -96,7 +96,19 @@ func GetCpuCoreUsage(c1 *CpuCoreStat, c2 *CpuCoreStat) (*CpuCoreUsage, error) {
|
|
96
96
|
itv := c2.Uptime() - c1.Uptime()
|
97
97
|
|
98
98
|
if itv == 0 {
|
99
|
-
return nil, errors.New("uptime difference is zero")
|
99
|
+
// return nil, errors.New("uptime difference is zero")
|
100
|
+
usage.User = 0
|
101
|
+
usage.Nice = 0
|
102
|
+
usage.Sys = 0
|
103
|
+
usage.Idle = 0
|
104
|
+
usage.Iowait = 0
|
105
|
+
usage.Hardirq = 0
|
106
|
+
usage.Softirq = 0
|
107
|
+
usage.Steal = 0
|
108
|
+
usage.Guest = 0
|
109
|
+
usage.GuestNice = 0
|
110
|
+
|
111
|
+
return usage, nil
|
100
112
|
} else if itv < 0 {
|
101
113
|
return nil, errors.New("uptime difference is negative")
|
102
114
|
}
|
data/lib/perfmonger.rb
CHANGED