process_linux.go 29 KB

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