Merge branch 'master' into refactor/parentwithcontext-ppidwithcontext

pull/1235/head
shirou 3 years ago committed by GitHub
commit 50cad0760c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,11 +3,11 @@ module github.com/shirou/gopsutil/v3
go 1.15
require (
github.com/google/go-cmp v0.5.6
github.com/google/go-cmp v0.5.7
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c
github.com/stretchr/testify v1.7.0
github.com/tklauser/go-sysconf v0.3.9
github.com/yusufpapurcu/wmi v1.2.2
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c
golang.org/x/sys v0.0.0-20220111092808-5a964db01320
)

@ -2,12 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -26,8 +25,8 @@ github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

@ -283,7 +283,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
family = "debian"
case "fedora":
family = "fedora"
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm", "rocky":
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm", "rocky", "almalinux":
family = "rhel"
case "suse", "opensuse", "opensuse-leap", "opensuse-tumbleweed", "opensuse-tumbleweed-kubic", "sles", "sled", "caasp":
family = "suse"

@ -10,9 +10,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/internal/common"
"github.com/shirou/gopsutil/v3/net"
"github.com/tklauser/go-sysconf"
@ -46,34 +44,25 @@ type _Ctype_struct___0 struct {
func pidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32
pids, err := callPsWithContext(ctx, "pid", 0, false, false)
kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
return ret, err
}
for _, pid := range pids {
v, err := strconv.Atoi(pid[0])
if err != nil {
return ret, err
}
ret = append(ret, int32(v))
for _, proc := range kprocs {
ret = append(ret, int32(proc.Proc.P_pid))
}
return ret, nil
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "ppid", p.Pid, false, false)
if err != nil {
return 0, err
}
v, err := strconv.Atoi(r[0][0])
k, err := p.getKProc()
if err != nil {
return 0, err
}
return int32(v), err
return k.Eproc.Ppid, nil
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
@ -81,7 +70,8 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
if err != nil {
return "", err
}
name := common.IntToString(k.Proc.P_comm[:])
name := common.ByteToString(k.Proc.P_comm[:])
if len(name) >= 15 {
cmdName, err := p.cmdNameWithContext(ctx)
@ -89,11 +79,11 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
return "", err
}
if len(cmdName) > 0 {
extendedName := filepath.Base(cmdName[0])
extendedName := filepath.Base(cmdName)
if strings.HasPrefix(extendedName, p.name) {
name = extendedName
} else {
name = cmdName[0]
name = cmdName
}
}
}
@ -101,44 +91,13 @@ func (p *Process) NameWithContext(ctx context.Context) (string, error) {
return name, nil
}
// cmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) cmdNameWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
if err != nil {
return nil, err
}
return r[0], err
}
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
r, err := callPsWithContext(ctx, "etime", p.Pid, false, false)
k, err := p.getKProc()
if err != nil {
return 0, err
}
elapsedSegments := strings.Split(strings.Replace(r[0][0], "-", ":", 1), ":")
var elapsedDurations []time.Duration
for i := len(elapsedSegments) - 1; i >= 0; i-- {
p, err := strconv.ParseInt(elapsedSegments[i], 10, 0)
if err != nil {
return 0, err
}
elapsedDurations = append(elapsedDurations, time.Duration(p))
}
elapsed := time.Duration(elapsedDurations[0]) * time.Second
if len(elapsedDurations) > 1 {
elapsed += time.Duration(elapsedDurations[1]) * time.Minute
}
if len(elapsedDurations) > 2 {
elapsed += time.Duration(elapsedDurations[2]) * time.Hour
}
if len(elapsedDurations) > 3 {
elapsed += time.Duration(elapsedDurations[3]) * time.Hour * 24
}
start := time.Now().Add(-elapsed)
return start.Unix() * 1000, nil
return k.Proc.P_starttime.Sec*1000 + int64(k.Proc.P_starttime.Usec)/1000, nil
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
@ -171,7 +130,7 @@ func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
}
// See: http://unix.superglobalmegacorp.com/Net2/newsrc/sys/ucred.h.html
userEffectiveUID := int32(k.Eproc.Ucred.UID)
userEffectiveUID := int32(k.Eproc.Ucred.Uid)
return []int32{userEffectiveUID}, nil
}
@ -183,7 +142,7 @@ func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
}
gids := make([]int32, 0, 3)
gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))
gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Pcred.P_svgid))
return gids, nil
}
@ -233,14 +192,6 @@ func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, e
return nil, common.ErrNotImplementedError
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
if err != nil {
return 0, err
}
return int32(len(r)), nil
}
func convertCPUTimes(s string) (ret float64, err error) {
var t int
var _tmp string
@ -287,56 +238,6 @@ func convertCPUTimes(s string) (ret float64, err error) {
return float64(t) / float64(clockTicks), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
if err != nil {
return nil, err
}
utime, err := convertCPUTimes(r[0][0])
if err != nil {
return nil, err
}
stime, err := convertCPUTimes(r[0][1])
if err != nil {
return nil, err
}
ret := &cpu.TimesStat{
CPU: "cpu",
User: utime,
System: stime,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
if err != nil {
return nil, err
}
rss, err := strconv.Atoi(r[0][0])
if err != nil {
return nil, err
}
vms, err := strconv.Atoi(r[0][1])
if err != nil {
return nil, err
}
pagein, err := strconv.Atoi(r[0][2])
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(rss) * 1024,
VMS: uint64(vms) * 1024,
Swap: uint64(pagein),
}
return ret, nil
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil {
@ -382,17 +283,8 @@ func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
// Returns a proc as defined here:
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html
func (p *Process) getKProc() (*KinfoProc, error) {
buf, err := unix.SysctlRaw("kern.proc.pid", int(p.Pid))
if err != nil {
return nil, err
}
k, err := parseKinfoProc(buf)
if err != nil {
return nil, err
}
return &k, nil
func (p *Process) getKProc() (*unix.KinfoProc, error) {
return unix.SysctlKinfoProc("kern.proc.pid", int(p.Pid))
}
// call ps command.

@ -9,6 +9,7 @@ package process
// #include <sys/errno.h>
// #include <sys/proc_info.h>
// #include <sys/sysctl.h>
// #include <mach/mach_time.h>
import "C"
import (
@ -18,12 +19,18 @@ import (
"strings"
"syscall"
"unsafe"
"github.com/shirou/gopsutil/v3/cpu"
)
var argMax int
var (
argMax int
timescaleToNanoSeconds float64
)
func init() {
argMax = getArgMax()
timescaleToNanoSeconds = getTimeScaleToNanoSeconds()
}
func getArgMax() int {
@ -39,6 +46,14 @@ func getArgMax() int {
return 0
}
func getTimeScaleToNanoSeconds() float64 {
var timeBaseInfo C.struct_mach_timebase_info
C.mach_timebase_info(&timeBaseInfo)
return float64(timeBaseInfo.numer) / float64(timeBaseInfo.denom)
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
var c C.char // need a var for unsafe.Sizeof need a var
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
@ -82,7 +97,7 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return C.GoString(&vpi.pvi_cdir.vip_path[0]), err
}
func procArgs(pid int32) (*[]byte, int, error) {
func procArgs(pid int32) ([]byte, int, error) {
var (
mib = [...]C.int{C.CTL_KERN, C.KERN_PROCARGS2, C.int(pid)}
size C.size_t = C.ulong(argMax)
@ -91,23 +106,27 @@ func procArgs(pid int32) (*[]byte, int, error) {
)
procargs := (*C.char)(C.malloc(C.ulong(argMax)))
defer C.free(unsafe.Pointer(procargs))
retval := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
retval, err := C.sysctl(&mib[0], 3, unsafe.Pointer(procargs), &size, C.NULL, 0)
if retval == 0 {
C.memcpy(unsafe.Pointer(&nargs), unsafe.Pointer(procargs), C.sizeof_int)
result = C.GoBytes(unsafe.Pointer(procargs), C.int(size))
// fmt.Printf("size: %d %d\n%s\n", size, nargs, hex.Dump(result))
return &result, int(nargs), nil
return result, int(nargs), nil
}
return nil, 0, fmt.Errorf("error: %d", retval)
return nil, 0, err
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return p.cmdlineSliceWithContext(ctx, true)
}
func (p *Process) cmdlineSliceWithContext(ctx context.Context, fallback bool) ([]string, error) {
pargs, nargs, err := procArgs(p.Pid)
if err != nil {
return nil, err
}
// The first bytes hold the nargs int, skip it.
args := bytes.Split((*pargs)[C.sizeof_int:], []byte{0})
args := bytes.Split((pargs)[C.sizeof_int:], []byte{0})
var argStr string
// The first element is the actual binary/command path.
// command := args[0]
@ -131,6 +150,20 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
return argSlice, err
}
// cmdNameWithContext returns the command name (including spaces) without any arguments
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
r, err := p.cmdlineSliceWithContext(ctx, false)
if err != nil {
return "", err
}
if len(r) == 0 {
return "", nil
}
return r[0], err
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := p.CmdlineSliceWithContext(ctx)
if err != nil {
@ -138,3 +171,49 @@ func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
}
return strings.Join(r, " "), err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return 0, err
}
return int32(ti.pti_threadnum), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return nil, err
}
ret := &cpu.TimesStat{
CPU: "cpu",
User: float64(ti.pti_total_user) * timescaleToNanoSeconds / 1e9,
System: float64(ti.pti_total_system) * timescaleToNanoSeconds / 1e9,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
const tiSize = C.sizeof_struct_proc_taskinfo
ti := (*C.struct_proc_taskinfo)(C.malloc(tiSize))
_, err := C.proc_pidinfo(C.int(p.Pid), C.PROC_PIDTASKINFO, 0, unsafe.Pointer(ti), tiSize)
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(ti.pti_resident_size),
VMS: uint64(ti.pti_virtual_size),
Swap: uint64(ti.pti_pageins),
}
return ret, nil
}

@ -10,6 +10,7 @@ import (
"strconv"
"strings"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/internal/common"
)
@ -47,6 +48,18 @@ func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
return strings.Join(r[0], " "), err
}
func (p *Process) cmdNameWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false, true)
if err != nil {
return "", err
}
if len(r) > 0 && len(r[0]) > 0 {
return r[0][0], err
}
return "", err
}
// CmdlineSliceWithContext returns the command line arguments of the process as a slice with each
// element being an argument. Because of current deficiencies in the way that the command
// line arguments are found, single arguments that have spaces in the will actually be
@ -59,3 +72,61 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
}
return r[0], err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true, false)
if err != nil {
return 0, err
}
return int32(len(r)), nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false, false)
if err != nil {
return nil, err
}
utime, err := convertCPUTimes(r[0][0])
if err != nil {
return nil, err
}
stime, err := convertCPUTimes(r[0][1])
if err != nil {
return nil, err
}
ret := &cpu.TimesStat{
CPU: "cpu",
User: utime,
System: stime,
}
return ret, nil
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false, false)
if err != nil {
return nil, err
}
rss, err := strconv.Atoi(r[0][0])
if err != nil {
return nil, err
}
vms, err := strconv.Atoi(r[0][1])
if err != nil {
return nil, err
}
pagein, err := strconv.Atoi(r[0][2])
if err != nil {
return nil, err
}
ret := &MemoryInfoStat{
RSS: uint64(rss) * 1024,
VMS: uint64(vms) * 1024,
Swap: uint64(pagein),
}
return ret, nil
}

@ -340,11 +340,11 @@ func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, e
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil {
if len(pids) == 0 {
return nil, ErrorNoChildren
}
return nil, err
}
if len(pids) == 0 {
return nil, ErrorNoChildren
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)

@ -46,26 +46,6 @@ func Test_Pids(t *testing.T) {
}
}
func Test_Pids_Fail(t *testing.T) {
if runtime.GOOS != "darwin" {
t.Skip("darwin only")
}
mu.Lock()
defer mu.Unlock()
invoke = common.FakeInvoke{Suffix: "fail"}
ret, err := Pids()
skipIfNotImplementedErr(t, err)
invoke = common.Invoke{}
if err != nil {
t.Errorf("error %v", err)
}
if len(ret) != 9 {
t.Errorf("wrong getted pid nums: %v/%d", ret, len(ret))
}
}
func Test_Pid_exists(t *testing.T) {
checkPid := os.Getpid()

@ -1,10 +0,0 @@
PID
245
247
248
249
254
262
264
265
267
Loading…
Cancel
Save