perfmonger 0.10.1 → 0.12.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 (45) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +9 -8
  4. data/HOWTO.md +0 -1
  5. data/NEWS +47 -3
  6. data/README.md +77 -27
  7. data/Rakefile +20 -9
  8. data/core/Makefile +10 -18
  9. data/core/build.sh +2 -2
  10. data/core/perfmonger-player.go +90 -40
  11. data/core/perfmonger-plot-formatter.go +18 -4
  12. data/core/perfmonger-recorder.go +22 -2
  13. data/core/perfmonger-summarizer.go +20 -14
  14. data/core/perfmonger-viewer.go +164 -0
  15. data/core/subsystem/Makefile +4 -0
  16. data/core/subsystem/perfmonger_linux.go +95 -0
  17. data/core/subsystem/perfmonger_linux_test.go +40 -0
  18. data/core/subsystem/stat.go +70 -0
  19. data/core/subsystem/stat_test.go +9 -0
  20. data/core/subsystem/usage.go +223 -66
  21. data/core/subsystem/usage_test.go +62 -32
  22. data/{bin → exe}/perfmonger +0 -0
  23. data/lib/perfmonger/command/fingerprint.rb +26 -1
  24. data/lib/perfmonger/command/live.rb +19 -0
  25. data/lib/perfmonger/command/play.rb +16 -0
  26. data/lib/perfmonger/command/plot.rb +25 -9
  27. data/lib/perfmonger/command/record.rb +1 -1
  28. data/lib/perfmonger/command/record_option.rb +16 -0
  29. data/lib/perfmonger/command/server.rb +1 -1
  30. data/lib/perfmonger/version.rb +1 -1
  31. data/misc/werker-box/Dockerfile +34 -0
  32. data/misc/werker-box/build-push.sh +7 -0
  33. data/perfmonger.gemspec +2 -1
  34. data/spec/data/busy100.pgr.played +3 -3
  35. data/spec/fingerprint_spec.rb +1 -1
  36. data/spec/live_spec.rb +2 -3
  37. data/spec/perfmonger_spec.rb +1 -1
  38. data/spec/play_spec.rb +1 -1
  39. data/spec/plot_spec.rb +16 -1
  40. data/spec/record_spec.rb +10 -1
  41. data/spec/spec_helper.rb +28 -3
  42. data/spec/stat_spec.rb +2 -2
  43. data/spec/summary_spec.rb +1 -1
  44. data/wercker.yml +29 -16
  45. metadata +28 -10
@@ -11,23 +11,30 @@ import (
11
11
  "io"
12
12
  "io/ioutil"
13
13
  "os"
14
+ "regexp"
14
15
  "sort"
15
16
 
16
17
  ss "github.com/hayamiz/perfmonger/core/subsystem"
17
18
  )
18
19
 
19
20
  type CmdOption struct {
20
- DiskFile string
21
- CpuFile string
22
- PerfmongerFile string
21
+ DiskFile string
22
+ CpuFile string
23
+ PerfmongerFile string
24
+ disk_only string
25
+ disk_only_regex *regexp.Regexp
23
26
  }
24
27
 
25
28
  func parseArgs() *CmdOption {
29
+ var err error
30
+
26
31
  opt := new(CmdOption)
27
32
 
28
33
  flag.StringVar(&opt.DiskFile, "diskfile", "./disk.dat", "Disk performance data file")
29
34
  flag.StringVar(&opt.CpuFile, "cpufile", "./cpu.dat", "CPU performance data file")
30
35
  flag.StringVar(&opt.PerfmongerFile, "perfmonger", "", "Perfmonger log file")
36
+ flag.StringVar(&opt.disk_only, "disk-only",
37
+ "", "Select disk devices by regex")
31
38
 
32
39
  flag.Parse()
33
40
 
@@ -36,6 +43,11 @@ func parseArgs() *CmdOption {
36
43
  os.Exit(1)
37
44
  }
38
45
 
46
+ opt.disk_only_regex, err = regexp.Compile(opt.disk_only)
47
+ if err != nil {
48
+ panic(err)
49
+ }
50
+
39
51
  return opt
40
52
  }
41
53
 
@@ -192,7 +204,9 @@ func main() {
192
204
  }
193
205
 
194
206
  // Disk usage
195
- dusage, err := ss.GetDiskUsage(prev_rec.Time, prev_rec.Disk, cur_rec.Time, cur_rec.Disk)
207
+ dusage, err := ss.GetDiskUsage1(prev_rec.Time, prev_rec.Disk,
208
+ cur_rec.Time, cur_rec.Disk,
209
+ opt.disk_only_regex)
196
210
  if err != nil {
197
211
  panic(err)
198
212
  }
@@ -36,6 +36,7 @@ type RecorderOption struct {
36
36
  no_intr bool
37
37
  no_disk bool
38
38
  no_net bool
39
+ no_mem bool
39
40
  debug bool
40
41
  listDevices bool
41
42
  player_bin string
@@ -43,6 +44,8 @@ type RecorderOption struct {
43
44
  targetDisks *map[string]bool
44
45
  background bool
45
46
  gzip bool
47
+ color bool
48
+ pretty bool
46
49
  }
47
50
 
48
51
  var option RecorderOption
@@ -74,6 +77,8 @@ func parseArgs() {
74
77
  false, "Do not record disk usage")
75
78
  flag.BoolVar(&option.no_net, "no-net",
76
79
  false, "Do not record net usage")
80
+ flag.BoolVar(&option.no_mem, "no-mem",
81
+ false, "Do not record memory usage")
77
82
  flag.BoolVar(&option.debug, "debug",
78
83
  false, "Enable debug mode")
79
84
  flag.BoolVar(&option.listDevices, "list-devices",
@@ -86,6 +91,10 @@ func parseArgs() {
86
91
  "", "Run perfmonger-player to show JSON output")
87
92
  flag.BoolVar(&option.gzip, "gzip",
88
93
  false, "Save a logfile in gzipped format")
94
+ flag.BoolVar(&option.color, "color",
95
+ false, "Colored output (for live subcmd)")
96
+ flag.BoolVar(&option.pretty, "pretty",
97
+ false, "Pretty output (for live subcmd)")
89
98
 
90
99
  flag.Parse()
91
100
 
@@ -190,7 +199,7 @@ func main() {
190
199
  parseArgs()
191
200
 
192
201
  hostname, _ := os.Hostname()
193
- cheader := &ss.CommonHeader{ss.Linux, hostname, time.Now()}
202
+ cheader := &ss.CommonHeader{Platform: ss.Linux, Hostname: hostname, StartTime: time.Now()}
194
203
 
195
204
  platform_header := ss.NewPlatformHeader()
196
205
 
@@ -206,7 +215,15 @@ func main() {
206
215
  var player_stdout io.ReadCloser = nil
207
216
 
208
217
  if option.player_bin != "" {
209
- player_cmd = exec.Command(option.player_bin)
218
+ if option.color {
219
+ if option.pretty {
220
+ player_cmd = exec.Command(option.player_bin, "-color", "-pretty")
221
+ } else {
222
+ player_cmd = exec.Command(option.player_bin, "-color")
223
+ }
224
+ } else {
225
+ player_cmd = exec.Command(option.player_bin)
226
+ }
210
227
  player_stdin, err = player_cmd.StdinPipe()
211
228
  if err != nil {
212
229
  fmt.Fprintf(os.Stderr, "Failed to get stdin of %s", option.player_bin)
@@ -326,6 +343,9 @@ func main() {
326
343
  if !option.no_net {
327
344
  ss.ReadNetStat(record)
328
345
  }
346
+ if !option.no_mem {
347
+ ss.ReadMemStat(record)
348
+ }
329
349
 
330
350
  err = enc.Encode(record)
331
351
  if err != nil {
@@ -3,7 +3,6 @@
3
3
  package main
4
4
 
5
5
  import (
6
- "bytes"
7
6
  "encoding/gob"
8
7
  "flag"
9
8
  "fmt"
@@ -12,6 +11,7 @@ import (
12
11
  "regexp"
13
12
  "sort"
14
13
 
14
+ projson "github.com/hayamiz/go-projson"
15
15
  ss "github.com/hayamiz/perfmonger/core/subsystem"
16
16
  )
17
17
 
@@ -128,7 +128,7 @@ func main() {
128
128
  option.disk_only_regex)
129
129
  }
130
130
 
131
- if fst_record.Disk != nil && lst_record.Disk != nil {
131
+ if fst_record.Net != nil && lst_record.Net != nil {
132
132
  net_usage, err = ss.GetNetUsage(
133
133
  fst_record.Time, fst_record.Net,
134
134
  lst_record.Time, lst_record.Net)
@@ -137,32 +137,38 @@ func main() {
137
137
  interval := lst_record.Time.Sub(fst_record.Time)
138
138
 
139
139
  if option.json {
140
- buf := bytes.NewBuffer([]byte{})
140
+ printer := projson.NewPrinter()
141
141
 
142
- buf.WriteString(fmt.Sprintf(`{"exectime":%.3f`, interval.Seconds()))
142
+ printer.BeginObject()
143
+ printer.PutKey("exectime")
144
+ printer.PutFloatFmt(interval.Seconds(), "%.3f")
143
145
  if cpu_usage != nil {
144
- buf.WriteString(`,"cpu":`)
145
- cpu_usage.WriteJsonTo(buf)
146
+ printer.PutKey("cpu")
147
+ cpu_usage.WriteJsonTo(printer)
146
148
  }
147
149
 
148
150
  if intr_usage != nil {
149
- buf.WriteString(`,"intr":`)
150
- intr_usage.WriteJsonTo(buf)
151
+ printer.PutKey("intr")
152
+ intr_usage.WriteJsonTo(printer)
151
153
  }
152
154
 
153
155
  if disk_usage != nil {
154
- buf.WriteString(`,"disk":`)
155
- disk_usage.WriteJsonTo(buf)
156
+ printer.PutKey("disk")
157
+ disk_usage.WriteJsonTo(printer)
156
158
  }
157
159
 
158
160
  if net_usage != nil {
159
- buf.WriteString(`,"net":`)
160
- net_usage.WriteJsonTo(buf)
161
+ printer.PutKey("net")
162
+ net_usage.WriteJsonTo(printer)
161
163
  }
162
164
 
163
- buf.WriteByte('}')
165
+ printer.FinishObject()
164
166
 
165
- fmt.Println(buf.String())
167
+ if str, err := printer.String(); err != nil {
168
+ fmt.Println("skip by err")
169
+ } else {
170
+ fmt.Println(str)
171
+ }
166
172
  } else {
167
173
  if option.title == "" {
168
174
  fmt.Println("== performance summary ==")
@@ -0,0 +1,164 @@
1
+ //usr/bin/env go run $0 $@ ; exit
2
+
3
+ package main
4
+
5
+ import (
6
+ "fmt"
7
+ "log"
8
+ "math"
9
+ "math/rand"
10
+ "time"
11
+
12
+ gocui "github.com/jroimartin/gocui"
13
+ termbox "github.com/nsf/termbox-go"
14
+ )
15
+
16
+ func main() {
17
+ g, err := gocui.NewGui(gocui.OutputNormal)
18
+ if err != nil {
19
+ log.Panicln(err)
20
+ }
21
+ defer g.Close()
22
+
23
+ g.SetManagerFunc(layout)
24
+
25
+ setupKeybind(g)
26
+
27
+ go func() {
28
+ for {
29
+ time.Sleep(1 * time.Second)
30
+ g.Update(layout)
31
+ }
32
+ }()
33
+
34
+ if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
35
+ log.Panicln(err)
36
+ }
37
+ }
38
+
39
+ func setupKeybind(g *gocui.Gui) {
40
+ if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
41
+ log.Panicln(err)
42
+ }
43
+
44
+ if err := g.SetKeybinding("", 'c', gocui.ModNone, changeColor); err != nil {
45
+ log.Panicln(err)
46
+ }
47
+
48
+ if err := g.SetKeybinding("", 'q', gocui.ModNone, quit); err != nil {
49
+ log.Panicln(err)
50
+ }
51
+ }
52
+
53
+ var colmode bool = false
54
+
55
+ func changeColor(g *gocui.Gui, v *gocui.View) error {
56
+ if colmode {
57
+ colmode = false
58
+ } else {
59
+ colmode = true
60
+ }
61
+ return nil
62
+ }
63
+
64
+ // Vertical bar characters
65
+ // ' ': \u0020
66
+ // '▁': \u2581
67
+ // '▂': \u2582
68
+ // '▃': \u2583
69
+ // '▄': \u2584
70
+ // '▅': \u2585
71
+ // '▆': \u2586
72
+ // '▇': \u2587
73
+ // '█': \u2588
74
+ var vbar_runes []rune = []rune(" ▁▂▃▄▅▆▇█")
75
+
76
+ func drawPercent(g *gocui.Gui, x int, y int, height int, pct float64, fg, bg termbox.Attribute) {
77
+ pct = math.Max(0.0, math.Min(100.0, pct))
78
+ pct_ndiv := 9
79
+ if height > 1 {
80
+ pct_ndiv += 8 * (height - 1)
81
+ }
82
+ pct_class := int(pct / (100.0 / float64(pct_ndiv)))
83
+
84
+ col := termbox.ColorBlue
85
+ if 25 <= pct && pct < 50 {
86
+ col = termbox.ColorGreen
87
+ } else if 50 <= pct && pct < 75 {
88
+ col = termbox.ColorYellow
89
+ } else if 75 <= pct {
90
+ col = termbox.ColorRed
91
+ }
92
+
93
+ if !colmode {
94
+ col = termbox.ColorWhite
95
+ }
96
+
97
+ for cell_pos := height - 1; cell_pos >= 0; cell_pos-- {
98
+ vbar_idx := intMin(pct_class, len(vbar_runes)-1)
99
+ termbox.SetCell(x, y+cell_pos, vbar_runes[vbar_idx], col, bg)
100
+ pct_class -= vbar_idx
101
+ }
102
+
103
+ // termbox.SetCell(x, y, vbar_runes[pct_class], fg, bg)
104
+ }
105
+
106
+ func drawRunes(g *gocui.Gui, x int, y int, runes []rune) {
107
+ for _, r := range runes {
108
+ termbox.SetCell(x, y, r, termbox.ColorDefault, termbox.ColorDefault)
109
+ x += 1
110
+ }
111
+ }
112
+
113
+ func intMin(x, y int) int {
114
+ if x > y {
115
+ return y
116
+ } else {
117
+ return x
118
+ }
119
+ }
120
+
121
+ var start_time int64 = time.Now().Unix()
122
+
123
+ func layout(g *gocui.Gui) error {
124
+ // maxX, maxY := g.Size()
125
+ // if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
126
+ // if err != gocui.ErrUnknownView {
127
+ // return err
128
+ // }
129
+ // fmt.Fprintln(v, "Hello world!")
130
+ // }
131
+
132
+ for coreid := 0; coreid < 8; coreid++ {
133
+ s := int64(coreid + 1)
134
+ rand.Seed(s)
135
+
136
+ inc := int(time.Now().Unix() - start_time)
137
+ for i := 0; i < inc; i++ {
138
+ rand.Float64()
139
+ }
140
+
141
+ for i := 0; i < 100; i++ {
142
+ // drawPercent(g, i+5, 1+coreid*5, 4, float64(i)/2.0, termbox.ColorDefault, termbox.ColorDefault)
143
+ drawPercent(g, i+6, 1+coreid*5, 4, rand.Float64()*100, termbox.ColorDefault, termbox.ColorDefault)
144
+
145
+ ch := '-'
146
+ if (i+inc)%10 == 0 {
147
+ ch = '+'
148
+ }
149
+ termbox.SetCell(i+6, 1+coreid*5+4, ch, termbox.ColorDefault, termbox.ColorDefault)
150
+ }
151
+ drawRunes(g, 0, 1+coreid*5, []rune(fmt.Sprintf("core%d", coreid)))
152
+
153
+ drawRunes(g, 100+6+1, 1+coreid*5, []rune(fmt.Sprintf("%%usr: % 2.1f", rand.Float64()*30)))
154
+ drawRunes(g, 100+6+1, 1+coreid*5+1, []rune(fmt.Sprintf("%%sys: % 2.1f", rand.Float64()*30)))
155
+ drawRunes(g, 100+6+1, 1+coreid*5+2, []rune(fmt.Sprintf("%%iow: % 2.1f", rand.Float64()*30)))
156
+ drawRunes(g, 100+6+1, 1+coreid*5+3, []rune(fmt.Sprintf("%%oth: % 2.1f", rand.Float64()*30)))
157
+ }
158
+
159
+ return nil
160
+ }
161
+
162
+ func quit(g *gocui.Gui, v *gocui.View) error {
163
+ return gocui.ErrQuit
164
+ }
@@ -1,3 +1,7 @@
1
1
 
2
2
  all:
3
3
  make -C ../
4
+
5
+ check:
6
+ go test
7
+
@@ -429,3 +429,98 @@ func ReadNetStat(record *StatRecord) error {
429
429
 
430
430
  return nil
431
431
  }
432
+
433
+ func ReadMemStat(record *StatRecord) error {
434
+ if record == nil {
435
+ return errors.New("Valid *StatRecord is required.")
436
+ }
437
+
438
+ mem_stat := NewMemStat()
439
+
440
+ f, err := os.Open("/proc/meminfo")
441
+ if err != nil {
442
+ return err
443
+ }
444
+ defer f.Close()
445
+ scanner := bufio.NewScanner(f)
446
+
447
+ for scanner.Scan() {
448
+ var key string
449
+ var val int64
450
+ line := scanner.Text()
451
+
452
+ n, err := fmt.Sscanf(line, "%s %d", &key, &val)
453
+
454
+ if err == io.EOF {
455
+ break
456
+ } else if err != nil {
457
+ return err
458
+ }
459
+ if n != 2 {
460
+ continue
461
+ }
462
+
463
+ switch key {
464
+ case "HugePages_Surp:":
465
+ mem_stat.HugePages_Surp = val
466
+ case "HugePages_Rsvd:":
467
+ mem_stat.HugePages_Rsvd = val
468
+ case "HugePages_Free:":
469
+ mem_stat.HugePages_Free = val
470
+ case "HugePages_Total:":
471
+ mem_stat.HugePages_Total = val
472
+ case "AnonHugePages:":
473
+ mem_stat.AnonHugePages = val
474
+ case "Committed_AS:":
475
+ mem_stat.Committed_AS = val
476
+ case "CommitLimit:":
477
+ mem_stat.CommitLimit = val
478
+ case "Bounce:":
479
+ mem_stat.Bounce = val
480
+ case "NFS_Unstable:":
481
+ mem_stat.NFS_Unstable = val
482
+ case "Shmem:":
483
+ mem_stat.Shmem = val
484
+ case "Slab:":
485
+ mem_stat.Slab = val
486
+ case "SReclaimable:":
487
+ mem_stat.SReclaimable = val
488
+ case "SUnreclaim:":
489
+ mem_stat.SUnreclaim = val
490
+ case "KernelStack:":
491
+ mem_stat.KernelStack = val
492
+ case "PageTables:":
493
+ mem_stat.PageTables = val
494
+ case "Mapped:":
495
+ mem_stat.Mapped = val
496
+ case "AnonPages:":
497
+ mem_stat.AnonPages = val
498
+ case "Writeback:":
499
+ mem_stat.Writeback = val
500
+ case "Dirty:":
501
+ mem_stat.Dirty = val
502
+ case "SwapFree:":
503
+ mem_stat.SwapFree = val
504
+ case "SwapTotal:":
505
+ mem_stat.SwapTotal = val
506
+ case "Inactive:":
507
+ mem_stat.Inactive = val
508
+ case "Active:":
509
+ mem_stat.Active = val
510
+ case "SwapCached:":
511
+ mem_stat.SwapCached = val
512
+ case "Cached:":
513
+ mem_stat.Cached = val
514
+ case "Buffers:":
515
+ mem_stat.Buffers = val
516
+ case "MemFree:":
517
+ mem_stat.MemFree = val
518
+ case "MemTotal:":
519
+ mem_stat.MemTotal = val
520
+ }
521
+ }
522
+
523
+ record.Mem = mem_stat
524
+
525
+ return nil
526
+ }