diff --git a/go.mod b/go.mod index 0a44219..788c8b0 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,11 @@ require ( fortio.org/dflag v1.5.2 fortio.org/log v1.3.0 fortio.org/version v1.0.2 + golang.org/x/sys v0.5.0 ) require ( fortio.org/sets v1.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/sys v0.5.0 // indirect ) diff --git a/num_fd.go b/num_fd.go new file mode 100644 index 0000000..9806802 --- /dev/null +++ b/num_fd.go @@ -0,0 +1,57 @@ +// Fortio CLI util: number of open filedescriptor. +// +// (c) 2023 Fortio Authors +// See LICENSE + +//go:build !windows +// +build !windows + +package scli // import "fortio.org/scli" + +import ( + "os" + "runtime" + + "fortio.org/log" +) + +func countDir(dir string) int { + f, err := os.Open(dir) + if err != nil { + log.Errf("Unable to open %s: %v", dir, err) + return -1 + } + /* to run lsof between stage to debug that on macos /dev/fd gets opened twice somehow + if log.LogDebug() { + log.Debugf("Sleeping after open before Readdirnames") + time.Sleep(30 * time.Second) + log.Debugf("Done sleeping, calling Readdirnames") + } + */ + names, err := f.Readdirnames(-1) + if err != nil { + log.Errf("Unable to read %s: %v", dir, err) + f.Close() + return -1 + } + if log.LogDebug() { + log.Debugf("Found %d names in %s: %v", len(names), dir, names) + // time.Sleep(60 * time.Second) + // log.Debugf("Done sleeping, closing dir") + } + f.Close() + return len(names) - 1 // for the dir we just opened +} + +func NumFD() int { + switch runtime.GOOS { + case "windows": + log.Fatalf("Shouldn't be reached (!windows build tag)") + return -1 // not reached (also, not possible) + case "darwin": + return countDir("/dev/fd") - 1 // macos seems to open 2 fds to Readdirnames /dev/fd + default: + // assume everyone else has a /proc/self/fd + return countDir("/proc/self/fd") + } +} diff --git a/num_fd_windows.go b/num_fd_windows.go new file mode 100644 index 0000000..61a079f --- /dev/null +++ b/num_fd_windows.go @@ -0,0 +1,42 @@ +// Fortio CLI util: number of open filedescriptor. +// +// (c) 2023 Fortio Authors +// See LICENSE + +//go:build windows +// +build windows + +package scli // import "fortio.org/scli" + +import ( + "syscall" + "unsafe" + + "fortio.org/log" + "golang.org/x/sys/windows" +) + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + getProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") +) + +func GetCurrentProcessHandleCount() int { + hdl, err := windows.GetCurrentProcess() + if err != nil { + log.Errf("GetCurrentProcess failed: %v", err) + return -1 + } + count := uint32(0) + ret, _, err := syscall.Syscall(getProcessHandleCount.Addr(), 2, uintptr(hdl), uintptr(unsafe.Pointer(&count)), 0) + log.Debugf("GetProcessHandleCount = %v, %v : %v", ret, err, count) + if ret == 0 { + log.Errf("GetProcessHandleCount failed: %v", err) + return -1 + } + return int(count) +} + +func NumFD() int { + return GetCurrentProcessHandleCount() +} diff --git a/sampleServer/main.go b/sampleServer/main.go index 03083c0..866490f 100644 --- a/sampleServer/main.go +++ b/sampleServer/main.go @@ -1,14 +1,26 @@ package main import ( + "time" + "fortio.org/cli" + "fortio.org/log" "fortio.org/scli" ) func main() { cli.MinArgs = 2 cli.MaxArgs = 4 - if !scli.ServerMain() { + srvStarted := scli.ServerMain() + time.Sleep(1 * time.Second) + log.Infof("FD count: %d", scli.NumFD()) + time.Sleep(20 * time.Second) + log.Infof("FD count: %d", scli.NumFD()) + // is it stable: + for i := 0; i < 10; i++ { + log.Infof("FD count: %d", scli.NumFD()) + } + if !srvStarted { // in reality in both case we'd start some actual server return } diff --git a/cli.go b/scli.go similarity index 100% rename from cli.go rename to scli.go