perfmonger 0.9.0 → 0.10.1

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.
@@ -59,7 +59,10 @@ func main() {
59
59
  if err != nil {
60
60
  panic(err)
61
61
  }
62
- dec := gob.NewDecoder(f)
62
+ defer f.Close()
63
+
64
+ input_reader := newPerfmongerLogReader(f)
65
+ dec := gob.NewDecoder(input_reader)
63
66
 
64
67
  err = dec.Decode(&cheader)
65
68
  if err == io.EOF {
@@ -103,6 +106,7 @@ func main() {
103
106
  lst_record := lst_records[idx]
104
107
 
105
108
  var cpu_usage *ss.CpuUsage = nil
109
+ var intr_usage *ss.InterruptUsage = nil
106
110
  var disk_usage *ss.DiskUsage = nil
107
111
  var net_usage *ss.NetUsage = nil
108
112
 
@@ -110,6 +114,13 @@ func main() {
110
114
  cpu_usage, err = ss.GetCpuUsage(fst_record.Cpu, lst_record.Cpu)
111
115
  }
112
116
 
117
+ if fst_record.Interrupt != nil && lst_record.Interrupt != nil {
118
+ intr_usage, err = ss.GetInterruptUsage(
119
+ fst_record.Time, fst_record.Interrupt,
120
+ lst_record.Time, lst_record.Interrupt,
121
+ )
122
+ }
123
+
113
124
  if fst_record.Disk != nil && lst_record.Disk != nil {
114
125
  disk_usage, err = ss.GetDiskUsage1(
115
126
  fst_record.Time, fst_record.Disk,
@@ -134,6 +145,11 @@ func main() {
134
145
  cpu_usage.WriteJsonTo(buf)
135
146
  }
136
147
 
148
+ if intr_usage != nil {
149
+ buf.WriteString(`,"intr":`)
150
+ intr_usage.WriteJsonTo(buf)
151
+ }
152
+
137
153
  if disk_usage != nil {
138
154
  buf.WriteString(`,"disk":`)
139
155
  disk_usage.WriteJsonTo(buf)
@@ -8,7 +8,10 @@ import (
8
8
  "fmt"
9
9
  "io"
10
10
  "os"
11
+ "os/exec"
11
12
  "runtime"
13
+ "strconv"
14
+ "strings"
12
15
  )
13
16
 
14
17
  type PlatformHeader LinuxHeader
@@ -92,7 +95,23 @@ func ReadCpuStat(record *StatRecord) error {
92
95
  defer f.Close()
93
96
 
94
97
  if record.Cpu == nil {
95
- record.Cpu = NewCpuStat(runtime.NumCPU())
98
+ num_core := 0
99
+ out, err := exec.Command("nproc", "--all").Output()
100
+ out_str := strings.TrimSpace(string(out))
101
+
102
+ if err == nil {
103
+ num_core, err = strconv.Atoi(out_str)
104
+
105
+ if err != nil {
106
+ num_core = 0
107
+ }
108
+ }
109
+
110
+ if num_core == 0 {
111
+ num_core = runtime.NumCPU()
112
+ }
113
+
114
+ record.Cpu = NewCpuStat(num_core)
96
115
  } else {
97
116
  record.Cpu.Clear()
98
117
  }
@@ -200,6 +219,89 @@ func ReadCpuStat(record *StatRecord) error {
200
219
  return nil
201
220
  }
202
221
 
222
+ func parseInterruptStatEntry(line string, num_core int) (*InterruptStatEntry, error) {
223
+ entry := new(InterruptStatEntry)
224
+
225
+ entry.NumCore = num_core
226
+ entry.IntrCounts = make([]int, num_core)
227
+
228
+ tokens := strings.Fields(line)
229
+
230
+ idx := 0
231
+
232
+ tok := tokens[0]
233
+ tok = strings.TrimRight(tok, ":")
234
+ if irqno, err := strconv.Atoi(tok); err == nil {
235
+ entry.IrqNo = irqno
236
+ entry.IrqType = ""
237
+ } else {
238
+ entry.IrqNo = -1
239
+ entry.IrqType = tok
240
+ }
241
+
242
+ for idx := 1; idx < num_core+1; idx += 1 {
243
+ var c int
244
+ var err error
245
+
246
+ if idx >= len(tokens) {
247
+ break
248
+ }
249
+
250
+ tok = tokens[idx]
251
+ if c, err = strconv.Atoi(tok); err != nil {
252
+ return nil, errors.New("Invalid string for IntrCounts element: " + tok)
253
+ }
254
+
255
+ entry.IntrCounts[idx-1] = c
256
+ }
257
+
258
+ idx = num_core + 1
259
+ if idx < len(tokens) {
260
+ entry.Descr = strings.Join(tokens[idx:], " ")
261
+ } else {
262
+ entry.Descr = ""
263
+ }
264
+
265
+ return entry, nil
266
+ }
267
+
268
+ func ReadInterruptStat(record *StatRecord) error {
269
+ intr_stat := NewInterruptStat()
270
+
271
+ if record == nil {
272
+ return errors.New("Valid *StatRecord is required.")
273
+ }
274
+
275
+ f, err := os.Open("/proc/interrupts")
276
+ if err != nil {
277
+ panic(err)
278
+ }
279
+ defer f.Close()
280
+ scan := bufio.NewScanner(f)
281
+
282
+ if !scan.Scan() {
283
+ return errors.New("/proc/interrupts seems to be empty")
284
+ }
285
+
286
+ cores := strings.Fields(scan.Text())
287
+ num_core := len(cores)
288
+
289
+ for scan.Scan() {
290
+ entry, err := parseInterruptStatEntry(scan.Text(), num_core)
291
+
292
+ if err != nil {
293
+ return err
294
+ }
295
+
296
+ intr_stat.Entries = append(intr_stat.Entries, entry)
297
+ intr_stat.NumEntries += 1
298
+ }
299
+
300
+ record.Interrupt = intr_stat
301
+
302
+ return nil
303
+ }
304
+
203
305
  func ReadDiskStats(record *StatRecord, targets *map[string]bool) error {
204
306
  if record == nil {
205
307
  return errors.New("Valid *StatRecord is required.")
@@ -294,6 +396,7 @@ func ReadNetStat(record *StatRecord) error {
294
396
  case line[0:7] == " face |":
295
397
  continue
296
398
  }
399
+ line = strings.Replace(line, ":", " ", -1)
297
400
 
298
401
  e := NewNetStatEntry()
299
402
 
@@ -314,7 +417,10 @@ func ReadNetStat(record *StatRecord) error {
314
417
  }
315
418
 
316
419
  // trim trailing ":" from devname
317
- e.Name = devname[0 : len(devname)-1]
420
+ if devname[len(devname)-1] == ':' {
421
+ devname = devname[0 : len(devname)-1]
422
+ }
423
+ e.Name = devname
318
424
 
319
425
  net_stat.Entries = append(net_stat.Entries, e)
320
426
  }
@@ -43,6 +43,20 @@ type SoftIrqStat struct {
43
43
  Rcu int64
44
44
  }
45
45
 
46
+ type InterruptStatEntry struct {
47
+ IrqNo int // >0 if associated with devices, -1 if not
48
+ IrqType string // set intr name if IrqNo == -1
49
+
50
+ NumCore int
51
+ IntrCounts []int
52
+ Descr string
53
+ }
54
+
55
+ type InterruptStat struct {
56
+ NumEntries uint
57
+ Entries []*InterruptStatEntry
58
+ }
59
+
46
60
  type DiskStatEntry struct {
47
61
  Major uint
48
62
  Minor uint
@@ -89,12 +103,13 @@ type NetStat struct {
89
103
  }
90
104
 
91
105
  type StatRecord struct {
92
- Time time.Time
93
- Cpu *CpuStat
94
- Proc *ProcStat
95
- Disk *DiskStat
96
- Softirq *SoftIrqStat
97
- Net *NetStat
106
+ Time time.Time
107
+ Cpu *CpuStat
108
+ Interrupt *InterruptStat
109
+ Proc *ProcStat
110
+ Disk *DiskStat
111
+ Softirq *SoftIrqStat
112
+ Net *NetStat
98
113
  }
99
114
 
100
115
  func (core_stat *CpuCoreStat) Clear() {
@@ -142,6 +157,14 @@ func (cpu_stat *CpuStat) Clear() {
142
157
  }
143
158
  }
144
159
 
160
+ func NewInterruptStat() *InterruptStat {
161
+ intr_stat := new(InterruptStat)
162
+ intr_stat.NumEntries = 0
163
+ intr_stat.Entries = make([]*InterruptStatEntry, 0)
164
+
165
+ return intr_stat
166
+ }
167
+
145
168
  func NewProcStat() *ProcStat {
146
169
  return &ProcStat{0, 0}
147
170
  }
@@ -204,6 +227,7 @@ func NewStatRecord() *StatRecord {
204
227
  nil,
205
228
  nil,
206
229
  nil,
230
+ nil,
207
231
  }
208
232
  }
209
233
 
@@ -29,6 +29,20 @@ type CpuUsage struct {
29
29
  CoreUsages []*CpuCoreUsage
30
30
  }
31
31
 
32
+ type CpuCoreIntrUsage struct {
33
+ Device float64 // intr/sec for devices
34
+ System float64 // intr/sec for system internal interrupts (timers, TLB miss, ...)
35
+ }
36
+
37
+ type InterruptUsage struct {
38
+ Interval time.Duration
39
+
40
+ NumEntries uint
41
+ NumCore int
42
+
43
+ CoreIntrUsages []*CpuCoreIntrUsage
44
+ }
45
+
32
46
  type DiskUsageEntry struct {
33
47
  Interval time.Duration
34
48
 
@@ -169,6 +183,68 @@ func GetCpuUsage(c1 *CpuStat, c2 *CpuStat) (*CpuUsage, error) {
169
183
  return usage, nil
170
184
  }
171
185
 
186
+ func GetInterruptUsage(t1 time.Time, i1 *InterruptStat, t2 time.Time, i2 *InterruptStat) (*InterruptUsage, error) {
187
+ num_core := i1.Entries[0].NumCore
188
+
189
+ usage := new(InterruptUsage)
190
+ usage.Interval = t2.Sub(t1)
191
+ usage.NumEntries = i1.NumEntries
192
+ usage.NumCore = num_core
193
+ usage.CoreIntrUsages = make([]*CpuCoreIntrUsage, num_core)
194
+
195
+ for coreid := 0; coreid < usage.NumCore; coreid += 1 {
196
+ core_usage := new(CpuCoreIntrUsage)
197
+ core_usage.Device = 0
198
+ core_usage.System = 0
199
+
200
+ core_dev_count := 0
201
+ core_sys_count := 0
202
+
203
+ for idx, istat_entry1 := range i1.Entries {
204
+ istat_entry2 := i2.Entries[idx]
205
+
206
+ if istat_entry1.IrqNo != istat_entry2.IrqNo ||
207
+ istat_entry1.IrqType != istat_entry2.IrqType {
208
+ return nil, errors.New("Intr stat format changed")
209
+ }
210
+
211
+ countup := istat_entry2.IntrCounts[coreid] - istat_entry1.IntrCounts[coreid]
212
+ if istat_entry1.IrqNo != -1 {
213
+ core_dev_count += countup
214
+ } else {
215
+ core_sys_count += countup
216
+ }
217
+ }
218
+
219
+ core_usage.Device = float64(core_dev_count) / usage.Interval.Seconds()
220
+ core_usage.System = float64(core_sys_count) / usage.Interval.Seconds()
221
+
222
+ usage.CoreIntrUsages[coreid] = core_usage
223
+ }
224
+
225
+ return usage, nil
226
+ }
227
+
228
+ func (intr_usage *InterruptUsage) WriteJsonTo(buf *bytes.Buffer) {
229
+ buf.WriteString("{")
230
+ buf.WriteString(`"core_dev_intr":[`)
231
+ for idx, core_usage := range intr_usage.CoreIntrUsages {
232
+ if idx > 0 {
233
+ buf.WriteString(",")
234
+ }
235
+ fmt.Fprintf(buf, "%.2f", core_usage.Device)
236
+ }
237
+ buf.WriteString(`],"core_sys_intr":[`)
238
+ for idx, core_usage := range intr_usage.CoreIntrUsages {
239
+ if idx > 0 {
240
+ buf.WriteString(",")
241
+ }
242
+ fmt.Fprintf(buf, "%.2f", core_usage.System)
243
+ }
244
+ buf.WriteString("]")
245
+ buf.WriteString("}")
246
+ }
247
+
172
248
  func (duentry *DiskUsageEntry) WriteJsonTo(buf *bytes.Buffer) {
173
249
  fmt.Fprintf(buf,
174
250
  `{"riops":%.2f,"wiops":%.2f,"rkbyteps":%.2f,"wkbyteps":%.2f,"rlatency":%.3f,"wlatency":%.3f,"rsize":%.2f,"wsize":%.2f,"qlen":%.2f}`,
@@ -65,6 +65,7 @@ func floatEqWithin(val1, val2, epsilon float64) bool {
65
65
 
66
66
  func TestGetCoreUsage(t *testing.T) {
67
67
  var err error
68
+ var usage *CpuCoreUsage
68
69
 
69
70
  c1 := new(CpuCoreStat)
70
71
  c2 := new(CpuCoreStat)
@@ -73,10 +74,22 @@ func TestGetCoreUsage(t *testing.T) {
73
74
  c2.User = 5
74
75
  c2.Sys = 5
75
76
 
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")
77
+ // should return usage 0% with no error if uptime is the same
78
+ usage, err = GetCpuCoreUsage(c1, c2)
79
+ if err != nil {
80
+ t.Error("err != nil, want nil")
81
+ }
82
+ if !(usage.User == 0 &&
83
+ usage.Nice == 0 &&
84
+ usage.Sys == 0 &&
85
+ usage.Idle == 0 &&
86
+ usage.Iowait == 0 &&
87
+ usage.Hardirq == 0 &&
88
+ usage.Softirq == 0 &&
89
+ usage.Steal == 0 &&
90
+ usage.Guest == 0 &&
91
+ usage.GuestNice == 0) {
92
+ t.Error("usage is not 0%, want 0%")
80
93
  }
81
94
 
82
95
  // should return error if c1.Uptime() is larger than c2.Uptime()
@@ -87,7 +100,6 @@ func TestGetCoreUsage(t *testing.T) {
87
100
  }
88
101
 
89
102
  // should return 75% usr and 25% sys usage
90
- var usage *CpuCoreUsage
91
103
  c1.Clear()
92
104
  c2.Clear()
93
105
  c1.User = 100
@@ -124,11 +136,11 @@ func TestGetCpuUsage(t *testing.T) {
124
136
  c2 := NewCpuStat(num_core)
125
137
 
126
138
  usage, err = GetCpuUsage(c1, c2)
127
- if err == nil {
128
- t.Error("Error should be returned because no difference between c1 and c2")
139
+ if err != nil {
140
+ t.Error("Error should not be returned")
129
141
  }
130
- if usage != nil {
131
- t.Error("Nil should be returned as usage")
142
+ if usage == nil {
143
+ t.Error("Nil should not be returned as usage")
132
144
  }
133
145
 
134
146
  c1.CoreStats[0].User = 100
@@ -141,13 +153,13 @@ func TestGetCpuUsage(t *testing.T) {
141
153
  c2.CoreStats[1].User = c1.CoreStats[0].User + 300
142
154
  c2.CoreStats[1].Sys = c1.CoreStats[0].Sys + 100
143
155
 
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
- }
156
+ // usage, err = GetCpuUsage(c1, c2)
157
+ // if err == nil {
158
+ // t.Error("Error should be returned because no progress in .All uptime")
159
+ // }
160
+ // if usage != nil {
161
+ // t.Error("Nil should be returned as usage")
162
+ // }
151
163
 
152
164
  c1.All.User = 200
153
165
  c1.All.Sys = 100
@@ -158,12 +170,12 @@ func TestGetCpuUsage(t *testing.T) {
158
170
  c2.CoreStats[0].Clear()
159
171
 
160
172
  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
- }
173
+ // if err == nil {
174
+ // t.Error("Error should be returned because no progress in .CoreStats[9] uptime")
175
+ // }
176
+ // if usage != nil {
177
+ // t.Error("Nil should be returned as usage")
178
+ // }
167
179
 
168
180
  c1.CoreStats[0].User = 100
169
181
  c1.CoreStats[0].Sys = 50
@@ -0,0 +1,31 @@
1
+ package main
2
+
3
+ import (
4
+ "bufio"
5
+ "compress/gzip"
6
+ "io"
7
+ )
8
+
9
+ func newPerfmongerLogReader(source io.Reader) io.Reader {
10
+ var ret io.Reader
11
+ reader := bufio.NewReader(source)
12
+
13
+ magic_numbers, e := reader.Peek(2)
14
+ if e != nil {
15
+ panic(e)
16
+ }
17
+
18
+ // check magic number
19
+ if magic_numbers[0] == 0x1f && magic_numbers[1] == 0x8b {
20
+ // gzipped gob input
21
+ ret, e = gzip.NewReader(reader)
22
+ if e != nil {
23
+ panic(e)
24
+ }
25
+ } else {
26
+ // plain gob input
27
+ ret = reader
28
+ }
29
+
30
+ return ret
31
+ }