diff options
| -rw-r--r-- | internal/event/event.go | 6 | ||||
| -rw-r--r-- | internal/eventloop.go | 5 | ||||
| -rw-r--r-- | internal/file/file.go | 67 | ||||
| -rw-r--r-- | internal/file/flags.go | 47 | ||||
| -rw-r--r-- | internal/flamegraph/iordata.go | 21 | ||||
| -rw-r--r-- | internal/flamegraph/iordata_test.go | 12 | ||||
| -rw-r--r-- | internal/flamegraph/worker.go | 2 |
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() |
