mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-26 13:48:59 +08:00
Merge branch 'master' of github.com:shirou/gopsutil into feature/lxd_boot_time
This commit is contained in:
commit
8b2468f1e7
@ -43,7 +43,7 @@ Available Architectures
|
||||
- Linux i386/amd64/arm(raspberry pi)
|
||||
- Windows/amd64
|
||||
- Darwin i386/amd64
|
||||
- OpenBDS amd64 (Thank you @mpfz0r!)
|
||||
- OpenBSD amd64 (Thank you @mpfz0r!)
|
||||
- Solaris amd64 (developed and tested on SmartOS/Illumos, Thank you @jen20!)
|
||||
|
||||
All works are implemented without cgo by porting c struct to golang struct.
|
||||
@ -187,7 +187,7 @@ boot_time x x x x x
|
||||
users x x x x x
|
||||
pids x x x x x
|
||||
pid_exists x x x x x
|
||||
net_connections x x
|
||||
net_connections x x x
|
||||
net_protocols x
|
||||
net_if_addrs
|
||||
net_if_stats
|
||||
|
@ -12,6 +12,9 @@ import (
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
// TimesStat contains the amounts of time the CPU has spent performing different
|
||||
// kinds of work. Time units are in USER_HZ or Jiffies (typically hundredths of
|
||||
// a second). It is based on linux /proc/stat file.
|
||||
type TimesStat struct {
|
||||
CPU string `json:"cpu"`
|
||||
User float64 `json:"user"`
|
||||
|
@ -3,6 +3,7 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
@ -81,8 +82,9 @@ func Info() ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
var dst []Win32_Processor
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(q, &dst)
|
||||
if err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
@ -113,8 +115,11 @@ func Info() ([]InfoStat, error) {
|
||||
// Name property is the key by which overall, per cpu and per core metric is known.
|
||||
func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
|
||||
var ret []Win32_PerfFormattedData_Counters_ProcessorInformation
|
||||
|
||||
q := wmi.CreateQuery(&ret, "")
|
||||
err := wmi.Query(q, &ret)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, q, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
@ -123,7 +128,9 @@ func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error)
|
||||
func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) {
|
||||
var ret []Win32_PerfFormattedData_PerfOS_System
|
||||
q := wmi.CreateQuery(&ret, "")
|
||||
err := wmi.Query(q, &ret)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, q, &ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func Partitions(all bool) ([]PartitionStat, error) {
|
||||
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
|
||||
ret := make(map[string]IOCountersStat)
|
||||
|
||||
r, err := unix.Sysctl("hw.diskstats")
|
||||
r, err := unix.SysctlRaw("hw.diskstats")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ package disk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"unsafe"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
@ -132,7 +132,9 @@ func IOCounters(names ...string) (map[string]IOCountersStat, error) {
|
||||
ret := make(map[string]IOCountersStat, 0)
|
||||
var dst []Win32_PerfFormattedData
|
||||
|
||||
err := wmi.Query("SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk ", &dst)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
@ -50,6 +51,11 @@ type CgroupMemStat struct {
|
||||
MemFailCnt uint64 `json:"memoryFailcnt"`
|
||||
}
|
||||
|
||||
func (m CgroupMemStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type CgroupDockerStat struct {
|
||||
ContainerID string `json:"containerID"`
|
||||
Name string `json:"name"`
|
||||
@ -57,3 +63,8 @@ type CgroupDockerStat struct {
|
||||
Status string `json:"status"`
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
|
||||
func (c CgroupDockerStat) String() string {
|
||||
s, _ := json.Marshal(c)
|
||||
return string(s)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -52,11 +51,6 @@ func GetDockerStat() ([]CgroupDockerStat, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c CgroupDockerStat) String() string {
|
||||
s, _ := json.Marshal(c)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// GetDockerIDList returnes a list of DockerID.
|
||||
// This requires certain permission.
|
||||
func GetDockerIDList() ([]string, error) {
|
||||
@ -220,11 +214,6 @@ func CgroupMemDocker(containerID string) (*CgroupMemStat, error) {
|
||||
return CgroupMem(containerID, common.HostSys("fs/cgroup/memory/docker"))
|
||||
}
|
||||
|
||||
func (m CgroupMemStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// getCgroupFilePath constructs file path to get targetted stats file.
|
||||
func getCgroupFilePath(containerID, base, target, file string) string {
|
||||
if len(base) == 0 {
|
||||
|
@ -552,7 +552,7 @@ func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
if len(files) == 0 {
|
||||
// CentOS has an intermediate /device directory:
|
||||
// https://github.com/giampaolo/psutil/issues/971
|
||||
files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/temp*_*"))
|
||||
files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/device/temp*_*"))
|
||||
if err != nil {
|
||||
return temperatures, err
|
||||
}
|
||||
@ -568,12 +568,12 @@ func SensorsTemperatures() ([]TemperatureStat, error) {
|
||||
if err != nil {
|
||||
return temperatures, err
|
||||
}
|
||||
temperature, err := strconv.ParseFloat(string(current), 64)
|
||||
temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
temperatures = append(temperatures, TemperatureStat{
|
||||
SensorKey: string(name),
|
||||
SensorKey: strings.TrimSpace(string(name)),
|
||||
Temperature: temperature / 1000.0,
|
||||
})
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func TestUsers(t *testing.T) {
|
||||
}
|
||||
empty := UserStat{}
|
||||
if len(v) == 0 {
|
||||
t.Errorf("Users is empty")
|
||||
t.Fatal("Users is empty")
|
||||
}
|
||||
for _, u := range v {
|
||||
if u == empty {
|
||||
|
@ -3,6 +3,7 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -109,7 +110,9 @@ func getMachineGuid() (string, error) {
|
||||
func GetOSInfo() (Win32_OperatingSystem, error) {
|
||||
var dst []Win32_OperatingSystem
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(q, &dst)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, q, &dst)
|
||||
if err != nil {
|
||||
return Win32_OperatingSystem{}, err
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"unsafe"
|
||||
|
||||
"github.com/StackExchange/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@ -49,7 +51,7 @@ var (
|
||||
ModNt = windows.NewLazyDLL("ntdll.dll")
|
||||
ModPdh = windows.NewLazyDLL("pdh.dll")
|
||||
ModPsapi = windows.NewLazyDLL("psapi.dll")
|
||||
|
||||
|
||||
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
||||
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
||||
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
||||
@ -110,3 +112,18 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err
|
||||
Counter: counter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
|
||||
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- wmi.Query(query, dst, connectServerArgs...)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-errChan:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -541,8 +541,8 @@ func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
|
||||
for _, pid := range pids {
|
||||
t, err := getProcInodes(root, pid, max)
|
||||
if err != nil {
|
||||
// skip if permission error
|
||||
if os.IsPermission(err) {
|
||||
// skip if permission error or no longer exists
|
||||
if os.IsPermission(err) || os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return ret, err
|
||||
|
@ -4,13 +4,18 @@ package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/shirou/gopsutil/internal/common"
|
||||
)
|
||||
|
||||
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
|
||||
|
||||
func ParseNetstat(output string, mode string,
|
||||
iocs map[string]IOCountersStat) error {
|
||||
lines := strings.Split(output, "\n")
|
||||
@ -92,7 +97,7 @@ func ParseNetstat(output string, mode string,
|
||||
}
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
netstat, err := exec.LookPath("/usr/bin/netstat")
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -146,8 +151,141 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return nil, errors.New("NetProtoCounters not implemented for openbsd")
|
||||
}
|
||||
|
||||
// Return a list of network connections opened.
|
||||
// Not Implemented for OpenBSD
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
return nil, errors.New("Connections not implemented for openbsd")
|
||||
func parseNetstatLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 5 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
|
||||
}
|
||||
|
||||
var netType, netFamily uint32
|
||||
switch f[0] {
|
||||
case "tcp":
|
||||
netType = syscall.SOCK_STREAM
|
||||
netFamily = syscall.AF_INET
|
||||
case "udp":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
netFamily = syscall.AF_INET
|
||||
case "tcp6":
|
||||
netType = syscall.SOCK_STREAM
|
||||
netFamily = syscall.AF_INET6
|
||||
case "udp6":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
netFamily = syscall.AF_INET6
|
||||
default:
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
|
||||
}
|
||||
|
||||
laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
|
||||
if err != nil {
|
||||
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
|
||||
}
|
||||
|
||||
n := ConnectionStat{
|
||||
Fd: uint32(0), // not supported
|
||||
Family: uint32(netFamily),
|
||||
Type: uint32(netType),
|
||||
Laddr: laddr,
|
||||
Raddr: raddr,
|
||||
Pid: int32(0), // not supported
|
||||
}
|
||||
if len(f) == 6 {
|
||||
n.Status = f[5]
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
|
||||
parse := func(l string) (Addr, error) {
|
||||
matches := portMatch.FindStringSubmatch(l)
|
||||
if matches == nil {
|
||||
return Addr{}, fmt.Errorf("wrong addr, %s", l)
|
||||
}
|
||||
host := matches[1]
|
||||
port := matches[2]
|
||||
if host == "*" {
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
host = "0.0.0.0"
|
||||
case syscall.AF_INET6:
|
||||
host = "::"
|
||||
default:
|
||||
return Addr{}, fmt.Errorf("unknown family, %d", family)
|
||||
}
|
||||
}
|
||||
lport, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return Addr{}, err
|
||||
}
|
||||
return Addr{IP: host, Port: uint32(lport)}, nil
|
||||
}
|
||||
|
||||
laddr, err = parse(local)
|
||||
if remote != "*.*" { // remote addr exists
|
||||
raddr, err = parse(remote)
|
||||
if err != nil {
|
||||
return laddr, raddr, err
|
||||
}
|
||||
}
|
||||
|
||||
return laddr, raddr, err
|
||||
}
|
||||
|
||||
// Return a list of network connections opened.
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
var ret []ConnectionStat
|
||||
|
||||
args := []string{"-na"}
|
||||
switch strings.ToLower(kind) {
|
||||
default:
|
||||
fallthrough
|
||||
case "":
|
||||
fallthrough
|
||||
case "all":
|
||||
fallthrough
|
||||
case "inet":
|
||||
// nothing to add
|
||||
case "inet4":
|
||||
args = append(args, "-finet")
|
||||
case "inet6":
|
||||
args = append(args, "-finet6")
|
||||
case "tcp":
|
||||
args = append(args, "-ptcp")
|
||||
case "tcp4":
|
||||
args = append(args, "-ptcp", "-finet")
|
||||
case "tcp6":
|
||||
args = append(args, "-ptcp", "-finet6")
|
||||
case "udp":
|
||||
args = append(args, "-pudp")
|
||||
case "udp4":
|
||||
args = append(args, "-pudp", "-finet")
|
||||
case "udp6":
|
||||
args = append(args, "-pudp", "-finet6")
|
||||
case "unix":
|
||||
return ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.Command(netstat, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
for _, line := range lines {
|
||||
if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
|
||||
continue
|
||||
}
|
||||
n, err := parseNetstatLine(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, n)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ type Process struct {
|
||||
|
||||
lastCPUTimes *cpu.TimesStat
|
||||
lastCPUTime time.Time
|
||||
|
||||
tgid int32
|
||||
}
|
||||
|
||||
type OpenFilesStat struct {
|
||||
|
@ -83,6 +83,9 @@ func (p *Process) Name() (string, error) {
|
||||
|
||||
return common.IntToString(k.Proc.P_comm[:]), nil
|
||||
}
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
func (p *Process) Exe() (string, error) {
|
||||
lsof_bin, err := exec.LookPath("lsof")
|
||||
if err != nil {
|
||||
@ -390,8 +393,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
results := make([]Process, 0, 50)
|
||||
func Processes() ([]*Process, error) {
|
||||
results := []*Process{}
|
||||
|
||||
mib := []int32{CTLKern, KernProc, KernProcAll, 0}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
@ -403,13 +406,6 @@ func processes() ([]Process, error) {
|
||||
k := KinfoProc{}
|
||||
procinfoLen := int(unsafe.Sizeof(k))
|
||||
count := int(length / uint64(procinfoLen))
|
||||
/*
|
||||
fmt.Println(length, procinfoLen, count)
|
||||
b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
|
||||
fmt.Println(b)
|
||||
kk, err := parseKinfoProc(b)
|
||||
fmt.Printf("%#v", kk)
|
||||
*/
|
||||
|
||||
// parse buf to procs
|
||||
for i := 0; i < count; i++ {
|
||||
@ -422,7 +418,7 @@ func processes() ([]Process, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
results = append(results, *p)
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
@ -41,6 +41,9 @@ func (p *Process) Ppid() (int32, error) {
|
||||
func (p *Process) Name() (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ type MemoryMapsStat struct {
|
||||
|
||||
func Pids() ([]int32, error) {
|
||||
var ret []int32
|
||||
procs, err := processes()
|
||||
procs, err := Processes()
|
||||
if err != nil {
|
||||
return ret, nil
|
||||
}
|
||||
@ -50,6 +50,9 @@ func (p *Process) Name() (string, error) {
|
||||
|
||||
return common.IntToString(k.Comm[:]), nil
|
||||
}
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
@ -278,8 +281,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
results := make([]Process, 0, 50)
|
||||
func Processes() ([]*Process, error) {
|
||||
results := []*Process{}
|
||||
|
||||
mib := []int32{CTLKern, KernProc, KernProcProc, 0}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
@ -302,7 +305,7 @@ func processes() ([]Process, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, *p)
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
@ -100,6 +100,16 @@ func (p *Process) Name() (string, error) {
|
||||
return p.name, nil
|
||||
}
|
||||
|
||||
// Tgid returns tgid, a Linux-synonym for user-space Pid
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
if p.tgid == 0 {
|
||||
if err := p.fillFromStatus(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return p.tgid, nil
|
||||
}
|
||||
|
||||
// Exe returns executable path of the process.
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return p.fillFromExe()
|
||||
@ -820,6 +830,12 @@ func (p *Process) fillFromStatus() error {
|
||||
return err
|
||||
}
|
||||
p.parent = int32(pval)
|
||||
case "Tgid":
|
||||
pval, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.tgid = int32(pval)
|
||||
case "Uid":
|
||||
p.uids = make([]int32, 0, 4)
|
||||
for _, i := range strings.Split(value, "\t") {
|
||||
@ -1017,6 +1033,27 @@ func Pids() ([]int32, error) {
|
||||
return readPidsFromDir(common.HostProc())
|
||||
}
|
||||
|
||||
// Process returns a slice of pointers to Process structs for all
|
||||
// currently running processes.
|
||||
func Processes() ([]*Process, error) {
|
||||
out := []*Process{}
|
||||
|
||||
pids, err := Pids()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
p, err := NewProcess(pid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func readPidsFromDir(path string) ([]int32, error) {
|
||||
var ret []int32
|
||||
|
||||
|
@ -25,7 +25,7 @@ type MemoryMapsStat struct {
|
||||
|
||||
func Pids() ([]int32, error) {
|
||||
var ret []int32
|
||||
procs, err := processes()
|
||||
procs, err := Processes()
|
||||
if err != nil {
|
||||
return ret, nil
|
||||
}
|
||||
@ -53,6 +53,9 @@ func (p *Process) Name() (string, error) {
|
||||
|
||||
return common.IntToString(k.Comm[:]), nil
|
||||
}
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
@ -268,8 +271,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func processes() ([]Process, error) {
|
||||
results := make([]Process, 0, 50)
|
||||
func Processes() ([]*Process, error) {
|
||||
results := []*Process{}
|
||||
|
||||
buf, length, err := CallKernProcSyscall(KernProcAll, 0)
|
||||
|
||||
@ -292,7 +295,7 @@ func processes() ([]Process, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, *p)
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
|
@ -3,6 +3,7 @@ package process
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@ -418,5 +419,24 @@ func Test_OpenFiles(t *testing.T) {
|
||||
for _, vv := range v {
|
||||
assert.NotEqual(t, "", vv.Path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_Kill(t *testing.T) {
|
||||
var cmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("choice", "/C", "YN", "/D", "Y", "/t", "3")
|
||||
} else {
|
||||
cmd = exec.Command("sleep", "3")
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
assert.NotNil(t, cmd.Run())
|
||||
wg.Done()
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
p, err := NewProcess(int32(cmd.Process.Pid))
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, p.Kill())
|
||||
wg.Wait()
|
||||
}
|
||||
|
@ -3,7 +3,9 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -93,35 +95,47 @@ func init() {
|
||||
}
|
||||
|
||||
func Pids() ([]int32, error) {
|
||||
// inspired by https://gist.github.com/henkman/3083408
|
||||
// and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329
|
||||
var ret []int32
|
||||
var read uint32 = 0
|
||||
var psSize uint32 = 1024
|
||||
const dwordSize uint32 = 4
|
||||
|
||||
procs, err := processes()
|
||||
if err != nil {
|
||||
for {
|
||||
ps := make([]uint32, psSize)
|
||||
if !w32.EnumProcesses(ps, uint32(len(ps)), &read) {
|
||||
return nil, fmt.Errorf("could not get w32.EnumProcesses")
|
||||
}
|
||||
if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one
|
||||
psSize += 1024
|
||||
continue
|
||||
}
|
||||
for _, pid := range ps[:read/dwordSize] {
|
||||
ret = append(ret, int32(pid))
|
||||
}
|
||||
return ret, nil
|
||||
|
||||
}
|
||||
|
||||
for _, proc := range procs {
|
||||
ret = append(ret, proc.Pid)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) Ppid() (int32, error) {
|
||||
dst, err := GetWin32Proc(p.Pid)
|
||||
ppid, _, _, err := getFromSnapProcess(p.Pid)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int32(dst[0].ParentProcessID), nil
|
||||
return ppid, nil
|
||||
}
|
||||
|
||||
func GetWin32Proc(pid int32) ([]Win32_Process, error) {
|
||||
var dst []Win32_Process
|
||||
query := fmt.Sprintf("WHERE ProcessId = %d", pid)
|
||||
q := wmi.CreateQuery(&dst, query)
|
||||
|
||||
if err := wmi.Query(q, &dst); err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, q, &dst)
|
||||
if err != nil {
|
||||
return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err)
|
||||
}
|
||||
|
||||
@ -133,11 +147,15 @@ func GetWin32Proc(pid int32) ([]Win32_Process, error) {
|
||||
}
|
||||
|
||||
func (p *Process) Name() (string, error) {
|
||||
dst, err := GetWin32Proc(p.Pid)
|
||||
_, _, name, err := getFromSnapProcess(p.Pid)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get Name: %s", err)
|
||||
}
|
||||
return dst[0].Name, nil
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Exe() (string, error) {
|
||||
@ -292,11 +310,11 @@ func (p *Process) Times() (*cpu.TimesStat, error) {
|
||||
// below from psutil's _psutil_windows.c, and in turn from Python's
|
||||
// Modules/posixmodule.c
|
||||
|
||||
user := float64(sysTimes.UserTime.HighDateTime) * 429.4967296 + float64(sysTimes.UserTime.LowDateTime) * 1e-7
|
||||
kernel := float64(sysTimes.KernelTime.HighDateTime) * 429.4967296 + float64(sysTimes.KernelTime.LowDateTime) * 1e-7
|
||||
user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7
|
||||
kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7
|
||||
|
||||
return &cpu.TimesStat{
|
||||
User: user,
|
||||
User: user,
|
||||
System: kernel,
|
||||
}, nil
|
||||
}
|
||||
@ -323,7 +341,9 @@ func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
||||
func (p *Process) Children() ([]*Process, error) {
|
||||
var dst []Win32_Process
|
||||
query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid))
|
||||
err := wmi.Query(query, &dst)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
|
||||
defer cancel()
|
||||
err := common.WMIQueryWithContext(ctx, query, &dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -392,10 +412,11 @@ func (p *Process) Terminate() error {
|
||||
}
|
||||
|
||||
func (p *Process) Kill() error {
|
||||
return common.ErrNotImplementedError
|
||||
process := os.Process{Pid: int(p.Pid)}
|
||||
return process.Kill()
|
||||
}
|
||||
|
||||
func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) {
|
||||
func getFromSnapProcess(pid int32) (int32, int32, string, error) {
|
||||
snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid))
|
||||
if snap == 0 {
|
||||
return 0, 0, "", windows.GetLastError()
|
||||
@ -422,20 +443,15 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) {
|
||||
}
|
||||
|
||||
// Get processes
|
||||
func processes() ([]*Process, error) {
|
||||
var dst []Win32_Process
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
err := wmi.Query(q, &dst)
|
||||
func Processes() ([]*Process, error) {
|
||||
pids, err := Pids()
|
||||
if err != nil {
|
||||
return []*Process{}, err
|
||||
}
|
||||
if len(dst) == 0 {
|
||||
return []*Process{}, fmt.Errorf("could not get Process")
|
||||
return []*Process{}, fmt.Errorf("could not get Processes %s", err)
|
||||
}
|
||||
|
||||
results := []*Process{}
|
||||
for _, proc := range dst {
|
||||
p, err := NewProcess(int32(proc.ProcessID))
|
||||
for _, pid := range pids {
|
||||
p, err := NewProcess(int32(pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -508,9 +524,9 @@ func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err e
|
||||
|
||||
type SYSTEM_TIMES struct {
|
||||
CreateTime syscall.Filetime
|
||||
ExitTime syscall.Filetime
|
||||
ExitTime syscall.Filetime
|
||||
KernelTime syscall.Filetime
|
||||
UserTime syscall.Filetime
|
||||
UserTime syscall.Filetime
|
||||
}
|
||||
|
||||
func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user