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,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