perfmonger 0.6.1 → 0.7.0

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