summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/event/event.go6
-rw-r--r--internal/eventloop.go5
-rw-r--r--internal/file/file.go67
-rw-r--r--internal/file/flags.go47
-rw-r--r--internal/flamegraph/iordata.go21
-rw-r--r--internal/flamegraph/iordata_test.go12
-rw-r--r--internal/flamegraph/worker.go2
7 files changed, 88 insertions, 72 deletions
diff --git a/internal/event/event.go b/internal/event/event.go
index 684761a..24cf173 100644
--- a/internal/event/event.go
+++ b/internal/event/event.go
@@ -81,11 +81,11 @@ func (e *Pair) String() string {
return sb.String()
}
-func (e *Pair) FlagsString() string {
+func (e *Pair) Flags() file.Flags {
if e.File == nil {
- return "N:flags"
+ return file.Flags(0)
}
- return e.File.FlagsString()
+ return e.File.Flags()
}
func (e *Pair) FileName() string {
diff --git a/internal/eventloop.go b/internal/eventloop.go
index 1800b31..c64d7e6 100644
--- a/internal/eventloop.go
+++ b/internal/eventloop.go
@@ -293,7 +293,7 @@ func (e *eventLoop) syscallExit(exitEv event.Event, ch chan<- *event.Pair) {
switch v.Cmd {
case syscall.F_SETFL:
canChange := syscall.O_APPEND | syscall.O_ASYNC | syscall.O_DIRECT | syscall.O_NOATIME | syscall.O_NONBLOCK
- fdFile.Flags |= (int32(v.Arg) & int32(canChange))
+ fdFile.AddFlags((int32(v.Arg) & int32(canChange)))
ev.File = fdFile
e.files[fd] = fdFile
case syscall.F_DUPFD:
@@ -301,8 +301,7 @@ func (e *eventLoop) syscallExit(exitEv event.Event, ch chan<- *event.Pair) {
e.files[newFd] = fdFile.Dup(newFd)
case syscall.F_DUPFD_CLOEXEC:
newFd := int32(retEvent.Ret)
- duppedFd := fdFile.Dup(newFd)
- duppedFd.Flags |= syscall.O_CLOEXEC
+ duppedFd := fdFile.DupAddFlags(newFd, syscall.O_CLOEXEC)
e.files[newFd] = duppedFd
}
diff --git a/internal/file/file.go b/internal/file/file.go
index 9754520..11d992d 100644
--- a/internal/file/file.go
+++ b/internal/file/file.go
@@ -12,24 +12,23 @@ import (
type File interface {
String() string
Name() string
- FlagsString() string
+ Flags() Flags
}
type FdFile struct {
fd int32
name string
- Flags int32
+ flags Flags
flagsFromProcFS bool
- unknownFlags bool
}
func NewFd(fd int32, name []byte, flags int32) FdFile {
f := FdFile{
fd: fd,
name: stringValue(name),
- Flags: flags,
+ flags: Flags(flags),
}
- if f.Flags == -1 {
+ if f.flags == -1 {
panic(fmt.Sprintf("DEBUG with -1 flags: %v", f))
}
return f
@@ -41,17 +40,11 @@ func NewFdWithPid(fd int32, pid uint32) (f FdFile) {
procPath := fmt.Sprintf("/proc/%d/fd/%d", pid, fd)
f.name, err = os.Readlink(procPath)
if err != nil {
- // fmt.Println("DEBUGXXX", procPath)
f.name = ""
}
- f.Flags, err = readFlagsFromFdInfo(fd, pid)
- if err != nil {
- f.unknownFlags = true
- f.Flags = 0
- } else {
- f.flagsFromProcFS = true
- }
+ f.flags, _ = readFlagsFromFdInfo(fd, pid)
+ f.flagsFromProcFS = true
return f
}
@@ -62,10 +55,17 @@ func (f FdFile) Dup(fd int32) FdFile {
return dupFd
}
-func readFlagsFromFdInfo(fd int32, pid uint32) (int32, error) {
+func (f FdFile) DupAddFlags(fd, flags int32) FdFile {
+ dupFd := f
+ dupFd.fd = fd
+ dupFd.flags = Flags(int32(dupFd.flags) | flags)
+ return dupFd
+}
+
+func readFlagsFromFdInfo(fd int32, pid uint32) (Flags, error) {
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/fdinfo/%d", pid, fd))
if err != nil {
- return 0, err
+ return unknownFlag, err
}
scanner := bufio.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
@@ -73,10 +73,10 @@ func readFlagsFromFdInfo(fd int32, pid uint32) (int32, error) {
if strings.HasPrefix(line, "flags:") {
flagsStr := strings.Fields(line)[1]
flags, err := strconv.ParseUint(flagsStr, 8, 32)
- return int32(flags), err
+ return Flags(flags), err
}
}
- return 0, scanner.Err()
+ return unknownFlag, scanner.Err()
}
func (f FdFile) Name() string {
@@ -94,27 +94,18 @@ func (f FdFile) String() string {
sb.WriteString("%(")
sb.WriteString(strconv.FormatInt(int64(f.fd), 10))
sb.WriteString(",")
- sb.WriteString(f.FlagsString())
+ sb.WriteString(f.Flags().String())
sb.WriteString(")")
return sb.String()
}
-func (f FdFile) FlagsString() string {
- var sb strings.Builder
-
- if f.unknownFlags {
- sb.WriteString("U") // Unknown
- }
- if f.flagsFromProcFS {
- sb.WriteString("P") // ProcFS
- }
- if f.unknownFlags || f.flagsFromProcFS {
- sb.WriteString(":flags") // ProcFS
- }
+func (f FdFile) Flags() Flags {
+ return f.flags
+}
- flagsToStr(&sb, f.Flags)
- return sb.String()
+func (f *FdFile) AddFlags(flags int32) {
+ f.flags = Flags(int32(f.flags) | flags)
}
type oldnameNewnameFile struct {
@@ -129,8 +120,8 @@ func (f oldnameNewnameFile) Name() string {
return f.Newname
}
-func (f oldnameNewnameFile) FlagsString() string {
- return ""
+func (f oldnameNewnameFile) Flags() Flags {
+ return unknownFlag
}
func (f oldnameNewnameFile) String() string {
@@ -141,7 +132,7 @@ func (f oldnameNewnameFile) String() string {
sb.WriteString(" ->new:")
sb.WriteString(f.Newname)
sb.WriteString("%(")
- sb.WriteString(f.FlagsString())
+ sb.WriteString(f.Flags().String())
sb.WriteString(")")
return sb.String()
@@ -159,8 +150,8 @@ func (f pathnameFile) Name() string {
return f.Pathname
}
-func (f pathnameFile) FlagsString() string {
- return ""
+func (f pathnameFile) Flags() Flags {
+ return unknownFlag
}
func (f pathnameFile) String() string {
@@ -169,7 +160,7 @@ func (f pathnameFile) String() string {
sb.WriteString("pathname:")
sb.WriteString(f.Pathname)
sb.WriteString("%(")
- sb.WriteString(f.FlagsString())
+ sb.WriteString(f.Flags().String())
sb.WriteString(")")
return sb.String()
diff --git a/internal/file/flags.go b/internal/file/flags.go
index 85c73ce..f03d904 100644
--- a/internal/file/flags.go
+++ b/internal/file/flags.go
@@ -1,13 +1,15 @@
package file
import (
- "fmt"
"os"
"strings"
"syscall"
)
-var flagsToHumanCache = map[int32]string{}
+type Flags int32
+
+var flagsToHumanCache = map[Flags]string{}
+var unknownFlag = Flags(-1)
type tuple struct {
syscallNr int
@@ -15,6 +17,7 @@ type tuple struct {
}
var flagsToHuman = []tuple{
+ {-1, "O_NONE"},
{syscall.O_RDONLY, "O_RDONLY"},
{syscall.O_WRONLY, "O_WRONLY"},
{syscall.O_RDWR, "O_RDWR"},
@@ -35,29 +38,45 @@ var flagsToHuman = []tuple{
{syscall.O_TRUNC, "O_TRUNC"},
}
-func flagsToStr(sb *strings.Builder, flags int32) {
- if str, ok := flagsToHumanCache[flags]; ok {
+func (f Flags) Is(flag int) bool {
+ if f == unknownFlag {
+ return false
+ }
+ if int(f)&flag == flag {
+ return true
+ }
+ return false
+}
+
+func (f Flags) BuildString(sb *strings.Builder) {
+ if str, ok := flagsToHumanCache[f]; ok {
sb.WriteString(str)
return
}
- str := strings.Join(flagsToStrs(flags), "|")
- flagsToHumanCache[flags] = fmt.Sprintf("%O=>%s", flags, str)
+ str := f.String()
+ flagsToHumanCache[f] = str
sb.WriteString(str)
}
-func flagsToStrs(flags int32) (result []string) {
+func (f Flags) String() string {
+ var strs []string
+
+ if f == -1 {
+ return "O_NONE"
+ }
- if int(flags)&(os.O_WRONLY|os.O_RDWR) == 0 {
+ if int(f)&(os.O_WRONLY|os.O_RDWR) == 0 {
// Must be read only then
- result = append(result, "O_RDONLY")
+ strs = append(strs, "O_RDONLY")
}
for _, toHuman := range flagsToHuman[1:] {
- if int(flags)&toHuman.syscallNr == toHuman.syscallNr {
- result = append(result, toHuman.str)
+ if int(f)&toHuman.syscallNr == toHuman.syscallNr {
+ strs = append(strs, toHuman.str)
}
}
- if len(result) == 0 {
- result = append(result, "non=>O_RDONLY")
+ if len(strs) == 0 {
+ strs = append(strs, "O_RDONLY")
}
- return
+
+ return strings.Join(strs, "|")
}
diff --git a/internal/flamegraph/iordata.go b/internal/flamegraph/iordata.go
index 2d4b46b..b91dbc3 100644
--- a/internal/flamegraph/iordata.go
+++ b/internal/flamegraph/iordata.go
@@ -3,9 +3,11 @@ package flamegraph
import (
"fmt"
"ior/internal/event"
+ "ior/internal/file"
"ior/internal/flags"
"ior/internal/types"
"iter"
+ "log"
"os"
"strings"
"time"
@@ -20,7 +22,7 @@ type traceIdType = types.TraceId
type commType = string
type pidType = uint32
type tidType = uint32
-type flagsType = string
+type flagsType = file.Flags
type pathMap map[pathType]map[traceIdType]map[commType]map[pidType]map[tidType]map[flagsType]counter
type iorData struct {
@@ -37,7 +39,7 @@ func newIorData() iorData {
func (iod iorData) add(ev *event.Pair) {
cnt := counter{count: 1, duration: ev.Duration, durationToPrev: ev.DurationToPrev}
iod.addPath(ev.FileName(), ev.EnterEv.GetTraceId(), ev.Comm, ev.EnterEv.GetPid(),
- ev.EnterEv.GetTid(), ev.FlagsString(), cnt)
+ ev.EnterEv.GetTid(), ev.Flags(), cnt)
}
func (iod iorData) addPath(path pathType, traceId traceIdType, comm commType,
@@ -116,8 +118,10 @@ func (iod iorData) commit() error {
filename := fmt.Sprintf("%s-%s-%s.ior.zst", hostname, flags.Get().FlamegraphName,
time.Now().Format("2006-01-02_15:04:05"))
+ log.Println("Writing", filename)
+ tmpFilename := fmt.Sprintf("%s.tmp", filename)
- file, err := os.Create(filename)
+ file, err := os.Create(tmpFilename)
if err != nil {
return err
}
@@ -126,8 +130,13 @@ func (iod iorData) commit() error {
encoder := zstd.NewWriter(file)
defer encoder.Close()
- // Write the data to a .txt file one line one entry and with a separator ␞, don't use JSON
- return nil
+ for line := range iod.lines() {
+ if _, err := encoder.Write([]byte(line + "\n")); err != nil {
+ return err
+ }
+ }
+
+ return os.Rename(tmpFilename, filename)
}
func (iod iorData) lines() iter.Seq[string] {
@@ -144,7 +153,7 @@ func (iod iorData) lines() iter.Seq[string] {
comm,
fmt.Sprint(pid),
fmt.Sprint(tid),
- flags,
+ flags.String(),
fmt.Sprintf("%d %d %d %d", cnt.count, cnt.duration, cnt.durationToPrev, cnt.bytes),
},
recordSeparator)
diff --git a/internal/flamegraph/iordata_test.go b/internal/flamegraph/iordata_test.go
index 7496067..ff17ca3 100644
--- a/internal/flamegraph/iordata_test.go
+++ b/internal/flamegraph/iordata_test.go
@@ -2,6 +2,7 @@ package flamegraph
import (
"ior/internal/types"
+ "syscall"
"testing"
)
@@ -12,7 +13,7 @@ func TestAddPath(t *testing.T) {
comm := commType("testComm")
pid := pidType(1234)
tid := tidType(5678)
- flags := flagsType("O_RDWR")
+ flags := flagsType(syscall.O_RDONLY)
cnt1 := counter{count: 1, duration: 1000, durationToPrev: 100}
iod.addPath(path, traceId, comm, pid, tid, flags, cnt1)
@@ -31,8 +32,8 @@ func TestAddPath(t *testing.T) {
}
func TestMerge(t *testing.T) {
- rdwrFlag := flagsType("O_RDWR")
- roFlag := flagsType("O_RDONLY")
+ rdwrFlag := flagsType(syscall.O_RDWR)
+ roFlag := flagsType(syscall.O_RDONLY)
traceId := types.SYS_ENTER_OPENAT
// Initialize two iorData instances with sample data
@@ -71,8 +72,8 @@ func TestMerge(t *testing.T) {
if len(merged.paths) != 2 {
t.Errorf("Expected 2 paths, got %d", len(merged.paths))
}
- if merged.paths["path1"][traceId]["comm1"][100][1000][flagsType("O_RDWR")].count != 10 {
- t.Errorf("Expected counter 10, got %d", merged.paths["path1"][1]["comm1"][100][1000][flagsType("O_RDWR")].count)
+ if merged.paths["path1"][traceId]["comm1"][100][1000][roFlag].count != 10 {
+ t.Errorf("Expected counter 10, got %d", merged.paths["path1"][1]["comm1"][100][1000][rdwrFlag].count)
}
if merged.paths["path2"][traceId]["comm2"][101][1000][roFlag].count != 60 {
t.Errorf("Expected counter 60, got %d", merged.paths["path2"][1]["comm2"][101][1000][roFlag].count)
@@ -88,7 +89,6 @@ func TestMerge(t *testing.T) {
var lines []string
for line := range merged.lines() {
- t.Log(line)
lines = append(lines, line)
}
diff --git a/internal/flamegraph/worker.go b/internal/flamegraph/worker.go
index a24be6d..40f6d3f 100644
--- a/internal/flamegraph/worker.go
+++ b/internal/flamegraph/worker.go
@@ -2,7 +2,6 @@ package flamegraph
import (
"context"
- "fmt"
"ior/internal/event"
"sync"
"time"
@@ -23,7 +22,6 @@ func (w worker) run(ctx context.Context, wg *sync.WaitGroup, ch <-chan *event.Pa
for {
select {
case ev := <-ch:
- fmt.Println("worker got event", ev)
w.iod.add(ev)
ev.Recycle()