github.com/sdibtacm/sandbox@v0.0.0-20200320120712-60470cf803dc/units/pstree/pstree.go (about)

     1  // Code copy from github.com/sbinet/pstree
     2  //+build linux
     3  
     4  // package pstree provides an API to retrieve the process tree from procfs.
     5  package pstree
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  	"strings"
    14  )
    15  
    16  // New returns the whole system process tree.
    17  func New() (*Tree, error) {
    18  	files, err := filepath.Glob("/proc/[0-9]*")
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  
    23  	procs := make(map[int]Process, len(files))
    24  	for _, dir := range files {
    25  		proc, err := scan(dir)
    26  		if err != nil {
    27  			return nil, err
    28  		}
    29  		if proc.Stat.Pid == 0 {
    30  			// process vanished since Glob.
    31  			continue
    32  		}
    33  		procs[proc.Stat.Pid] = proc
    34  	}
    35  
    36  	for pid, proc := range procs {
    37  		if proc.Stat.Ppid == 0 {
    38  			continue
    39  		}
    40  		parent, ok := procs[proc.Stat.Ppid]
    41  		if !ok {
    42  			log.Panicf(
    43  				"internal logic error. parent of [%d] does not exist!",
    44  				pid,
    45  			)
    46  		}
    47  		parent.Children = append(parent.Children, pid)
    48  		procs[parent.Stat.Pid] = parent
    49  	}
    50  
    51  	for pid, proc := range procs {
    52  		if len(proc.Children) > 0 {
    53  			sort.Ints(proc.Children)
    54  		}
    55  		procs[pid] = proc
    56  	}
    57  
    58  	tree := &Tree{
    59  		Procs: procs,
    60  	}
    61  	return tree, err
    62  }
    63  
    64  const (
    65  	statfmt = "%c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"
    66  )
    67  
    68  // ProcessStat contains process information.
    69  // see: http://man7.org/linux/man-pages/man5/proc.5.html
    70  type ProcessStat struct {
    71  	Pid       int    // process ID
    72  	Comm      string // filename of the executable in parentheses
    73  	State     byte   // process state
    74  	Ppid      int    // pid of the parent process
    75  	Pgrp      int    // process group ID of the process
    76  	Session   int    // session ID of the process
    77  	Tty       int    // controlling terminal of the process
    78  	Tpgid     int    // ID of foreground process group
    79  	Flags     uint32 // kernel flags word of the process
    80  	Minflt    uint64 // number of minor faults the process has made which have not required loading a memory page from disk
    81  	Cminflt   uint64 // number of minor faults the process's waited-for children have made
    82  	Majflt    uint64 // number of major faults the process has made which have required loading a memory page from disk
    83  	Cmajflt   uint64 // number of major faults the process's waited-for children have made
    84  	Utime     uint64 // user time in clock ticks
    85  	Stime     uint64 // system time in clock ticks
    86  	Cutime    uint64 // children user time in clock ticks
    87  	Cstime    uint64 // children system time in clock ticks
    88  	Priority  int64  // priority
    89  	Nice      int64  // the nice value
    90  	Nthreads  int64  // number of threads in this process
    91  	Itrealval int64  // time in jiffies before next SIGALRM is sent to the process due to an interval timer
    92  	Starttime int64  // time the process started after system boot in clock ticks
    93  	Vsize     uint64 // virtual memory size in bytes
    94  	Rss       int64  // resident set size: number of pages the process has in real memory
    95  }
    96  
    97  func scan(dir string) (Process, error) {
    98  	f, err := os.Open(filepath.Join(dir, "stat"))
    99  	if err != nil {
   100  		// process vanished since Glob.
   101  		return Process{}, nil
   102  	}
   103  	defer f.Close()
   104  
   105  	var proc Process
   106  
   107  	_, err = fmt.Fscanf(f, "%d %s", &proc.Stat.Pid, &proc.Stat.Comm)
   108  	if err != nil {
   109  		return proc, err
   110  	}
   111  
   112  	// some of command will have space, we should get all
   113  	for !strings.HasSuffix(proc.Stat.Comm, ")") {
   114  		var str string
   115  		_, err = fmt.Fscanf(f, "%s", &str)
   116  		if err != nil {
   117  			return proc, err
   118  		}
   119  		proc.Stat.Comm += str
   120  	}
   121  
   122  	_, err = fmt.Fscanf(
   123  		f, statfmt,
   124  		&proc.Stat.State,
   125  		&proc.Stat.Ppid, &proc.Stat.Pgrp, &proc.Stat.Session,
   126  		&proc.Stat.Tty, &proc.Stat.Tpgid, &proc.Stat.Flags,
   127  		&proc.Stat.Minflt, &proc.Stat.Cminflt, &proc.Stat.Majflt, &proc.Stat.Cmajflt,
   128  		&proc.Stat.Utime, &proc.Stat.Stime,
   129  		&proc.Stat.Cutime, &proc.Stat.Cstime,
   130  		&proc.Stat.Priority,
   131  		&proc.Stat.Nice,
   132  		&proc.Stat.Nthreads,
   133  		&proc.Stat.Itrealval, &proc.Stat.Starttime,
   134  		&proc.Stat.Vsize, &proc.Stat.Rss,
   135  	)
   136  	if err != nil {
   137  		return proc, err
   138  	}
   139  
   140  	proc.Name = proc.Stat.Comm
   141  	if strings.HasPrefix(proc.Name, "(") && strings.HasSuffix(proc.Name, ")") {
   142  		proc.Name = proc.Name[1 : len(proc.Name)-1]
   143  	}
   144  	return proc, nil
   145  }
   146  
   147  // Tree is a tree of processes.
   148  type Tree struct {
   149  	Procs map[int]Process
   150  }
   151  
   152  // Process stores information about a UNIX process.
   153  type Process struct {
   154  	Name     string
   155  	Stat     ProcessStat
   156  	Children []int
   157  }