perfmonger 0.6.1 → 0.7.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 (87) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +6 -0
  3. data/.tachikoma.yml +1 -0
  4. data/.travis.yml +18 -6
  5. data/Gemfile +1 -3
  6. data/Guardfile +26 -0
  7. data/NEWS +21 -0
  8. data/README.md +8 -9
  9. data/Rakefile +33 -1
  10. data/core/Makefile +23 -0
  11. data/core/build.sh +48 -0
  12. data/core/perfmonger-player.go +165 -0
  13. data/core/perfmonger-recorder.go +296 -0
  14. data/core/perfmonger-summarizer.go +207 -0
  15. data/core/subsystem/Makefile +3 -0
  16. data/core/subsystem/perfmonger.go +60 -0
  17. data/core/subsystem/perfmonger_darwin.go +22 -0
  18. data/core/subsystem/perfmonger_linux.go +292 -0
  19. data/core/subsystem/perfmonger_linux_test.go +73 -0
  20. data/core/subsystem/stat.go +214 -0
  21. data/core/subsystem/stat_test.go +281 -0
  22. data/core/subsystem/usage.go +410 -0
  23. data/core/subsystem/usage_test.go +496 -0
  24. data/lib/exec/operationBinding.rb.svn-base +59 -0
  25. data/lib/exec/perfmonger-player_darwin_amd64 +0 -0
  26. data/lib/exec/perfmonger-player_linux_386 +0 -0
  27. data/lib/exec/perfmonger-player_linux_amd64 +0 -0
  28. data/lib/exec/perfmonger-recorder_darwin_amd64 +0 -0
  29. data/lib/exec/perfmonger-recorder_linux_386 +0 -0
  30. data/lib/exec/perfmonger-recorder_linux_amd64 +0 -0
  31. data/lib/exec/perfmonger-summarizer_darwin_amd64 +0 -0
  32. data/lib/exec/perfmonger-summarizer_linux_386 +0 -0
  33. data/lib/exec/perfmonger-summarizer_linux_amd64 +0 -0
  34. data/lib/exec/perfmonger-summary_linux_386 +0 -0
  35. data/lib/exec/perfmonger-summary_linux_amd64 +0 -0
  36. data/lib/perfmonger/cli.rb +8 -3
  37. data/lib/perfmonger/command/core.rb +62 -0
  38. data/lib/perfmonger/command/live.rb +39 -0
  39. data/lib/perfmonger/command/play.rb +56 -0
  40. data/lib/perfmonger/command/plot.rb +30 -22
  41. data/lib/perfmonger/command/record.rb +3 -2
  42. data/lib/perfmonger/command/record_option.rb +40 -59
  43. data/lib/perfmonger/command/server.rb +7 -2
  44. data/lib/perfmonger/command/stat.rb +2 -2
  45. data/lib/perfmonger/command/stat_option.rb +1 -1
  46. data/lib/perfmonger/command/summary.rb +11 -326
  47. data/lib/perfmonger/version.rb +1 -3
  48. data/lib/perfmonger.rb +3 -0
  49. data/misc/_perfmonger +128 -0
  50. data/misc/perfmonger-completion.bash +49 -0
  51. data/perfmonger.gemspec +6 -5
  52. data/spec/data/busy100.pgr +0 -0
  53. data/spec/fingerprint_spec.rb +35 -0
  54. data/spec/live_spec.rb +25 -0
  55. data/spec/perfmonger_spec.rb +37 -0
  56. data/spec/play_spec.rb +21 -0
  57. data/spec/plot_spec.rb +42 -0
  58. data/spec/record_spec.rb +15 -0
  59. data/spec/spec_helper.rb +33 -0
  60. data/spec/stat_spec.rb +15 -0
  61. data/spec/summary_spec.rb +51 -0
  62. data/spec/support/aruba.rb +11 -0
  63. data/wercker.yml +59 -0
  64. metadata +117 -45
  65. data/ext/perfmonger/extconf.rb +0 -19
  66. data/ext/perfmonger/perfmonger.h +0 -58
  67. data/ext/perfmonger/perfmonger_record.c +0 -754
  68. data/ext/perfmonger/sysstat/common.c +0 -627
  69. data/ext/perfmonger/sysstat/common.h +0 -207
  70. data/ext/perfmonger/sysstat/ioconf.c +0 -515
  71. data/ext/perfmonger/sysstat/ioconf.h +0 -84
  72. data/ext/perfmonger/sysstat/iostat.c +0 -1100
  73. data/ext/perfmonger/sysstat/iostat.h +0 -121
  74. data/ext/perfmonger/sysstat/libsysstat.h +0 -19
  75. data/ext/perfmonger/sysstat/mpstat.c +0 -953
  76. data/ext/perfmonger/sysstat/mpstat.h +0 -79
  77. data/ext/perfmonger/sysstat/rd_stats.c +0 -2388
  78. data/ext/perfmonger/sysstat/rd_stats.h +0 -651
  79. data/ext/perfmonger/sysstat/sysconfig.h +0 -13
  80. data/test/run-test.sh +0 -39
  81. data/test/spec/bin_spec.rb +0 -37
  82. data/test/spec/data/2devices.expected +0 -42
  83. data/test/spec/data/2devices.output +0 -42
  84. data/test/spec/spec_helper.rb +0 -20
  85. data/test/spec/summary_spec.rb +0 -193
  86. data/test/test-perfmonger.c +0 -145
  87. data/test/test.h +0 -9
@@ -0,0 +1,296 @@
1
+ //usr/bin/env go run $0 $@ ; exit
2
+
3
+ package main
4
+
5
+ import (
6
+ "bufio"
7
+ "encoding/gob"
8
+ "flag"
9
+ "fmt"
10
+ "io"
11
+ "os"
12
+ "os/exec"
13
+ "os/signal"
14
+ "strings"
15
+ "time"
16
+
17
+ "golang.org/x/crypto/ssh/terminal"
18
+
19
+ ss "github.com/hayamiz/perfmonger/core/subsystem"
20
+ )
21
+
22
+ type RecorderOption struct {
23
+ interval time.Duration
24
+ no_interval_backoff bool
25
+ timeout time.Duration
26
+ start_delay time.Duration
27
+ devsParts []string
28
+ output string
29
+ no_cpu bool
30
+ no_disk bool
31
+ no_net bool
32
+ debug bool
33
+ listDevices bool
34
+ player_bin string
35
+ disks string
36
+ targetDisks *map[string]bool
37
+ }
38
+
39
+ var option RecorderOption
40
+
41
+ // By default, measurement interval backoff is enabled.
42
+ // Minimum resoluton guaranteed: BACKOFF_RATIO / BACKOFF_THRESH
43
+ const (
44
+ BACKOFF_THRESH = 1000.0
45
+ BACKOFF_RATIO = 2.0
46
+ )
47
+
48
+ func parseArgs() {
49
+ // set options
50
+ flag.DurationVar(&option.interval, "interval",
51
+ time.Second, "Measurement interval")
52
+ flag.BoolVar(&option.no_interval_backoff, "no-interval-backoff",
53
+ false, "Disable interval backoff")
54
+ flag.DurationVar(&option.timeout, "timeout",
55
+ time.Second*0, "Measurement timeout")
56
+ flag.DurationVar(&option.start_delay, "start-delay",
57
+ time.Second*0, "Wait time before measurement")
58
+ flag.StringVar(&option.output, "output",
59
+ "-", "Output file name")
60
+ flag.BoolVar(&option.no_cpu, "no-cpu",
61
+ false, "Do not record CPU usage")
62
+ flag.BoolVar(&option.no_disk, "no-disk",
63
+ false, "Do not record disk usage")
64
+ flag.BoolVar(&option.no_net, "no-net",
65
+ false, "Do not record net usage")
66
+ flag.BoolVar(&option.debug, "debug",
67
+ false, "Enable debug mode")
68
+ flag.BoolVar(&option.listDevices, "list-devices",
69
+ false, "List devices and exits")
70
+ flag.StringVar(&option.disks, "disks",
71
+ "", "Disk devices to be monitored")
72
+ flag.StringVar(&option.player_bin, "player-bin",
73
+ "", "Run perfmonger-player to show JSON output")
74
+
75
+ flag.Parse()
76
+
77
+ if option.player_bin == "" && terminal.IsTerminal(int(os.Stdout.Fd())) &&
78
+ option.output == "-" {
79
+ fmt.Fprintf(os.Stderr, "[recording to data.pgr]\n")
80
+ option.output = "data.pgr"
81
+ }
82
+
83
+ if option.disks == "" {
84
+ option.targetDisks = nil
85
+ } else {
86
+ option.targetDisks = new(map[string]bool)
87
+ *option.targetDisks = make(map[string]bool)
88
+ for _, dev := range strings.Split(option.disks, ",") {
89
+ (*option.targetDisks)[dev] = true
90
+ }
91
+ }
92
+
93
+ if option.debug {
94
+ os.Stderr.WriteString(
95
+ fmt.Sprintf(
96
+ `== option
97
+ - output : %s
98
+ - interval : %s
99
+ - debug : %t
100
+ - remainings: %s
101
+ `,
102
+ option.output,
103
+ option.interval.String(),
104
+ option.debug,
105
+ fmt.Sprint(flag.Args())))
106
+ }
107
+ }
108
+
109
+ func main() {
110
+ var enc *gob.Encoder
111
+ var out *bufio.Writer
112
+ var err error
113
+
114
+ parseArgs()
115
+
116
+ hostname, _ := os.Hostname()
117
+ cheader := &ss.CommonHeader{ss.Linux, hostname, time.Now()}
118
+
119
+ platform_header := ss.NewPlatformHeader()
120
+
121
+ if option.listDevices {
122
+ for _, name := range platform_header.DevsParts {
123
+ os.Stderr.WriteString(name + "\n")
124
+ }
125
+ return
126
+ }
127
+
128
+ var player_cmd *exec.Cmd = nil
129
+ var player_stdin io.WriteCloser = nil
130
+ var player_stdout io.ReadCloser = nil
131
+
132
+ if option.player_bin != "" {
133
+ player_cmd = exec.Command(option.player_bin)
134
+ player_stdin, err = player_cmd.StdinPipe()
135
+ if err != nil {
136
+ fmt.Fprintf(os.Stderr, "Failed to get stdin of %s", option.player_bin)
137
+ player_cmd = nil
138
+ player_stdin = nil
139
+ }
140
+ player_stdout, err = player_cmd.StdoutPipe()
141
+ if err != nil {
142
+ fmt.Fprintf(os.Stderr, "Failed to get stdout of %s", option.player_bin)
143
+ player_cmd = nil
144
+ player_stdin = nil
145
+ player_stdout = nil
146
+ }
147
+
148
+ err = player_cmd.Start()
149
+ if err != nil {
150
+ fmt.Fprintf(os.Stderr, "Failed to start %s", option.player_bin)
151
+ player_cmd = nil
152
+ player_stdin = nil
153
+ player_stdout = nil
154
+ }
155
+
156
+ // read stdout of player and write to stdout
157
+ go func() {
158
+ var buf = make([]byte, 4096)
159
+ for {
160
+ n, err := player_stdout.Read(buf)
161
+ if err == io.EOF {
162
+ break
163
+ } else if err != nil {
164
+ panic(err)
165
+ }
166
+
167
+ if n == 0 {
168
+ continue
169
+ }
170
+ os.Stdout.Write(buf[0:n])
171
+ }
172
+ }()
173
+ }
174
+
175
+ if option.output == "-" {
176
+ out = bufio.NewWriter(os.Stdout)
177
+ if player_stdin != nil {
178
+ out = bufio.NewWriter(player_stdin)
179
+ }
180
+ } else {
181
+ file, err := os.Create(option.output)
182
+ if err != nil {
183
+ panic(err)
184
+ }
185
+ defer file.Close()
186
+
187
+ if player_stdin != nil {
188
+ out = bufio.NewWriter(io.MultiWriter(file, player_stdin))
189
+ } else {
190
+ out = bufio.NewWriter(file)
191
+ }
192
+ }
193
+
194
+ enc = gob.NewEncoder(out)
195
+
196
+ // Write the beginning sections
197
+ err = enc.Encode(cheader)
198
+ if err != nil {
199
+ panic(err)
200
+ }
201
+
202
+ err = enc.Encode(platform_header)
203
+ if err != nil {
204
+ panic(err)
205
+ }
206
+
207
+ // start delay
208
+ time.Sleep(option.start_delay)
209
+
210
+ var timeout_ch <-chan time.Time
211
+ var timeout_time time.Time
212
+ if option.timeout == time.Second*0 {
213
+ // dummy channel
214
+ timeout_ch = make(<-chan time.Time)
215
+ timeout_time = time.Now()
216
+ } else {
217
+ timeout_ch = time.After(option.timeout)
218
+ timeout_time = time.Now().Add(option.timeout)
219
+ }
220
+
221
+ // Loops
222
+ sigint_ch := make(chan os.Signal, 1)
223
+ running := true
224
+ next_time := time.Now()
225
+ record := ss.NewStatRecord()
226
+ backoff_counter := 0
227
+
228
+ // cause SIGINT to break loop
229
+ signal.Notify(sigint_ch, os.Interrupt)
230
+
231
+ for {
232
+ record.Time = time.Now()
233
+
234
+ if !option.no_cpu {
235
+ ss.ReadCpuStat(record)
236
+ }
237
+ if !option.no_disk {
238
+ ss.ReadDiskStats(record, option.targetDisks)
239
+ }
240
+ if !option.no_net {
241
+ ss.ReadNetStat(record)
242
+ }
243
+
244
+ err = enc.Encode(record)
245
+ if err != nil {
246
+ break
247
+ }
248
+ out.Flush()
249
+
250
+ if !running {
251
+ break
252
+ }
253
+
254
+ if !option.no_interval_backoff {
255
+ backoff_counter++
256
+ if backoff_counter >= BACKOFF_THRESH {
257
+ backoff_counter -= BACKOFF_THRESH
258
+
259
+ option.interval *= BACKOFF_RATIO
260
+ if option.interval.Seconds() > 3600.0 {
261
+ option.interval = time.Hour
262
+ }
263
+ }
264
+ }
265
+
266
+ next_time = next_time.Add(option.interval)
267
+
268
+ // wait for next iteration
269
+ select {
270
+ case <-sigint_ch:
271
+ running = false
272
+ break
273
+ case <-timeout_ch:
274
+ running = false
275
+ break
276
+ case <-time.After(next_time.Sub(time.Now())):
277
+ break
278
+ }
279
+
280
+ // If next_time and timeout_time is very close,
281
+ // avoid recording twice in a very short time
282
+ if option.timeout != time.Second*0 &&
283
+ timeout_time.Sub(next_time).Seconds() < 0.01 {
284
+ running = false
285
+ }
286
+ }
287
+
288
+ out.Flush()
289
+
290
+ if player_stdin != nil {
291
+ player_stdin.Close()
292
+ _ = player_cmd.Wait()
293
+ }
294
+
295
+ return
296
+ }
@@ -0,0 +1,207 @@
1
+ //usr/bin/env go run $0 $@ ; exit
2
+
3
+ package main
4
+
5
+ import (
6
+ "bytes"
7
+ "encoding/gob"
8
+ "flag"
9
+ "fmt"
10
+ "io"
11
+ "os"
12
+ "sort"
13
+
14
+ ss "github.com/hayamiz/perfmonger/core/subsystem"
15
+ )
16
+
17
+ type SummaryOption struct {
18
+ logfile string
19
+ title string
20
+ json bool
21
+ }
22
+
23
+ var option SummaryOption
24
+
25
+ func parseArgs() {
26
+ flag.BoolVar(&option.json, "json",
27
+ false, "Show summary in JSON")
28
+ flag.StringVar(&option.title, "title",
29
+ "", "Title of summary")
30
+
31
+ flag.Parse()
32
+
33
+ if len(flag.Args()) < 1 {
34
+ os.Exit(1)
35
+ }
36
+
37
+ option.logfile = flag.Args()[0]
38
+ }
39
+
40
+ func main() {
41
+ var cheader ss.CommonHeader
42
+ var pheader ss.PlatformHeader
43
+
44
+ parseArgs()
45
+
46
+ f, err := os.Open(option.logfile)
47
+ if err != nil {
48
+ panic(err)
49
+ }
50
+ dec := gob.NewDecoder(f)
51
+
52
+ err = dec.Decode(&cheader)
53
+ if err == io.EOF {
54
+ return
55
+ }
56
+ if err != nil {
57
+ panic(err)
58
+ }
59
+ err = dec.Decode(&pheader)
60
+ if err == io.EOF {
61
+ return
62
+ }
63
+ if err != nil {
64
+ panic(err)
65
+ }
66
+
67
+ var fst_record ss.StatRecord
68
+ // read first record
69
+ err = dec.Decode(&fst_record)
70
+ if err == io.EOF {
71
+ return
72
+ } else if err != nil {
73
+ panic(err)
74
+ }
75
+
76
+ // loop until last line
77
+ var lst_records [2]ss.StatRecord
78
+ idx := 0
79
+ for {
80
+ err = dec.Decode(&lst_records[idx])
81
+ if err == io.EOF {
82
+ idx ^= 1
83
+ break
84
+ } else if err != nil {
85
+ panic(err)
86
+ }
87
+
88
+ idx ^= 1
89
+ }
90
+
91
+ lst_record := lst_records[idx]
92
+
93
+ var cpu_usage *ss.CpuUsage = nil
94
+ var disk_usage *ss.DiskUsage = nil
95
+ var net_usage *ss.NetUsage = nil
96
+
97
+ if fst_record.Cpu != nil && lst_record.Cpu != nil {
98
+ cpu_usage, err = ss.GetCpuUsage(fst_record.Cpu, lst_record.Cpu)
99
+ }
100
+
101
+ if fst_record.Disk != nil && lst_record.Disk != nil {
102
+ disk_usage, err = ss.GetDiskUsage(
103
+ fst_record.Time, fst_record.Disk,
104
+ lst_record.Time, lst_record.Disk)
105
+ }
106
+
107
+ if fst_record.Disk != nil && lst_record.Disk != nil {
108
+ net_usage, err = ss.GetNetUsage(
109
+ fst_record.Time, fst_record.Net,
110
+ lst_record.Time, lst_record.Net)
111
+ }
112
+
113
+ interval := lst_record.Time.Sub(fst_record.Time)
114
+
115
+ if option.json {
116
+ buf := bytes.NewBuffer([]byte{})
117
+
118
+ buf.WriteString(fmt.Sprintf(`{"exectime":%.3f`, interval.Seconds()))
119
+ if cpu_usage != nil {
120
+ buf.WriteString(`,"cpu":`)
121
+ cpu_usage.WriteJsonTo(buf)
122
+ }
123
+
124
+ if disk_usage != nil {
125
+ buf.WriteString(`,"disk":`)
126
+ disk_usage.WriteJsonTo(buf)
127
+ }
128
+
129
+ if disk_usage != nil {
130
+ buf.WriteString(`,"net":`)
131
+ net_usage.WriteJsonTo(buf)
132
+ }
133
+
134
+ buf.WriteByte('}')
135
+
136
+ fmt.Println(buf.String())
137
+ } else {
138
+ if option.title == "" {
139
+ fmt.Println("== performance summary ==")
140
+ } else {
141
+ fmt.Printf("== performance summary of '%s' ==\n", option.title)
142
+ }
143
+ fmt.Printf(`
144
+ Duration: %.3f sec
145
+
146
+ `,
147
+ interval.Seconds())
148
+ if cpu_usage != nil {
149
+ fmt.Printf(`* Average CPU usage (MAX: %d %%)
150
+ * Non-idle usage: %.2f %%
151
+ %%usr: %.2f %%
152
+ %%sys: %.2f %%
153
+ %%irq: %.2f %%
154
+ %%soft: %.2f %%
155
+ %%other: %.2f %%
156
+ * Idle usage: %.2f %%
157
+ %%iowait: %.2f %%
158
+ %%idle: %.2f %%
159
+
160
+ `,
161
+ 100*cpu_usage.NumCore,
162
+ 100.0*float64(cpu_usage.NumCore)-cpu_usage.All.Idle-cpu_usage.All.Iowait,
163
+ cpu_usage.All.User+cpu_usage.All.Nice,
164
+ cpu_usage.All.Sys,
165
+ cpu_usage.All.Hardirq,
166
+ cpu_usage.All.Softirq,
167
+ cpu_usage.All.Steal,
168
+ cpu_usage.All.Idle+cpu_usage.All.Iowait,
169
+ cpu_usage.All.Iowait, cpu_usage.All.Idle)
170
+ }
171
+
172
+ if disk_usage != nil {
173
+ devices := []string{}
174
+
175
+ for device, _ := range *disk_usage {
176
+ if device != "total" {
177
+ devices = append(devices, device)
178
+ }
179
+ }
180
+ sort.Strings(devices)
181
+ if len(devices) > 1 {
182
+ devices = append(devices, "total")
183
+ }
184
+
185
+ for _, device := range devices {
186
+ e := (*disk_usage)[device]
187
+ fmt.Printf(`* Average DEVICE usage: %s
188
+ read IOPS: %.2f
189
+ write IOPS: %.2f
190
+ read throughput: %.2f MB/s
191
+ write throughput: %.2f MB/s
192
+ read latency: %.1f usec
193
+ write latency: %.1f usec
194
+ read amount: %.2f MB
195
+ write amount: %.2f MB
196
+
197
+ `,
198
+ device,
199
+ e.RdIops, e.WrIops,
200
+ e.RdSecps*512.0/1024.0/1024.0, e.WrSecps*512.0/1024.0/1024.0,
201
+ e.RdLatency*1000.0, e.WrLatency*1000.0,
202
+ float64(e.RdSectors*512)/1024.0/1024.0,
203
+ float64(e.WrSectors*512)/1024.0/1024.0)
204
+ }
205
+ }
206
+ }
207
+ }
@@ -0,0 +1,3 @@
1
+
2
+ all:
3
+ make -C ../
@@ -0,0 +1,60 @@
1
+ package subsystem
2
+
3
+ import (
4
+ "time"
5
+ )
6
+
7
+ /*
8
+
9
+ PerfMonger binary format:
10
+
11
+ 1. Common header
12
+ * platform type tag
13
+ * host info
14
+ * timestamp
15
+ * ...
16
+ 2. Platform-dependent header
17
+ * List of devices
18
+ * List of NICs
19
+ * List of IRQs
20
+ * CPU topology
21
+ * ...
22
+ 3. Record section
23
+ * Platform-dependent record data stream
24
+
25
+ */
26
+
27
+ //
28
+ // Common header
29
+ //
30
+
31
+ const (
32
+ Linux = 1
33
+ Darwin = 2
34
+ )
35
+
36
+ type PlatformType int
37
+
38
+ type CommonHeader struct {
39
+ Platform PlatformType
40
+ Hostname string
41
+ StartTime time.Time
42
+ }
43
+
44
+ //
45
+ // Platform-dependent header
46
+ //
47
+
48
+ type LinuxDevice struct {
49
+ Name string
50
+ Parts []string
51
+ }
52
+
53
+ type LinuxHeader struct {
54
+ Devices map[string]LinuxDevice
55
+ DevsParts []string
56
+ }
57
+
58
+ type DarwinHeader struct {
59
+ DevsParts []string
60
+ }
@@ -0,0 +1,22 @@
1
+ // +build darwin
2
+
3
+ package subsystem
4
+
5
+ type PlatformHeader DarwinHeader
6
+
7
+ func NewPlatformHeader() *DarwinHeader {
8
+ header := new(DarwinHeader)
9
+ return header
10
+ }
11
+
12
+ func ReadCpuStat(record *StatRecord) error {
13
+ return nil
14
+ }
15
+
16
+ func ReadDiskStats(record *StatRecord, targets *map[string]bool) error {
17
+ return nil
18
+ }
19
+
20
+ func ReadNetStat(record *StatRecord) error {
21
+ return nil
22
+ }