perfmonger 0.9.0 → 0.10.1

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