Skip to content

Commit 2c5f92b

Browse files
committed
Rename CLI options and semantics
1 parent fa88eb5 commit 2c5f92b

File tree

6 files changed

+162
-175
lines changed

6 files changed

+162
-175
lines changed

bench/benchreporter/benchmark.go

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/json"
66
"fmt"
77
"os"
8-
"path"
98
"path/filepath"
109
"strconv"
1110
"strings"
@@ -25,7 +24,8 @@ import (
2524
var _ reporter.Reporter = (*BenchmarkReporter)(nil)
2625

2726
type BenchmarkReporter struct {
28-
benchDataDir string
27+
saveInputsTo string
28+
f *os.File
2929
rep reporter.Reporter
3030
uid int
3131
gid int
@@ -70,14 +70,16 @@ func (r *BenchmarkReporter) SupportsReportTraceEvent() bool {
7070
}
7171

7272
type fallbackSymbol struct {
73-
FrameID libpf.FrameID
74-
Symbol string
73+
FileID libpf.FileID
74+
AddressOrLine libpf.AddressOrLineno
75+
Symbol string
7576
}
7677

7778
func (r *BenchmarkReporter) ReportFallbackSymbol(frameID libpf.FrameID, symbol string) {
7879
r.store("FallbackSymbol", &fallbackSymbol{
79-
FrameID: frameID,
80-
Symbol: symbol,
80+
FileID: frameID.FileID(),
81+
AddressOrLine: frameID.AddressOrLine(),
82+
Symbol: symbol,
8183
})
8284
r.rep.ReportFallbackSymbol(frameID, symbol)
8385
}
@@ -155,30 +157,31 @@ func (r *BenchmarkReporter) ReportMetrics(timestamp uint32, ids []uint32, values
155157

156158
func (r *BenchmarkReporter) Stop() {
157159
r.rep.Stop()
160+
_ = r.f.Close()
158161
}
159162

160163
func (r *BenchmarkReporter) GetMetrics() reporter.Metrics {
161164
return r.rep.GetMetrics()
162165
}
163166

164-
func NewBenchmarkReporter(benchDataDir string, rep reporter.Reporter) (*BenchmarkReporter, error) {
167+
func NewBenchmarkReporter(saveInputsTo string, rep reporter.Reporter) (*BenchmarkReporter, error) {
165168
r := &BenchmarkReporter{
166-
benchDataDir: benchDataDir,
169+
saveInputsTo: saveInputsTo,
167170
rep: rep,
168171
}
169172
r.uid, r.gid = originUser()
170173

171-
if err := os.MkdirAll(benchDataDir, 0o755); err != nil {
172-
return nil, err
174+
var err error
175+
if r.f, err = os.OpenFile(saveInputsTo,
176+
os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644); err != nil {
177+
return nil, fmt.Errorf("failed to open file %s: %v", saveInputsTo, err)
173178
}
174179

175-
if r.uid != 0 || r.gid != 0 {
176-
changeDirOwner(benchDataDir, r.uid, r.gid)
180+
if err = r.f.Chown(r.uid, r.gid); err != nil {
181+
return nil, fmt.Errorf("failed to change ownership of %s to %d:%d: %v",
182+
saveInputsTo, r.uid, r.gid, err)
177183
}
178184

179-
// Just for storing the initial timestamp.
180-
r.store("Start", libpf.Void{})
181-
182185
return r, nil
183186
}
184187

@@ -192,27 +195,40 @@ func originUser() (uid, gid int) {
192195
return
193196
}
194197

195-
var counter atomic.Uint64
198+
type metaInfo struct {
199+
TS int64 `json:"ts"`
200+
Name string `json:"name"`
201+
}
196202

197-
// store stores data as JSON.
203+
// store appends data as NDJSON to the output file.
198204
func (r *BenchmarkReporter) store(name string, data any) {
199-
ts := time.Now().UnixNano()
200-
id := counter.Add(1)
201-
fileName := fmt.Sprintf("%d_%06x_%s.json", ts, id, name)
202-
pathName := path.Join(r.benchDataDir, fileName)
205+
meta := metaInfo{
206+
TS: time.Now().UnixNano(),
207+
Name: name,
208+
}
203209

204-
// encode data to JSON
205-
bytes, err := json.Marshal(data)
210+
// encode meta data to JSON
211+
bytes, err := json.Marshal(meta)
206212
if err != nil {
207213
panic(err)
208214
}
215+
if err = appendToFile(r.f, bytes); err != nil {
216+
panic(err)
217+
}
209218

210-
//nolint:gosec
211-
if err = os.WriteFile(pathName, bytes, 0o644); err != nil {
219+
// encode reporter input to JSON
220+
bytes, err = json.Marshal(data)
221+
if err != nil {
222+
panic(err)
223+
}
224+
if err = appendToFile(r.f, bytes); err != nil {
212225
panic(err)
213226
}
227+
}
214228

215-
changeOwner(pathName, r.uid, r.gid)
229+
func appendToFile(f *os.File, bytes []byte) error {
230+
_, err := f.Write(append(bytes, '\n'))
231+
return err
216232
}
217233

218234
func changeOwner(pathName string, uid, gid int) {
@@ -230,23 +246,23 @@ func changeDirOwner(dirName string, uid, gid int) {
230246
}
231247
}
232248

233-
func GRPCInterceptor(benchProtoDir string) grpc.UnaryClientInterceptor {
234-
if benchProtoDir != "" {
235-
if err := os.MkdirAll(benchProtoDir, 0o755); err != nil {
249+
func GRPCInterceptor(saveDir string) grpc.UnaryClientInterceptor {
250+
if saveDir != "" {
251+
if err := os.MkdirAll(saveDir, 0o755); err != nil {
236252
log.Errorf("Failed to create directory for storing protobuf messages: %v", err)
237253
return nil
238254
}
239255

240256
uid, gid := originUser()
241257

242258
if uid != 0 || gid != 0 {
243-
changeDirOwner(benchProtoDir, uid, gid)
259+
changeDirOwner(saveDir, uid, gid)
244260
}
245261

246262
// return interceptor to write the uncompressed protobuf messages to disk.
247263
return func(ctx context.Context, method string, req, reply any,
248264
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
249-
storeProtobuf(benchProtoDir, req, uid, gid)
265+
storeProtobuf(saveDir, req, uid, gid)
250266
return invoker(ctx, method, req, reply, cc, opts...)
251267
}
252268
}

bench/benchreporter/replay.go

Lines changed: 34 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"io"
78
"os"
8-
"path/filepath"
9-
"sort"
10-
"strings"
119
"time"
1210

1311
log "github.com/sirupsen/logrus"
@@ -16,117 +14,81 @@ import (
1614
"github.com/open-telemetry/opentelemetry-ebpf-profiler/reporter"
1715
)
1816

19-
type fileInfo struct {
20-
name string
21-
timestamp int64
22-
id uint64
23-
funcName string
24-
}
25-
26-
// Replay replays the stored data from benchDataDir.
17+
// Replay replays the stored data from replayInputsFrom.
2718
// The argument r is the reporter that will receive the replayed data.
28-
func Replay(ctx context.Context, benchDataDir string, rep reporter.Reporter) error {
29-
files, err := os.ReadDir(benchDataDir)
19+
func Replay(ctx context.Context, replayInputsFrom string, rep reporter.Reporter) error {
20+
stream, err := os.Open(replayInputsFrom)
3021
if err != nil {
31-
return fmt.Errorf("failed to read directory %s: %v", benchDataDir, err)
22+
return fmt.Errorf("failed to open file %s: %v", replayInputsFrom, err)
3223
}
24+
decoder := json.NewDecoder(stream)
3325

34-
fileInfos := make([]fileInfo, 0, len(files))
26+
var m metaInfo
27+
var curTS int64
3528

36-
for _, f := range files {
37-
if !strings.HasSuffix(f.Name(), ".json") {
38-
continue
29+
for {
30+
if err = decoder.Decode(&m); err != nil {
31+
// EOF is returned at the end of the stream.
32+
if err != io.EOF {
33+
return err
34+
}
35+
break
3936
}
4037

41-
name := f.Name()
42-
// scan name for timestamp, counter and function name
43-
var timestamp int64
44-
var id uint64
45-
var funcName string
46-
if _, err = fmt.Sscanf(name, "%d_%x_%s", &timestamp, &id, &funcName); err != nil {
47-
log.Errorf("Failed to parse file name %s: %v", name, err)
48-
continue
38+
if curTS != 0 {
39+
time.Sleep(time.Duration(m.TS-curTS) * time.Nanosecond)
4940
}
50-
funcName = strings.TrimSuffix(funcName, ".json")
51-
52-
fileInfos = append(fileInfos, fileInfo{
53-
name: name,
54-
timestamp: timestamp,
55-
id: id,
56-
funcName: funcName,
57-
})
58-
}
59-
60-
if len(fileInfos) == 0 {
61-
return nil
62-
}
63-
64-
// Sort fileInfos ascending by ID.
65-
sort.Slice(fileInfos, func(i, j int) bool {
66-
return fileInfos[i].id < fileInfos[j].id
67-
})
41+
curTS = m.TS
6842

69-
if fileInfos[0].funcName != "Start" {
70-
return fmt.Errorf("first function name must be \"Start\", instead it is \"%s\"",
71-
fileInfos[0].funcName)
72-
}
73-
74-
curTS := fileInfos[0].timestamp
75-
76-
// Replay the stored data
77-
for _, fi := range fileInfos[1:] {
78-
time.Sleep(time.Duration(fi.timestamp-curTS) * time.Nanosecond)
79-
curTS = fi.timestamp
80-
81-
switch fi.funcName {
43+
switch m.Name {
8244
case "TraceEvent":
8345
var v traceEvent
84-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
46+
if err = decodeTo(decoder, &v); err == nil {
8547
rep.ReportTraceEvent(v.Trace, v.Meta)
8648
}
8749
case "CountForTrace":
8850
var v countForTrace
89-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
51+
if err = decodeTo(decoder, &v); err == nil {
9052
rep.ReportCountForTrace(v.TraceHash, v.Count, v.Meta)
9153
}
9254
case "FramesForTrace":
9355
var v libpf.Trace
94-
if err = dataFromFileInfo[libpf.Trace](benchDataDir, fi, &v); err == nil {
56+
if err = decodeTo[libpf.Trace](decoder, &v); err == nil {
9557
rep.ReportFramesForTrace(&v)
9658
}
9759
case "FallbackSymbol":
9860
var v fallbackSymbol
99-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
100-
rep.ReportFallbackSymbol(v.FrameID, v.Symbol)
61+
if err = decodeTo(decoder, &v); err == nil {
62+
rep.ReportFallbackSymbol(libpf.NewFrameID(v.FileID, v.AddressOrLine), v.Symbol)
10163
}
102-
case "ExectableMetadata":
64+
case "ExecutableMetadata":
10365
var v executableMetadata
104-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
66+
if err = decodeTo(decoder, &v); err == nil {
10567
rep.ExecutableMetadata(context.Background(), v.FileID, v.FileName, v.BuildID,
10668
v.Interp, nil)
10769
}
10870
case "FrameMetadata":
10971
var v frameMetadata
110-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
72+
if err = decodeTo(decoder, &v); err == nil {
11173
rep.FrameMetadata(v.FileID, v.AddressOrLine, v.LineNumber, v.FunctionOffset,
11274
v.FunctionName, v.FilePath)
11375
}
11476
case "HostMetadata":
11577
var v hostMetadata
116-
if err = dataFromFileInfo(benchDataDir, fi, &v); err == nil {
78+
if err = decodeTo(decoder, &v); err == nil {
11779
rep.ReportHostMetadata(v.Metadata)
11880
}
11981
case "Metrics":
12082
var v metrics
121-
if err = dataFromFileInfo[metrics](benchDataDir, fi, &v); err == nil {
83+
if err = decodeTo[metrics](decoder, &v); err == nil {
12284
rep.ReportMetrics(v.Timestamp, v.IDs, v.Values)
12385
}
12486
default:
125-
err = fmt.Errorf("unsupported function name in file %s: %s", fi.name, fi.funcName)
87+
err = fmt.Errorf("unsupported function name in file %s: %s", replayInputsFrom, m.Name)
12688
}
12789

12890
if err != nil {
129-
log.Errorf("Failed to replay data from file %s: %v", fi.name, err)
91+
log.Errorf("Failed to replay data from file %s: %v", m.Name, err)
13092
}
13193

13294
if err = ctx.Err(); err != nil {
@@ -137,16 +99,9 @@ func Replay(ctx context.Context, benchDataDir string, rep reporter.Reporter) err
13799
return nil
138100
}
139101

140-
func dataFromFileInfo[T any](dir string, fi fileInfo, data *T) error {
141-
pathName := filepath.Join(dir, fi.name)
142-
f, err := os.Open(pathName)
143-
if err != nil {
144-
return fmt.Errorf("failed to open file %s: %v", pathName, err)
145-
}
146-
defer f.Close()
147-
148-
if err = json.NewDecoder(f).Decode(data); err != nil {
149-
return fmt.Errorf("failed to decode JSON from file %s: %v", pathName, err)
102+
func decodeTo[T any](decoder *json.Decoder, data *T) error {
103+
if err := decoder.Decode(data); err != nil {
104+
return fmt.Errorf("failed to decode JSON: %v", err)
150105
}
151106

152107
return nil

0 commit comments

Comments
 (0)