Skip to content

Commit

Permalink
Extend testing of procspy.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Wilkie authored and tomwilkie committed Dec 9, 2015
1 parent b94751a commit 2372bff
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 44 deletions.
11 changes: 11 additions & 0 deletions common/fs/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fs

import (
"io"
"os"
)

// Open is a mockable version of os.Open
var Open = func(path string) (io.ReadWriteCloser, error) {
return os.Open(path)
}
20 changes: 0 additions & 20 deletions probe/endpoint/procspy/example_test.go

This file was deleted.

File renamed without changes.
48 changes: 24 additions & 24 deletions probe/endpoint/procspy/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ package procspy

import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"syscall"

"github.com/weaveworks/scope/common/fs"
)

var (
Expand All @@ -19,69 +22,66 @@ func SetProcRoot(root string) {
procRoot = root
}

// made variables for mocking
var (
readDir = ioutil.ReadDir
lstat = syscall.Lstat
stat = syscall.Stat
open = fs.Open
)

// walkProcPid walks over all numerical (PID) /proc entries, and sees if their
// ./fd/* files are symlink to sockets. Returns a map from socket ID (inode)
// to PID. Will return an error if /proc isn't there.
func walkProcPid(buf *bytes.Buffer) (map[uint64]Proc, error) {
fh, err := os.Open(procRoot)
if err != nil {
return nil, err
}

dirNames, err := fh.Readdirnames(-1)
fh.Close()
dirNames, err := readDir(procRoot)
if err != nil {
return nil, err
}

var (
res = map[uint64]Proc{}
namespaces = map[uint64]struct{}{}
stat syscall.Stat_t
statT syscall.Stat_t
)
for _, dirName := range dirNames {
for _, entry := range dirNames {
dirName := entry.Name()
pid, err := strconv.ParseUint(dirName, 10, 0)
if err != nil {
// Not a number, so not a PID subdir.
continue
}

fdBase := filepath.Join(procRoot, dirName, "fd")
dfh, err := os.Open(fdBase)
fds, err := readDir(fdBase)
if err != nil {
// Process is be gone by now, or we don't have access.
continue
}

fdNames, err := dfh.Readdirnames(-1)
dfh.Close()
if err != nil {
continue
}

// Read network namespace, and if we haven't seen it before,
// read /proc/<pid>/net/tcp
err = syscall.Lstat(filepath.Join(procRoot, dirName, "/ns/net"), &stat)
err = lstat(filepath.Join(procRoot, dirName, "/ns/net"), &statT)
if err != nil {
continue
}

if _, ok := namespaces[stat.Ino]; !ok {
namespaces[stat.Ino] = struct{}{}
if _, ok := namespaces[statT.Ino]; !ok {
namespaces[statT.Ino] = struct{}{}
readFile(filepath.Join(procRoot, dirName, "/net/tcp"), buf)
readFile(filepath.Join(procRoot, dirName, "/net/tcp6"), buf)
}

var name string
for _, fdName := range fdNames {
for _, fd := range fds {
// Direct use of syscall.Stat() to save garbage.
err = syscall.Stat(filepath.Join(fdBase, fdName), &stat)
err = stat(filepath.Join(fdBase, fd.Name()), &statT)
if err != nil {
continue
}

// We want sockets only.
if stat.Mode&syscall.S_IFMT != syscall.S_IFSOCK {
if statT.Mode&syscall.S_IFMT != syscall.S_IFSOCK {
continue
}

Expand All @@ -92,7 +92,7 @@ func walkProcPid(buf *bytes.Buffer) (map[uint64]Proc, error) {
}
}

res[stat.Ino] = Proc{
res[statT.Ino] = Proc{
PID: uint(pid),
Name: name,
}
Expand All @@ -104,7 +104,7 @@ func walkProcPid(buf *bytes.Buffer) (map[uint64]Proc, error) {

// procName does a pid->name lookup.
func procName(base string) string {
fh, err := os.Open(filepath.Join(base, "/comm"))
fh, err := open(filepath.Join(base, "/comm"))
if err != nil {
return ""
}
Expand Down
57 changes: 57 additions & 0 deletions probe/endpoint/procspy/proc_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package procspy

import (
"bytes"
"reflect"
"syscall"
"testing"

"github.com/weaveworks/scope/test/fs"
)

var mockFS = fs.Dir("",
fs.Dir("proc",
fs.Dir("1",
fs.Dir("fd",
fs.File{
FName: "16",
FStat: syscall.Stat_t{
Ino: 45,
Mode: syscall.S_IFSOCK,
},
},
),
fs.File{
FName: "comm",
FContents: "foo\n",
},
fs.Dir("ns",
fs.File{
FName: "net",
FStat: syscall.Stat_t{},
},
),
),
),
)

func TestWalkProcPid(t *testing.T) {
oldReadDir, oldLstat, oldStat, oldOpen := readDir, lstat, stat, open
defer func() { readDir, lstat, stat, open = oldReadDir, oldLstat, oldStat, oldOpen }()
readDir, lstat, stat, open = mockFS.ReadDir, mockFS.Lstat, mockFS.Stat, mockFS.Open

buf := bytes.Buffer{}
have, err := walkProcPid(&buf)
if err != nil {
t.Fatal(err)
}
want := map[uint64]Proc{
16: {
PID: 1,
Name: "foo",
},
}
if !reflect.DeepEqual(want, have) {
t.Fatalf("%s", have)
}
}
Loading

0 comments on commit 2372bff

Please sign in to comment.