mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 09:41:57 +00:00
Drop all the dependencies we had on procspy by moving some code to probe.
This commit is contained in:
@@ -3,6 +3,7 @@ package docker
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
@@ -16,18 +17,18 @@ const (
|
||||
|
||||
// These vars are exported for testing.
|
||||
var (
|
||||
NewProcessTreeStub = process.NewTree
|
||||
NewProcessTreeStub = proc.NewTree
|
||||
)
|
||||
|
||||
// Tagger is a tagger that tags Docker container information to process
|
||||
// nodes that have a PID.
|
||||
type Tagger struct {
|
||||
registry Registry
|
||||
procWalker process.Walker
|
||||
procWalker proc.Walker
|
||||
}
|
||||
|
||||
// NewTagger returns a usable Tagger.
|
||||
func NewTagger(registry Registry, procWalker process.Walker) *Tagger {
|
||||
func NewTagger(registry Registry, procWalker proc.Walker) *Tagger {
|
||||
return &Tagger{
|
||||
registry: registry,
|
||||
procWalker: procWalker,
|
||||
@@ -44,7 +45,7 @@ func (t *Tagger) Tag(r report.Report) (report.Report, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (t *Tagger) tag(tree process.Tree, topology *report.Topology) {
|
||||
func (t *Tagger) tag(tree proc.Tree, topology *report.Topology) {
|
||||
for nodeID, nodeMetadata := range topology.Nodes {
|
||||
pidStr, ok := nodeMetadata.Metadata[process.PID]
|
||||
if !ok {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
@@ -27,7 +27,7 @@ func TestTagger(t *testing.T) {
|
||||
oldProcessTree := docker.NewProcessTreeStub
|
||||
defer func() { docker.NewProcessTreeStub = oldProcessTree }()
|
||||
|
||||
docker.NewProcessTreeStub = func(_ process.Walker) (process.Tree, error) {
|
||||
docker.NewProcessTreeStub = func(_ proc.Walker) (proc.Tree, error) {
|
||||
return &mockProcessTree{map[int]int{2: 1}}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
@@ -42,7 +42,7 @@ var SpyDuration = prometheus.NewSummaryVec(
|
||||
[]string{},
|
||||
)
|
||||
|
||||
// NewReporter creates a new Reporter that invokes procspy.Connections to
|
||||
// NewReporter creates a new Reporter that invokes Connections to
|
||||
// generate a report.Report that contains every discovered (spied) connection
|
||||
// on the host machine, at the granularity of host and port. That information
|
||||
// is stored in the Endpoint topology. It optionally enriches that topology
|
||||
@@ -94,7 +94,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
}(time.Now())
|
||||
|
||||
rpt := report.MakeReport()
|
||||
conns, err := procspy.Connections(r.includeProcesses)
|
||||
conns, err := proc.Connections(r.includeProcesses)
|
||||
if err != nil {
|
||||
return rpt, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/endpoint"
|
||||
"github.com/weaveworks/scope/report"
|
||||
@@ -20,7 +20,7 @@ var (
|
||||
fixProcessPID = uint(4242)
|
||||
fixProcessName = "nginx"
|
||||
|
||||
fixConnections = []procspy.Connection{
|
||||
fixConnections = []proc.Connection{
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: fixLocalAddress,
|
||||
@@ -37,14 +37,14 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
fixConnectionsWithProcesses = []procspy.Connection{
|
||||
fixConnectionsWithProcesses = []proc.Connection{
|
||||
{
|
||||
Transport: "tcp",
|
||||
LocalAddress: fixLocalAddress,
|
||||
LocalPort: fixLocalPort,
|
||||
RemoteAddress: fixRemoteAddress,
|
||||
RemotePort: fixRemotePort,
|
||||
Proc: procspy.Proc{
|
||||
Proc: proc.Proc{
|
||||
PID: fixProcessPID,
|
||||
Name: fixProcessName,
|
||||
},
|
||||
@@ -55,7 +55,7 @@ var (
|
||||
LocalPort: fixLocalPort,
|
||||
RemoteAddress: fixRemoteAddress,
|
||||
RemotePort: fixRemotePort,
|
||||
Proc: procspy.Proc{
|
||||
Proc: proc.Proc{
|
||||
PID: fixProcessPID,
|
||||
Name: fixProcessName,
|
||||
},
|
||||
@@ -64,7 +64,7 @@ var (
|
||||
)
|
||||
|
||||
func TestSpyNoProcesses(t *testing.T) {
|
||||
procspy.SetFixtures(fixConnections)
|
||||
proc.SetFixtures(fixConnections)
|
||||
|
||||
const (
|
||||
nodeID = "heinz-tomato-ketchup" // TODO rename to hostID
|
||||
@@ -100,7 +100,7 @@ func TestSpyNoProcesses(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSpyWithProcesses(t *testing.T) {
|
||||
procspy.SetFixtures(fixConnectionsWithProcesses)
|
||||
proc.SetFixtures(fixConnectionsWithProcesses)
|
||||
|
||||
const (
|
||||
nodeID = "nikon" // TODO rename to hostID
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/procspy"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/probe/docker"
|
||||
"github.com/weaveworks/scope/probe/endpoint"
|
||||
"github.com/weaveworks/scope/probe/host"
|
||||
@@ -67,7 +67,7 @@ func main() {
|
||||
}
|
||||
log.Printf("publishing to: %s", strings.Join(targets, ", "))
|
||||
|
||||
procspy.SetProcRoot(*procRoot)
|
||||
proc.SetProcRoot(*procRoot)
|
||||
|
||||
if *httpListen != "" {
|
||||
log.Printf("profiling data being exported to %s", *httpListen)
|
||||
@@ -112,7 +112,7 @@ func main() {
|
||||
|
||||
var (
|
||||
endpointReporter = endpoint.NewReporter(hostID, hostName, *spyProcs, *useConntrack)
|
||||
processCache = process.NewCachingWalker(process.NewWalker(*procRoot))
|
||||
processCache = proc.NewCachingWalker(proc.NewWalker(*procRoot))
|
||||
tickers = []Ticker{processCache}
|
||||
reporters = []Reporter{
|
||||
endpointReporter,
|
||||
|
||||
26
probe/proc/fixture.go
Normal file
26
probe/proc/fixture.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package proc
|
||||
|
||||
// SetFixtures declares constant Connection and ConnectionProcs which will
|
||||
// always be returned by the package-level Connections and Processes
|
||||
// functions. It's designed to be used in tests.
|
||||
|
||||
type fixedConnIter []Connection
|
||||
|
||||
func (f *fixedConnIter) Next() *Connection {
|
||||
if len(*f) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
car := (*f)[0]
|
||||
*f = (*f)[1:]
|
||||
|
||||
return &car
|
||||
}
|
||||
|
||||
// SetFixtures is used in test scenarios to have known output.
|
||||
func SetFixtures(c []Connection) {
|
||||
cbConnections = func(bool) (ConnIter, error) {
|
||||
f := fixedConnIter(c)
|
||||
return &f, nil
|
||||
}
|
||||
}
|
||||
125
probe/proc/proc.go
Normal file
125
probe/proc/proc.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package proc
|
||||
|
||||
// /proc-based implementation.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
procRoot = "/proc"
|
||||
)
|
||||
|
||||
// SetProcRoot sets the location of the proc filesystem.
|
||||
func SetProcRoot(root string) {
|
||||
procRoot = root
|
||||
}
|
||||
|
||||
// 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() (map[uint64]Proc, error) {
|
||||
fh, err := os.Open(procRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dirNames, err := fh.Readdirnames(-1)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
res = map[uint64]Proc{}
|
||||
stat syscall.Stat_t
|
||||
)
|
||||
for _, dirName := range dirNames {
|
||||
pid, err := strconv.ParseUint(dirName, 10, 0)
|
||||
if err != nil {
|
||||
// Not a number, so not a PID subdir.
|
||||
continue
|
||||
}
|
||||
|
||||
fdBase := procRoot + "/" + dirName + "/fd/"
|
||||
dfh, err := os.Open(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
|
||||
}
|
||||
|
||||
var name string
|
||||
|
||||
for _, fdName := range fdNames {
|
||||
// Direct use of syscall.Stat() to save garbage.
|
||||
err = syscall.Stat(fdBase + fdName, &stat)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// We want sockets only.
|
||||
if stat.Mode & syscall.S_IFMT != syscall.S_IFSOCK {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
if name = procName(path.Join(procRoot, dirName)); name == "" {
|
||||
// Process might be gone by now
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
res[stat.Ino] = Proc{
|
||||
PID: uint(pid),
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// procName does a pid->name lookup.
|
||||
func procName(base string) string {
|
||||
fh, err := os.Open(base + "/comm")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
name := make([]byte, 64)
|
||||
l, err := fh.Read(name)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if l < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// drop trailing "\n"
|
||||
return string(name[:l - 1])
|
||||
}
|
||||
|
||||
// readFile reads an arbitrary file into a buffer. It's a variable so it can
|
||||
// be overwritten for benchmarks. That's bad practice and we should change it
|
||||
// to be a dependency.
|
||||
var readFile = func(filename string, buf *bytes.Buffer) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = buf.ReadFrom(f)
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
164
probe/proc/procnet.go
Normal file
164
probe/proc/procnet.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ProcNet is an iterator to parse /proc/net/tcp{,6} files.
|
||||
type ProcNet struct {
|
||||
b []byte
|
||||
c Connection
|
||||
wantedState uint
|
||||
bytesLocal, bytesRemote [16]byte
|
||||
}
|
||||
|
||||
// NewProcNet gives a new ProcNet parser.
|
||||
func NewProcNet(b []byte, wantedState uint) *ProcNet {
|
||||
return &ProcNet{
|
||||
b: b,
|
||||
c: Connection{},
|
||||
wantedState: wantedState,
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next connection. All buffers are re-used, so if you want
|
||||
// to keep the IPs you have to copy them.
|
||||
func (p *ProcNet) Next() *Connection {
|
||||
again:
|
||||
if len(p.b) == 0 {
|
||||
return nil
|
||||
}
|
||||
b := p.b
|
||||
|
||||
if p.b[2] == 's' {
|
||||
// Skip header
|
||||
p.b = nextLine(b)
|
||||
goto again
|
||||
}
|
||||
|
||||
var (
|
||||
local, remote, state, inode []byte
|
||||
)
|
||||
_, b = nextField(b) // 'sl' column
|
||||
local, b = nextField(b)
|
||||
remote, b = nextField(b)
|
||||
state, b = nextField(b)
|
||||
if parseHex(state) != p.wantedState {
|
||||
p.b = nextLine(b)
|
||||
goto again
|
||||
}
|
||||
_, b = nextField(b) // 'tx_queue' column
|
||||
_, b = nextField(b) // 'rx_queue' column
|
||||
_, b = nextField(b) // 'tr' column
|
||||
_, b = nextField(b) // 'uid' column
|
||||
_, b = nextField(b) // 'timeout' column
|
||||
inode, b = nextField(b)
|
||||
|
||||
p.c.LocalAddress, p.c.LocalPort = scanAddressNA(local, &p.bytesLocal)
|
||||
p.c.RemoteAddress, p.c.RemotePort = scanAddressNA(remote, &p.bytesRemote)
|
||||
p.c.inode = parseDec(inode)
|
||||
p.b = nextLine(b)
|
||||
return &p.c
|
||||
}
|
||||
|
||||
// scanAddressNA parses 'A12CF62E:00AA' to the address/port. Handles IPv4 and
|
||||
// IPv6 addresses. The address is a big endian 32 bit ints, hex encoded. We
|
||||
// just decode the hex and flip the bytes in every group of 4.
|
||||
func scanAddressNA(in []byte, buf *[16]byte) (net.IP, uint16) {
|
||||
col := bytes.IndexByte(in, ':')
|
||||
if col == -1 {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
// Network address is big endian. Can be either ipv4 or ipv6.
|
||||
address := hexDecode32bigNA(in[:col], buf)
|
||||
return net.IP(address), uint16(parseHex(in[col + 1:]))
|
||||
}
|
||||
|
||||
// hexDecode32big decodes sequences of 32bit big endian bytes.
|
||||
func hexDecode32bigNA(src []byte, buf *[16]byte) []byte {
|
||||
blocks := len(src) / 8
|
||||
for block := 0; block < blocks; block++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
a := fromHexChar(src[block * 8 + i * 2])
|
||||
b := fromHexChar(src[block * 8 + i * 2 + 1])
|
||||
buf[block * 4 + 3 - i] = (a << 4) | b
|
||||
}
|
||||
}
|
||||
return buf[:blocks * 4]
|
||||
}
|
||||
|
||||
func nextField(s []byte) ([]byte, []byte) {
|
||||
// Skip whitespace.
|
||||
for i, b := range s {
|
||||
if b != ' ' {
|
||||
s = s[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Up until the next whitespace field.
|
||||
for i, b := range s {
|
||||
if b == ' ' {
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func nextLine(s []byte) []byte {
|
||||
i := bytes.IndexByte(s, '\n')
|
||||
if i == -1 {
|
||||
return nil
|
||||
}
|
||||
return s[i + 1:]
|
||||
}
|
||||
|
||||
// Simplified copy of strconv.ParseUint(16).
|
||||
func parseHex(s []byte) uint {
|
||||
n := uint(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
n *= 16
|
||||
n += uint(fromHexChar(s[i]))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Simplified copy of strconv.ParseUint(10).
|
||||
func parseDec(s []byte) uint64 {
|
||||
n := uint64(0)
|
||||
for _, c := range s {
|
||||
n *= 10
|
||||
n += uint64(c - '0')
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// hexDecode32big decodes sequences of 32bit big endian bytes.
|
||||
func hexDecode32big(src []byte) []byte {
|
||||
dst := make([]byte, len(src) / 2)
|
||||
blocks := len(src) / 8
|
||||
for block := 0; block < blocks; block++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
a := fromHexChar(src[block * 8 + i * 2])
|
||||
b := fromHexChar(src[block * 8 + i * 2 + 1])
|
||||
dst[block * 4 + 3 - i] = (a << 4) | b
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// fromHexChar converts a hex character into its value.
|
||||
func fromHexChar(c byte) uint8 {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10
|
||||
}
|
||||
return 0
|
||||
}
|
||||
40
probe/proc/spy.go
Normal file
40
probe/proc/spy.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
tcpEstablished = 1 // according to /include/net/tcp_states.h
|
||||
)
|
||||
|
||||
// Connection is a (TCP) connection. The Proc struct might not be filled in.
|
||||
type Connection struct {
|
||||
Transport string
|
||||
LocalAddress net.IP
|
||||
LocalPort uint16
|
||||
RemoteAddress net.IP
|
||||
RemotePort uint16
|
||||
inode uint64
|
||||
Proc
|
||||
}
|
||||
|
||||
// Proc is a single process with PID and process name.
|
||||
type Proc struct {
|
||||
PID uint
|
||||
Name string
|
||||
}
|
||||
|
||||
// ConnIter is returned by Connections().
|
||||
type ConnIter interface {
|
||||
Next() *Connection
|
||||
}
|
||||
|
||||
// Connections returns all established (TCP) connections. If processes is
|
||||
// false we'll just list all TCP connections, and there is no need to be root.
|
||||
// If processes is true it'll additionally try to lookup the process owning the
|
||||
// connection, filling in the Proc field. You will need to run this as root to
|
||||
// find all processes.
|
||||
func Connections(processes bool) (ConnIter, error) {
|
||||
return cbConnections(processes)
|
||||
}
|
||||
52
probe/proc/spy_linux.go
Normal file
52
probe/proc/spy_linux.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(make([]byte, 0, 5000))
|
||||
},
|
||||
}
|
||||
|
||||
type pnConnIter struct {
|
||||
pn *ProcNet
|
||||
buf *bytes.Buffer
|
||||
procs map[uint64]Proc
|
||||
}
|
||||
|
||||
func (c *pnConnIter) Next() *Connection {
|
||||
n := c.pn.Next()
|
||||
if n == nil {
|
||||
// Done!
|
||||
bufPool.Put(c.buf)
|
||||
return nil
|
||||
}
|
||||
if proc, ok := c.procs[n.inode]; ok {
|
||||
n.Proc = proc
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// cbConnections sets Connections()
|
||||
var cbConnections = func(processes bool) (ConnIter, error) {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
readFile(path.Join(procRoot, "net", "tcp"), buf)
|
||||
readFile(path.Join(procRoot, "net", "tcp6"), buf)
|
||||
var procs map[uint64]Proc
|
||||
if processes {
|
||||
var err error
|
||||
if procs, err = walkProcPid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &pnConnIter{
|
||||
pn: NewProcNet(buf.Bytes(), tcpEstablished),
|
||||
buf: buf,
|
||||
procs: procs,
|
||||
}, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package process
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,15 +1,15 @@
|
||||
package process_test
|
||||
package proc_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
)
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
walker := &mockWalker{
|
||||
processes: []process.Process{
|
||||
processes: []proc.Process{
|
||||
{PID: 1, PPID: 0, Comm: "init"},
|
||||
{PID: 2, PPID: 1, Comm: "bash"},
|
||||
{PID: 3, PPID: 1, Comm: "apache", Threads: 2},
|
||||
@@ -17,7 +17,7 @@ func TestTree(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
tree, err := process.NewTree(walker)
|
||||
tree, err := proc.NewTree(walker)
|
||||
if err != nil {
|
||||
t.Fatalf("newProcessTree error: %v", err)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package process
|
||||
package proc
|
||||
|
||||
import "sync"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package process
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package process
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package process_test
|
||||
package proc_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
@@ -25,10 +25,10 @@ func (p mockProcess) IsDir() bool { return true }
|
||||
func (p mockProcess) Sys() interface{} { return nil }
|
||||
|
||||
func TestWalker(t *testing.T) {
|
||||
oldReadDir, oldReadFile := process.ReadDir, process.ReadFile
|
||||
oldReadDir, oldReadFile := proc.ReadDir, proc.ReadFile
|
||||
defer func() {
|
||||
process.ReadDir = oldReadDir
|
||||
process.ReadFile = oldReadFile
|
||||
proc.ReadDir = oldReadDir
|
||||
proc.ReadFile = oldReadFile
|
||||
}()
|
||||
|
||||
processes := map[string]mockProcess{
|
||||
@@ -39,7 +39,7 @@ func TestWalker(t *testing.T) {
|
||||
"1": {name: "1", comm: "init\n"},
|
||||
}
|
||||
|
||||
process.ReadDir = func(path string) ([]os.FileInfo, error) {
|
||||
proc.ReadDir = func(path string) ([]os.FileInfo, error) {
|
||||
result := []os.FileInfo{}
|
||||
for _, p := range processes {
|
||||
result = append(result, p)
|
||||
@@ -47,7 +47,7 @@ func TestWalker(t *testing.T) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
process.ReadFile = func(path string) ([]byte, error) {
|
||||
proc.ReadFile = func(path string) ([]byte, error) {
|
||||
splits := strings.Split(path, "/")
|
||||
|
||||
pid := splits[len(splits)-2]
|
||||
@@ -71,16 +71,16 @@ func TestWalker(t *testing.T) {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
want := map[int]process.Process{
|
||||
want := map[int]proc.Process{
|
||||
3: {PID: 3, PPID: 2, Comm: "curl", Cmdline: "curl google.com", Threads: 1},
|
||||
2: {PID: 2, PPID: 1, Comm: "bash", Cmdline: "", Threads: 1},
|
||||
4: {PID: 4, PPID: 3, Comm: "apache", Cmdline: "", Threads: 1},
|
||||
1: {PID: 1, PPID: 0, Comm: "init", Cmdline: "", Threads: 1},
|
||||
}
|
||||
|
||||
have := map[int]process.Process{}
|
||||
walker := process.NewWalker("unused")
|
||||
err := walker.Walk(func(p process.Process) {
|
||||
have := map[int]proc.Process{}
|
||||
walker := proc.NewWalker("unused")
|
||||
err := walker.Walk(func(p proc.Process) {
|
||||
have[p.PID] = p
|
||||
})
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
package process_test
|
||||
package proc_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
type mockWalker struct {
|
||||
processes []proc.Process
|
||||
}
|
||||
|
||||
func (m *mockWalker) Walk(f func(proc.Process)) error {
|
||||
for _, p := range m.processes {
|
||||
f(p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestBasicWalk(t *testing.T) {
|
||||
var (
|
||||
procRoot = "/proc"
|
||||
procFunc = func(process.Process) {}
|
||||
procFunc = func(proc.Process) {}
|
||||
)
|
||||
if err := process.NewWalker(procRoot).Walk(procFunc); err != nil {
|
||||
if err := proc.NewWalker(procRoot).Walk(procFunc); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
processes := []process.Process{
|
||||
processes := []proc.Process{
|
||||
{PID: 1, PPID: 0, Comm: "init"},
|
||||
{PID: 2, PPID: 1, Comm: "bash"},
|
||||
{PID: 3, PPID: 1, Comm: "apache", Threads: 2},
|
||||
@@ -28,7 +39,7 @@ func TestCache(t *testing.T) {
|
||||
walker := &mockWalker{
|
||||
processes: processes,
|
||||
}
|
||||
cachingWalker := process.NewCachingWalker(walker)
|
||||
cachingWalker := proc.NewCachingWalker(walker)
|
||||
err := cachingWalker.Tick()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -39,7 +50,7 @@ func TestCache(t *testing.T) {
|
||||
t.Errorf("%v (%v)", test.Diff(processes, have), err)
|
||||
}
|
||||
|
||||
walker.processes = []process.Process{}
|
||||
walker.processes = []proc.Process{}
|
||||
have, err = all(cachingWalker)
|
||||
if err != nil || !reflect.DeepEqual(processes, have) {
|
||||
t.Errorf("%v (%v)", test.Diff(processes, have), err)
|
||||
@@ -51,15 +62,15 @@ func TestCache(t *testing.T) {
|
||||
}
|
||||
|
||||
have, err = all(cachingWalker)
|
||||
want := []process.Process{}
|
||||
want := []proc.Process{}
|
||||
if err != nil || !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("%v (%v)", test.Diff(want, have), err)
|
||||
}
|
||||
}
|
||||
|
||||
func all(w process.Walker) ([]process.Process, error) {
|
||||
all := []process.Process{}
|
||||
err := w.Walk(func(p process.Process) {
|
||||
func all(w proc.Walker) ([]proc.Process, error) {
|
||||
all := []proc.Process{}
|
||||
err := w.Walk(func(p proc.Process) {
|
||||
all = append(all, p)
|
||||
})
|
||||
return all, err
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
)
|
||||
|
||||
// We use these keys in node metadata
|
||||
@@ -18,11 +19,11 @@ const (
|
||||
// Reporter generates Reports containing the Process topology.
|
||||
type Reporter struct {
|
||||
scope string
|
||||
walker Walker
|
||||
walker proc.Walker
|
||||
}
|
||||
|
||||
// NewReporter makes a new Reporter.
|
||||
func NewReporter(walker Walker, scope string) *Reporter {
|
||||
func NewReporter(walker proc.Walker, scope string) *Reporter {
|
||||
return &Reporter{
|
||||
scope: scope,
|
||||
walker: walker,
|
||||
@@ -42,7 +43,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
|
||||
func (r *Reporter) processTopology() (report.Topology, error) {
|
||||
t := report.MakeTopology()
|
||||
err := r.walker.Walk(func(p Process) {
|
||||
err := r.walker.Walk(func(p proc.Process) {
|
||||
pidstr := strconv.Itoa(p.PID)
|
||||
nodeID := report.MakeProcessNodeID(r.scope, pidstr)
|
||||
t.Nodes[nodeID] = report.MakeNode()
|
||||
|
||||
@@ -4,16 +4,17 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/weaveworks/scope/probe/proc"
|
||||
"github.com/weaveworks/scope/probe/process"
|
||||
"github.com/weaveworks/scope/report"
|
||||
"github.com/weaveworks/scope/test"
|
||||
)
|
||||
|
||||
type mockWalker struct {
|
||||
processes []process.Process
|
||||
processes []proc.Process
|
||||
}
|
||||
|
||||
func (m *mockWalker) Walk(f func(process.Process)) error {
|
||||
func (m *mockWalker) Walk(f func(proc.Process)) error {
|
||||
for _, p := range m.processes {
|
||||
f(p)
|
||||
}
|
||||
@@ -22,7 +23,7 @@ func (m *mockWalker) Walk(f func(process.Process)) error {
|
||||
|
||||
func TestReporter(t *testing.T) {
|
||||
walker := &mockWalker{
|
||||
processes: []process.Process{
|
||||
processes: []proc.Process{
|
||||
{PID: 1, PPID: 0, Comm: "init"},
|
||||
{PID: 2, PPID: 1, Comm: "bash"},
|
||||
{PID: 3, PPID: 1, Comm: "apache", Threads: 2},
|
||||
|
||||
Reference in New Issue
Block a user