github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/process/process.go (about)

     1  package process
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"runtime"
     8  	"sort"
     9  	"time"
    10  
    11  	"github.com/gofiber/fiber/v2/internal/gopsutil/common"
    12  	"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
    13  	"github.com/gofiber/fiber/v2/internal/gopsutil/mem"
    14  )
    15  
    16  var (
    17  	invoke                 common.Invoker = common.Invoke{}
    18  	ErrorNoChildren                       = errors.New("process does not have children")
    19  	ErrorProcessNotRunning                = errors.New("process does not exist")
    20  )
    21  
    22  type Process struct {
    23  	Pid            int32 `json:"pid"`
    24  	name           string
    25  	status         string
    26  	parent         int32
    27  	numCtxSwitches *NumCtxSwitchesStat
    28  	uids           []int32
    29  	gids           []int32
    30  	groups         []int32
    31  	numThreads     int32
    32  	memInfo        *MemoryInfoStat
    33  	sigInfo        *SignalInfoStat
    34  	createTime     int64
    35  
    36  	lastCPUTimes *cpu.TimesStat
    37  	lastCPUTime  time.Time
    38  
    39  	tgid int32
    40  }
    41  
    42  type OpenFilesStat struct {
    43  	Path string `json:"path"`
    44  	Fd   uint64 `json:"fd"`
    45  }
    46  
    47  type MemoryInfoStat struct {
    48  	RSS    uint64 `json:"rss"`    // bytes
    49  	VMS    uint64 `json:"vms"`    // bytes
    50  	HWM    uint64 `json:"hwm"`    // bytes
    51  	Data   uint64 `json:"data"`   // bytes
    52  	Stack  uint64 `json:"stack"`  // bytes
    53  	Locked uint64 `json:"locked"` // bytes
    54  	Swap   uint64 `json:"swap"`   // bytes
    55  }
    56  
    57  type SignalInfoStat struct {
    58  	PendingProcess uint64 `json:"pending_process"`
    59  	PendingThread  uint64 `json:"pending_thread"`
    60  	Blocked        uint64 `json:"blocked"`
    61  	Ignored        uint64 `json:"ignored"`
    62  	Caught         uint64 `json:"caught"`
    63  }
    64  
    65  type RlimitStat struct {
    66  	Resource int32  `json:"resource"`
    67  	Soft     int32  `json:"soft"` //TODO too small. needs to be uint64
    68  	Hard     int32  `json:"hard"` //TODO too small. needs to be uint64
    69  	Used     uint64 `json:"used"`
    70  }
    71  
    72  type IOCountersStat struct {
    73  	ReadCount  uint64 `json:"readCount"`
    74  	WriteCount uint64 `json:"writeCount"`
    75  	ReadBytes  uint64 `json:"readBytes"`
    76  	WriteBytes uint64 `json:"writeBytes"`
    77  }
    78  
    79  type NumCtxSwitchesStat struct {
    80  	Voluntary   int64 `json:"voluntary"`
    81  	Involuntary int64 `json:"involuntary"`
    82  }
    83  
    84  type PageFaultsStat struct {
    85  	MinorFaults      uint64 `json:"minorFaults"`
    86  	MajorFaults      uint64 `json:"majorFaults"`
    87  	ChildMinorFaults uint64 `json:"childMinorFaults"`
    88  	ChildMajorFaults uint64 `json:"childMajorFaults"`
    89  }
    90  
    91  // Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h
    92  // from libc6-dev package in Ubuntu 16.10
    93  const (
    94  	RLIMIT_CPU        int32 = 0
    95  	RLIMIT_FSIZE      int32 = 1
    96  	RLIMIT_DATA       int32 = 2
    97  	RLIMIT_STACK      int32 = 3
    98  	RLIMIT_CORE       int32 = 4
    99  	RLIMIT_RSS        int32 = 5
   100  	RLIMIT_NPROC      int32 = 6
   101  	RLIMIT_NOFILE     int32 = 7
   102  	RLIMIT_MEMLOCK    int32 = 8
   103  	RLIMIT_AS         int32 = 9
   104  	RLIMIT_LOCKS      int32 = 10
   105  	RLIMIT_SIGPENDING int32 = 11
   106  	RLIMIT_MSGQUEUE   int32 = 12
   107  	RLIMIT_NICE       int32 = 13
   108  	RLIMIT_RTPRIO     int32 = 14
   109  	RLIMIT_RTTIME     int32 = 15
   110  )
   111  
   112  func (p Process) String() string {
   113  	s, _ := json.Marshal(p)
   114  	return string(s)
   115  }
   116  
   117  func (o OpenFilesStat) String() string {
   118  	s, _ := json.Marshal(o)
   119  	return string(s)
   120  }
   121  
   122  func (m MemoryInfoStat) String() string {
   123  	s, _ := json.Marshal(m)
   124  	return string(s)
   125  }
   126  
   127  func (r RlimitStat) String() string {
   128  	s, _ := json.Marshal(r)
   129  	return string(s)
   130  }
   131  
   132  func (i IOCountersStat) String() string {
   133  	s, _ := json.Marshal(i)
   134  	return string(s)
   135  }
   136  
   137  func (p NumCtxSwitchesStat) String() string {
   138  	s, _ := json.Marshal(p)
   139  	return string(s)
   140  }
   141  
   142  // Pids returns a slice of process ID list which are running now.
   143  func Pids() ([]int32, error) {
   144  	return PidsWithContext(context.Background())
   145  }
   146  
   147  func PidsWithContext(ctx context.Context) ([]int32, error) {
   148  	pids, err := pidsWithContext(ctx)
   149  	sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] })
   150  	return pids, err
   151  }
   152  
   153  // NewProcess creates a new Process instance, it only stores the pid and
   154  // checks that the process exists. Other method on Process can be used
   155  // to get more information about the process. An error will be returned
   156  // if the process does not exist.
   157  func NewProcess(pid int32) (*Process, error) {
   158  	p := &Process{Pid: pid}
   159  
   160  	exists, err := PidExists(pid)
   161  	if err != nil {
   162  		return p, err
   163  	}
   164  	if !exists {
   165  		return p, ErrorProcessNotRunning
   166  	}
   167  	_, err = p.CreateTime()
   168  	return p, err
   169  }
   170  
   171  func PidExists(pid int32) (bool, error) {
   172  	return PidExistsWithContext(context.Background(), pid)
   173  }
   174  
   175  // Background returns true if the process is in background, false otherwise.
   176  func (p *Process) Background() (bool, error) {
   177  	return p.BackgroundWithContext(context.Background())
   178  }
   179  
   180  func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) {
   181  	fg, err := p.ForegroundWithContext(ctx)
   182  	if err != nil {
   183  		return false, err
   184  	}
   185  	return !fg, err
   186  }
   187  
   188  // If interval is 0, return difference from last call(non-blocking).
   189  // If interval > 0, wait interval sec and return diffrence between start and end.
   190  func (p *Process) Percent(interval time.Duration) (float64, error) {
   191  	return p.PercentWithContext(context.Background(), interval)
   192  }
   193  
   194  func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) {
   195  	cpuTimes, err := p.Times()
   196  	if err != nil {
   197  		return 0, err
   198  	}
   199  	now := time.Now()
   200  
   201  	if interval > 0 {
   202  		p.lastCPUTimes = cpuTimes
   203  		p.lastCPUTime = now
   204  		if err := common.Sleep(ctx, interval); err != nil {
   205  			return 0, err
   206  		}
   207  		cpuTimes, err = p.Times()
   208  		now = time.Now()
   209  		if err != nil {
   210  			return 0, err
   211  		}
   212  	} else {
   213  		if p.lastCPUTimes == nil {
   214  			// invoked first time
   215  			p.lastCPUTimes = cpuTimes
   216  			p.lastCPUTime = now
   217  			return 0, nil
   218  		}
   219  	}
   220  
   221  	numcpu := runtime.NumCPU()
   222  	delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu)
   223  	ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu)
   224  	p.lastCPUTimes = cpuTimes
   225  	p.lastCPUTime = now
   226  	return ret, nil
   227  }
   228  
   229  // IsRunning returns whether the process is still running or not.
   230  func (p *Process) IsRunning() (bool, error) {
   231  	return p.IsRunningWithContext(context.Background())
   232  }
   233  
   234  func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
   235  	createTime, err := p.CreateTimeWithContext(ctx)
   236  	if err != nil {
   237  		return false, err
   238  	}
   239  	p2, err := NewProcess(p.Pid)
   240  	if err == ErrorProcessNotRunning {
   241  		return false, nil
   242  	}
   243  	createTime2, err := p2.CreateTimeWithContext(ctx)
   244  	if err != nil {
   245  		return false, err
   246  	}
   247  	return createTime == createTime2, nil
   248  }
   249  
   250  // CreateTime returns created time of the process in milliseconds since the epoch, in UTC.
   251  func (p *Process) CreateTime() (int64, error) {
   252  	return p.CreateTimeWithContext(context.Background())
   253  }
   254  
   255  func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
   256  	if p.createTime != 0 {
   257  		return p.createTime, nil
   258  	}
   259  	createTime, err := p.createTimeWithContext(ctx)
   260  	p.createTime = createTime
   261  	return p.createTime, err
   262  }
   263  
   264  func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 {
   265  	if delta == 0 {
   266  		return 0
   267  	}
   268  	delta_proc := t2.Total() - t1.Total()
   269  	overall_percent := ((delta_proc / delta) * 100) * float64(numcpu)
   270  	return overall_percent
   271  }
   272  
   273  // MemoryPercent returns how many percent of the total RAM this process uses
   274  func (p *Process) MemoryPercent() (float32, error) {
   275  	return p.MemoryPercentWithContext(context.Background())
   276  }
   277  
   278  func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) {
   279  	machineMemory, err := mem.VirtualMemory()
   280  	if err != nil {
   281  		return 0, err
   282  	}
   283  	total := machineMemory.Total
   284  
   285  	processMemory, err := p.MemoryInfo()
   286  	if err != nil {
   287  		return 0, err
   288  	}
   289  	used := processMemory.RSS
   290  
   291  	return (100 * float32(used) / float32(total)), nil
   292  }
   293  
   294  // CPU_Percent returns how many percent of the CPU time this process uses
   295  func (p *Process) CPUPercent() (float64, error) {
   296  	return p.CPUPercentWithContext(context.Background())
   297  }
   298  
   299  func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) {
   300  	crt_time, err := p.CreateTime()
   301  	if err != nil {
   302  		return 0, err
   303  	}
   304  
   305  	cput, err := p.Times()
   306  	if err != nil {
   307  		return 0, err
   308  	}
   309  
   310  	created := time.Unix(0, crt_time*int64(time.Millisecond))
   311  	totalTime := time.Since(created).Seconds()
   312  	if totalTime <= 0 {
   313  		return 0, nil
   314  	}
   315  
   316  	return 100 * cput.Total() / totalTime, nil
   317  }
   318  
   319  // Groups returns all group IDs(include supplementary groups) of the process as a slice of the int
   320  func (p *Process) Groups() ([]int32, error) {
   321  	return p.GroupsWithContext(context.Background())
   322  }