process.go 8.1 KB

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