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.
- checksums.yaml +5 -13
- data/.gitignore +6 -0
- data/.tachikoma.yml +1 -0
- data/.travis.yml +18 -6
- data/Gemfile +1 -3
- data/Guardfile +26 -0
- data/NEWS +21 -0
- data/README.md +8 -9
- data/Rakefile +33 -1
- data/core/Makefile +23 -0
- data/core/build.sh +48 -0
- data/core/perfmonger-player.go +165 -0
- data/core/perfmonger-recorder.go +296 -0
- data/core/perfmonger-summarizer.go +207 -0
- data/core/subsystem/Makefile +3 -0
- data/core/subsystem/perfmonger.go +60 -0
- data/core/subsystem/perfmonger_darwin.go +22 -0
- data/core/subsystem/perfmonger_linux.go +292 -0
- data/core/subsystem/perfmonger_linux_test.go +73 -0
- data/core/subsystem/stat.go +214 -0
- data/core/subsystem/stat_test.go +281 -0
- data/core/subsystem/usage.go +410 -0
- data/core/subsystem/usage_test.go +496 -0
- data/lib/exec/operationBinding.rb.svn-base +59 -0
- data/lib/exec/perfmonger-player_darwin_amd64 +0 -0
- data/lib/exec/perfmonger-player_linux_386 +0 -0
- data/lib/exec/perfmonger-player_linux_amd64 +0 -0
- data/lib/exec/perfmonger-recorder_darwin_amd64 +0 -0
- data/lib/exec/perfmonger-recorder_linux_386 +0 -0
- data/lib/exec/perfmonger-recorder_linux_amd64 +0 -0
- data/lib/exec/perfmonger-summarizer_darwin_amd64 +0 -0
- data/lib/exec/perfmonger-summarizer_linux_386 +0 -0
- data/lib/exec/perfmonger-summarizer_linux_amd64 +0 -0
- data/lib/exec/perfmonger-summary_linux_386 +0 -0
- data/lib/exec/perfmonger-summary_linux_amd64 +0 -0
- data/lib/perfmonger/cli.rb +8 -3
- data/lib/perfmonger/command/core.rb +62 -0
- data/lib/perfmonger/command/live.rb +39 -0
- data/lib/perfmonger/command/play.rb +56 -0
- data/lib/perfmonger/command/plot.rb +30 -22
- data/lib/perfmonger/command/record.rb +3 -2
- data/lib/perfmonger/command/record_option.rb +40 -59
- data/lib/perfmonger/command/server.rb +7 -2
- data/lib/perfmonger/command/stat.rb +2 -2
- data/lib/perfmonger/command/stat_option.rb +1 -1
- data/lib/perfmonger/command/summary.rb +11 -326
- data/lib/perfmonger/version.rb +1 -3
- data/lib/perfmonger.rb +3 -0
- data/misc/_perfmonger +128 -0
- data/misc/perfmonger-completion.bash +49 -0
- data/perfmonger.gemspec +6 -5
- data/spec/data/busy100.pgr +0 -0
- data/spec/fingerprint_spec.rb +35 -0
- data/spec/live_spec.rb +25 -0
- data/spec/perfmonger_spec.rb +37 -0
- data/spec/play_spec.rb +21 -0
- data/spec/plot_spec.rb +42 -0
- data/spec/record_spec.rb +15 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/stat_spec.rb +15 -0
- data/spec/summary_spec.rb +51 -0
- data/spec/support/aruba.rb +11 -0
- data/wercker.yml +59 -0
- metadata +117 -45
- data/ext/perfmonger/extconf.rb +0 -19
- data/ext/perfmonger/perfmonger.h +0 -58
- data/ext/perfmonger/perfmonger_record.c +0 -754
- data/ext/perfmonger/sysstat/common.c +0 -627
- data/ext/perfmonger/sysstat/common.h +0 -207
- data/ext/perfmonger/sysstat/ioconf.c +0 -515
- data/ext/perfmonger/sysstat/ioconf.h +0 -84
- data/ext/perfmonger/sysstat/iostat.c +0 -1100
- data/ext/perfmonger/sysstat/iostat.h +0 -121
- data/ext/perfmonger/sysstat/libsysstat.h +0 -19
- data/ext/perfmonger/sysstat/mpstat.c +0 -953
- data/ext/perfmonger/sysstat/mpstat.h +0 -79
- data/ext/perfmonger/sysstat/rd_stats.c +0 -2388
- data/ext/perfmonger/sysstat/rd_stats.h +0 -651
- data/ext/perfmonger/sysstat/sysconfig.h +0 -13
- data/test/run-test.sh +0 -39
- data/test/spec/bin_spec.rb +0 -37
- data/test/spec/data/2devices.expected +0 -42
- data/test/spec/data/2devices.output +0 -42
- data/test/spec/spec_helper.rb +0 -20
- data/test/spec/summary_spec.rb +0 -193
- data/test/test-perfmonger.c +0 -145
- 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
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/perfmonger/cli.rb
CHANGED
@@ -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 "
|
43
|
+
when "live"
|
44
44
|
[0, command_name]
|
45
|
-
when "
|
45
|
+
when "record"
|
46
46
|
[1, command_name]
|
47
|
-
when "
|
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
|