Merge branch 'master' into master

pull/1210/head
Matthieu MOREL 3 years ago committed by GitHub
commit b0469a470b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -29,10 +29,3 @@ jobs:
- name: Test
run: |
go test -coverprofile='coverage.out' -covermode=atomic ./...
- name: Upload Code Coverage
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
files: coverage.out
flags: ${{ runner.os }},go-${{ matrix.go-version }}
token: ${{ secrets.CODECOV_TOKEN }}

@ -28,7 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------
internal/common/binary.go in the gopsutil is copied and modifid from golang/encoding/binary.go.
internal/common/binary.go in the gopsutil is copied and modified from golang/encoding/binary.go.

@ -7,7 +7,7 @@ challenge is porting all psutil functions on some architectures.
## v3 migration
from v3.20.10, gopsutil becomes v3 which breaks backwards compatiblity.
from v3.20.10, gopsutil becomes v3 which breaks backwards compatibility.
See [v3Changes.md](_tools/v3migration/v3Changes.md) more detail changes.
## Tag semantics
@ -21,7 +21,7 @@ for example, v2.17.04 means
- 17: release year, 2017
- 04: release month
gopsutil aims to keep backwards-compatiblity until major version change.
gopsutil aims to keep backwards compatibility until major version change.
Tagged at every end of month, but if there are only a few commits, it
can be skipped.
@ -130,7 +130,7 @@ will provide useful information.
- CacheSize
- Flags (ex: "fpu vme de pse tsc msr pae mce cx8 ...")
- Microcode
- load/LoadAvg() (linux, freebsd)
- load/Avg() (linux, freebsd, solaris)
- Load1
- Load5
- Load15
@ -229,7 +229,7 @@ Some code is ported from Ohai. many thanks.
|**HostInfo** | | | | | | |
|hostname |x |x |x |x |x |x |
|uptime |x |x |x |x | |x |
|proces |x |x |x | | |x |
|process |x |x |x | | |x |
|os |x |x |x |x |x |x |
|platform |x |x |x |x | |x |
|platformfamily |x |x |x |x | |x |

@ -4,10 +4,14 @@
package disk
import (
"bufio"
"bytes"
"context"
"encoding/binary"
"fmt"
"os/exec"
"strconv"
"strings"
"golang.org/x/sys/unix"
@ -163,7 +167,29 @@ func getFsType(stat unix.Statfs_t) string {
}
func SerialNumberWithContext(ctx context.Context, name string) (string, error) {
return "", common.ErrNotImplementedError
geom, err := exec.LookPath("geom")
if err != nil {
return "", fmt.Errorf("find geom: %w", err)
}
geomOut, err := invoke.CommandWithContext(ctx, geom, "disk", "list", name)
if err != nil {
return "", fmt.Errorf("exec geom: %w", err)
}
s := bufio.NewScanner(bytes.NewReader(geomOut))
serial := ""
for s.Scan() {
flds := strings.Fields(s.Text())
if len(flds) == 2 && flds[0] == "ident:" {
if flds[1] != "(null)" {
serial = flds[1]
}
break
}
}
if err = s.Err(); err != nil {
return "", err
}
return serial, nil
}
func LabelWithContext(ctx context.Context, name string) (string, error) {

@ -57,7 +57,7 @@ func GetDockerStatWithContext(ctx context.Context) ([]CgroupDockerStat, error) {
return ret, nil
}
// GetDockerIDList returnes a list of DockerID.
// GetDockerIDList returns a list of DockerID.
// This requires certain permission.
func GetDockerIDList() ([]string, error) {
return GetDockerIDListWithContext(context.Background())
@ -86,7 +86,7 @@ func GetDockerIDListWithContext(ctx context.Context) ([]string, error) {
return ret, nil
}
// CgroupCPU returnes specified cgroup id CPU status.
// CgroupCPU returns specified cgroup id CPU status.
// containerID is same as docker id if you use docker.
// If you use container via systemd.slice, you could use
// containerID = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/
@ -94,7 +94,7 @@ func CgroupCPU(containerID string, base string) (*CgroupCPUStat, error) {
return CgroupCPUWithContext(context.Background(), containerID, base)
}
// CgroupCPUUsage returnes specified cgroup id CPU usage.
// CgroupCPUUsage returns specified cgroup id CPU usage.
// containerID is same as docker id if you use docker.
// If you use container via systemd.slice, you could use
// containerID = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/

@ -19,7 +19,7 @@ func GetDockerStatWithContext(ctx context.Context) ([]CgroupDockerStat, error) {
return nil, ErrDockerNotAvailable
}
// GetDockerIDList returnes a list of DockerID.
// GetDockerIDList returns a list of DockerID.
// This requires certain permission.
func GetDockerIDList() ([]string, error) {
return GetDockerIDListWithContext(context.Background())
@ -29,7 +29,7 @@ func GetDockerIDListWithContext(ctx context.Context) ([]string, error) {
return nil, ErrDockerNotAvailable
}
// CgroupCPU returnes specified cgroup id CPU status.
// CgroupCPU returns specified cgroup id CPU status.
// containerid is same as docker id if you use docker.
// If you use container via systemd.slice, you could use
// containerid = docker-<container id>.scope and base=/sys/fs/cgroup/cpuacct/system.slice/

@ -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"

@ -64,7 +64,7 @@ func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...stri
type FakeInvoke struct {
Suffix string // Suffix species expected file name suffix such as "fail"
Error error // If Error specfied, return the error.
Error error // If Error specified, return the error.
}
// Command in FakeInvoke returns from expected file if exists.

@ -100,8 +100,9 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
if err != nil {
return 0, err
}
t := uint64(time.Now().Unix()) - uint64(b)
return t, nil
currentTime := float64(time.Now().UnixNano()) / float64(time.Second)
t := currentTime - b
return uint64(t), nil
}
return 0, fmt.Errorf("could not find btime")

@ -39,7 +39,7 @@ func AvgWithContext(ctx context.Context) (*AvgStat, error) {
return ret, nil
}
// Misc returnes miscellaneous host-wide statistics.
// Misc returns miscellaneous host-wide statistics.
// darwin use ps command to get process running/blocked count.
// Almost same as FreeBSD implementation, but state is different.
// U means 'Uninterruptible Sleep'.

@ -68,7 +68,7 @@ func fileAvgWithContext() (*AvgStat, error) {
return ret, nil
}
// Misc returnes miscellaneous host-wide statistics.
// Misc returns miscellaneous host-wide statistics.
// Note: the name should be changed near future.
func Misc() (*MiscStat, error) {
return MiscWithContext(context.Background())

@ -4,11 +4,12 @@
package load
import (
"bufio"
"bytes"
"context"
"os/exec"
"strconv"
"strings"
"github.com/shirou/gopsutil/v3/internal/common"
)
func Avg() (*AvgStat, error) {
@ -16,7 +17,45 @@ func Avg() (*AvgStat, error) {
}
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
return nil, common.ErrNotImplementedError
kstat, err := exec.LookPath("kstat")
if err != nil {
return nil, err
}
out, err := invoke.CommandWithContext(ctx, kstat, "-p", "unix:0:system_misc:avenrun_*")
if err != nil {
return nil, err
}
avg := &AvgStat{}
scanner := bufio.NewScanner(bytes.NewReader(out))
for scanner.Scan() {
flds := strings.Fields(scanner.Text())
if len(flds) < 2 {
continue
}
var tgt *float64
switch {
case strings.HasSuffix(flds[0], ":avenrun_1min"):
tgt = &avg.Load1
case strings.HasSuffix(flds[0], ":avenrun_5min"):
tgt = &avg.Load5
case strings.HasSuffix(flds[0], ":avenrun_15min"):
tgt = &avg.Load15
default:
continue
}
v, err := strconv.ParseInt(flds[1], 10, 64)
if err != nil {
return nil, err
}
*tgt = float64(v) / (1 << 8)
}
if err = scanner.Err(); err != nil {
return nil, err
}
return avg, nil
}
func Misc() (*MiscStat, error) {

@ -305,7 +305,7 @@ func fillFromMeminfoWithContext() (*VirtualMemoryStat, *VirtualMemoryExStat, err
if !memavail {
if activeFile && inactiveFile && sReclaimable {
ret.Available = calcuateAvailVmem(ret, retEx)
ret.Available = calculateAvailVmem(ret, retEx)
} else {
ret.Available = ret.Cached + ret.Free
}
@ -387,10 +387,10 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
return ret, nil
}
// calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
// calculateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
// "MemAvailable:" column. It reimplements an algorithm from the link below
// https://github.com/giampaolo/psutil/pull/890
func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 {
func calculateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 {
var watermarkLow uint64
fn := common.HostProc("zoneinfo")

@ -105,7 +105,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
return iocounters, nil
}
// NetIOCountersByFile is an method which is added just a compatibility for linux.
// IOCountersByFile exists just for compatibility with Linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}

@ -257,7 +257,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
return ret, nil
}
// NetIOCountersByFile is an method which is added just a compatibility for linux.
// IOCountersByFile exists just for compatibility with Linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}

@ -95,7 +95,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
return ret, nil
}
// NetIOCountersByFile is an method which is added just a compatibility for linux.
// IOCountersByFile exists just for compatibility with Linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}

@ -40,7 +40,7 @@ const ( // Conntrack Column numbers
ctSEARCH_RESTART
)
// NetIOCounters returnes network I/O statistics for every network
// NetIOCounters returns network I/O statistics for every network
// interface installed on the system. If pernic argument is false,
// return only sum of all information (which name is 'all'). If true,
// every network interface installed on the system is returned
@ -188,7 +188,7 @@ func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoC
line := lines[i]
r := strings.IndexRune(line, ':')
if r == -1 {
return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
return nil, errors.New(filename + " is not formatted correctly, expected ':'.")
}
proto := strings.ToLower(line[:r])
if !protos[proto] {
@ -204,7 +204,7 @@ func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoC
i++
statValues := strings.Split(lines[i][r+2:], " ")
if len(statNames) != len(statValues) {
return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
return nil, errors.New(filename + " is not formatted correctly, expected same number of columns.")
}
stat := ProtoCountersStat{
Protocol: proto,
@ -543,7 +543,7 @@ func statsFromInodesWithContext(ctx context.Context, root string, pid int32, tma
return ret, nil
}
// getProcInodes returnes fd of the pid.
// getProcInodes returns fd of the pid.
func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap)
@ -553,7 +553,7 @@ func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, erro
return ret, err
}
defer f.Close()
dirEntries, err := f.ReadDir(max)
dirEntries, err := readDir(f, max)
if err != nil {
return ret, err
}

@ -0,0 +1,12 @@
//go:build !go1.16
// +build !go1.16
package net
import (
"os"
)
func readDir(f *os.File, max int) ([]os.FileInfo, error) {
return f.Readdir(max)
}

@ -0,0 +1,12 @@
//go:build go1.16
// +build go1.16
package net
import (
"os"
)
func readDir(f *os.File, max int) ([]os.DirEntry, error) {
return f.ReadDir(max)
}

@ -139,7 +139,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
return ret, nil
}
// NetIOCountersByFile is an method which is added just a compatibility for linux.
// IOCountersByFile exists just for compatibility with Linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}

@ -200,7 +200,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
return counters, nil
}
// NetIOCountersByFile is an method which is added just a compatibility for linux.
// IOCountersByFile exists just for compatibility with Linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}

@ -47,13 +47,13 @@ type Process struct {
const (
// Running marks a task a running or runnable (on the run queue)
Running = "running"
// Blocked marks a task waiting on a short, uninterruptable operation (usually IO)
// Blocked marks a task waiting on a short, uninterruptible operation (usually I/O)
Blocked = "blocked"
// Idle marks a task sleeping for more than about 20 seconds
Idle = "idle"
// Lock marks a task waiting to acquire a lock
Lock = "lock"
// Sleep marks task waiting for short, interruptable operation
// Sleep marks task waiting for short, interruptible operation
Sleep = "sleep"
// Stop marks a stopped process
Stop = "stop"
@ -230,7 +230,7 @@ func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) {
}
// If interval is 0, return difference from last call(non-blocking).
// If interval > 0, wait interval sec and return diffrence between start and end.
// If interval > 0, wait interval sec and return difference between start and end.
func (p *Process) Percent(interval time.Duration) (float64, error) {
return p.PercentWithContext(context.Background(), interval)
}
@ -402,6 +402,15 @@ func (p *Process) Parent() (*Process, error) {
return p.ParentWithContext(context.Background())
}
// ParentWithContext returns parent Process of the process.
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
ppid, err := p.PpidWithContext(ctx)
if err != nil {
return nil, err
}
return NewProcessWithContext(ctx, ppid)
}
// Status returns the process status.
// Return value could be one of these.
// R: Running S: Sleep T: Stop I: Idle

@ -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,61 +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
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
out, err := common.CallLsofWithContext(ctx, invoke, p.Pid, "-FR")
if err != nil {
return nil, err
}
for _, line := range out {
if len(line) >= 1 && line[0] == 'R' {
v, err := strconv.Atoi(line[1:])
if err != nil {
return nil, err
}
return NewProcessWithContext(ctx, int32(v))
}
}
return nil, fmt.Errorf("could not find parent line")
return k.Proc.P_starttime.Sec*1000 + int64(k.Proc.P_starttime.Usec)/1000, nil
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
@ -188,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
}
@ -200,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
}
@ -250,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
@ -304,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 {
@ -399,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
}

@ -74,10 +74,6 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
return []string{""}, common.ErrNotImplementedError
}

@ -111,11 +111,11 @@ func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error)
}
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError
k, err := p.getKProc()
if err != nil {
return 0, err
}
return k.Start.Sec*1000 + k.Start.Usec/1000, nil
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {

@ -123,17 +123,6 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return p.fillFromCwdWithContext()
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return nil, err
}
if p.parent == 0 {
return nil, fmt.Errorf("wrong number of parents")
}
return NewProcessWithContext(ctx, p.parent)
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
@ -351,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)
@ -537,7 +526,7 @@ func (p *Process) fillFromLimitsWithContext() ([]RlimitStat, error) {
// Assert that last item is a Hard limit
statItem.Hard, err = limitToUint(str[len(str)-1])
if err != nil {
// On error remove last item an try once again since it can be unit or header line
// On error remove last item and try once again since it can be unit or header line
str = str[:len(str)-1]
statItem.Hard, err = limitToUint(str[len(str)-1])
if err != nil {

@ -142,10 +142,6 @@ func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
k, err := p.getKProc()
if err != nil {

@ -74,10 +74,6 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
return []string{""}, common.ErrNotImplementedError
}

@ -88,10 +88,6 @@ func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return p.fillFromPathCwdWithContext(ctx)
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
return []string{""}, common.ErrNotImplementedError
}

@ -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()

@ -189,7 +189,7 @@ type winLUIDAndAttributes struct {
}
// TOKEN_PRIVILEGES
type winTokenPriviledges struct {
type winTokenPrivileges struct {
PrivilegeCount winDWord
Privileges [1]winLUIDAndAttributes
}
@ -218,23 +218,23 @@ func init() {
}
defer token.Close()
tokenPriviledges := winTokenPriviledges{PrivilegeCount: 1}
tokenPrivileges := winTokenPrivileges{PrivilegeCount: 1}
lpName := syscall.StringToUTF16("SeDebugPrivilege")
ret, _, _ := procLookupPrivilegeValue.Call(
0,
uintptr(unsafe.Pointer(&lpName[0])),
uintptr(unsafe.Pointer(&tokenPriviledges.Privileges[0].Luid)))
uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0].Luid)))
if ret == 0 {
return
}
tokenPriviledges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED
tokenPrivileges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED
procAdjustTokenPrivileges.Call(
uintptr(token),
0,
uintptr(unsafe.Pointer(&tokenPriviledges)),
uintptr(unsafe.Sizeof(tokenPriviledges)),
uintptr(unsafe.Pointer(&tokenPrivileges)),
uintptr(unsafe.Sizeof(tokenPrivileges)),
0,
0)
}
@ -435,15 +435,6 @@ func (p *Process) CwdWithContext(_ context.Context) (string, error) {
return "", nil
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
ppid, err := p.PpidWithContext(ctx)
if err != nil {
return nil, fmt.Errorf("could not get ParentProcessID: %s", err)
}
return NewProcessWithContext(ctx, ppid)
}
func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
return []string{""}, common.ErrNotImplementedError
}
@ -703,6 +694,9 @@ func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, er
0, true, windows.DUPLICATE_SAME_ACCESS) != nil {
continue
}
// release the new handle
defer windows.CloseHandle(windows.Handle(file))
fileType, _ := windows.GetFileType(windows.Handle(file))
if fileType != windows.FILE_TYPE_DISK {
continue

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