From 1fcd0795632751f95299fdeda52fc416d178992b Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Wed, 9 Dec 2015 17:42:36 +0000 Subject: [PATCH] Use Readdirnames to reduce number of stats we're doing. --- common/fs/fs.go | 15 +++++++++++++++ probe/endpoint/procspy/proc.go | 14 +++++++------- probe/process/walker_linux.go | 5 ++--- test/fs/fs.go | 23 +++++++++++++++++++++++ 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/common/fs/fs.go b/common/fs/fs.go index da4d903fa..6b9eb4162 100644 --- a/common/fs/fs.go +++ b/common/fs/fs.go @@ -10,6 +10,7 @@ import ( // Interface is the filesystem interface type. type Interface interface { ReadDir(string) ([]os.FileInfo, error) + ReadDirNames(string) ([]string, error) ReadFile(string) ([]byte, error) Lstat(string, *syscall.Stat_t) error Stat(string, *syscall.Stat_t) error @@ -25,6 +26,15 @@ func (realFS) ReadDir(path string) ([]os.FileInfo, error) { return ioutil.ReadDir(path) } +func (realFS) ReadDirNames(path string) ([]string, error) { + fh, err := os.Open(path) + if err != nil { + return nil, err + } + defer fh.Close() + return fh.Readdirnames(-1) +} + func (realFS) ReadFile(path string) ([]byte, error) { return ioutil.ReadFile(path) } @@ -48,6 +58,11 @@ func ReadDir(path string) ([]os.FileInfo, error) { return fs.ReadDir(path) } +// ReadDirNames see os.File.ReadDirNames +func ReadDirNames(path string) ([]string, error) { + return fs.ReadDirNames(path) +} + // ReadFile see ioutil.ReadFile func ReadFile(path string) ([]byte, error) { return fs.ReadFile(path) diff --git a/probe/endpoint/procspy/proc.go b/probe/endpoint/procspy/proc.go index 312c0177e..6823a3ede 100644 --- a/probe/endpoint/procspy/proc.go +++ b/probe/endpoint/procspy/proc.go @@ -34,15 +34,10 @@ func walkProcPid(buf *bytes.Buffer, walker process.Walker) (map[uint64]Proc, err walker.Walk(func(p process.Process) { dirName := strconv.Itoa(p.PID) fdBase := filepath.Join(procRoot, dirName, "fd") - fds, err := fs.ReadDir(fdBase) - if err != nil { - // Process is be gone by now, or we don't have access. - return - } // Read network namespace, and if we haven't seen it before, // read /proc//net/tcp - err = fs.Lstat(filepath.Join(procRoot, dirName, "/ns/net"), &statT) + err := fs.Lstat(filepath.Join(procRoot, dirName, "/ns/net"), &statT) if err != nil { return } @@ -53,9 +48,14 @@ func walkProcPid(buf *bytes.Buffer, walker process.Walker) (map[uint64]Proc, err readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf) } + fds, err := fs.ReadDirNames(fdBase) + if err != nil { + // Process is be gone by now, or we don't have access. + return + } for _, fd := range fds { // Direct use of syscall.Stat() to save garbage. - err = fs.Stat(filepath.Join(fdBase, fd.Name()), &statT) + err = fs.Stat(filepath.Join(fdBase, fd), &statT) if err != nil { continue } diff --git a/probe/process/walker_linux.go b/probe/process/walker_linux.go index 7b0431174..5eecc5d76 100644 --- a/probe/process/walker_linux.go +++ b/probe/process/walker_linux.go @@ -23,13 +23,12 @@ func NewWalker(procRoot string) Walker { // passes one-by-one to the supplied function. Walk is only made public // so that is can be tested. func (w *walker) Walk(f func(Process)) error { - dirEntries, err := fs.ReadDir(w.procRoot) + dirEntries, err := fs.ReadDirNames(w.procRoot) if err != nil { return err } - for _, dirEntry := range dirEntries { - filename := dirEntry.Name() + for _, filename := range dirEntries { pid, err := strconv.Atoi(filename) if err != nil { continue diff --git a/test/fs/fs.go b/test/fs/fs.go index 56b6a87d5..b59f37dc8 100644 --- a/test/fs/fs.go +++ b/test/fs/fs.go @@ -89,6 +89,24 @@ func (p dir) ReadDir(path string) ([]os.FileInfo, error) { return fs.ReadDir(tail) } +func (p dir) ReadDirNames(path string) ([]string, error) { + if path == "/" { + result := []string{} + for _, v := range p.entries { + result = append(result, v.Name()) + } + return result, nil + } + + head, tail := split(path) + fs, ok := p.entries[head] + if !ok { + return nil, fmt.Errorf("Not found: %s", path) + } + + return fs.ReadDirNames(tail) +} + func (p dir) ReadFile(path string) ([]byte, error) { if path == "/" { return nil, fmt.Errorf("I'm a directory!") @@ -156,6 +174,11 @@ func (p File) ReadDir(path string) ([]os.FileInfo, error) { return nil, fmt.Errorf("I'm a file!") } +// ReadDirNames implements FS +func (p File) ReadDirNames(path string) ([]string, error) { + return nil, fmt.Errorf("I'm a file!") +} + // ReadFile implements FS func (p File) ReadFile(path string) ([]byte, error) { if path != "/" {