mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
46ce80758d
In prep for reducing garbage, being able to reuse memory. So far this doesn't actually reuse much. This is just changing signatures around. But some improvement in any case: bradfitz@tsdev:~/src/tailscale.com$ ~/go/bin/benchstat before after name old time/op new time/op delta GetList-8 11.8ms ± 9% 9.9ms ± 3% -15.98% (p=0.000 n=10+10) name old alloc/op new alloc/op delta GetList-8 99.5kB ± 2% 91.9kB ± 0% -7.62% (p=0.000 n=9+9) name old allocs/op new allocs/op delta GetList-8 3.05k ± 1% 2.93k ± 0% -3.83% (p=0.000 n=8+9) More later, once parsers can reuse strings from previous parses. Updates #5958 Change-Id: I76cd5048246dd24d11c4e263d8bb8041747fb2b0 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
118 lines
2.4 KiB
Go
118 lines
2.4 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package portlist
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"tailscale.com/envknob"
|
|
)
|
|
|
|
// Port is a listening port on the machine.
|
|
type Port struct {
|
|
Proto string // "tcp" or "udp"
|
|
Port uint16 // port number
|
|
Process string // optional process name, if found
|
|
|
|
inode string // OS-specific; "socket:[165614651]" on Linux
|
|
}
|
|
|
|
// List is a list of Ports.
|
|
type List []Port
|
|
|
|
func (a *Port) lessThan(b *Port) bool {
|
|
if a.Port < b.Port {
|
|
return true
|
|
} else if a.Port > b.Port {
|
|
return false
|
|
}
|
|
|
|
if a.Proto < b.Proto {
|
|
return true
|
|
} else if a.Proto > b.Proto {
|
|
return false
|
|
}
|
|
|
|
if a.inode < b.inode {
|
|
return true
|
|
} else if a.inode > b.inode {
|
|
return false
|
|
}
|
|
|
|
if a.Process < b.Process {
|
|
return true
|
|
} else if a.Process > b.Process {
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (a List) sameInodes(b List) bool {
|
|
if a == nil || b == nil || len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := range a {
|
|
if a[i].Proto != b[i].Proto ||
|
|
a[i].Port != b[i].Port ||
|
|
a[i].inode != b[i].inode {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (pl List) String() string {
|
|
var sb strings.Builder
|
|
for _, v := range pl {
|
|
fmt.Fprintf(&sb, "%-3s %5d %-17s %#v\n",
|
|
v.Proto, v.Port, v.inode, v.Process)
|
|
}
|
|
return strings.TrimRight(sb.String(), "\n")
|
|
}
|
|
|
|
var debugDisablePortlist = envknob.RegisterBool("TS_DEBUG_DISABLE_PORTLIST")
|
|
|
|
func (p *Poller) getList() (List, error) {
|
|
if debugDisablePortlist() {
|
|
return nil, nil
|
|
}
|
|
var err error
|
|
p.scratch, err = appendListeningPorts(p.scratch[:0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listPorts: %s", err)
|
|
}
|
|
pl := sortAndDedup(p.scratch)
|
|
if pl.sameInodes(p.prev) {
|
|
// Nothing changed, skip inode lookup
|
|
return p.prev, nil
|
|
}
|
|
pl, err = addProcesses(pl)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("addProcesses: %s", err)
|
|
}
|
|
return pl, nil
|
|
}
|
|
|
|
// sortAndDedup sorts ps in place (by Port.lessThan) and then returns
|
|
// a subset of it with duplicate (Proto, Port) removed.
|
|
func sortAndDedup(ps List) List {
|
|
sort.Slice(ps, func(i, j int) bool {
|
|
return (&ps[i]).lessThan(&ps[j])
|
|
})
|
|
out := ps[:0]
|
|
var last Port
|
|
for _, p := range ps {
|
|
protoPort := Port{Proto: p.Proto, Port: p.Port}
|
|
if last == protoPort {
|
|
continue
|
|
}
|
|
out = append(out, p)
|
|
last = protoPort
|
|
}
|
|
return out
|
|
}
|