mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-28 13:48:49 +08:00
implements some of process information on linux/amd64.
This commit is contained in:
parent
7b61bc5837
commit
1703a5b606
45
process.go
45
process.go
@ -1,5 +1,11 @@
|
|||||||
package gopsutil
|
package gopsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
type Process struct {
|
type Process struct {
|
||||||
Pid int32 `json:"pid"`
|
Pid int32 `json:"pid"`
|
||||||
Ppid int32 `json:"ppid"`
|
Ppid int32 `json:"ppid"`
|
||||||
@ -80,3 +86,42 @@ func Pid_exists(pid int32) (bool, error) {
|
|||||||
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// POSIX
|
||||||
|
func getTerminalMap() (map[uint64]string, error){
|
||||||
|
ret := make(map[uint64]string)
|
||||||
|
termfiles := make([]string, 0)
|
||||||
|
|
||||||
|
d, err := os.Open("/dev")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
|
||||||
|
devnames, err := d.Readdirnames(-1)
|
||||||
|
for _, devname := range devnames{
|
||||||
|
if strings.HasPrefix(devname, "/dev/tty"){
|
||||||
|
termfiles = append(termfiles, "/dev/tty/" + devname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptsd, err := os.Open("/dev/pts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer ptsd.Close()
|
||||||
|
|
||||||
|
ptsnames, err := ptsd.Readdirnames(-1)
|
||||||
|
for _, ptsname := range ptsnames{
|
||||||
|
termfiles = append(termfiles, "/dev/pts/" + ptsname)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range termfiles{
|
||||||
|
stat := syscall.Stat_t{}
|
||||||
|
syscall.Stat(name, &stat)
|
||||||
|
rdev := stat.Rdev
|
||||||
|
ret[rdev] = strings.Replace(name, "/dev", "", -1)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
160
process_linux.go
160
process_linux.go
@ -8,13 +8,38 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PRIO_PROCESS = 0 // linux/resource.h
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewProcess(pid int32) (*Process, error) {
|
func NewProcess(pid int32) (*Process, error) {
|
||||||
p := &Process{
|
p := &Process{
|
||||||
Pid: int32(pid),
|
Pid: int32(pid),
|
||||||
}
|
}
|
||||||
go fillFromStat(pid, p)
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(4)
|
||||||
|
go func() {
|
||||||
|
wg.Done()
|
||||||
|
fillFromStat(pid, p)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
fillFromStatus(pid, p)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
go fillFromfd(pid, p)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
go fillFromCmdline(pid, p)
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// user := parseInt32(fields[13])
|
// user := parseInt32(fields[13])
|
||||||
@ -33,22 +58,88 @@ func NewProcess(pid int32) (*Process, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse to int32 without error
|
||||||
func parseInt32(val string) int32 {
|
func parseInt32(val string) int32 {
|
||||||
vv, _ := strconv.ParseInt(val, 10, 32)
|
vv, _ := strconv.ParseInt(val, 10, 32)
|
||||||
return int32(vv)
|
return int32(vv)
|
||||||
}
|
}
|
||||||
|
// Parse to uint64 without error
|
||||||
|
func parseUint64(val string) uint64 {
|
||||||
|
vv, _ := strconv.ParseInt(val, 10, 64)
|
||||||
|
return uint64(vv)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
// Get num_fds from /proc/(pid)/fd
|
||||||
func fillFromStatm(pid int32, p *Process) error{
|
func fillFromfd(pid int32, p *Process) error {
|
||||||
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "statm")
|
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "fd")
|
||||||
|
d, err := os.Open(statPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
fnames, err := d.Readdirnames(-1)
|
||||||
|
p.Num_fds = int32(len(fnames))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cmdline from /proc/(pid)/cmdline
|
||||||
|
func fillFromCmdline(pid int32, p *Process) error {
|
||||||
|
cmdPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "cmdline")
|
||||||
|
cmdline, err := ioutil.ReadFile(cmdPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// remove \u0000
|
||||||
|
p.Cmdline = strings.TrimFunc(string(cmdline), func(r rune) bool {
|
||||||
|
if r == '\u0000' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get various status from /proc/(pid)/status
|
||||||
|
func fillFromStatus(pid int32, p *Process) error {
|
||||||
|
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "status")
|
||||||
contents, err := ioutil.ReadFile(statPath)
|
contents, err := ioutil.ReadFile(statPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fields := strings.Fields(string(contents))
|
lines := strings.Split(string(contents), "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
field := strings.Split(line, ":")
|
||||||
|
if len(field) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// fmt.Printf("%s ->__%s__\n", field[0], strings.Trim(field[1], " \t"))
|
||||||
|
switch field[0] {
|
||||||
|
case "Name":
|
||||||
|
p.Name = strings.Trim(field[1], " \t")
|
||||||
|
case "State":
|
||||||
|
// get between "(" and ")"
|
||||||
|
s := strings.Index(field[1], "(") + 1
|
||||||
|
e := strings.Index(field[1], "(") + 1
|
||||||
|
p.Status = field[1][s:e]
|
||||||
|
// case "PPid": // filled by fillFromStat
|
||||||
|
case "Uid":
|
||||||
|
for _, i := range strings.Split(field[1], "\t") {
|
||||||
|
p.Uids = append(p.Uids, parseInt32(i))
|
||||||
|
}
|
||||||
|
case "Gid":
|
||||||
|
for _, i := range strings.Split(field[1], "\t") {
|
||||||
|
p.Gids = append(p.Uids, parseInt32(i))
|
||||||
|
}
|
||||||
|
case "Threads":
|
||||||
|
p.Num_Threads = parseInt32(field[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func fillFromStat(pid int32, p *Process) error {
|
func fillFromStat(pid int32, p *Process) error {
|
||||||
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat")
|
statPath := filepath.Join("/", "proc", strconv.Itoa(int(pid)), "stat")
|
||||||
@ -58,49 +149,28 @@ func fillFromStat(pid int32, p *Process) error {
|
|||||||
}
|
}
|
||||||
fields := strings.Fields(string(contents))
|
fields := strings.Fields(string(contents))
|
||||||
|
|
||||||
p.Name = strings.Trim(fields[1], "()") // remove "(" and ")"
|
termmap, err := getTerminalMap()
|
||||||
p.Status, _ = getState(fields[2][0])
|
if err == nil{
|
||||||
|
p.Terminal = termmap[parseUint64(fields[6])]
|
||||||
|
}
|
||||||
|
|
||||||
p.Ppid = parseInt32(fields[3])
|
p.Ppid = parseInt32(fields[3])
|
||||||
// p.Terminal, _ = strconv.Atoi(fields[6])
|
utime, _ := strconv.ParseFloat(fields[11], 64)
|
||||||
// p.Priority, _ = strconv.Atoi(fields[17])
|
stime, _ := strconv.ParseFloat(fields[11], 64)
|
||||||
p.Nice = parseInt32(fields[18])
|
|
||||||
// p.Processor, _ = strconv.Atoi(fields[38])
|
p.Cpu_times = CPU_TimesStat{
|
||||||
|
User: float32(utime / CLOCK_TICKS),
|
||||||
|
System: float32(stime / CLOCK_TICKS),
|
||||||
|
}
|
||||||
|
|
||||||
|
// p.Nice = parseInt32(fields[18])
|
||||||
|
// use syscall instead of parse Stat file
|
||||||
|
nice, _ := syscall.Getpriority(PRIO_PROCESS, int(pid))
|
||||||
|
p.Nice = int32(nice) // FIXME: is this true?
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getState(status uint8) (string, error) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
>>> psutil.STATUS_RUNNING
|
|
||||||
'running'
|
|
||||||
>>> psutil.STATUS_SLEEPING
|
|
||||||
'sleeping'
|
|
||||||
>>> psutil.STATUS_DISK_SLEEP
|
|
||||||
'disk-sleep'
|
|
||||||
>>> psutil.STATUS_STOPPED
|
|
||||||
'stopped'
|
|
||||||
>>> psutil.STATUS_TRACING_STOP
|
|
||||||
'tracing-stop'
|
|
||||||
>>> psutil.STATUS_ZOMBIE
|
|
||||||
'zombie'
|
|
||||||
>>> psutil.STATUS_DEAD
|
|
||||||
'dead'
|
|
||||||
>>> psutil.STATUS_WAKE_KILL
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
AttributeError: 'ModuleWrapper' object has no attribute 'STATUS_WAKE_KILL'
|
|
||||||
>>> psutil.STATUS_WAKING
|
|
||||||
'waking'
|
|
||||||
>>> psutil.STATUS_IDLE
|
|
||||||
'idle'
|
|
||||||
>>> psutil.STATUS_LOCKED
|
|
||||||
'locked'
|
|
||||||
>>> psutil.STATUS_WAITING
|
|
||||||
'waiting'
|
|
||||||
*/
|
|
||||||
return "running", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processes() ([]*Process, error) {
|
func processes() ([]*Process, error) {
|
||||||
ret := make([]*Process, 0)
|
ret := make([]*Process, 0)
|
||||||
|
|
||||||
|
8
process_linux_amd64.go
Normal file
8
process_linux_amd64.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// +build linux
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
|
package gopsutil
|
||||||
|
|
||||||
|
const(
|
||||||
|
CLOCK_TICKS = 2 // FIXME: /usr/include/x86_64-linux-gnu/bits/time.h
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user