mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-26 13:48:59 +08:00

The previous godoc string was slightly confusing and only described information that can be deduced from the function signature.
546 lines
15 KiB
Go
546 lines
15 KiB
Go
package process
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"runtime"
|
|
"sort"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/shirou/gopsutil/cpu"
|
|
"github.com/shirou/gopsutil/internal/common"
|
|
"github.com/shirou/gopsutil/mem"
|
|
"github.com/shirou/gopsutil/net"
|
|
)
|
|
|
|
var (
|
|
invoke common.Invoker = common.Invoke{}
|
|
ErrorNoChildren = errors.New("process does not have children")
|
|
ErrorProcessNotRunning = errors.New("process does not exist")
|
|
)
|
|
|
|
type Process struct {
|
|
Pid int32 `json:"pid"`
|
|
name string
|
|
status string
|
|
parent int32
|
|
parentMutex *sync.RWMutex // for windows ppid cache
|
|
numCtxSwitches *NumCtxSwitchesStat
|
|
uids []int32
|
|
gids []int32
|
|
groups []int32
|
|
numThreads int32
|
|
memInfo *MemoryInfoStat
|
|
sigInfo *SignalInfoStat
|
|
createTime int64
|
|
|
|
lastCPUTimes *cpu.TimesStat
|
|
lastCPUTime time.Time
|
|
|
|
tgid int32
|
|
}
|
|
|
|
type OpenFilesStat struct {
|
|
Path string `json:"path"`
|
|
Fd uint64 `json:"fd"`
|
|
}
|
|
|
|
type MemoryInfoStat struct {
|
|
RSS uint64 `json:"rss"` // bytes
|
|
VMS uint64 `json:"vms"` // bytes
|
|
HWM uint64 `json:"hwm"` // bytes
|
|
Data uint64 `json:"data"` // bytes
|
|
Stack uint64 `json:"stack"` // bytes
|
|
Locked uint64 `json:"locked"` // bytes
|
|
Swap uint64 `json:"swap"` // bytes
|
|
}
|
|
|
|
type SignalInfoStat struct {
|
|
PendingProcess uint64 `json:"pending_process"`
|
|
PendingThread uint64 `json:"pending_thread"`
|
|
Blocked uint64 `json:"blocked"`
|
|
Ignored uint64 `json:"ignored"`
|
|
Caught uint64 `json:"caught"`
|
|
}
|
|
|
|
type RlimitStat struct {
|
|
Resource int32 `json:"resource"`
|
|
Soft int32 `json:"soft"` //TODO too small. needs to be uint64
|
|
Hard int32 `json:"hard"` //TODO too small. needs to be uint64
|
|
Used uint64 `json:"used"`
|
|
}
|
|
|
|
type IOCountersStat struct {
|
|
ReadCount uint64 `json:"readCount"`
|
|
WriteCount uint64 `json:"writeCount"`
|
|
ReadBytes uint64 `json:"readBytes"`
|
|
WriteBytes uint64 `json:"writeBytes"`
|
|
}
|
|
|
|
type NumCtxSwitchesStat struct {
|
|
Voluntary int64 `json:"voluntary"`
|
|
Involuntary int64 `json:"involuntary"`
|
|
}
|
|
|
|
type PageFaultsStat struct {
|
|
MinorFaults uint64 `json:"minorFaults"`
|
|
MajorFaults uint64 `json:"majorFaults"`
|
|
ChildMinorFaults uint64 `json:"childMinorFaults"`
|
|
ChildMajorFaults uint64 `json:"childMajorFaults"`
|
|
}
|
|
|
|
// Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h
|
|
// from libc6-dev package in Ubuntu 16.10
|
|
const (
|
|
RLIMIT_CPU int32 = 0
|
|
RLIMIT_FSIZE int32 = 1
|
|
RLIMIT_DATA int32 = 2
|
|
RLIMIT_STACK int32 = 3
|
|
RLIMIT_CORE int32 = 4
|
|
RLIMIT_RSS int32 = 5
|
|
RLIMIT_NPROC int32 = 6
|
|
RLIMIT_NOFILE int32 = 7
|
|
RLIMIT_MEMLOCK int32 = 8
|
|
RLIMIT_AS int32 = 9
|
|
RLIMIT_LOCKS int32 = 10
|
|
RLIMIT_SIGPENDING int32 = 11
|
|
RLIMIT_MSGQUEUE int32 = 12
|
|
RLIMIT_NICE int32 = 13
|
|
RLIMIT_RTPRIO int32 = 14
|
|
RLIMIT_RTTIME int32 = 15
|
|
)
|
|
|
|
func (p Process) String() string {
|
|
s, _ := json.Marshal(p)
|
|
return string(s)
|
|
}
|
|
|
|
func (o OpenFilesStat) String() string {
|
|
s, _ := json.Marshal(o)
|
|
return string(s)
|
|
}
|
|
|
|
func (m MemoryInfoStat) String() string {
|
|
s, _ := json.Marshal(m)
|
|
return string(s)
|
|
}
|
|
|
|
func (r RlimitStat) String() string {
|
|
s, _ := json.Marshal(r)
|
|
return string(s)
|
|
}
|
|
|
|
func (i IOCountersStat) String() string {
|
|
s, _ := json.Marshal(i)
|
|
return string(s)
|
|
}
|
|
|
|
func (p NumCtxSwitchesStat) String() string {
|
|
s, _ := json.Marshal(p)
|
|
return string(s)
|
|
}
|
|
|
|
// Pids returns a slice of process ID list which are running now.
|
|
func Pids() ([]int32, error) {
|
|
return PidsWithContext(context.Background())
|
|
}
|
|
|
|
func PidsWithContext(ctx context.Context) ([]int32, error) {
|
|
pids, err := pidsWithContext(ctx)
|
|
sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] })
|
|
return pids, err
|
|
}
|
|
|
|
// Processes returns a slice of pointers to Process structs for all
|
|
// currently running processes.
|
|
func Processes() ([]*Process, error) {
|
|
return ProcessesWithContext(context.Background())
|
|
}
|
|
|
|
// NewProcess creates a new Process instance, it only stores the pid and
|
|
// checks that the process exists. Other method on Process can be used
|
|
// to get more information about the process. An error will be returned
|
|
// if the process does not exist.
|
|
func NewProcess(pid int32) (*Process, error) {
|
|
return NewProcessWithContext(context.Background(), pid)
|
|
}
|
|
|
|
func NewProcessWithContext(ctx context.Context, pid int32) (*Process, error) {
|
|
p := &Process{
|
|
Pid: pid,
|
|
parentMutex: new(sync.RWMutex),
|
|
}
|
|
|
|
exists, err := PidExistsWithContext(ctx, pid)
|
|
if err != nil {
|
|
return p, err
|
|
}
|
|
if !exists {
|
|
return p, ErrorProcessNotRunning
|
|
}
|
|
p.CreateTimeWithContext(ctx)
|
|
return p, nil
|
|
}
|
|
|
|
func PidExists(pid int32) (bool, error) {
|
|
return PidExistsWithContext(context.Background(), pid)
|
|
}
|
|
|
|
// Background returns true if the process is in background, false otherwise.
|
|
func (p *Process) Background() (bool, error) {
|
|
return p.BackgroundWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) {
|
|
fg, err := p.ForegroundWithContext(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return !fg, err
|
|
}
|
|
|
|
// If interval is 0, return difference from last call(non-blocking).
|
|
// If interval > 0, wait interval sec and return diffrence between start and end.
|
|
func (p *Process) Percent(interval time.Duration) (float64, error) {
|
|
return p.PercentWithContext(context.Background(), interval)
|
|
}
|
|
|
|
func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) {
|
|
cpuTimes, err := p.TimesWithContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
now := time.Now()
|
|
|
|
if interval > 0 {
|
|
p.lastCPUTimes = cpuTimes
|
|
p.lastCPUTime = now
|
|
if err := common.Sleep(ctx, interval); err != nil {
|
|
return 0, err
|
|
}
|
|
cpuTimes, err = p.TimesWithContext(ctx)
|
|
now = time.Now()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
} else {
|
|
if p.lastCPUTimes == nil {
|
|
// invoked first time
|
|
p.lastCPUTimes = cpuTimes
|
|
p.lastCPUTime = now
|
|
return 0, nil
|
|
}
|
|
}
|
|
|
|
numcpu := runtime.NumCPU()
|
|
delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu)
|
|
ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu)
|
|
p.lastCPUTimes = cpuTimes
|
|
p.lastCPUTime = now
|
|
return ret, nil
|
|
}
|
|
|
|
// IsRunning returns whether the process is still running or not.
|
|
func (p *Process) IsRunning() (bool, error) {
|
|
return p.IsRunningWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
|
|
createTime, err := p.CreateTimeWithContext(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
p2, err := NewProcessWithContext(ctx, p.Pid)
|
|
if err == ErrorProcessNotRunning {
|
|
return false, nil
|
|
}
|
|
createTime2, err := p2.CreateTimeWithContext(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return createTime == createTime2, nil
|
|
}
|
|
|
|
// CreateTime returns created time of the process in milliseconds since the epoch, in UTC.
|
|
func (p *Process) CreateTime() (int64, error) {
|
|
return p.CreateTimeWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
|
|
if p.createTime != 0 {
|
|
return p.createTime, nil
|
|
}
|
|
createTime, err := p.createTimeWithContext(ctx)
|
|
p.createTime = createTime
|
|
return p.createTime, err
|
|
}
|
|
|
|
func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 {
|
|
if delta == 0 {
|
|
return 0
|
|
}
|
|
delta_proc := t2.Total() - t1.Total()
|
|
overall_percent := ((delta_proc / delta) * 100) * float64(numcpu)
|
|
return overall_percent
|
|
}
|
|
|
|
// MemoryPercent returns how many percent of the total RAM this process uses
|
|
func (p *Process) MemoryPercent() (float32, error) {
|
|
return p.MemoryPercentWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) {
|
|
machineMemory, err := mem.VirtualMemoryWithContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
total := machineMemory.Total
|
|
|
|
processMemory, err := p.MemoryInfoWithContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
used := processMemory.RSS
|
|
|
|
return (100 * float32(used) / float32(total)), nil
|
|
}
|
|
|
|
// CPU_Percent returns how many percent of the CPU time this process uses
|
|
func (p *Process) CPUPercent() (float64, error) {
|
|
return p.CPUPercentWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) {
|
|
crt_time, err := p.createTimeWithContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
cput, err := p.TimesWithContext(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
created := time.Unix(0, crt_time*int64(time.Millisecond))
|
|
totalTime := time.Since(created).Seconds()
|
|
if totalTime <= 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
return 100 * cput.Total() / totalTime, nil
|
|
}
|
|
|
|
// Groups returns all group IDs(include supplementary groups) of the process as a slice of the int
|
|
func (p *Process) Groups() ([]int32, error) {
|
|
return p.GroupsWithContext(context.Background())
|
|
}
|
|
|
|
// Ppid returns Parent Process ID of the process.
|
|
func (p *Process) Ppid() (int32, error) {
|
|
return p.PpidWithContext(context.Background())
|
|
}
|
|
|
|
// Name returns name of the process.
|
|
func (p *Process) Name() (string, error) {
|
|
return p.NameWithContext(context.Background())
|
|
}
|
|
|
|
// Exe returns executable path of the process.
|
|
func (p *Process) Exe() (string, error) {
|
|
return p.ExeWithContext(context.Background())
|
|
}
|
|
|
|
// Cmdline returns the command line arguments of the process as a string with
|
|
// each argument separated by 0x20 ascii character.
|
|
func (p *Process) Cmdline() (string, error) {
|
|
return p.CmdlineWithContext(context.Background())
|
|
}
|
|
|
|
// CmdlineSlice returns the command line arguments of the process as a slice with each
|
|
// element being an argument.
|
|
func (p *Process) CmdlineSlice() ([]string, error) {
|
|
return p.CmdlineSliceWithContext(context.Background())
|
|
}
|
|
|
|
// Cwd returns current working directory of the process.
|
|
func (p *Process) Cwd() (string, error) {
|
|
return p.CwdWithContext(context.Background())
|
|
}
|
|
|
|
// Parent returns parent Process of the process.
|
|
func (p *Process) Parent() (*Process, error) {
|
|
return p.ParentWithContext(context.Background())
|
|
}
|
|
|
|
// Status returns the process status.
|
|
// Return value could be one of these.
|
|
// R: Running S: Sleep T: Stop I: Idle
|
|
// Z: Zombie W: Wait L: Lock
|
|
// The character is same within all supported platforms.
|
|
func (p *Process) Status() (string, error) {
|
|
return p.StatusWithContext(context.Background())
|
|
}
|
|
|
|
// Foreground returns true if the process is in foreground, false otherwise.
|
|
func (p *Process) Foreground() (bool, error) {
|
|
return p.ForegroundWithContext(context.Background())
|
|
}
|
|
|
|
// Uids returns user ids of the process as a slice of the int
|
|
func (p *Process) Uids() ([]int32, error) {
|
|
return p.UidsWithContext(context.Background())
|
|
}
|
|
|
|
// Gids returns group ids of the process as a slice of the int
|
|
func (p *Process) Gids() ([]int32, error) {
|
|
return p.GidsWithContext(context.Background())
|
|
}
|
|
|
|
// Terminal returns a terminal which is associated with the process.
|
|
func (p *Process) Terminal() (string, error) {
|
|
return p.TerminalWithContext(context.Background())
|
|
}
|
|
|
|
// Nice returns a nice value (priority).
|
|
func (p *Process) Nice() (int32, error) {
|
|
return p.NiceWithContext(context.Background())
|
|
}
|
|
|
|
// IOnice returns process I/O nice value (priority).
|
|
func (p *Process) IOnice() (int32, error) {
|
|
return p.IOniceWithContext(context.Background())
|
|
}
|
|
|
|
// Rlimit returns Resource Limits.
|
|
func (p *Process) Rlimit() ([]RlimitStat, error) {
|
|
return p.RlimitWithContext(context.Background())
|
|
}
|
|
|
|
// RlimitUsage returns Resource Limits.
|
|
// If gatherUsed is true, the currently used value will be gathered and added
|
|
// to the resulting RlimitStat.
|
|
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
|
|
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
|
|
}
|
|
|
|
// IOCounters returns IO Counters.
|
|
func (p *Process) IOCounters() (*IOCountersStat, error) {
|
|
return p.IOCountersWithContext(context.Background())
|
|
}
|
|
|
|
// NumCtxSwitches returns the number of the context switches of the process.
|
|
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
|
|
return p.NumCtxSwitchesWithContext(context.Background())
|
|
}
|
|
|
|
// NumFDs returns the number of File Descriptors used by the process.
|
|
func (p *Process) NumFDs() (int32, error) {
|
|
return p.NumFDsWithContext(context.Background())
|
|
}
|
|
|
|
// NumThreads returns the number of threads used by the process.
|
|
func (p *Process) NumThreads() (int32, error) {
|
|
return p.NumThreadsWithContext(context.Background())
|
|
}
|
|
|
|
func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
|
|
return p.ThreadsWithContext(context.Background())
|
|
}
|
|
|
|
// Times returns CPU times of the process.
|
|
func (p *Process) Times() (*cpu.TimesStat, error) {
|
|
return p.TimesWithContext(context.Background())
|
|
}
|
|
|
|
// CPUAffinity returns CPU affinity of the process.
|
|
func (p *Process) CPUAffinity() ([]int32, error) {
|
|
return p.CPUAffinityWithContext(context.Background())
|
|
}
|
|
|
|
// MemoryInfo returns generic process memory information,
|
|
// such as RSS and VMS.
|
|
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
|
|
return p.MemoryInfoWithContext(context.Background())
|
|
}
|
|
|
|
// MemoryInfoEx returns platform-specific process memory information.
|
|
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
|
return p.MemoryInfoExWithContext(context.Background())
|
|
}
|
|
|
|
// PageFaultsInfo returns the process's page fault counters.
|
|
func (p *Process) PageFaults() (*PageFaultsStat, error) {
|
|
return p.PageFaultsWithContext(context.Background())
|
|
}
|
|
|
|
// Children returns the children of the process represented as a slice
|
|
// of pointers to Process type.
|
|
func (p *Process) Children() ([]*Process, error) {
|
|
return p.ChildrenWithContext(context.Background())
|
|
}
|
|
|
|
// OpenFiles returns a slice of OpenFilesStat opend by the process.
|
|
// OpenFilesStat includes a file path and file descriptor.
|
|
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
|
|
return p.OpenFilesWithContext(context.Background())
|
|
}
|
|
|
|
// Connections returns a slice of net.ConnectionStat used by the process.
|
|
// This returns all kind of the connection. This means TCP, UDP or UNIX.
|
|
func (p *Process) Connections() ([]net.ConnectionStat, error) {
|
|
return p.ConnectionsWithContext(context.Background())
|
|
}
|
|
|
|
// Connections returns a slice of net.ConnectionStat used by the process at most `max`.
|
|
func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) {
|
|
return p.ConnectionsMaxWithContext(context.Background(), max)
|
|
}
|
|
|
|
// NetIOCounters returns NetIOCounters of the process.
|
|
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
|
|
return p.NetIOCountersWithContext(context.Background(), pernic)
|
|
}
|
|
|
|
// MemoryMaps get memory maps from /proc/(pid)/smaps
|
|
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
|
|
return p.MemoryMapsWithContext(context.Background(), grouped)
|
|
}
|
|
|
|
// Tgid returns thread group id of the process.
|
|
func (p *Process) Tgid() (int32, error) {
|
|
return p.TgidWithContext(context.Background())
|
|
}
|
|
|
|
// SendSignal sends a unix.Signal to the process.
|
|
func (p *Process) SendSignal(sig syscall.Signal) error {
|
|
return p.SendSignalWithContext(context.Background(), sig)
|
|
}
|
|
|
|
// Suspend sends SIGSTOP to the process.
|
|
func (p *Process) Suspend() error {
|
|
return p.SuspendWithContext(context.Background())
|
|
}
|
|
|
|
// Resume sends SIGCONT to the process.
|
|
func (p *Process) Resume() error {
|
|
return p.ResumeWithContext(context.Background())
|
|
}
|
|
|
|
// Terminate sends SIGTERM to the process.
|
|
func (p *Process) Terminate() error {
|
|
return p.TerminateWithContext(context.Background())
|
|
}
|
|
|
|
// Kill sends SIGKILL to the process.
|
|
func (p *Process) Kill() error {
|
|
return p.KillWithContext(context.Background())
|
|
}
|
|
|
|
// Username returns a username of the process.
|
|
func (p *Process) Username() (string, error) {
|
|
return p.UsernameWithContext(context.Background())
|
|
}
|