process_linux.go 29 KB

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