mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
ProcNet.Next does not allocate Connection structs, for efficiency. Instead it always returns a *Connection pointing to the same instance. As a result, any mutations by the caller to struct elements that aren't actually set by ProcNet.Next, in particular Connection.Proc, are carried across to subsequent calls. This had hilarious consequences: connections referencing an inode which we hadn't come across during proc walking would be associated with the process corresponding to the last successfully looked up inode. The fix is to clear out the garbage left over from previous calls. Fixes #2638.
94 lines
1.8 KiB
Go
94 lines
1.8 KiB
Go
package procspy
|
|
|
|
import (
|
|
"bytes"
|
|
"sync"
|
|
|
|
"github.com/weaveworks/scope/probe/process"
|
|
)
|
|
|
|
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
|
|
} else {
|
|
// ProcNet.Next() always returns a pointer to the same
|
|
// struct. We therefore must clear any garbage left over from
|
|
// the previous call.
|
|
n.Proc = Proc{}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// NewConnectionScanner creates a new Linux ConnectionScanner
|
|
func NewConnectionScanner(walker process.Walker, processes bool) ConnectionScanner {
|
|
scanner := &linuxScanner{}
|
|
if processes {
|
|
scanner.r = newBackgroundReader(walker)
|
|
}
|
|
return scanner
|
|
}
|
|
|
|
// NewSyncConnectionScanner creates a new synchronous Linux ConnectionScanner
|
|
func NewSyncConnectionScanner(walker process.Walker, processes bool) ConnectionScanner {
|
|
scanner := &linuxScanner{}
|
|
if processes {
|
|
scanner.r = newForegroundReader(walker)
|
|
}
|
|
return scanner
|
|
}
|
|
|
|
type linuxScanner struct {
|
|
r reader
|
|
}
|
|
|
|
func (s *linuxScanner) Connections() (ConnIter, error) {
|
|
// buffer for contents of /proc/<pid>/net/tcp
|
|
buf := bufPool.Get().(*bytes.Buffer)
|
|
buf.Reset()
|
|
|
|
var procs map[uint64]*Proc
|
|
if s.r != nil {
|
|
var err error
|
|
if procs, err = s.r.getWalkedProcPid(buf); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if buf.Len() == 0 {
|
|
readFile(procRoot+"/net/tcp", buf)
|
|
if ipv6IsSupported {
|
|
readFile(procRoot+"/net/tcp6", buf)
|
|
}
|
|
}
|
|
|
|
return &pnConnIter{
|
|
pn: NewProcNet(buf.Bytes()),
|
|
buf: buf,
|
|
procs: procs,
|
|
}, nil
|
|
}
|
|
|
|
func (s *linuxScanner) Stop() {
|
|
if s.r != nil {
|
|
s.r.stop()
|
|
}
|
|
}
|