...

Source file src/github.com/shirou/gopsutil/process/process_linux.go

Documentation: github.com/shirou/gopsutil/process

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

View as plain text