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,496 @@
1
+ package subsystem
2
+
3
+ import (
4
+ "bytes"
5
+ "encoding/json"
6
+ "math"
7
+ "regexp"
8
+ "strconv"
9
+ "strings"
10
+ "testing"
11
+ "time"
12
+ )
13
+
14
+ func isValidJson(byt []byte) bool {
15
+ var val interface{}
16
+ err := json.Unmarshal(byt, &val)
17
+ return err == nil
18
+ }
19
+
20
+ func jsonHasKey(byt []byte, key_path string) bool {
21
+ var val interface{}
22
+ err := json.Unmarshal(byt, &val)
23
+ if err != nil {
24
+ return false
25
+ }
26
+
27
+ keys := strings.Split(key_path, ".")
28
+
29
+ array_re := regexp.MustCompile("^\\[(\\d+)\\]$")
30
+
31
+ for _, key := range keys {
32
+ var idx int = -1
33
+ m := array_re.FindStringSubmatch(key)
34
+ if len(m) > 0 {
35
+ _i, err := strconv.ParseInt(m[1], 10, 32)
36
+ if err != nil {
37
+ return false
38
+ }
39
+ idx = int(_i)
40
+ }
41
+
42
+ switch val.(type) {
43
+ case map[string]interface{}:
44
+ v, ok := val.(map[string]interface{})[key]
45
+ if !ok {
46
+ return false
47
+ }
48
+ val = v
49
+ case []interface{}:
50
+ if idx < 0 {
51
+ return false
52
+ }
53
+ val = val.([]interface{})[idx]
54
+ default:
55
+ return false
56
+ }
57
+ }
58
+
59
+ return true
60
+ }
61
+
62
+ func floatEqWithin(val1, val2, epsilon float64) bool {
63
+ return math.Abs(val1-val2) < epsilon
64
+ }
65
+
66
+ func TestGetCoreUsage(t *testing.T) {
67
+ var err error
68
+
69
+ c1 := new(CpuCoreStat)
70
+ c2 := new(CpuCoreStat)
71
+
72
+ c1.User = 10
73
+ c2.User = 5
74
+ c2.Sys = 5
75
+
76
+ // should return error if uptime is the same
77
+ _, err = GetCpuCoreUsage(c1, c2)
78
+ if err == nil {
79
+ t.Error("err == nil, want non-nil")
80
+ }
81
+
82
+ // should return error if c1.Uptime() is larger than c2.Uptime()
83
+ c1.User = 100
84
+ _, err = GetCpuCoreUsage(c1, c2)
85
+ if err == nil {
86
+ t.Error("err == nil, want non-nil")
87
+ }
88
+
89
+ // should return 75% usr and 25% sys usage
90
+ var usage *CpuCoreUsage
91
+ c1.Clear()
92
+ c2.Clear()
93
+ c1.User = 100
94
+ c1.Sys = 50
95
+ c2.User = c1.User + 300
96
+ c2.Sys = c1.Sys + 100
97
+ usage, err = GetCpuCoreUsage(c1, c2)
98
+ if err != nil {
99
+ t.Errorf("err == %v, want nil", err)
100
+ }
101
+ if usage == nil {
102
+ t.Error("usage == nil, want non-nil")
103
+ }
104
+ if !floatEqWithin(usage.User, 75.0, 0.01) {
105
+ t.Errorf("usage.User = %v, want 75.0", usage.User)
106
+ }
107
+ if !floatEqWithin(usage.Sys, 25.0, 0.01) {
108
+ t.Errorf("usage.Sys = %v, want 25.0", usage.User)
109
+ }
110
+
111
+ buf := bytes.NewBuffer([]byte{})
112
+ usage.WriteJsonTo(buf)
113
+ if !isValidJson(buf.Bytes()) {
114
+ t.Errorf("Invalid JSON: %s", buf.String())
115
+ }
116
+ }
117
+
118
+ func TestGetCpuUsage(t *testing.T) {
119
+ var err error
120
+ var usage *CpuUsage
121
+
122
+ num_core := 2
123
+ c1 := NewCpuStat(num_core)
124
+ c2 := NewCpuStat(num_core)
125
+
126
+ usage, err = GetCpuUsage(c1, c2)
127
+ if err == nil {
128
+ t.Error("Error should be returned because no difference between c1 and c2")
129
+ }
130
+ if usage != nil {
131
+ t.Error("Nil should be returned as usage")
132
+ }
133
+
134
+ c1.CoreStats[0].User = 100
135
+ c1.CoreStats[0].Sys = 50
136
+ c1.CoreStats[1].User = 100
137
+ c1.CoreStats[1].Sys = 50
138
+
139
+ c2.CoreStats[0].User = c1.CoreStats[0].User + 300
140
+ c2.CoreStats[0].Sys = c1.CoreStats[0].Sys + 100
141
+ c2.CoreStats[1].User = c1.CoreStats[0].User + 300
142
+ c2.CoreStats[1].Sys = c1.CoreStats[0].Sys + 100
143
+
144
+ usage, err = GetCpuUsage(c1, c2)
145
+ if err == nil {
146
+ t.Error("Error should be returned because no progress in .All uptime")
147
+ }
148
+ if usage != nil {
149
+ t.Error("Nil should be returned as usage")
150
+ }
151
+
152
+ c1.All.User = 200
153
+ c1.All.Sys = 100
154
+ c2.All.User = c1.All.User + 600
155
+ c2.All.Sys = c1.All.Sys + 200
156
+
157
+ c1.CoreStats[0].Clear()
158
+ c2.CoreStats[0].Clear()
159
+
160
+ usage, err = GetCpuUsage(c1, c2)
161
+ if err == nil {
162
+ t.Error("Error should be returned because no progress in .CoreStats[9] uptime")
163
+ }
164
+ if usage != nil {
165
+ t.Error("Nil should be returned as usage")
166
+ }
167
+
168
+ c1.CoreStats[0].User = 100
169
+ c1.CoreStats[0].Sys = 50
170
+ c2.CoreStats[0].User = c1.CoreStats[0].User + 300
171
+ c2.CoreStats[0].Sys = c1.CoreStats[0].Sys + 100
172
+ usage, err = GetCpuUsage(c1, c2)
173
+ if err != nil {
174
+ t.Error("Error should not be returned.")
175
+ }
176
+ if usage == nil {
177
+ t.Error("usage == nil, want non-nil")
178
+ }
179
+ if !floatEqWithin(usage.All.User, 150.0, 0.01) {
180
+ t.Errorf("usage.User = %v, want 150.0", usage.All.User)
181
+ }
182
+ if !floatEqWithin(usage.All.Sys, 50.0, 0.01) {
183
+ t.Errorf("usage.Sys = %v, want 50.0", usage.All.User)
184
+ }
185
+ if !floatEqWithin(usage.CoreUsages[0].User, 75.0, 0.01) {
186
+ t.Errorf("usage.User = %v, want 75.0", usage.CoreUsages[0].User)
187
+ }
188
+ if !floatEqWithin(usage.CoreUsages[0].Sys, 25.0, 0.01) {
189
+ t.Errorf("usage.Sys = %v, want 25.0", usage.CoreUsages[0].User)
190
+ }
191
+ if !floatEqWithin(usage.CoreUsages[1].User, 75.0, 0.01) {
192
+ t.Errorf("usage.User = %v, want 75.0", usage.CoreUsages[1].User)
193
+ }
194
+ if !floatEqWithin(usage.CoreUsages[1].Sys, 25.0, 0.01) {
195
+ t.Errorf("usage.Sys = %v, want 25.0", usage.CoreUsages[1].User)
196
+ }
197
+
198
+ buf := bytes.NewBuffer([]byte{})
199
+ usage.WriteJsonTo(buf)
200
+ if !isValidJson(buf.Bytes()) {
201
+ t.Errorf("Invalid JSON: %s", buf.String())
202
+ }
203
+
204
+ assertHasKey := func(key_path string) {
205
+ if !jsonHasKey(buf.Bytes(), key_path) {
206
+ t.Errorf("%v is not present in JSON:\n%v", key_path, buf.String())
207
+ }
208
+ }
209
+ assertHasKey("num_core")
210
+ assertHasKey("cores")
211
+ assertHasKey("cores.[0]")
212
+ assertHasKey("cores.[0].usr")
213
+ assertHasKey("cores.[0].sys")
214
+ assertHasKey("cores.[0].nice")
215
+ assertHasKey("cores.[0].idle")
216
+ assertHasKey("cores.[0].iowait")
217
+ assertHasKey("cores.[0].hardirq")
218
+ assertHasKey("cores.[0].softirq")
219
+ assertHasKey("cores.[0].steal")
220
+ assertHasKey("cores.[0].guest")
221
+ assertHasKey("cores.[0].guestnice")
222
+ }
223
+
224
+ func TestDiskUsage(t *testing.T) {
225
+ d1 := NewDiskStat()
226
+ d2 := NewDiskStat()
227
+ t1, perr := time.Parse(time.RFC3339, "2012-01-23T01:23:45+09:00")
228
+ t2 := t1
229
+ if perr != nil {
230
+ t.Error("Timestamp parse error")
231
+ }
232
+
233
+ _, err := GetDiskUsage(t1, d1, t2, d2)
234
+ if err == nil {
235
+ t.Error("Error should be returned because timestamps are the same")
236
+ }
237
+
238
+ interval_duration := time.Second * 2
239
+ interval := interval_duration.Seconds()
240
+ t2 = t1.Add(interval_duration)
241
+
242
+ _, err = GetDiskUsage(t1, d1, t2, d2)
243
+ if err == nil {
244
+ t.Error("Error should be returned because no entries in DiskStat")
245
+ }
246
+
247
+ d1.Entries = append(d1.Entries, NewDiskStatEntry())
248
+ d1.Entries[0].Name = "sda"
249
+ d1.Entries[0].RdIos = 100
250
+ d1.Entries[0].RdSectors = 200
251
+ d1.Entries[0].RdTicks = 500
252
+
253
+ d2.Entries = append(d2.Entries, NewDiskStatEntry())
254
+ d2.Entries[0].Name = "sda"
255
+ d2.Entries[0].RdIos = d1.Entries[0].RdIos + 200
256
+ d2.Entries[0].RdSectors = d1.Entries[0].RdSectors + 150
257
+ d2.Entries[0].RdTicks = d1.Entries[0].RdTicks + 1000
258
+
259
+ var usage *DiskUsage
260
+ usage, err = GetDiskUsage(t1, d1, t2, d2)
261
+ if err != nil {
262
+ t.Error("Error should be returned.")
263
+ }
264
+ _, sda_ok := (*usage)["sda"]
265
+ _, total_ok := (*usage)["total"]
266
+ if len(*usage) != 2 || !sda_ok || !total_ok {
267
+ t.Errorf("DiskUsage = %v, want 2 entries 'sda' and 'total'", *usage)
268
+ }
269
+ if (*usage)["sda"].Interval != interval_duration {
270
+ t.Errorf("sda.Interval = %v, want %v", (*usage)["sda"].Interval, interval_duration)
271
+ }
272
+ if !floatEqWithin((*usage)["sda"].RdIops, 200.0/interval, 0.001) {
273
+ t.Errorf("sda.RdIops = %v, want %v", (*usage)["sda"].RdIops, 200.0/interval)
274
+ }
275
+ if !floatEqWithin((*usage)["sda"].RdLatency, 1000.0/200.0, 0.001) {
276
+ t.Errorf("sda.RdLatency = %v, want %v", (*usage)["sda"].RdLatency, 1000.0/200.0)
277
+ }
278
+ if (*usage)["sda"].RdSectors != 150 {
279
+ t.Errorf("sda.RdSectors = %v, want %v", (*usage)["sda"].RdSectors, 350)
280
+ }
281
+
282
+ buf := bytes.NewBuffer([]byte{})
283
+ usage.WriteJsonTo(buf)
284
+ if !isValidJson(buf.Bytes()) {
285
+ t.Errorf("invalid json: %s", buf.String())
286
+ }
287
+
288
+ d1.Entries = append(d1.Entries, NewDiskStatEntry())
289
+ d1.Entries[1].Name = "sdb"
290
+ d1.Entries[1].RdIos = 200
291
+ d1.Entries[1].RdSectors = 400
292
+ d1.Entries[1].RdTicks = 10000
293
+
294
+ d2.Entries = append(d2.Entries, NewDiskStatEntry())
295
+ d2.Entries[1].Name = "sdb"
296
+ d2.Entries[1].RdIos = d1.Entries[1].RdIos + 300
297
+ d2.Entries[1].RdSectors = d1.Entries[1].RdSectors + 200
298
+ d2.Entries[1].RdTicks = d1.Entries[1].RdTicks + 1000
299
+
300
+ usage, err = GetDiskUsage(t1, d1, t2, d2)
301
+ if err != nil {
302
+ t.Error("Error should not be returned.")
303
+ }
304
+ _, sda_ok = (*usage)["sda"]
305
+ _, sdb_ok := (*usage)["sda"]
306
+ _, total_ok = (*usage)["total"]
307
+ if len(*usage) != 3 || !sda_ok || !sdb_ok || !total_ok {
308
+ t.Errorf("DiskUsage = %v, want 3 entries 'sda', 'sdb' and 'total'.",
309
+ *usage)
310
+ }
311
+ if !floatEqWithin((*usage)["sdb"].RdIops, 300.0/interval, 0.001) {
312
+ t.Errorf("sdb.RdIops = %v, want %v", (*usage)["sdb"].RdIops, 300.0/interval)
313
+ }
314
+ if !floatEqWithin((*usage)["sdb"].RdLatency, 1000.0/300.0, 0.001) {
315
+ t.Errorf("sdb.RdLatency = %v, want %v", (*usage)["sdb"].RdLatency, 1000.0/300.0)
316
+ }
317
+ if (*usage)["sdb"].RdSectors != 200.0 {
318
+ t.Errorf("sdb.RdSectors = %v, want %v", (*usage)["sdb"].RdSectors, 200)
319
+ }
320
+
321
+ if !floatEqWithin(
322
+ (*usage)["sda"].RdIops+(*usage)["sdb"].RdIops,
323
+ (*usage)["total"].RdIops, 0.001) {
324
+ t.Errorf("sda.RdIops+sdb.RdIops = %v, total.RdIops = %v, want %v",
325
+ (*usage)["sda"].RdIops+(*usage)["sdb"].RdIops, (*usage)["total"].RdIops,
326
+ (200.0+300.0)/interval)
327
+ }
328
+ weighted_latency := (*usage)["sda"].RdLatency*(200.0/(200.0+300.0)) + (*usage)["sdb"].RdLatency*(300.0/(200.0+300.0))
329
+ if !floatEqWithin(weighted_latency, (*usage)["total"].RdLatency, 0.001) {
330
+ t.Errorf("weighted avg latency(sda+sdb) = %v, total.RdLatency = %v, want %v",
331
+ weighted_latency, (*usage)["total"].RdLatency,
332
+ (2.0/5.0)*1000.0/200.0+(3.0/5.0)*1000.0/300.0)
333
+ }
334
+ if (*usage)["total"].RdSectors != 350 {
335
+ t.Errorf("total.RdSectors = %v, want %v", (*usage)["total"].RdSectors, 350)
336
+ }
337
+
338
+ buf = bytes.NewBuffer([]byte{})
339
+ usage.WriteJsonTo(buf)
340
+ if !isValidJson(buf.Bytes()) {
341
+ t.Errorf("invalid json: %s", buf.String())
342
+ }
343
+
344
+ assertHasKey := func(key_path string) {
345
+ if !jsonHasKey(buf.Bytes(), key_path) {
346
+ t.Errorf("%v is not present in JSON:\n%v", key_path, buf.String())
347
+ }
348
+ }
349
+ assertHasKey("devices")
350
+ assertHasKey("sda")
351
+ assertHasKey("sda.riops")
352
+ assertHasKey("sda.wiops")
353
+ assertHasKey("sda.rkbyteps")
354
+ assertHasKey("sda.wkbyteps")
355
+ assertHasKey("sda.rlatency")
356
+ assertHasKey("sda.wlatency")
357
+ assertHasKey("sda.rsize")
358
+ assertHasKey("sda.wsize")
359
+ assertHasKey("sda.qlen")
360
+ assertHasKey("total")
361
+ }
362
+
363
+ func TestGetNetUsage(t *testing.T) {
364
+ n1 := NewNetStat()
365
+ n2 := NewNetStat()
366
+
367
+ t1, perr := time.Parse(time.RFC3339, "2012-01-23T01:23:45+09:00")
368
+ t2 := t1
369
+ if perr != nil {
370
+ t.Error("Timestamp parse error")
371
+ }
372
+
373
+ _, err := GetNetUsage(t1, n1, t2, n2)
374
+ if err == nil {
375
+ t.Error("Error should be returned because timestamps are the same")
376
+ }
377
+
378
+ interval_duration := time.Second * 2
379
+ interval := interval_duration.Seconds()
380
+ t2 = t1.Add(interval_duration)
381
+
382
+ _, err = GetNetUsage(t1, n1, t2, n2)
383
+ if err == nil {
384
+ t.Error("Error should be returned because no entries in NetStat")
385
+ }
386
+
387
+ n1.Entries = append(n1.Entries, NewNetStatEntry())
388
+ n1.Entries[0].Name = "lo"
389
+ n1.Entries[0].RxBytes = 12345
390
+ n1.Entries[0].RxPackets = 12345
391
+
392
+ n2.Entries = append(n2.Entries, NewNetStatEntry())
393
+ n2.Entries[0].Name = "lo"
394
+ n2.Entries[0].RxBytes = n1.Entries[0].RxBytes + 8000
395
+ n2.Entries[0].RxPackets = n1.Entries[0].RxPackets + 100
396
+
397
+ var usage *NetUsage
398
+ usage, err = GetNetUsage(t1, n1, t2, n2)
399
+ if err != nil {
400
+ t.Errorf("Error should not be returned with:\n n1 = %v\n n2 = %v",
401
+ n1, n2)
402
+ }
403
+ _, lo_ok := (*usage)["lo"]
404
+ _, total_ok := (*usage)["total"]
405
+ if len(*usage) != 2 || !lo_ok || !total_ok {
406
+ t.Errorf("NetUsage = %v, want 2 entries \"lo\" and \"total\"", usage)
407
+ }
408
+
409
+ if (*usage)["lo"].Interval != interval_duration {
410
+ t.Errorf("lo.Interval = %v, want %v", (*usage)["lo"].Interval, interval_duration)
411
+ }
412
+ if !floatEqWithin((*usage)["lo"].RxBytesPerSec, 8000.0/interval, 0.001) {
413
+ t.Errorf("lo.RxBytesPerSec = %v, want %v", (*usage)["lo"].RxBytesPerSec, 8000.0/interval)
414
+ }
415
+ if !floatEqWithin((*usage)["lo"].RxPacketsPerSec, 100.0/interval, 0.001) {
416
+ t.Errorf("lo.RxPacketsPerSec = %v, want %v", (*usage)["lo"].RxPacketsPerSec, 100.0/interval)
417
+ }
418
+
419
+ buf := bytes.NewBuffer([]byte{})
420
+ usage.WriteJsonTo(buf)
421
+ if !isValidJson(buf.Bytes()) {
422
+ t.Errorf("invalid json: %s", buf.String())
423
+ }
424
+
425
+ n1.Entries = append(n1.Entries, NewNetStatEntry())
426
+ n1.Entries[1].Name = "eth0"
427
+ n1.Entries[1].RxBytes = 45678
428
+ n1.Entries[1].RxPackets = 123
429
+
430
+ usage, err = GetNetUsage(t1, n1, t2, n2)
431
+ if err != nil {
432
+ t.Errorf("Error should not be returned with:\n n1 = %v\n n2 = %v",
433
+ n1, n2)
434
+ }
435
+ _, lo_ok = (*usage)["lo"]
436
+ _, total_ok = (*usage)["total"]
437
+ if len(*usage) != 2 || !lo_ok || !total_ok {
438
+ t.Errorf(`NetUsage = %v, want 2 entry "lo" and "total".`, usage)
439
+ }
440
+
441
+ n2.Entries = append(n2.Entries, NewNetStatEntry())
442
+ n2.Entries[1].Name = "eth0"
443
+ n2.Entries[1].RxBytes = n1.Entries[1].RxBytes + 7000
444
+ n2.Entries[1].RxPackets = n1.Entries[1].RxPackets + 150
445
+
446
+ usage, err = GetNetUsage(t1, n1, t2, n2)
447
+ if err != nil {
448
+ t.Errorf("Error should not be returned with:\n n1 = %v\n n2 = %v",
449
+ n1, n2)
450
+ }
451
+ _, lo_ok = (*usage)["lo"]
452
+ _, eth0_ok := (*usage)["eth0"]
453
+ _, total_ok = (*usage)["total"]
454
+ if len(*usage) != 3 || !lo_ok || !eth0_ok || !total_ok {
455
+ t.Errorf(`NetUsage = %v, want 3 entry "lo", "eth0" and "total".`, usage)
456
+ }
457
+
458
+ if !floatEqWithin((*usage)["eth0"].RxBytesPerSec, 7000.0/interval, 0.001) {
459
+ t.Errorf("eth0.RxBytesPerSec = %v, want %v", (*usage)["eth0"].RxBytesPerSec, 7000.0/interval)
460
+ }
461
+ if !floatEqWithin((*usage)["eth0"].RxPacketsPerSec, 150.0/interval, 0.001) {
462
+ t.Errorf("eth0.RxPacketsPerSec = %v, want %v", (*usage)["eth0"].RxPacketsPerSec, 150.0/interval)
463
+ }
464
+
465
+ if !floatEqWithin((*usage)["total"].RxBytesPerSec, (8000.0+7000.0)/interval, 0.001) {
466
+ t.Errorf("total.RxBytesPerSec = %v, want %v", (*usage)["total"].RxBytesPerSec,
467
+ (8000.0+7000.0)/interval)
468
+ }
469
+ if !floatEqWithin((*usage)["total"].RxPacketsPerSec, (100.0+150.0)/interval, 0.001) {
470
+ t.Errorf("total.RxPacketsPerSec = %v, want %v", (*usage)["total"].RxPacketsPerSec,
471
+ (100.0+150.0)/interval)
472
+ }
473
+
474
+ buf = bytes.NewBuffer([]byte{})
475
+ usage.WriteJsonTo(buf)
476
+ if !isValidJson(buf.Bytes()) {
477
+ t.Errorf("invalid json: %s", buf.String())
478
+ }
479
+
480
+ assertHasKey := func(key_path string) {
481
+ if !jsonHasKey(buf.Bytes(), key_path) {
482
+ t.Errorf("%v is not present in JSON:\n%v", key_path, buf.String())
483
+ }
484
+ }
485
+ assertHasKey("devices")
486
+ assertHasKey("devices.[0]")
487
+ assertHasKey("eth0")
488
+ assertHasKey("eth0.rxkbyteps")
489
+ assertHasKey("eth0.txkbyteps")
490
+ assertHasKey("eth0.rxpktps")
491
+ assertHasKey("eth0.txpktps")
492
+ assertHasKey("eth0.rxerrps")
493
+ assertHasKey("eth0.txerrps")
494
+ assertHasKey("eth0.rxdropps")
495
+ assertHasKey("eth0.txdropps")
496
+ }
@@ -0,0 +1,59 @@
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
+
29
+ //
30
+ // Common header
31
+ //
32
+
33
+ const (
34
+ Linux = 1
35
+ Darwin = 2
36
+ )
37
+
38
+ type PlatformType int
39
+
40
+ type CommonHeader struct {
41
+ Platform PlatformType
42
+ Hostname string
43
+ StartTime time.Time
44
+ }
45
+
46
+
47
+ //
48
+ // Platform-dependent header
49
+ //
50
+
51
+ type LinuxDevice struct {
52
+ Name string
53
+ Parts []string
54
+ }
55
+
56
+ type LinuxHeader struct {
57
+ Devices map[string]LinuxDevice
58
+ DevsParts []string
59
+ }
@@ -40,12 +40,16 @@ EOS
40
40
  # important command first: sort by [priority, name]
41
41
  command_name = command.command_name
42
42
  case command_name
43
- when "record"
43
+ when "live"
44
44
  [0, command_name]
45
- when "stat"
45
+ when "record"
46
46
  [1, command_name]
47
- when "plot"
47
+ when "play"
48
48
  [2, command_name]
49
+ when "stat"
50
+ [3, command_name]
51
+ when "plot"
52
+ [4, command_name]
49
53
  else
50
54
  [999, command_name]
51
55
  end
@@ -71,6 +75,7 @@ EOS
71
75
 
72
76
  Commands:
73
77
  #{command_list_str}
78
+
74
79
  EOS
75
80
 
76
81
  parser.summary_indent = " "
@@ -0,0 +1,62 @@
1
+
2
+ module PerfMonger
3
+ module Command
4
+
5
+ class CoreFinder
6
+ class << self
7
+ def find(name, os = nil, arch = nil)
8
+ # check os
9
+ unless os
10
+ case RUBY_PLATFORM
11
+ when /linux/
12
+ os = "linux"
13
+ when /darwin/
14
+ os = "darwin"
15
+ else
16
+ os = nil
17
+ end
18
+ end
19
+
20
+ # check arch
21
+ unless arch
22
+ case RUBY_PLATFORM
23
+ when /x86_64|amd64/
24
+ arch = "amd64"
25
+ when /i\d86/
26
+ arch = "386"
27
+ else
28
+ arch = nil
29
+ end
30
+ end
31
+
32
+ if !os || !arch
33
+ return nil
34
+ end
35
+
36
+ suffix = "_" + os + "_" + arch
37
+
38
+ path = File.expand_path("../../../exec/perfmonger-#{name}#{suffix}", __FILE__)
39
+
40
+ if File.executable?(path)
41
+ return path
42
+ else
43
+ return nil
44
+ end
45
+ end
46
+
47
+ def recorder
48
+ self.find("recorder")
49
+ end
50
+
51
+ def player
52
+ self.find("player")
53
+ end
54
+
55
+ def summarizer
56
+ self.find("summarizer")
57
+ end
58
+ end
59
+ end
60
+
61
+ end # module
62
+ end # module
@@ -0,0 +1,39 @@
1
+ require 'optparse'
2
+ require 'tempfile'
3
+ require 'tmpdir'
4
+ require 'json'
5
+
6
+ module PerfMonger
7
+ module Command
8
+
9
+ class LiveOption < RecordOption
10
+ def make_command
11
+ cmd = super()
12
+ @player_bin = ::PerfMonger::Command::CoreFinder.player()
13
+ cmd += ["-player-bin", @player_bin]
14
+ end
15
+ end
16
+
17
+ class LiveCommand < BaseCommand
18
+ register_command 'live', 'Record and play system performance information in JSON'
19
+
20
+ def initialize
21
+ super
22
+ end
23
+
24
+ def run(argv)
25
+ @argv, @option = PerfMonger::Command::LiveOption.parse(argv)
26
+
27
+ exec_live_cmd()
28
+ end
29
+
30
+ private
31
+ def exec_live_cmd()
32
+ cmd = @option.make_command
33
+
34
+ Process.exec(*cmd)
35
+ end
36
+ end
37
+
38
+ end # module Command
39
+ end # module PerfMonger