3 Star 0 Fork 0

mirrors_shirou/gopsutil_1

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
process_linux.go 27.49 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
// +build linux
package process
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common"
"github.com/shirou/gopsutil/net"
"golang.org/x/sys/unix"
)
var PageSize = uint64(os.Getpagesize())
const (
PrioProcess = 0 // linux/resource.h
ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
)
// MemoryInfoExStat is different between OSes
type MemoryInfoExStat struct {
RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes
Shared uint64 `json:"shared"` // bytes
Text uint64 `json:"text"` // bytes
Lib uint64 `json:"lib"` // bytes
Data uint64 `json:"data"` // bytes
Dirty uint64 `json:"dirty"` // bytes
}
func (m MemoryInfoExStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
type MemoryMapsStat struct {
Path string `json:"path"`
Rss uint64 `json:"rss"`
Size uint64 `json:"size"`
Pss uint64 `json:"pss"`
SharedClean uint64 `json:"sharedClean"`
SharedDirty uint64 `json:"sharedDirty"`
PrivateClean uint64 `json:"privateClean"`
PrivateDirty uint64 `json:"privateDirty"`
Referenced uint64 `json:"referenced"`
Anonymous uint64 `json:"anonymous"`
Swap uint64 `json:"swap"`
}
// String returns JSON value of the process.
func (m MemoryMapsStat) String() string {
s, _ := json.Marshal(m)
return string(s)
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
_, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return -1, err
}
return ppid, nil
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
if p.name == "" {
if err := p.fillFromStatusWithContext(ctx); err != nil {
return "", err
}
}
return p.name, nil
}
func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
if p.tgid == 0 {
if err := p.fillFromStatusWithContext(ctx); err != nil {
return 0, err
}
}
return p.tgid, nil
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return p.fillFromExeWithContext(ctx)
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
return p.fillFromCmdlineWithContext(ctx)
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return p.fillSliceFromCmdlineWithContext(ctx)
}
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
_, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return 0, err
}
return createTime, nil
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return p.fillFromCwdWithContext(ctx)
}
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 {
return "", err
}
return p.status, nil
}
func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "stat")
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return false, err
}
fields := strings.Fields(string(contents))
if len(fields) < 8 {
return false, fmt.Errorf("insufficient data in %s", statPath)
}
pgid := fields[4]
tpgid := fields[7]
return pgid == tpgid, nil
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return []int32{}, err
}
return p.uids, nil
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return []int32{}, err
}
return p.gids, nil
}
func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return []int32{}, err
}
return p.groups, nil
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return "", err
}
termmap, err := getTerminalMap()
if err != nil {
return "", err
}
terminal := termmap[t]
return terminal, nil
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
_, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return 0, err
}
return nice, nil
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(ctx, false)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
rlimits, err := p.fillFromLimitsWithContext(ctx)
if !gatherUsed || err != nil {
return rlimits, err
}
_, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return nil, err
}
if err := p.fillFromStatusWithContext(ctx); err != nil {
return nil, err
}
for i := range rlimits {
rs := &rlimits[i]
switch rs.Resource {
case RLIMIT_CPU:
times, err := p.TimesWithContext(ctx)
if err != nil {
return nil, err
}
rs.Used = uint64(times.User + times.System)
case RLIMIT_DATA:
rs.Used = uint64(p.memInfo.Data)
case RLIMIT_STACK:
rs.Used = uint64(p.memInfo.Stack)
case RLIMIT_RSS:
rs.Used = uint64(p.memInfo.RSS)
case RLIMIT_NOFILE:
n, err := p.NumFDsWithContext(ctx)
if err != nil {
return nil, err
}
rs.Used = uint64(n)
case RLIMIT_MEMLOCK:
rs.Used = uint64(p.memInfo.Locked)
case RLIMIT_AS:
rs.Used = uint64(p.memInfo.VMS)
case RLIMIT_LOCKS:
//TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority.
case RLIMIT_SIGPENDING:
rs.Used = p.sigInfo.PendingProcess
case RLIMIT_NICE:
// The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased.
// So effectively: if rs.Soft == 0 { rs.Soft = rs.Used }
rs.Used = uint64(nice)
case RLIMIT_RTPRIO:
rs.Used = uint64(rtprio)
}
}
return rlimits, err
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
return p.fillFromIOWithContext(ctx)
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return nil, err
}
return p.numCtxSwitches, nil
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
_, fnames, err := p.fillFromfdListWithContext(ctx)
return int32(len(fnames)), err
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
err := p.fillFromStatusWithContext(ctx)
if err != nil {
return 0, err
}
return p.numThreads, nil
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task")
tids, err := readPidsFromDir(taskPath)
if err != nil {
return nil, err
}
for _, tid := range tids {
_, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid)
if err != nil {
return nil, err
}
ret[tid] = cpuTimes
}
return ret, nil
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
_, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx)
if err != nil {
return nil, err
}
return cpuTimes, nil
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
meminfo, _, err := p.fillFromStatmWithContext(ctx)
if err != nil {
return nil, err
}
return meminfo, nil
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
_, memInfoEx, err := p.fillFromStatmWithContext(ctx)
if err != nil {
return nil, err
}
return memInfoEx, nil
}
func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
_, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx)
if err != nil {
return nil, err
}
return pageFaults, nil
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil {
if pids == nil || len(pids) == 0 {
return nil, ErrorNoChildren
}
return nil, err
}
ret := make([]*Process, 0, len(pids))
for _, pid := range pids {
np, err := NewProcessWithContext(ctx, pid)
if err != nil {
return nil, err
}
ret = append(ret, np)
}
return ret, nil
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
_, ofs, err := p.fillFromfdWithContext(ctx)
if err != nil {
return nil, err
}
ret := make([]OpenFilesStat, len(ofs))
for i, o := range ofs {
ret[i] = *o
}
return ret, nil
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
}
func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev")
return net.IOCountersByFileWithContext(ctx, pernic, filename)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
pid := p.Pid
var ret []MemoryMapsStat
if grouped {
ret = make([]MemoryMapsStat, 1)
}
smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps")
contents, err := ioutil.ReadFile(smapsPath)
if err != nil {
return nil, err
}
lines := strings.Split(string(contents), "\n")
// function of parsing a block
getBlock := func(firstLine []string, block []string) (MemoryMapsStat, error) {
m := MemoryMapsStat{}
m.Path = firstLine[len(firstLine)-1]
for _, line := range block {
if strings.Contains(line, "VmFlags") {
continue
}
field := strings.Split(line, ":")
if len(field) < 2 {
continue
}
v := strings.Trim(field[1], "kB") // remove last "kB"
v = strings.TrimSpace(v)
t, err := strconv.ParseUint(v, 10, 64)
if err != nil {
return m, err
}
switch field[0] {
case "Size":
m.Size = t
case "Rss":
m.Rss = t
case "Pss":
m.Pss = t
case "Shared_Clean":
m.SharedClean = t
case "Shared_Dirty":
m.SharedDirty = t
case "Private_Clean":
m.PrivateClean = t
case "Private_Dirty":
m.PrivateDirty = t
case "Referenced":
m.Referenced = t
case "Anonymous":
m.Anonymous = t
case "Swap":
m.Swap = t
}
}
return m, nil
}
var firstLine []string
blocks := make([]string, 0, 16)
for i, line := range lines {
fields := strings.Fields(line)
if (len(fields) > 0 && !strings.HasSuffix(fields[0], ":")) || i == len(lines)-1 {
// new block section
if len(firstLine) > 0 && len(blocks) > 0 {
g, err := getBlock(firstLine, blocks)
if err != nil {
return &ret, err
}
if grouped {
ret[0].Size += g.Size
ret[0].Rss += g.Rss
ret[0].Pss += g.Pss
ret[0].SharedClean += g.SharedClean
ret[0].SharedDirty += g.SharedDirty
ret[0].PrivateClean += g.PrivateClean
ret[0].PrivateDirty += g.PrivateDirty
ret[0].Referenced += g.Referenced
ret[0].Anonymous += g.Anonymous
ret[0].Swap += g.Swap
} else {
ret = append(ret, g)
}
}
// starts new block
blocks = make([]string, 0, 16)
firstLine = fields
} else {
blocks = append(blocks, line)
}
}
return &ret, nil
}
/**
** Internal functions
**/
func limitToInt(val string) (int32, error) {
if val == "unlimited" {
return math.MaxInt32, nil
} else {
res, err := strconv.ParseInt(val, 10, 32)
if err != nil {
return 0, err
}
return int32(res), nil
}
}
// Get num_fds from /proc/(pid)/limits
func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) {
pid := p.Pid
limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits")
d, err := os.Open(limitsFile)
if err != nil {
return nil, err
}
defer d.Close()
var limitStats []RlimitStat
limitsScanner := bufio.NewScanner(d)
for limitsScanner.Scan() {
var statItem RlimitStat
str := strings.Fields(limitsScanner.Text())
// Remove the header line
if strings.Contains(str[len(str)-1], "Units") {
continue
}
// Assert that last item is a Hard limit
statItem.Hard, err = limitToInt(str[len(str)-1])
if err != nil {
// On error remove last item an try once again since it can be unit or header line
str = str[:len(str)-1]
statItem.Hard, err = limitToInt(str[len(str)-1])
if err != nil {
return nil, err
}
}
// Remove last item from string
str = str[:len(str)-1]
//Now last item is a Soft limit
statItem.Soft, err = limitToInt(str[len(str)-1])
if err != nil {
return nil, err
}
// Remove last item from string
str = str[:len(str)-1]
//The rest is a stats name
resourceName := strings.Join(str, " ")
switch resourceName {
case "Max cpu time":
statItem.Resource = RLIMIT_CPU
case "Max file size":
statItem.Resource = RLIMIT_FSIZE
case "Max data size":
statItem.Resource = RLIMIT_DATA
case "Max stack size":
statItem.Resource = RLIMIT_STACK
case "Max core file size":
statItem.Resource = RLIMIT_CORE
case "Max resident set":
statItem.Resource = RLIMIT_RSS
case "Max processes":
statItem.Resource = RLIMIT_NPROC
case "Max open files":
statItem.Resource = RLIMIT_NOFILE
case "Max locked memory":
statItem.Resource = RLIMIT_MEMLOCK
case "Max address space":
statItem.Resource = RLIMIT_AS
case "Max file locks":
statItem.Resource = RLIMIT_LOCKS
case "Max pending signals":
statItem.Resource = RLIMIT_SIGPENDING
case "Max msgqueue size":
statItem.Resource = RLIMIT_MSGQUEUE
case "Max nice priority":
statItem.Resource = RLIMIT_NICE
case "Max realtime priority":
statItem.Resource = RLIMIT_RTPRIO
case "Max realtime timeout":
statItem.Resource = RLIMIT_RTTIME
default:
continue
}
limitStats = append(limitStats, statItem)
}
if err := limitsScanner.Err(); err != nil {
return nil, err
}
return limitStats, nil
}
// Get list of /proc/(pid)/fd files
func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) {
pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "fd")
d, err := os.Open(statPath)
if err != nil {
return statPath, []string{}, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
return statPath, fnames, err
}
// Get num_fds from /proc/(pid)/fd
func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) {
statPath, fnames, err := p.fillFromfdListWithContext(ctx)
if err != nil {
return 0, nil, err
}
numFDs := int32(len(fnames))
var openfiles []*OpenFilesStat
for _, fd := range fnames {
fpath := filepath.Join(statPath, fd)
filepath, err := os.Readlink(fpath)
if err != nil {
continue
}
t, err := strconv.ParseUint(fd, 10, 64)
if err != nil {
return numFDs, openfiles, err
}
o := &OpenFilesStat{
Path: filepath,
Fd: t,
}
openfiles = append(openfiles, o)
}
return numFDs, openfiles, nil
}
// Get cwd from /proc/(pid)/cwd
func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) {
pid := p.Pid
cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd")
cwd, err := os.Readlink(cwdPath)
if err != nil {
return "", err
}
return string(cwd), nil
}
// Get exe from /proc/(pid)/exe
func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) {
pid := p.Pid
exePath := common.HostProc(strconv.Itoa(int(pid)), "exe")
exe, err := os.Readlink(exePath)
if err != nil {
return "", err
}
return string(exe), nil
}
// Get cmdline from /proc/(pid)/cmdline
func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) {
pid := p.Pid
cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
cmdline, err := ioutil.ReadFile(cmdPath)
if err != nil {
return "", err
}
ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
if r == '\u0000' {
return true
}
return false
})
return strings.Join(ret, " "), nil
}
func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) {
pid := p.Pid
cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
cmdline, err := ioutil.ReadFile(cmdPath)
if err != nil {
return nil, err
}
if len(cmdline) == 0 {
return nil, nil
}
if cmdline[len(cmdline)-1] == 0 {
cmdline = cmdline[:len(cmdline)-1]
}
parts := bytes.Split(cmdline, []byte{0})
var strParts []string
for _, p := range parts {
strParts = append(strParts, string(p))
}
return strParts, nil
}
// Get IO status from /proc/(pid)/io
func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) {
pid := p.Pid
ioPath := common.HostProc(strconv.Itoa(int(pid)), "io")
ioline, err := ioutil.ReadFile(ioPath)
if err != nil {
return nil, err
}
lines := strings.Split(string(ioline), "\n")
ret := &IOCountersStat{}
for _, line := range lines {
field := strings.Fields(line)
if len(field) < 2 {
continue
}
t, err := strconv.ParseUint(field[1], 10, 64)
if err != nil {
return nil, err
}
param := field[0]
if strings.HasSuffix(param, ":") {
param = param[:len(param)-1]
}
switch param {
case "syscr":
ret.ReadCount = t
case "syscw":
ret.WriteCount = t
case "read_bytes":
ret.ReadBytes = t
case "write_bytes":
ret.WriteBytes = t
}
}
return ret, nil
}
// Get memory info from /proc/(pid)/statm
func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) {
pid := p.Pid
memPath := common.HostProc(strconv.Itoa(int(pid)), "statm")
contents, err := ioutil.ReadFile(memPath)
if err != nil {
return nil, nil, err
}
fields := strings.Split(string(contents), " ")
vms, err := strconv.ParseUint(fields[0], 10, 64)
if err != nil {
return nil, nil, err
}
rss, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return nil, nil, err
}
memInfo := &MemoryInfoStat{
RSS: rss * PageSize,
VMS: vms * PageSize,
}
shared, err := strconv.ParseUint(fields[2], 10, 64)
if err != nil {
return nil, nil, err
}
text, err := strconv.ParseUint(fields[3], 10, 64)
if err != nil {
return nil, nil, err
}
lib, err := strconv.ParseUint(fields[4], 10, 64)
if err != nil {
return nil, nil, err
}
dirty, err := strconv.ParseUint(fields[5], 10, 64)
if err != nil {
return nil, nil, err
}
memInfoEx := &MemoryInfoExStat{
RSS: rss * PageSize,
VMS: vms * PageSize,
Shared: shared * PageSize,
Text: text * PageSize,
Lib: lib * PageSize,
Dirty: dirty * PageSize,
}
return memInfo, memInfoEx, nil
}
// Get various status from /proc/(pid)/status
func (p *Process) fillFromStatusWithContext(ctx context.Context) error {
pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return err
}
lines := strings.Split(string(contents), "\n")
p.numCtxSwitches = &NumCtxSwitchesStat{}
p.memInfo = &MemoryInfoStat{}
p.sigInfo = &SignalInfoStat{}
for _, line := range lines {
tabParts := strings.SplitN(line, "\t", 2)
if len(tabParts) < 2 {
continue
}
value := tabParts[1]
switch strings.TrimRight(tabParts[0], ":") {
case "Name":
p.name = strings.Trim(value, " \t")
if len(p.name) >= 15 {
cmdlineSlice, err := p.CmdlineSlice()
if err != nil {
return err
}
if len(cmdlineSlice) > 0 {
extendedName := filepath.Base(cmdlineSlice[0])
if strings.HasPrefix(extendedName, p.name) {
p.name = extendedName
} else {
p.name = cmdlineSlice[0]
}
}
}
// Ensure we have a copy and not reference into slice
p.name = string([]byte(p.name))
case "State":
p.status = value[0:1]
// Ensure we have a copy and not reference into slice
p.status = string([]byte(p.status))
case "PPid", "Ppid":
pval, err := strconv.ParseInt(value, 10, 32)
if err != nil {
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") {
v, err := strconv.ParseInt(i, 10, 32)
if err != nil {
return err
}
p.uids = append(p.uids, int32(v))
}
case "Gid":
p.gids = make([]int32, 0, 4)
for _, i := range strings.Split(value, "\t") {
v, err := strconv.ParseInt(i, 10, 32)
if err != nil {
return err
}
p.gids = append(p.gids, int32(v))
}
case "Groups":
groups := strings.Fields(value)
p.groups = make([]int32, 0, len(groups))
for _, i := range groups {
v, err := strconv.ParseInt(i, 10, 32)
if err != nil {
return err
}
p.groups = append(p.groups, int32(v))
}
case "Threads":
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
p.numThreads = int32(v)
case "voluntary_ctxt_switches":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
p.numCtxSwitches.Voluntary = v
case "nonvoluntary_ctxt_switches":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
p.numCtxSwitches.Involuntary = v
case "VmRSS":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.RSS = v * 1024
case "VmSize":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.VMS = v * 1024
case "VmSwap":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Swap = v * 1024
case "VmHWM":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.HWM = v * 1024
case "VmData":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Data = v * 1024
case "VmStk":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Stack = v * 1024
case "VmLck":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Locked = v * 1024
case "SigPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingThread = v
case "ShdPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingProcess = v
case "SigBlk":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Blocked = v
case "SigIgn":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Ignored = v
case "SigCgt":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Caught = v
}
}
return nil
}
func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
pid := p.Pid
var statPath string
if tid == -1 {
statPath = common.HostProc(strconv.Itoa(int(pid)), "stat")
} else {
statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat")
}
contents, err := ioutil.ReadFile(statPath)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
fields := strings.Fields(string(contents))
i := 1
for !strings.HasSuffix(fields[i], ")") {
i++
}
terminal, err := strconv.ParseUint(fields[i+5], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
ppid, err := strconv.ParseInt(fields[i+2], 10, 32)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
utime, err := strconv.ParseFloat(fields[i+12], 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
stime, err := strconv.ParseFloat(fields[i+13], 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
// There is no such thing as iotime in stat file. As an approximation, we
// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
// docs). Note: I am assuming at least Linux 2.6.18
iotime, err := strconv.ParseFloat(fields[i+40], 64)
if err != nil {
iotime = 0 // Ancient linux version, most likely
}
cpuTimes := &cpu.TimesStat{
CPU: "cpu",
User: float64(utime / ClockTicks),
System: float64(stime / ClockTicks),
Iowait: float64(iotime / ClockTicks),
}
bootTime, _ := common.BootTimeWithContext(ctx)
t, err := strconv.ParseUint(fields[i+20], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
ctime := (t / uint64(ClockTicks)) + uint64(bootTime)
createTime := int64(ctime * 1000)
rtpriority, err := strconv.ParseInt(fields[i+16], 10, 32)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
if rtpriority < 0 {
rtpriority = rtpriority*-1 - 1
} else {
rtpriority = 0
}
// p.Nice = mustParseInt32(fields[18])
// use syscall instead of parse Stat file
snice, _ := unix.Getpriority(PrioProcess, int(pid))
nice := int32(snice) // FIXME: is this true?
minFault, err := strconv.ParseUint(fields[i+8], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
cMinFault, err := strconv.ParseUint(fields[i+9], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
majFault, err := strconv.ParseUint(fields[i+10], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
cMajFault, err := strconv.ParseUint(fields[i+11], 10, 64)
if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err
}
faults := &PageFaultsStat{
MinorFaults: minFault,
MajorFaults: majFault,
ChildMinorFaults: cMinFault,
ChildMajorFaults: cMajFault,
}
return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil
}
func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
return p.fillFromTIDStatWithContext(ctx, -1)
}
func pidsWithContext(ctx context.Context) ([]int32, error) {
return readPidsFromDir(common.HostProc())
}
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
out := []*Process{}
pids, err := PidsWithContext(ctx)
if err != nil {
return out, err
}
for _, pid := range pids {
p, err := NewProcessWithContext(ctx, pid)
if err != nil {
continue
}
out = append(out, p)
}
return out, nil
}
func readPidsFromDir(path string) ([]int32, error) {
var ret []int32
d, err := os.Open(path)
if err != nil {
return nil, err
}
defer d.Close()
fnames, err := d.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, fname := range fnames {
pid, err := strconv.ParseInt(fname, 10, 32)
if err != nil {
// if not numeric name, just skip
continue
}
ret = append(ret, int32(pid))
}
return ret, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_shirou/gopsutil_1.git
git@gitee.com:mirrors_shirou/gopsutil_1.git
mirrors_shirou
gopsutil_1
gopsutil_1
v3.21.2

搜索帮助