github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/process/process_linux.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package process
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"math"
    14  	"os"
    15  	"path/filepath"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/tklauser/go-sysconf"
    20  	"github.com/unionj-cloud/go-doudou/toolkit/cpu"
    21  	"github.com/unionj-cloud/go-doudou/toolkit/internal/common"
    22  	"github.com/unionj-cloud/go-doudou/toolkit/net"
    23  	"golang.org/x/sys/unix"
    24  )
    25  
    26  var pageSize = uint64(os.Getpagesize())
    27  
    28  const prioProcess = 0 // linux/resource.h
    29  
    30  var clockTicks = 100 // default value
    31  
    32  func init() {
    33  	clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
    34  	// ignore errors
    35  	if err == nil {
    36  		clockTicks = int(clkTck)
    37  	}
    38  }
    39  
    40  // MemoryInfoExStat is different between OSes
    41  type MemoryInfoExStat struct {
    42  	RSS    uint64 `json:"rss"`    // bytes
    43  	VMS    uint64 `json:"vms"`    // bytes
    44  	Shared uint64 `json:"shared"` // bytes
    45  	Text   uint64 `json:"text"`   // bytes
    46  	Lib    uint64 `json:"lib"`    // bytes
    47  	Data   uint64 `json:"data"`   // bytes
    48  	Dirty  uint64 `json:"dirty"`  // bytes
    49  }
    50  
    51  func (m MemoryInfoExStat) String() string {
    52  	s, _ := json.Marshal(m)
    53  	return string(s)
    54  }
    55  
    56  type MemoryMapsStat struct {
    57  	Path         string `json:"path"`
    58  	Rss          uint64 `json:"rss"`
    59  	Size         uint64 `json:"size"`
    60  	Pss          uint64 `json:"pss"`
    61  	SharedClean  uint64 `json:"sharedClean"`
    62  	SharedDirty  uint64 `json:"sharedDirty"`
    63  	PrivateClean uint64 `json:"privateClean"`
    64  	PrivateDirty uint64 `json:"privateDirty"`
    65  	Referenced   uint64 `json:"referenced"`
    66  	Anonymous    uint64 `json:"anonymous"`
    67  	Swap         uint64 `json:"swap"`
    68  }
    69  
    70  // String returns JSON value of the process.
    71  func (m MemoryMapsStat) String() string {
    72  	s, _ := json.Marshal(m)
    73  	return string(s)
    74  }
    75  
    76  func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
    77  	_, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
    78  	if err != nil {
    79  		return -1, err
    80  	}
    81  	return ppid, nil
    82  }
    83  
    84  func (p *Process) NameWithContext(ctx context.Context) (string, error) {
    85  	if p.name == "" {
    86  		if err := p.fillNameWithContext(ctx); err != nil {
    87  			return "", err
    88  		}
    89  	}
    90  	return p.name, nil
    91  }
    92  
    93  func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
    94  	if p.tgid == 0 {
    95  		if err := p.fillFromStatusWithContext(ctx); err != nil {
    96  			return 0, err
    97  		}
    98  	}
    99  	return p.tgid, nil
   100  }
   101  
   102  func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
   103  	return p.fillFromExeWithContext()
   104  }
   105  
   106  func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
   107  	return p.fillFromCmdlineWithContext(ctx)
   108  }
   109  
   110  func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
   111  	return p.fillSliceFromCmdlineWithContext(ctx)
   112  }
   113  
   114  func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
   115  	_, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx)
   116  	if err != nil {
   117  		return 0, err
   118  	}
   119  	return createTime, nil
   120  }
   121  
   122  func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
   123  	return p.fillFromCwdWithContext()
   124  }
   125  
   126  func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
   127  	err := p.fillFromStatusWithContext(ctx)
   128  	if err != nil {
   129  		return []string{""}, err
   130  	}
   131  	return []string{p.status}, nil
   132  }
   133  
   134  func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
   135  	// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
   136  	pid := p.Pid
   137  	statPath := common.HostProc(strconv.Itoa(int(pid)), "stat")
   138  	contents, err := ioutil.ReadFile(statPath)
   139  	if err != nil {
   140  		return false, err
   141  	}
   142  	fields := strings.Fields(string(contents))
   143  	if len(fields) < 8 {
   144  		return false, fmt.Errorf("insufficient data in %s", statPath)
   145  	}
   146  	pgid := fields[4]
   147  	tpgid := fields[7]
   148  	return pgid == tpgid, nil
   149  }
   150  
   151  func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
   152  	err := p.fillFromStatusWithContext(ctx)
   153  	if err != nil {
   154  		return []int32{}, err
   155  	}
   156  	return p.uids, nil
   157  }
   158  
   159  func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
   160  	err := p.fillFromStatusWithContext(ctx)
   161  	if err != nil {
   162  		return []int32{}, err
   163  	}
   164  	return p.gids, nil
   165  }
   166  
   167  func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
   168  	err := p.fillFromStatusWithContext(ctx)
   169  	if err != nil {
   170  		return []int32{}, err
   171  	}
   172  	return p.groups, nil
   173  }
   174  
   175  func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
   176  	t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
   177  	if err != nil {
   178  		return "", err
   179  	}
   180  	termmap, err := getTerminalMap()
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  	terminal := termmap[t]
   185  	return terminal, nil
   186  }
   187  
   188  func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
   189  	_, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx)
   190  	if err != nil {
   191  		return 0, err
   192  	}
   193  	return nice, nil
   194  }
   195  
   196  func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
   197  	return 0, common.ErrNotImplementedError
   198  }
   199  
   200  func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
   201  	return p.RlimitUsageWithContext(ctx, false)
   202  }
   203  
   204  func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
   205  	rlimits, err := p.fillFromLimitsWithContext()
   206  	if !gatherUsed || err != nil {
   207  		return rlimits, err
   208  	}
   209  
   210  	_, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	if err := p.fillFromStatusWithContext(ctx); err != nil {
   215  		return nil, err
   216  	}
   217  
   218  	for i := range rlimits {
   219  		rs := &rlimits[i]
   220  		switch rs.Resource {
   221  		case RLIMIT_CPU:
   222  			times, err := p.TimesWithContext(ctx)
   223  			if err != nil {
   224  				return nil, err
   225  			}
   226  			rs.Used = uint64(times.User + times.System)
   227  		case RLIMIT_DATA:
   228  			rs.Used = uint64(p.memInfo.Data)
   229  		case RLIMIT_STACK:
   230  			rs.Used = uint64(p.memInfo.Stack)
   231  		case RLIMIT_RSS:
   232  			rs.Used = uint64(p.memInfo.RSS)
   233  		case RLIMIT_NOFILE:
   234  			n, err := p.NumFDsWithContext(ctx)
   235  			if err != nil {
   236  				return nil, err
   237  			}
   238  			rs.Used = uint64(n)
   239  		case RLIMIT_MEMLOCK:
   240  			rs.Used = uint64(p.memInfo.Locked)
   241  		case RLIMIT_AS:
   242  			rs.Used = uint64(p.memInfo.VMS)
   243  		case RLIMIT_LOCKS:
   244  			// TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority.
   245  		case RLIMIT_SIGPENDING:
   246  			rs.Used = p.sigInfo.PendingProcess
   247  		case RLIMIT_NICE:
   248  			// 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.
   249  			// So effectively: if rs.Soft == 0 { rs.Soft = rs.Used }
   250  			rs.Used = uint64(nice)
   251  		case RLIMIT_RTPRIO:
   252  			rs.Used = uint64(rtprio)
   253  		}
   254  	}
   255  
   256  	return rlimits, err
   257  }
   258  
   259  func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
   260  	return p.fillFromIOWithContext()
   261  }
   262  
   263  func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
   264  	err := p.fillFromStatusWithContext(ctx)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  	return p.numCtxSwitches, nil
   269  }
   270  
   271  func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
   272  	_, fnames, err := p.fillFromfdListWithContext(ctx)
   273  	return int32(len(fnames)), err
   274  }
   275  
   276  func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
   277  	err := p.fillFromStatusWithContext(ctx)
   278  	if err != nil {
   279  		return 0, err
   280  	}
   281  	return p.numThreads, nil
   282  }
   283  
   284  func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
   285  	ret := make(map[int32]*cpu.TimesStat)
   286  	taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task")
   287  
   288  	tids, err := readPidsFromDir(taskPath)
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  
   293  	for _, tid := range tids {
   294  		_, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid)
   295  		if err != nil {
   296  			return nil, err
   297  		}
   298  		ret[tid] = &cpuTimes
   299  	}
   300  
   301  	return ret, nil
   302  }
   303  
   304  func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
   305  	_, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx)
   306  	if err != nil {
   307  		return nil, err
   308  	}
   309  	return &cpuTimes, nil
   310  }
   311  
   312  func (p *Process) times1() (cpu.TimesStat, error) {
   313  	_, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(context.Background())
   314  	if err != nil {
   315  		return cpu.TimesStat{}, err
   316  	}
   317  	return cpuTimes, nil
   318  }
   319  
   320  func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
   321  	return nil, common.ErrNotImplementedError
   322  }
   323  
   324  func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
   325  	meminfo, _, err := p.fillFromStatmWithContext()
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	return meminfo, nil
   330  }
   331  
   332  func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
   333  	_, memInfoEx, err := p.fillFromStatmWithContext()
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	return memInfoEx, nil
   338  }
   339  
   340  func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
   341  	_, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	return &pageFaults, nil
   346  }
   347  
   348  func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
   349  	pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  	if len(pids) == 0 {
   354  		return nil, ErrorNoChildren
   355  	}
   356  	ret := make([]*Process, 0, len(pids))
   357  	for _, pid := range pids {
   358  		np, err := NewProcessWithContext(ctx, pid)
   359  		if err != nil {
   360  			return nil, err
   361  		}
   362  		ret = append(ret, np)
   363  	}
   364  	return ret, nil
   365  }
   366  
   367  func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
   368  	_, ofs, err := p.fillFromfdWithContext(ctx)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	ret := make([]OpenFilesStat, len(ofs))
   373  	for i, o := range ofs {
   374  		ret[i] = *o
   375  	}
   376  
   377  	return ret, nil
   378  }
   379  
   380  func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
   381  	return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
   382  }
   383  
   384  func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
   385  	return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max)
   386  }
   387  
   388  func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
   389  	pid := p.Pid
   390  	var ret []MemoryMapsStat
   391  	smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps")
   392  	if grouped {
   393  		ret = make([]MemoryMapsStat, 1)
   394  		// If smaps_rollup exists (require kernel >= 4.15), then we will use it
   395  		// for pre-summed memory information for a process.
   396  		smapsRollupPath := common.HostProc(strconv.Itoa(int(pid)), "smaps_rollup")
   397  		if _, err := os.Stat(smapsRollupPath); !os.IsNotExist(err) {
   398  			smapsPath = smapsRollupPath
   399  		}
   400  	}
   401  	contents, err := ioutil.ReadFile(smapsPath)
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  	lines := strings.Split(string(contents), "\n")
   406  
   407  	// function of parsing a block
   408  	getBlock := func(firstLine []string, block []string) (MemoryMapsStat, error) {
   409  		m := MemoryMapsStat{}
   410  		m.Path = firstLine[len(firstLine)-1]
   411  
   412  		for _, line := range block {
   413  			if strings.Contains(line, "VmFlags") {
   414  				continue
   415  			}
   416  			field := strings.Split(line, ":")
   417  			if len(field) < 2 {
   418  				continue
   419  			}
   420  			v := strings.Trim(field[1], "kB") // remove last "kB"
   421  			v = strings.TrimSpace(v)
   422  			t, err := strconv.ParseUint(v, 10, 64)
   423  			if err != nil {
   424  				return m, err
   425  			}
   426  
   427  			switch field[0] {
   428  			case "Size":
   429  				m.Size = t
   430  			case "Rss":
   431  				m.Rss = t
   432  			case "Pss":
   433  				m.Pss = t
   434  			case "Shared_Clean":
   435  				m.SharedClean = t
   436  			case "Shared_Dirty":
   437  				m.SharedDirty = t
   438  			case "Private_Clean":
   439  				m.PrivateClean = t
   440  			case "Private_Dirty":
   441  				m.PrivateDirty = t
   442  			case "Referenced":
   443  				m.Referenced = t
   444  			case "Anonymous":
   445  				m.Anonymous = t
   446  			case "Swap":
   447  				m.Swap = t
   448  			}
   449  		}
   450  		return m, nil
   451  	}
   452  
   453  	var firstLine []string
   454  	blocks := make([]string, 0, 16)
   455  
   456  	for i, line := range lines {
   457  		fields := strings.Fields(line)
   458  		if (len(fields) > 0 && !strings.HasSuffix(fields[0], ":")) || i == len(lines)-1 {
   459  			// new block section
   460  			if len(firstLine) > 0 && len(blocks) > 0 {
   461  				g, err := getBlock(firstLine, blocks)
   462  				if err != nil {
   463  					return &ret, err
   464  				}
   465  				if grouped {
   466  					ret[0].Size += g.Size
   467  					ret[0].Rss += g.Rss
   468  					ret[0].Pss += g.Pss
   469  					ret[0].SharedClean += g.SharedClean
   470  					ret[0].SharedDirty += g.SharedDirty
   471  					ret[0].PrivateClean += g.PrivateClean
   472  					ret[0].PrivateDirty += g.PrivateDirty
   473  					ret[0].Referenced += g.Referenced
   474  					ret[0].Anonymous += g.Anonymous
   475  					ret[0].Swap += g.Swap
   476  				} else {
   477  					ret = append(ret, g)
   478  				}
   479  			}
   480  			// starts new block
   481  			blocks = make([]string, 0, 16)
   482  			firstLine = fields
   483  		} else {
   484  			blocks = append(blocks, line)
   485  		}
   486  	}
   487  
   488  	return &ret, nil
   489  }
   490  
   491  func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
   492  	environPath := common.HostProc(strconv.Itoa(int(p.Pid)), "environ")
   493  
   494  	environContent, err := ioutil.ReadFile(environPath)
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  
   499  	return strings.Split(string(environContent), "\000"), nil
   500  }
   501  
   502  /**
   503  ** Internal functions
   504  **/
   505  
   506  func limitToUint(val string) (uint64, error) {
   507  	if val == "unlimited" {
   508  		return math.MaxUint64, nil
   509  	}
   510  	res, err := strconv.ParseUint(val, 10, 64)
   511  	if err != nil {
   512  		return 0, err
   513  	}
   514  	return res, nil
   515  }
   516  
   517  // Get num_fds from /proc/(pid)/limits
   518  func (p *Process) fillFromLimitsWithContext() ([]RlimitStat, error) {
   519  	pid := p.Pid
   520  	limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits")
   521  	d, err := os.Open(limitsFile)
   522  	if err != nil {
   523  		return nil, err
   524  	}
   525  	defer d.Close()
   526  
   527  	var limitStats []RlimitStat
   528  
   529  	limitsScanner := bufio.NewScanner(d)
   530  	for limitsScanner.Scan() {
   531  		var statItem RlimitStat
   532  
   533  		str := strings.Fields(limitsScanner.Text())
   534  
   535  		// Remove the header line
   536  		if strings.Contains(str[len(str)-1], "Units") {
   537  			continue
   538  		}
   539  
   540  		// Assert that last item is a Hard limit
   541  		statItem.Hard, err = limitToUint(str[len(str)-1])
   542  		if err != nil {
   543  			// On error remove last item and try once again since it can be unit or header line
   544  			str = str[:len(str)-1]
   545  			statItem.Hard, err = limitToUint(str[len(str)-1])
   546  			if err != nil {
   547  				return nil, err
   548  			}
   549  		}
   550  		// Remove last item from string
   551  		str = str[:len(str)-1]
   552  
   553  		// Now last item is a Soft limit
   554  		statItem.Soft, err = limitToUint(str[len(str)-1])
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  		// Remove last item from string
   559  		str = str[:len(str)-1]
   560  
   561  		// The rest is a stats name
   562  		resourceName := strings.Join(str, " ")
   563  		switch resourceName {
   564  		case "Max cpu time":
   565  			statItem.Resource = RLIMIT_CPU
   566  		case "Max file size":
   567  			statItem.Resource = RLIMIT_FSIZE
   568  		case "Max data size":
   569  			statItem.Resource = RLIMIT_DATA
   570  		case "Max stack size":
   571  			statItem.Resource = RLIMIT_STACK
   572  		case "Max core file size":
   573  			statItem.Resource = RLIMIT_CORE
   574  		case "Max resident set":
   575  			statItem.Resource = RLIMIT_RSS
   576  		case "Max processes":
   577  			statItem.Resource = RLIMIT_NPROC
   578  		case "Max open files":
   579  			statItem.Resource = RLIMIT_NOFILE
   580  		case "Max locked memory":
   581  			statItem.Resource = RLIMIT_MEMLOCK
   582  		case "Max address space":
   583  			statItem.Resource = RLIMIT_AS
   584  		case "Max file locks":
   585  			statItem.Resource = RLIMIT_LOCKS
   586  		case "Max pending signals":
   587  			statItem.Resource = RLIMIT_SIGPENDING
   588  		case "Max msgqueue size":
   589  			statItem.Resource = RLIMIT_MSGQUEUE
   590  		case "Max nice priority":
   591  			statItem.Resource = RLIMIT_NICE
   592  		case "Max realtime priority":
   593  			statItem.Resource = RLIMIT_RTPRIO
   594  		case "Max realtime timeout":
   595  			statItem.Resource = RLIMIT_RTTIME
   596  		default:
   597  			continue
   598  		}
   599  
   600  		limitStats = append(limitStats, statItem)
   601  	}
   602  
   603  	if err := limitsScanner.Err(); err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	return limitStats, nil
   608  }
   609  
   610  // Get list of /proc/(pid)/fd files
   611  func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) {
   612  	pid := p.Pid
   613  	statPath := common.HostProc(strconv.Itoa(int(pid)), "fd")
   614  	d, err := os.Open(statPath)
   615  	if err != nil {
   616  		return statPath, []string{}, err
   617  	}
   618  	defer d.Close()
   619  	fnames, err := d.Readdirnames(-1)
   620  	return statPath, fnames, err
   621  }
   622  
   623  // Get num_fds from /proc/(pid)/fd
   624  func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) {
   625  	statPath, fnames, err := p.fillFromfdListWithContext(ctx)
   626  	if err != nil {
   627  		return 0, nil, err
   628  	}
   629  	numFDs := int32(len(fnames))
   630  
   631  	var openfiles []*OpenFilesStat
   632  	for _, fd := range fnames {
   633  		fpath := filepath.Join(statPath, fd)
   634  		filepath, err := os.Readlink(fpath)
   635  		if err != nil {
   636  			continue
   637  		}
   638  		t, err := strconv.ParseUint(fd, 10, 64)
   639  		if err != nil {
   640  			return numFDs, openfiles, err
   641  		}
   642  		o := &OpenFilesStat{
   643  			Path: filepath,
   644  			Fd:   t,
   645  		}
   646  		openfiles = append(openfiles, o)
   647  	}
   648  
   649  	return numFDs, openfiles, nil
   650  }
   651  
   652  // Get cwd from /proc/(pid)/cwd
   653  func (p *Process) fillFromCwdWithContext() (string, error) {
   654  	pid := p.Pid
   655  	cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd")
   656  	cwd, err := os.Readlink(cwdPath)
   657  	if err != nil {
   658  		return "", err
   659  	}
   660  	return string(cwd), nil
   661  }
   662  
   663  // Get exe from /proc/(pid)/exe
   664  func (p *Process) fillFromExeWithContext() (string, error) {
   665  	pid := p.Pid
   666  	exePath := common.HostProc(strconv.Itoa(int(pid)), "exe")
   667  	exe, err := os.Readlink(exePath)
   668  	if err != nil {
   669  		return "", err
   670  	}
   671  	return string(exe), nil
   672  }
   673  
   674  // Get cmdline from /proc/(pid)/cmdline
   675  func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) {
   676  	pid := p.Pid
   677  	cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
   678  	cmdline, err := ioutil.ReadFile(cmdPath)
   679  	if err != nil {
   680  		return "", err
   681  	}
   682  	ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
   683  		return r == '\u0000'
   684  	})
   685  
   686  	return strings.Join(ret, " "), nil
   687  }
   688  
   689  func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) {
   690  	pid := p.Pid
   691  	cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
   692  	cmdline, err := ioutil.ReadFile(cmdPath)
   693  	if err != nil {
   694  		return nil, err
   695  	}
   696  	if len(cmdline) == 0 {
   697  		return nil, nil
   698  	}
   699  
   700  	cmdline = bytes.TrimRight(cmdline, "\x00")
   701  
   702  	parts := bytes.Split(cmdline, []byte{0})
   703  	var strParts []string
   704  	for _, p := range parts {
   705  		strParts = append(strParts, string(p))
   706  	}
   707  
   708  	return strParts, nil
   709  }
   710  
   711  // Get IO status from /proc/(pid)/io
   712  func (p *Process) fillFromIOWithContext() (*IOCountersStat, error) {
   713  	pid := p.Pid
   714  	ioPath := common.HostProc(strconv.Itoa(int(pid)), "io")
   715  	ioline, err := ioutil.ReadFile(ioPath)
   716  	if err != nil {
   717  		return nil, err
   718  	}
   719  	lines := strings.Split(string(ioline), "\n")
   720  	ret := &IOCountersStat{}
   721  
   722  	for _, line := range lines {
   723  		field := strings.Fields(line)
   724  		if len(field) < 2 {
   725  			continue
   726  		}
   727  		t, err := strconv.ParseUint(field[1], 10, 64)
   728  		if err != nil {
   729  			return nil, err
   730  		}
   731  		param := strings.TrimSuffix(field[0], ":")
   732  		switch param {
   733  		case "syscr":
   734  			ret.ReadCount = t
   735  		case "syscw":
   736  			ret.WriteCount = t
   737  		case "read_bytes":
   738  			ret.ReadBytes = t
   739  		case "write_bytes":
   740  			ret.WriteBytes = t
   741  		}
   742  	}
   743  
   744  	return ret, nil
   745  }
   746  
   747  // Get memory info from /proc/(pid)/statm
   748  func (p *Process) fillFromStatmWithContext() (*MemoryInfoStat, *MemoryInfoExStat, error) {
   749  	pid := p.Pid
   750  	memPath := common.HostProc(strconv.Itoa(int(pid)), "statm")
   751  	contents, err := ioutil.ReadFile(memPath)
   752  	if err != nil {
   753  		return nil, nil, err
   754  	}
   755  	fields := strings.Split(string(contents), " ")
   756  
   757  	vms, err := strconv.ParseUint(fields[0], 10, 64)
   758  	if err != nil {
   759  		return nil, nil, err
   760  	}
   761  	rss, err := strconv.ParseUint(fields[1], 10, 64)
   762  	if err != nil {
   763  		return nil, nil, err
   764  	}
   765  	memInfo := &MemoryInfoStat{
   766  		RSS: rss * pageSize,
   767  		VMS: vms * pageSize,
   768  	}
   769  
   770  	shared, err := strconv.ParseUint(fields[2], 10, 64)
   771  	if err != nil {
   772  		return nil, nil, err
   773  	}
   774  	text, err := strconv.ParseUint(fields[3], 10, 64)
   775  	if err != nil {
   776  		return nil, nil, err
   777  	}
   778  	lib, err := strconv.ParseUint(fields[4], 10, 64)
   779  	if err != nil {
   780  		return nil, nil, err
   781  	}
   782  	dirty, err := strconv.ParseUint(fields[5], 10, 64)
   783  	if err != nil {
   784  		return nil, nil, err
   785  	}
   786  
   787  	memInfoEx := &MemoryInfoExStat{
   788  		RSS:    rss * pageSize,
   789  		VMS:    vms * pageSize,
   790  		Shared: shared * pageSize,
   791  		Text:   text * pageSize,
   792  		Lib:    lib * pageSize,
   793  		Dirty:  dirty * pageSize,
   794  	}
   795  
   796  	return memInfo, memInfoEx, nil
   797  }
   798  
   799  // Get name from /proc/(pid)/comm or /proc/(pid)/status
   800  func (p *Process) fillNameWithContext(ctx context.Context) error {
   801  	err := p.fillFromCommWithContext()
   802  	if err == nil && p.name != "" && len(p.name) < 15 {
   803  		return nil
   804  	}
   805  	return p.fillFromStatusWithContext(ctx)
   806  }
   807  
   808  // Get name from /proc/(pid)/comm
   809  func (p *Process) fillFromCommWithContext() error {
   810  	pid := p.Pid
   811  	statPath := common.HostProc(strconv.Itoa(int(pid)), "comm")
   812  	contents, err := ioutil.ReadFile(statPath)
   813  	if err != nil {
   814  		return err
   815  	}
   816  
   817  	p.name = strings.TrimSuffix(string(contents), "\n")
   818  	return nil
   819  }
   820  
   821  // Get various status from /proc/(pid)/status
   822  func (p *Process) fillFromStatus() error {
   823  	return p.fillFromStatusWithContext(context.Background())
   824  }
   825  
   826  func (p *Process) fillFromStatusWithContext(ctx context.Context) error {
   827  	pid := p.Pid
   828  	statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
   829  	contents, err := ioutil.ReadFile(statPath)
   830  	if err != nil {
   831  		return err
   832  	}
   833  	lines := strings.Split(string(contents), "\n")
   834  	p.numCtxSwitches = &NumCtxSwitchesStat{}
   835  	p.memInfo = &MemoryInfoStat{}
   836  	p.sigInfo = &SignalInfoStat{}
   837  	for _, line := range lines {
   838  		tabParts := strings.SplitN(line, "\t", 2)
   839  		if len(tabParts) < 2 {
   840  			continue
   841  		}
   842  		value := tabParts[1]
   843  		switch strings.TrimRight(tabParts[0], ":") {
   844  		case "Name":
   845  			p.name = strings.Trim(value, " \t")
   846  			if len(p.name) >= 15 {
   847  				cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
   848  				if err != nil {
   849  					return err
   850  				}
   851  				if len(cmdlineSlice) > 0 {
   852  					extendedName := filepath.Base(cmdlineSlice[0])
   853  					if strings.HasPrefix(extendedName, p.name) {
   854  						p.name = extendedName
   855  					} else {
   856  						p.name = cmdlineSlice[0]
   857  					}
   858  				}
   859  			}
   860  			// Ensure we have a copy and not reference into slice
   861  			p.name = string([]byte(p.name))
   862  		case "State":
   863  			p.status = convertStatusChar(value[0:1])
   864  			// Ensure we have a copy and not reference into slice
   865  			p.status = string([]byte(p.status))
   866  		case "PPid", "Ppid":
   867  			pval, err := strconv.ParseInt(value, 10, 32)
   868  			if err != nil {
   869  				return err
   870  			}
   871  			p.parent = int32(pval)
   872  		case "Tgid":
   873  			pval, err := strconv.ParseInt(value, 10, 32)
   874  			if err != nil {
   875  				return err
   876  			}
   877  			p.tgid = int32(pval)
   878  		case "Uid":
   879  			p.uids = make([]int32, 0, 4)
   880  			for _, i := range strings.Split(value, "\t") {
   881  				v, err := strconv.ParseInt(i, 10, 32)
   882  				if err != nil {
   883  					return err
   884  				}
   885  				p.uids = append(p.uids, int32(v))
   886  			}
   887  		case "Gid":
   888  			p.gids = make([]int32, 0, 4)
   889  			for _, i := range strings.Split(value, "\t") {
   890  				v, err := strconv.ParseInt(i, 10, 32)
   891  				if err != nil {
   892  					return err
   893  				}
   894  				p.gids = append(p.gids, int32(v))
   895  			}
   896  		case "Groups":
   897  			groups := strings.Fields(value)
   898  			p.groups = make([]int32, 0, len(groups))
   899  			for _, i := range groups {
   900  				v, err := strconv.ParseInt(i, 10, 32)
   901  				if err != nil {
   902  					return err
   903  				}
   904  				p.groups = append(p.groups, int32(v))
   905  			}
   906  		case "Threads":
   907  			v, err := strconv.ParseInt(value, 10, 32)
   908  			if err != nil {
   909  				return err
   910  			}
   911  			p.numThreads = int32(v)
   912  		case "voluntary_ctxt_switches":
   913  			v, err := strconv.ParseInt(value, 10, 64)
   914  			if err != nil {
   915  				return err
   916  			}
   917  			p.numCtxSwitches.Voluntary = v
   918  		case "nonvoluntary_ctxt_switches":
   919  			v, err := strconv.ParseInt(value, 10, 64)
   920  			if err != nil {
   921  				return err
   922  			}
   923  			p.numCtxSwitches.Involuntary = v
   924  		case "VmRSS":
   925  			value := strings.Trim(value, " kB") // remove last "kB"
   926  			v, err := strconv.ParseUint(value, 10, 64)
   927  			if err != nil {
   928  				return err
   929  			}
   930  			p.memInfo.RSS = v * 1024
   931  		case "VmSize":
   932  			value := strings.Trim(value, " kB") // remove last "kB"
   933  			v, err := strconv.ParseUint(value, 10, 64)
   934  			if err != nil {
   935  				return err
   936  			}
   937  			p.memInfo.VMS = v * 1024
   938  		case "VmSwap":
   939  			value := strings.Trim(value, " kB") // remove last "kB"
   940  			v, err := strconv.ParseUint(value, 10, 64)
   941  			if err != nil {
   942  				return err
   943  			}
   944  			p.memInfo.Swap = v * 1024
   945  		case "VmHWM":
   946  			value := strings.Trim(value, " kB") // remove last "kB"
   947  			v, err := strconv.ParseUint(value, 10, 64)
   948  			if err != nil {
   949  				return err
   950  			}
   951  			p.memInfo.HWM = v * 1024
   952  		case "VmData":
   953  			value := strings.Trim(value, " kB") // remove last "kB"
   954  			v, err := strconv.ParseUint(value, 10, 64)
   955  			if err != nil {
   956  				return err
   957  			}
   958  			p.memInfo.Data = v * 1024
   959  		case "VmStk":
   960  			value := strings.Trim(value, " kB") // remove last "kB"
   961  			v, err := strconv.ParseUint(value, 10, 64)
   962  			if err != nil {
   963  				return err
   964  			}
   965  			p.memInfo.Stack = v * 1024
   966  		case "VmLck":
   967  			value := strings.Trim(value, " kB") // remove last "kB"
   968  			v, err := strconv.ParseUint(value, 10, 64)
   969  			if err != nil {
   970  				return err
   971  			}
   972  			p.memInfo.Locked = v * 1024
   973  		case "SigPnd":
   974  			if len(value) > 16 {
   975  				value = value[len(value)-16:]
   976  			}
   977  			v, err := strconv.ParseUint(value, 16, 64)
   978  			if err != nil {
   979  				return err
   980  			}
   981  			p.sigInfo.PendingThread = v
   982  		case "ShdPnd":
   983  			if len(value) > 16 {
   984  				value = value[len(value)-16:]
   985  			}
   986  			v, err := strconv.ParseUint(value, 16, 64)
   987  			if err != nil {
   988  				return err
   989  			}
   990  			p.sigInfo.PendingProcess = v
   991  		case "SigBlk":
   992  			if len(value) > 16 {
   993  				value = value[len(value)-16:]
   994  			}
   995  			v, err := strconv.ParseUint(value, 16, 64)
   996  			if err != nil {
   997  				return err
   998  			}
   999  			p.sigInfo.Blocked = v
  1000  		case "SigIgn":
  1001  			if len(value) > 16 {
  1002  				value = value[len(value)-16:]
  1003  			}
  1004  			v, err := strconv.ParseUint(value, 16, 64)
  1005  			if err != nil {
  1006  				return err
  1007  			}
  1008  			p.sigInfo.Ignored = v
  1009  		case "SigCgt":
  1010  			if len(value) > 16 {
  1011  				value = value[len(value)-16:]
  1012  			}
  1013  			v, err := strconv.ParseUint(value, 16, 64)
  1014  			if err != nil {
  1015  				return err
  1016  			}
  1017  			p.sigInfo.Caught = v
  1018  		}
  1019  
  1020  	}
  1021  	return nil
  1022  }
  1023  
  1024  func (p *Process) fillFromTIDStat(tid int32) (uint64, int32, cpu.TimesStat, int64, uint32, int32, PageFaultsStat, error) {
  1025  	return p.fillFromTIDStatWithContext(context.Background(), tid)
  1026  }
  1027  
  1028  func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, cpu.TimesStat, int64, uint32, int32, PageFaultsStat, error) {
  1029  	pid := p.Pid
  1030  	var statPath string
  1031  
  1032  	if tid == -1 {
  1033  		statPath = common.HostProc(strconv.Itoa(int(pid)), "stat")
  1034  	} else {
  1035  		statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat")
  1036  	}
  1037  
  1038  	contents, err := ioutil.ReadFile(statPath)
  1039  	if err != nil {
  1040  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1041  	}
  1042  	// Indexing from one, as described in `man proc` about the file /proc/[pid]/stat
  1043  	fields := splitProcStat(contents)
  1044  
  1045  	terminal, err := strconv.ParseUint(fields[7], 10, 64)
  1046  	if err != nil {
  1047  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1048  	}
  1049  
  1050  	ppid, err := strconv.ParseInt(fields[4], 10, 32)
  1051  	if err != nil {
  1052  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1053  	}
  1054  	utime, err := strconv.ParseFloat(fields[14], 64)
  1055  	if err != nil {
  1056  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1057  	}
  1058  
  1059  	stime, err := strconv.ParseFloat(fields[15], 64)
  1060  	if err != nil {
  1061  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1062  	}
  1063  
  1064  	// There is no such thing as iotime in stat file.  As an approximation, we
  1065  	// will use delayacct_blkio_ticks (aggregated block I/O delays, as per Linux
  1066  	// docs).  Note: I am assuming at least Linux 2.6.18
  1067  	var iotime float64
  1068  	if len(fields) > 42 {
  1069  		iotime, err = strconv.ParseFloat(fields[42], 64)
  1070  		if err != nil {
  1071  			iotime = 0 // Ancient linux version, most likely
  1072  		}
  1073  	} else {
  1074  		iotime = 0 // e.g. SmartOS containers
  1075  	}
  1076  
  1077  	cpuTimes := cpu.TimesStat{
  1078  		CPU:    "cpu",
  1079  		User:   utime / float64(clockTicks),
  1080  		System: stime / float64(clockTicks),
  1081  		Iowait: iotime / float64(clockTicks),
  1082  	}
  1083  
  1084  	bootTime, _ := common.BootTimeWithContext(ctx)
  1085  	t, err := strconv.ParseUint(fields[22], 10, 64)
  1086  	if err != nil {
  1087  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1088  	}
  1089  	ctime := (t / uint64(clockTicks)) + uint64(bootTime)
  1090  	createTime := int64(ctime * 1000)
  1091  
  1092  	rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
  1093  	if err != nil {
  1094  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1095  	}
  1096  	if rtpriority < 0 {
  1097  		rtpriority = rtpriority*-1 - 1
  1098  	} else {
  1099  		rtpriority = 0
  1100  	}
  1101  
  1102  	//	p.Nice = mustParseInt32(fields[18])
  1103  	// use syscall instead of parse Stat file
  1104  	snice, _ := unix.Getpriority(prioProcess, int(pid))
  1105  	nice := int32(snice) // FIXME: is this true?
  1106  
  1107  	minFault, err := strconv.ParseUint(fields[10], 10, 64)
  1108  	if err != nil {
  1109  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1110  	}
  1111  	cMinFault, err := strconv.ParseUint(fields[11], 10, 64)
  1112  	if err != nil {
  1113  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1114  	}
  1115  	majFault, err := strconv.ParseUint(fields[12], 10, 64)
  1116  	if err != nil {
  1117  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1118  	}
  1119  	cMajFault, err := strconv.ParseUint(fields[13], 10, 64)
  1120  	if err != nil {
  1121  		return 0, 0, cpu.TimesStat{}, 0, 0, 0, PageFaultsStat{}, err
  1122  	}
  1123  
  1124  	faults := PageFaultsStat{
  1125  		MinorFaults:      minFault,
  1126  		MajorFaults:      majFault,
  1127  		ChildMinorFaults: cMinFault,
  1128  		ChildMajorFaults: cMajFault,
  1129  	}
  1130  
  1131  	return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil
  1132  }
  1133  
  1134  func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, cpu.TimesStat, int64, uint32, int32, PageFaultsStat, error) {
  1135  	return p.fillFromTIDStatWithContext(ctx, -1)
  1136  }
  1137  
  1138  func pidsWithContext(ctx context.Context) ([]int32, error) {
  1139  	return readPidsFromDir(common.HostProc())
  1140  }
  1141  
  1142  func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
  1143  	out := []*Process{}
  1144  
  1145  	pids, err := PidsWithContext(ctx)
  1146  	if err != nil {
  1147  		return out, err
  1148  	}
  1149  
  1150  	for _, pid := range pids {
  1151  		p, err := NewProcessWithContext(ctx, pid)
  1152  		if err != nil {
  1153  			continue
  1154  		}
  1155  		out = append(out, p)
  1156  	}
  1157  
  1158  	return out, nil
  1159  }
  1160  
  1161  func readPidsFromDir(path string) ([]int32, error) {
  1162  	var ret []int32
  1163  
  1164  	d, err := os.Open(path)
  1165  	if err != nil {
  1166  		return nil, err
  1167  	}
  1168  	defer d.Close()
  1169  
  1170  	fnames, err := d.Readdirnames(-1)
  1171  	if err != nil {
  1172  		return nil, err
  1173  	}
  1174  	for _, fname := range fnames {
  1175  		pid, err := strconv.ParseInt(fname, 10, 32)
  1176  		if err != nil {
  1177  			// if not numeric name, just skip
  1178  			continue
  1179  		}
  1180  		ret = append(ret, int32(pid))
  1181  	}
  1182  
  1183  	return ret, nil
  1184  }
  1185  
  1186  func splitProcStat(content []byte) []string {
  1187  	nameStart := bytes.IndexByte(content, '(')
  1188  	nameEnd := bytes.LastIndexByte(content, ')')
  1189  	restFields := strings.Fields(string(content[nameEnd+2:])) // +2 skip ') '
  1190  	name := content[nameStart+1 : nameEnd]
  1191  	pid := strings.TrimSpace(string(content[:nameStart]))
  1192  	fields := make([]string, 3, len(restFields)+3)
  1193  	fields[1] = string(pid)
  1194  	fields[2] = string(name)
  1195  	fields = append(fields, restFields...)
  1196  	return fields
  1197  }