proc.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package system
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "path/filepath"
  6. "strconv"
  7. "strings"
  8. )
  9. // State is the status of a process.
  10. type State rune
  11. const ( // Only values for Linux 3.14 and later are listed here
  12. Dead State = 'X'
  13. DiskSleep State = 'D'
  14. Running State = 'R'
  15. Sleeping State = 'S'
  16. Stopped State = 'T'
  17. TracingStop State = 't'
  18. Zombie State = 'Z'
  19. )
  20. // String forms of the state from proc(5)'s documentation for
  21. // /proc/[pid]/status' "State" field.
  22. func (s State) String() string {
  23. switch s {
  24. case Dead:
  25. return "dead"
  26. case DiskSleep:
  27. return "disk sleep"
  28. case Running:
  29. return "running"
  30. case Sleeping:
  31. return "sleeping"
  32. case Stopped:
  33. return "stopped"
  34. case TracingStop:
  35. return "tracing stop"
  36. case Zombie:
  37. return "zombie"
  38. default:
  39. return fmt.Sprintf("unknown (%c)", s)
  40. }
  41. }
  42. // Stat_t represents the information from /proc/[pid]/stat, as
  43. // described in proc(5) with names based on the /proc/[pid]/status
  44. // fields.
  45. type Stat_t struct {
  46. // PID is the process ID.
  47. PID uint
  48. // Name is the command run by the process.
  49. Name string
  50. // State is the state of the process.
  51. State State
  52. // StartTime is the number of clock ticks after system boot (since
  53. // Linux 2.6).
  54. StartTime uint64
  55. }
  56. // Stat returns a Stat_t instance for the specified process.
  57. func Stat(pid int) (stat Stat_t, err error) {
  58. bytes, err := ioutil.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat"))
  59. if err != nil {
  60. return stat, err
  61. }
  62. return parseStat(string(bytes))
  63. }
  64. // GetProcessStartTime is deprecated. Use Stat(pid) and
  65. // Stat_t.StartTime instead.
  66. func GetProcessStartTime(pid int) (string, error) {
  67. stat, err := Stat(pid)
  68. if err != nil {
  69. return "", err
  70. }
  71. return fmt.Sprintf("%d", stat.StartTime), nil
  72. }
  73. func parseStat(data string) (stat Stat_t, err error) {
  74. // From proc(5), field 2 could contain space and is inside `(` and `)`.
  75. // The following is an example:
  76. // 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
  77. i := strings.LastIndex(data, ")")
  78. if i <= 2 || i >= len(data)-1 {
  79. return stat, fmt.Errorf("invalid stat data: %q", data)
  80. }
  81. parts := strings.SplitN(data[:i], "(", 2)
  82. if len(parts) != 2 {
  83. return stat, fmt.Errorf("invalid stat data: %q", data)
  84. }
  85. stat.Name = parts[1]
  86. _, err = fmt.Sscanf(parts[0], "%d", &stat.PID)
  87. if err != nil {
  88. return stat, err
  89. }
  90. // parts indexes should be offset by 3 from the field number given
  91. // proc(5), because parts is zero-indexed and we've removed fields
  92. // one (PID) and two (Name) in the paren-split.
  93. parts = strings.Split(data[i+2:], " ")
  94. var state int
  95. fmt.Sscanf(parts[3-3], "%c", &state)
  96. stat.State = State(state)
  97. fmt.Sscanf(parts[22-3], "%d", &stat.StartTime)
  98. return stat, nil
  99. }