-
Notifications
You must be signed in to change notification settings - Fork 6
/
count.go
213 lines (186 loc) · 4.85 KB
/
count.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package perf
import (
"errors"
"fmt"
"io"
"os"
"text/tabwriter"
"time"
"golang.org/x/sys/unix"
)
// Count is a measurement taken by an Event.
//
// The Value field is always present and populated.
//
// The TimeEnabled field is populated if ReadFormat.TimeEnabled is set on
// the Event the Count was read from. Ditto for TimeRunning and ID.
//
// Label is set based on the Label field of the Attr associated with the
// event. See the documentation there for more details.
type Count struct {
Value uint64
Enabled time.Duration
Running time.Duration
ID uint64
Label string
}
func (c Count) String() string {
if c.Label != "" {
return fmt.Sprintf("%s = %d", c.Label, c.Value)
}
return fmt.Sprint(c.Value)
}
var errGroup = errors.New("calling ReadCount on group Event")
// ReadCount reads the measurement associated with ev. If the Event was
// configured with CountFormat.Group, ReadCount returns an error.
func (ev *Event) ReadCount() (Count, error) {
var c Count
if err := ev.ok(); err != nil {
return c, err
}
if ev.a.CountFormat.Group {
return c, errGroup
}
// TODO(acln): use rdpmc on x86 instead of read(2)?
buf := make([]byte, ev.a.CountFormat.readSize())
_, err := unix.Read(ev.perffd, buf)
if err != nil {
return c, os.NewSyscallError("read", err)
}
f := fields(buf)
f.count(&c, ev.a.CountFormat)
c.Label = ev.a.Label
return c, err
}
// GroupCount is a group of measurements taken by an Event group.
//
// Fields are populated as described in the Count documentation.
type GroupCount struct {
Enabled time.Duration
Running time.Duration
Values []struct {
Value uint64
ID uint64
Label string
}
}
type errWriter struct {
w io.Writer
err error // sticky
}
func (ew *errWriter) Write(b []byte) (int, error) {
if ew.err != nil {
return 0, ew.err
}
n, err := ew.w.Write(b)
ew.err = err
return n, err
}
// PrintValues prints a table of gc.Values to w.
func (gc GroupCount) PrintValues(w io.Writer) error {
ew := &errWriter{w: w}
tw := new(tabwriter.Writer)
tw.Init(ew, 0, 8, 1, ' ', 0)
if gc.Values[0].ID != 0 {
fmt.Fprintln(tw, "label\tvalue\tID")
} else {
fmt.Fprintln(tw, "label\tvalue")
}
for _, v := range gc.Values {
if v.ID != 0 {
fmt.Fprintf(tw, "%s\t%d\t%d\n", v.Label, v.Value, v.ID)
} else {
fmt.Fprintf(tw, "%s\t%d\n", v.Label, v.Value)
}
}
tw.Flush()
return ew.err
}
var errNotGroup = errors.New("calling ReadGroupCount on non-group Event")
// ReadGroupCount reads the measurements associated with ev. If the Event
// was not configued with CountFormat.Group, ReadGroupCount returns an error.
func (ev *Event) ReadGroupCount() (GroupCount, error) {
var gc GroupCount
if err := ev.ok(); err != nil {
return gc, err
}
if !ev.a.CountFormat.Group {
return gc, errNotGroup
}
size := ev.a.CountFormat.groupReadSize(1 + len(ev.group))
buf := make([]byte, size)
_, err := unix.Read(ev.perffd, buf)
if err != nil {
return gc, os.NewSyscallError("read", err)
}
f := fields(buf)
f.groupCount(&gc, ev.a.CountFormat)
gc.Values[0].Label = ev.a.Label
for i := 0; i < len(ev.group); i++ {
gc.Values[i+1].Label = ev.group[i].a.Label
}
return gc, nil
}
// CountFormat configures the format of Count or GroupCount measurements.
//
// Enabled and Running configure the Event to include time enabled and
// time running measurements to the counts. Usually, these two values are
// equal. They may differ when events are multiplexed.
//
// If ID is set, a unique ID is assigned to the associated event. For a
// given event, this ID matches the ID reported by the (*Event).ID method.
//
// If Group is set, the Event measures a group of events together: callers
// must use ReadGroupCount. If Group is not set, the Event measures a single
// counter: callers must use ReadCount.
type CountFormat struct {
Enabled bool
Running bool
ID bool
Group bool
}
// readSize returns the buffer size required for a Count read. Assumes
// f.Group is not set.
func (f CountFormat) readSize() int {
size := 8 // value is always set
if f.Enabled {
size += 8
}
if f.Running {
size += 8
}
if f.ID {
size += 8
}
return size
}
// groupReadSize returns the buffer size required for a GroupCount read.
// Assumes f.Group is set.
func (f CountFormat) groupReadSize(events int) int {
hsize := 8 // the number of events is always set
if f.Enabled {
hsize += 8
}
if f.Running {
hsize += 8
}
vsize := 8 // each event contains at least a value
if f.ID {
vsize += 8
}
return hsize + events*vsize
}
// marshal marshals the CountFormat into a uint64.
func (f CountFormat) marshal() uint64 {
// Always keep this in sync with the type definition above.
fields := []bool{
f.Enabled,
f.Running,
f.ID,
f.Group,
}
return marshalBitwiseUint64(fields)
}