perfmonger 0.10.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }