summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-21 16:13:40 +0200
committerPaul Buetow <paul@buetow.org>2026-02-21 16:13:40 +0200
commit2c2cbe07f5e10fdb996e2a039cde84be44866f18 (patch)
tree97654c2c9ba9fc91cb569ab0521c4c67247abc0b /internal
parenteebc9cba272c1b20296ab998262298c5da99e047 (diff)
Add integration test framework: plan, workload binary, harness scaffolding
- INTEGRATIONTESTS-PLAN.md: full design for e2e integration tests - integrationtests/cmd/ioworkload: standalone binary with 13 I/O scenarios - integrationtests/expectations.go: ExpectedEvent type and assertion helpers - integrationtests/parse.go: .ior.zst parser producing TestResult - Export IterRecord and LoadFromFile in flamegraph package - Fix TraceId -> TraceID, StringByName returns error instead of panic Amp-Thread-ID: https://ampcode.com/threads/T-019c8031-c106-757a-95a0-7a5457163ce7 Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/flamegraph/collapsed.go8
-rw-r--r--internal/flamegraph/iordata.go66
2 files changed, 44 insertions, 30 deletions
diff --git a/internal/flamegraph/collapsed.go b/internal/flamegraph/collapsed.go
index aa0d81c..f04a38d 100644
--- a/internal/flamegraph/collapsed.go
+++ b/internal/flamegraph/collapsed.go
@@ -59,11 +59,15 @@ func (c Collapsed) Write(iorDataFile string) (string, error) {
for record := range iod.iter() {
var fieldValues []string
for _, fieldName := range c.fields {
- fieldValues = append(fieldValues, record.StringByName(fieldName))
+ v, err := record.StringByName(fieldName)
+ if err != nil {
+ return outFile, fmt.Errorf("field %s: %w", fieldName, err)
+ }
+ fieldValues = append(fieldValues, v)
}
writer.Write([]byte(fmt.Sprintf("%s %d\n",
strings.Join(fieldValues, ";"),
- record.cnt.ValueByName(c.countField),
+ record.Cnt.ValueByName(c.countField),
)))
}
writer.Flush()
diff --git a/internal/flamegraph/iordata.go b/internal/flamegraph/iordata.go
index 463ed48..eec92fd 100644
--- a/internal/flamegraph/iordata.go
+++ b/internal/flamegraph/iordata.go
@@ -44,6 +44,15 @@ func newIorDataFromFile(filename string) (iorData, error) {
return iod, nil
}
+// LoadFromFile loads an .ior.zst file and returns an iterator over all records.
+func LoadFromFile(filename string) (iter.Seq[IterRecord], error) {
+ iod, err := newIorDataFromFile(filename)
+ if err != nil {
+ return nil, fmt.Errorf("load ior data from %s: %w", filename, err)
+ }
+ return iod.iter(), nil
+}
+
func cloneString(s string) string {
// Clone the string by creating a new string with the same content
// This is a workaround to avoid using unsafe package
@@ -186,55 +195,56 @@ func (iod *iorData) deserialize(buf *bytes.Buffer) error {
return dec.Decode(&iod.paths)
}
-// Record returned by the iterator
-type iterRecord struct {
- path pathType
- traceId traceIdType
- comm commType
- pid pidType
- tid tidType
- flags flagsType
- cnt Counter
+// IterRecord is a single record returned by the iterator.
+type IterRecord struct {
+ Path string
+ TraceID types.TraceId
+ Comm string
+ Pid uint32
+ Tid uint32
+ Flags file.Flags
+ Cnt Counter
}
-func (ir iterRecord) StringByName(name string) string {
+// StringByName returns the string representation of a field by name.
+// Returns an error if the field name is not recognized.
+func (ir IterRecord) StringByName(name string) (string, error) {
switch name {
case "path":
- return strings.Join(strings.Split(ir.path, "/"), ";/")
+ return strings.Join(strings.Split(ir.Path, "/"), ";/"), nil
case "comm":
- return ir.comm
+ return ir.Comm, nil
case "tracepoint":
- return ir.traceId.String()
+ return ir.TraceID.String(), nil
case "pid":
- return fmt.Sprint(ir.pid)
+ return fmt.Sprint(ir.Pid), nil
case "tid":
- return fmt.Sprint(ir.tid)
+ return fmt.Sprint(ir.Tid), nil
case "flags":
- return ir.flags.String()
+ return ir.Flags.String(), nil
default:
- panic(fmt.Sprintln("No", name, "in record"))
+ return "", fmt.Errorf("unknown field %q in record", name)
}
}
-func (iod iorData) iter() iter.Seq[iterRecord] {
- return func(yield func(iterRecord) bool) {
+func (iod iorData) iter() iter.Seq[IterRecord] {
+ return func(yield func(IterRecord) bool) {
for path, traceIdMap := range iod.paths {
for traceId, commMap := range traceIdMap {
for comm, pidMap := range commMap {
for pid, tidMap := range pidMap {
for tid, flagsMap := range tidMap {
for flags, cnt := range flagsMap {
- record := iterRecord{
- path: path,
- traceId: traceId,
- comm: comm,
- pid: pid,
- tid: tid,
- flags: flags,
- cnt: cnt,
+ record := IterRecord{
+ Path: path,
+ TraceID: traceId,
+ Comm: comm,
+ Pid: pid,
+ Tid: tid,
+ Flags: flags,
+ Cnt: cnt,
}
if !yield(record) {
- // Stop iteration if yield returns false
return
}
}