Merge branch 'master' of github.com:shirou/gopsutil into feature/lxd_boot_time

pull/441/head
shirou 7 years ago
commit 8b2468f1e7

@ -43,7 +43,7 @@ Available Architectures
- Linux i386/amd64/arm(raspberry pi) - Linux i386/amd64/arm(raspberry pi)
- Windows/amd64 - Windows/amd64
- Darwin i386/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!) - Solaris amd64 (developed and tested on SmartOS/Illumos, Thank you @jen20!)
All works are implemented without cgo by porting c struct to golang struct. 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 users x x x x x
pids x x x x x pids x x x x x
pid_exists 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_protocols x
net_if_addrs net_if_addrs
net_if_stats net_if_stats

@ -12,6 +12,9 @@ import (
"github.com/shirou/gopsutil/internal/common" "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 { type TimesStat struct {
CPU string `json:"cpu"` CPU string `json:"cpu"`
User float64 `json:"user"` User float64 `json:"user"`

@ -3,6 +3,7 @@
package cpu package cpu
import ( import (
"context"
"fmt" "fmt"
"unsafe" "unsafe"
@ -81,8 +82,9 @@ func Info() ([]InfoStat, error) {
var ret []InfoStat var ret []InfoStat
var dst []Win32_Processor var dst []Win32_Processor
q := wmi.CreateQuery(&dst, "") q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst) ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
if err != nil { defer cancel()
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
return ret, err 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. // Name property is the key by which overall, per cpu and per core metric is known.
func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) { func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
var ret []Win32_PerfFormattedData_Counters_ProcessorInformation var ret []Win32_PerfFormattedData_Counters_ProcessorInformation
q := wmi.CreateQuery(&ret, "") 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 return ret, err
} }
@ -123,7 +128,9 @@ func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error)
func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) { func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) {
var ret []Win32_PerfFormattedData_PerfOS_System var ret []Win32_PerfFormattedData_PerfOS_System
q := wmi.CreateQuery(&ret, "") 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 return ret, err
} }

@ -66,7 +66,7 @@ func Partitions(all bool) ([]PartitionStat, error) {
func IOCounters(names ...string) (map[string]IOCountersStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat) ret := make(map[string]IOCountersStat)
r, err := unix.Sysctl("hw.diskstats") r, err := unix.SysctlRaw("hw.diskstats")
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -4,9 +4,9 @@ package disk
import ( import (
"bytes" "bytes"
"context"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -132,7 +132,9 @@ func IOCounters(names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat, 0) ret := make(map[string]IOCountersStat, 0)
var dst []Win32_PerfFormattedData 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 { if err != nil {
return ret, err return ret, err
} }

@ -1,6 +1,7 @@
package docker package docker
import ( import (
"encoding/json"
"errors" "errors"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
@ -50,6 +51,11 @@ type CgroupMemStat struct {
MemFailCnt uint64 `json:"memoryFailcnt"` MemFailCnt uint64 `json:"memoryFailcnt"`
} }
func (m CgroupMemStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
type CgroupDockerStat struct { type CgroupDockerStat struct {
ContainerID string `json:"containerID"` ContainerID string `json:"containerID"`
Name string `json:"name"` Name string `json:"name"`
@ -57,3 +63,8 @@ type CgroupDockerStat struct {
Status string `json:"status"` Status string `json:"status"`
Running bool `json:"running"` Running bool `json:"running"`
} }
func (c CgroupDockerStat) String() string {
s, _ := json.Marshal(c)
return string(s)
}

@ -3,7 +3,6 @@
package docker package docker
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
@ -52,11 +51,6 @@ func GetDockerStat() ([]CgroupDockerStat, error) {
return ret, nil return ret, nil
} }
func (c CgroupDockerStat) String() string {
s, _ := json.Marshal(c)
return string(s)
}
// GetDockerIDList returnes a list of DockerID. // GetDockerIDList returnes a list of DockerID.
// This requires certain permission. // This requires certain permission.
func GetDockerIDList() ([]string, error) { func GetDockerIDList() ([]string, error) {
@ -220,11 +214,6 @@ func CgroupMemDocker(containerID string) (*CgroupMemStat, error) {
return CgroupMem(containerID, common.HostSys("fs/cgroup/memory/docker")) 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. // getCgroupFilePath constructs file path to get targetted stats file.
func getCgroupFilePath(containerID, base, target, file string) string { func getCgroupFilePath(containerID, base, target, file string) string {
if len(base) == 0 { if len(base) == 0 {

@ -552,7 +552,7 @@ func SensorsTemperatures() ([]TemperatureStat, error) {
if len(files) == 0 { if len(files) == 0 {
// CentOS has an intermediate /device directory: // CentOS has an intermediate /device directory:
// https://github.com/giampaolo/psutil/issues/971 // 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 { if err != nil {
return temperatures, err return temperatures, err
} }
@ -568,12 +568,12 @@ func SensorsTemperatures() ([]TemperatureStat, error) {
if err != nil { if err != nil {
return temperatures, err return temperatures, err
} }
temperature, err := strconv.ParseFloat(string(current), 64) temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
if err != nil { if err != nil {
continue continue
} }
temperatures = append(temperatures, TemperatureStat{ temperatures = append(temperatures, TemperatureStat{
SensorKey: string(name), SensorKey: strings.TrimSpace(string(name)),
Temperature: temperature / 1000.0, Temperature: temperature / 1000.0,
}) })
} }

@ -56,7 +56,7 @@ func TestUsers(t *testing.T) {
} }
empty := UserStat{} empty := UserStat{}
if len(v) == 0 { if len(v) == 0 {
t.Errorf("Users is empty") t.Fatal("Users is empty")
} }
for _, u := range v { for _, u := range v {
if u == empty { if u == empty {

@ -3,6 +3,7 @@
package host package host
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
@ -109,7 +110,9 @@ func getMachineGuid() (string, error) {
func GetOSInfo() (Win32_OperatingSystem, error) { func GetOSInfo() (Win32_OperatingSystem, error) {
var dst []Win32_OperatingSystem var dst []Win32_OperatingSystem
q := wmi.CreateQuery(&dst, "") 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 { if err != nil {
return Win32_OperatingSystem{}, err return Win32_OperatingSystem{}, err
} }

@ -3,8 +3,10 @@
package common package common
import ( import (
"context"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -110,3 +112,18 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err
Counter: counter, Counter: counter,
}, nil }, 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 { for _, pid := range pids {
t, err := getProcInodes(root, pid, max) t, err := getProcInodes(root, pid, max)
if err != nil { if err != nil {
// skip if permission error // skip if permission error or no longer exists
if os.IsPermission(err) { if os.IsPermission(err) || os.IsNotExist(err) {
continue continue
} }
return ret, err return ret, err

@ -4,13 +4,18 @@ package net
import ( import (
"errors" "errors"
"fmt"
"os/exec" "os/exec"
"regexp"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
func ParseNetstat(output string, mode string, func ParseNetstat(output string, mode string,
iocs map[string]IOCountersStat) error { iocs map[string]IOCountersStat) error {
lines := strings.Split(output, "\n") lines := strings.Split(output, "\n")
@ -92,7 +97,7 @@ func ParseNetstat(output string, mode string,
} }
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
netstat, err := exec.LookPath("/usr/bin/netstat") netstat, err := exec.LookPath("netstat")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -146,8 +151,141 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for openbsd") return nil, errors.New("NetProtoCounters 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. // Return a list of network connections opened.
// Not Implemented for OpenBSD
func Connections(kind string) ([]ConnectionStat, error) { func Connections(kind string) ([]ConnectionStat, error) {
return nil, errors.New("Connections not implemented for openbsd") 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 lastCPUTimes *cpu.TimesStat
lastCPUTime time.Time lastCPUTime time.Time
tgid int32
} }
type OpenFilesStat struct { type OpenFilesStat struct {

@ -83,6 +83,9 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Proc.P_comm[:]), nil return common.IntToString(k.Proc.P_comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
lsof_bin, err := exec.LookPath("lsof") lsof_bin, err := exec.LookPath("lsof")
if err != nil { if err != nil {
@ -390,8 +393,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) results := []*Process{}
mib := []int32{CTLKern, KernProc, KernProcAll, 0} mib := []int32{CTLKern, KernProc, KernProcAll, 0}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)
@ -403,13 +406,6 @@ func processes() ([]Process, error) {
k := KinfoProc{} k := KinfoProc{}
procinfoLen := int(unsafe.Sizeof(k)) procinfoLen := int(unsafe.Sizeof(k))
count := int(length / uint64(procinfoLen)) 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 // parse buf to procs
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
@ -422,7 +418,7 @@ func processes() ([]Process, error) {
if err != nil { if err != nil {
continue continue
} }
results = append(results, *p) results = append(results, p)
} }
return results, nil return results, nil

@ -41,6 +41,9 @@ func (p *Process) Ppid() (int32, error) {
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }

@ -22,7 +22,7 @@ type MemoryMapsStat struct {
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
var ret []int32 var ret []int32
procs, err := processes() procs, err := Processes()
if err != nil { if err != nil {
return ret, nil return ret, nil
} }
@ -50,6 +50,9 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Comm[:]), nil return common.IntToString(k.Comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
@ -278,8 +281,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) results := []*Process{}
mib := []int32{CTLKern, KernProc, KernProcProc, 0} mib := []int32{CTLKern, KernProc, KernProcProc, 0}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)
@ -302,7 +305,7 @@ func processes() ([]Process, error) {
continue continue
} }
results = append(results, *p) results = append(results, p)
} }
return results, nil return results, nil

@ -100,6 +100,16 @@ func (p *Process) Name() (string, error) {
return p.name, nil 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. // Exe returns executable path of the process.
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.fillFromExe() return p.fillFromExe()
@ -820,6 +830,12 @@ func (p *Process) fillFromStatus() error {
return err return err
} }
p.parent = int32(pval) p.parent = int32(pval)
case "Tgid":
pval, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
p.tgid = int32(pval)
case "Uid": case "Uid":
p.uids = make([]int32, 0, 4) p.uids = make([]int32, 0, 4)
for _, i := range strings.Split(value, "\t") { for _, i := range strings.Split(value, "\t") {
@ -1017,6 +1033,27 @@ func Pids() ([]int32, error) {
return readPidsFromDir(common.HostProc()) 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) { func readPidsFromDir(path string) ([]int32, error) {
var ret []int32 var ret []int32

@ -25,7 +25,7 @@ type MemoryMapsStat struct {
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
var ret []int32 var ret []int32
procs, err := processes() procs, err := Processes()
if err != nil { if err != nil {
return ret, nil return ret, nil
} }
@ -53,6 +53,9 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Comm[:]), nil return common.IntToString(k.Comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
@ -268,8 +271,8 @@ func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) results := []*Process{}
buf, length, err := CallKernProcSyscall(KernProcAll, 0) buf, length, err := CallKernProcSyscall(KernProcAll, 0)
@ -292,7 +295,7 @@ func processes() ([]Process, error) {
continue continue
} }
results = append(results, *p) results = append(results, p)
} }
return results, nil return results, nil

@ -3,6 +3,7 @@ package process
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"os/user" "os/user"
"reflect" "reflect"
"runtime" "runtime"
@ -418,5 +419,24 @@ func Test_OpenFiles(t *testing.T) {
for _, vv := range v { for _, vv := range v {
assert.NotEqual(t, "", vv.Path) 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 package process
import ( import (
"context"
"fmt" "fmt"
"os"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -93,35 +95,47 @@ func init() {
} }
func Pids() ([]int32, error) { 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 ret []int32
var read uint32 = 0
procs, err := processes() var psSize uint32 = 1024
if err != nil { const dwordSize uint32 = 4
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 return ret, nil
}
for _, proc := range procs {
ret = append(ret, proc.Pid)
} }
return ret, nil
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
dst, err := GetWin32Proc(p.Pid) ppid, _, _, err := getFromSnapProcess(p.Pid)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return ppid, nil
return int32(dst[0].ParentProcessID), nil
} }
func GetWin32Proc(pid int32) ([]Win32_Process, error) { func GetWin32Proc(pid int32) ([]Win32_Process, error) {
var dst []Win32_Process var dst []Win32_Process
query := fmt.Sprintf("WHERE ProcessId = %d", pid) query := fmt.Sprintf("WHERE ProcessId = %d", pid)
q := wmi.CreateQuery(&dst, query) q := wmi.CreateQuery(&dst, query)
ctx, cancel := context.WithTimeout(context.Background(), common.Timeout)
if err := wmi.Query(q, &dst); err != nil { defer cancel()
err := common.WMIQueryWithContext(ctx, q, &dst)
if err != nil {
return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err) 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) { func (p *Process) Name() (string, error) {
dst, err := GetWin32Proc(p.Pid) _, _, name, err := getFromSnapProcess(p.Pid)
if err != nil { if err != nil {
return "", fmt.Errorf("could not get Name: %s", err) 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) { 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 // below from psutil's _psutil_windows.c, and in turn from Python's
// Modules/posixmodule.c // Modules/posixmodule.c
user := float64(sysTimes.UserTime.HighDateTime) * 429.4967296 + float64(sysTimes.UserTime.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 kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7
return &cpu.TimesStat{ return &cpu.TimesStat{
User: user, User: user,
System: kernel, System: kernel,
}, nil }, nil
} }
@ -323,7 +341,9 @@ func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
var dst []Win32_Process var dst []Win32_Process
query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid)) 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 { if err != nil {
return nil, err return nil, err
} }
@ -392,10 +412,11 @@ func (p *Process) Terminate() error {
} }
func (p *Process) Kill() 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)) snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid))
if snap == 0 { if snap == 0 {
return 0, 0, "", windows.GetLastError() return 0, 0, "", windows.GetLastError()
@ -422,20 +443,15 @@ func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) {
} }
// Get processes // Get processes
func processes() ([]*Process, error) { func Processes() ([]*Process, error) {
var dst []Win32_Process pids, err := Pids()
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil { if err != nil {
return []*Process{}, err return []*Process{}, fmt.Errorf("could not get Processes %s", err)
}
if len(dst) == 0 {
return []*Process{}, fmt.Errorf("could not get Process")
} }
results := []*Process{} results := []*Process{}
for _, proc := range dst { for _, pid := range pids {
p, err := NewProcess(int32(proc.ProcessID)) p, err := NewProcess(int32(pid))
if err != nil { if err != nil {
continue continue
} }
@ -508,9 +524,9 @@ func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err e
type SYSTEM_TIMES struct { type SYSTEM_TIMES struct {
CreateTime syscall.Filetime CreateTime syscall.Filetime
ExitTime syscall.Filetime ExitTime syscall.Filetime
KernelTime syscall.Filetime KernelTime syscall.Filetime
UserTime syscall.Filetime UserTime syscall.Filetime
} }
func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) { func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {

Loading…
Cancel
Save