sigar_linux_common.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. // Copyright (c) 2012 VMware, Inc.
  2. // +build freebsd linux
  3. package gosigar
  4. import (
  5. "bufio"
  6. "bytes"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "os"
  11. "os/user"
  12. "path/filepath"
  13. "strconv"
  14. "strings"
  15. "syscall"
  16. )
  17. var system struct {
  18. ticks uint64
  19. btime uint64
  20. }
  21. var Procd string
  22. func getLinuxBootTime() {
  23. // grab system boot time
  24. readFile(Procd+"/stat", func(line string) bool {
  25. if strings.HasPrefix(line, "btime") {
  26. system.btime, _ = strtoull(line[6:])
  27. return false // stop reading
  28. }
  29. return true
  30. })
  31. }
  32. func (self *LoadAverage) Get() error {
  33. line, err := ioutil.ReadFile(Procd + "/loadavg")
  34. if err != nil {
  35. return nil
  36. }
  37. fields := strings.Fields(string(line))
  38. self.One, _ = strconv.ParseFloat(fields[0], 64)
  39. self.Five, _ = strconv.ParseFloat(fields[1], 64)
  40. self.Fifteen, _ = strconv.ParseFloat(fields[2], 64)
  41. return nil
  42. }
  43. func (self *Mem) Get() error {
  44. table, err := parseMeminfo()
  45. if err != nil {
  46. return err
  47. }
  48. self.Total, _ = table["MemTotal"]
  49. self.Free, _ = table["MemFree"]
  50. buffers, _ := table["Buffers"]
  51. cached, _ := table["Cached"]
  52. if available, ok := table["MemAvailable"]; ok {
  53. // MemAvailable is in /proc/meminfo (kernel 3.14+)
  54. self.ActualFree = available
  55. } else {
  56. self.ActualFree = self.Free + buffers + cached
  57. }
  58. self.Used = self.Total - self.Free
  59. self.ActualUsed = self.Total - self.ActualFree
  60. return nil
  61. }
  62. func (self *Swap) Get() error {
  63. table, err := parseMeminfo()
  64. if err != nil {
  65. return err
  66. }
  67. self.Total, _ = table["SwapTotal"]
  68. self.Free, _ = table["SwapFree"]
  69. self.Used = self.Total - self.Free
  70. return nil
  71. }
  72. func (self *Cpu) Get() error {
  73. return readFile(Procd+"/stat", func(line string) bool {
  74. if len(line) > 4 && line[0:4] == "cpu " {
  75. parseCpuStat(self, line)
  76. return false
  77. }
  78. return true
  79. })
  80. }
  81. func (self *CpuList) Get() error {
  82. capacity := len(self.List)
  83. if capacity == 0 {
  84. capacity = 4
  85. }
  86. list := make([]Cpu, 0, capacity)
  87. err := readFile(Procd+"/stat", func(line string) bool {
  88. if len(line) > 3 && line[0:3] == "cpu" && line[3] != ' ' {
  89. cpu := Cpu{}
  90. parseCpuStat(&cpu, line)
  91. list = append(list, cpu)
  92. }
  93. return true
  94. })
  95. self.List = list
  96. return err
  97. }
  98. func (self *FileSystemList) Get() error {
  99. capacity := len(self.List)
  100. if capacity == 0 {
  101. capacity = 10
  102. }
  103. fslist := make([]FileSystem, 0, capacity)
  104. err := readFile(getMountTableFileName(), func(line string) bool {
  105. fields := strings.Fields(line)
  106. fs := FileSystem{}
  107. fs.DevName = fields[0]
  108. fs.DirName = fields[1]
  109. fs.SysTypeName = fields[2]
  110. fs.Options = fields[3]
  111. fslist = append(fslist, fs)
  112. return true
  113. })
  114. self.List = fslist
  115. return err
  116. }
  117. func (self *ProcList) Get() error {
  118. dir, err := os.Open(Procd)
  119. if err != nil {
  120. return err
  121. }
  122. defer dir.Close()
  123. const readAllDirnames = -1 // see os.File.Readdirnames doc
  124. names, err := dir.Readdirnames(readAllDirnames)
  125. if err != nil {
  126. return err
  127. }
  128. capacity := len(names)
  129. list := make([]int, 0, capacity)
  130. for _, name := range names {
  131. if name[0] < '0' || name[0] > '9' {
  132. continue
  133. }
  134. pid, err := strconv.Atoi(name)
  135. if err == nil {
  136. list = append(list, pid)
  137. }
  138. }
  139. self.List = list
  140. return nil
  141. }
  142. func (self *ProcState) Get(pid int) error {
  143. data, err := readProcFile(pid, "stat")
  144. if err != nil {
  145. return err
  146. }
  147. // Extract the comm value with is surrounded by parentheses.
  148. lIdx := bytes.Index(data, []byte("("))
  149. rIdx := bytes.LastIndex(data, []byte(")"))
  150. if lIdx < 0 || rIdx < 0 || lIdx >= rIdx || rIdx+2 >= len(data) {
  151. return fmt.Errorf("failed to extract comm for pid %d from '%v'", pid, string(data))
  152. }
  153. self.Name = string(data[lIdx+1 : rIdx])
  154. // Extract the rest of the fields that we are interested in.
  155. fields := bytes.Fields(data[rIdx+2:])
  156. if len(fields) <= 36 {
  157. return fmt.Errorf("expected more stat fields for pid %d from '%v'", pid, string(data))
  158. }
  159. interests := bytes.Join([][]byte{
  160. fields[0], // state
  161. fields[1], // ppid
  162. fields[2], // pgrp
  163. fields[4], // tty_nr
  164. fields[15], // priority
  165. fields[16], // nice
  166. fields[36], // processor (last processor executed on)
  167. }, []byte(" "))
  168. var state string
  169. _, err = fmt.Fscan(bytes.NewBuffer(interests),
  170. &state,
  171. &self.Ppid,
  172. &self.Pgid,
  173. &self.Tty,
  174. &self.Priority,
  175. &self.Nice,
  176. &self.Processor,
  177. )
  178. if err != nil {
  179. return fmt.Errorf("failed to parse stat fields for pid %d from '%v': %v", pid, string(data), err)
  180. }
  181. self.State = RunState(state[0])
  182. // Read /proc/[pid]/status to get the uid, then lookup uid to get username.
  183. status, err := getProcStatus(pid)
  184. if err != nil {
  185. return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
  186. }
  187. uids, err := getUIDs(status)
  188. if err != nil {
  189. return fmt.Errorf("failed to read process status for pid %d: %v", pid, err)
  190. }
  191. user, err := user.LookupId(uids[0])
  192. if err == nil {
  193. self.Username = user.Username
  194. } else {
  195. self.Username = uids[0]
  196. }
  197. return nil
  198. }
  199. func (self *ProcMem) Get(pid int) error {
  200. contents, err := readProcFile(pid, "statm")
  201. if err != nil {
  202. return err
  203. }
  204. fields := strings.Fields(string(contents))
  205. size, _ := strtoull(fields[0])
  206. self.Size = size << 12
  207. rss, _ := strtoull(fields[1])
  208. self.Resident = rss << 12
  209. share, _ := strtoull(fields[2])
  210. self.Share = share << 12
  211. contents, err = readProcFile(pid, "stat")
  212. if err != nil {
  213. return err
  214. }
  215. fields = strings.Fields(string(contents))
  216. self.MinorFaults, _ = strtoull(fields[10])
  217. self.MajorFaults, _ = strtoull(fields[12])
  218. self.PageFaults = self.MinorFaults + self.MajorFaults
  219. return nil
  220. }
  221. func (self *ProcTime) Get(pid int) error {
  222. contents, err := readProcFile(pid, "stat")
  223. if err != nil {
  224. return err
  225. }
  226. fields := strings.Fields(string(contents))
  227. user, _ := strtoull(fields[13])
  228. sys, _ := strtoull(fields[14])
  229. // convert to millis
  230. self.User = user * (1000 / system.ticks)
  231. self.Sys = sys * (1000 / system.ticks)
  232. self.Total = self.User + self.Sys
  233. // convert to millis
  234. self.StartTime, _ = strtoull(fields[21])
  235. self.StartTime /= system.ticks
  236. self.StartTime += system.btime
  237. self.StartTime *= 1000
  238. return nil
  239. }
  240. func (self *ProcArgs) Get(pid int) error {
  241. contents, err := readProcFile(pid, "cmdline")
  242. if err != nil {
  243. return err
  244. }
  245. bbuf := bytes.NewBuffer(contents)
  246. var args []string
  247. for {
  248. arg, err := bbuf.ReadBytes(0)
  249. if err == io.EOF {
  250. break
  251. }
  252. args = append(args, string(chop(arg)))
  253. }
  254. self.List = args
  255. return nil
  256. }
  257. func (self *ProcEnv) Get(pid int) error {
  258. contents, err := readProcFile(pid, "environ")
  259. if err != nil {
  260. return err
  261. }
  262. if self.Vars == nil {
  263. self.Vars = map[string]string{}
  264. }
  265. pairs := bytes.Split(contents, []byte{0})
  266. for _, kv := range pairs {
  267. parts := bytes.SplitN(kv, []byte{'='}, 2)
  268. if len(parts) != 2 {
  269. continue
  270. }
  271. key := string(bytes.TrimSpace(parts[0]))
  272. if key == "" {
  273. continue
  274. }
  275. self.Vars[key] = string(bytes.TrimSpace(parts[1]))
  276. }
  277. return nil
  278. }
  279. func (self *ProcExe) Get(pid int) error {
  280. fields := map[string]*string{
  281. "exe": &self.Name,
  282. "cwd": &self.Cwd,
  283. "root": &self.Root,
  284. }
  285. for name, field := range fields {
  286. val, err := os.Readlink(procFileName(pid, name))
  287. if err != nil {
  288. return err
  289. }
  290. *field = val
  291. }
  292. return nil
  293. }
  294. func parseMeminfo() (map[string]uint64, error) {
  295. table := map[string]uint64{}
  296. err := readFile(Procd+"/meminfo", func(line string) bool {
  297. fields := strings.Split(line, ":")
  298. if len(fields) != 2 {
  299. return true // skip on errors
  300. }
  301. valueUnit := strings.Fields(fields[1])
  302. value, err := strtoull(valueUnit[0])
  303. if err != nil {
  304. return true // skip on errors
  305. }
  306. if len(valueUnit) > 1 && valueUnit[1] == "kB" {
  307. value *= 1024
  308. }
  309. table[fields[0]] = value
  310. return true
  311. })
  312. return table, err
  313. }
  314. func readFile(file string, handler func(string) bool) error {
  315. contents, err := ioutil.ReadFile(file)
  316. if err != nil {
  317. return err
  318. }
  319. reader := bufio.NewReader(bytes.NewBuffer(contents))
  320. for {
  321. line, _, err := reader.ReadLine()
  322. if err == io.EOF {
  323. break
  324. }
  325. if !handler(string(line)) {
  326. break
  327. }
  328. }
  329. return nil
  330. }
  331. func strtoull(val string) (uint64, error) {
  332. return strconv.ParseUint(val, 10, 64)
  333. }
  334. func procFileName(pid int, name string) string {
  335. return Procd + "/" + strconv.Itoa(pid) + "/" + name
  336. }
  337. func readProcFile(pid int, name string) (content []byte, err error) {
  338. path := procFileName(pid, name)
  339. // Panics have been reported when reading proc files, let's recover and
  340. // report the path if this happens
  341. // See https://github.com/elastic/beats/issues/6692
  342. defer func() {
  343. if r := recover(); r != nil {
  344. content = nil
  345. err = fmt.Errorf("recovered panic when reading proc file '%s': %v", path, r)
  346. }
  347. }()
  348. contents, err := ioutil.ReadFile(path)
  349. if err != nil {
  350. if perr, ok := err.(*os.PathError); ok {
  351. if perr.Err == syscall.ENOENT {
  352. return nil, syscall.ESRCH
  353. }
  354. }
  355. }
  356. return contents, err
  357. }
  358. // getProcStatus reads /proc/[pid]/status which contains process status
  359. // information in human readable form.
  360. func getProcStatus(pid int) (map[string]string, error) {
  361. status := make(map[string]string, 42)
  362. path := filepath.Join(Procd, strconv.Itoa(pid), "status")
  363. err := readFile(path, func(line string) bool {
  364. fields := strings.SplitN(line, ":", 2)
  365. if len(fields) == 2 {
  366. status[fields[0]] = strings.TrimSpace(fields[1])
  367. }
  368. return true
  369. })
  370. return status, err
  371. }
  372. // getUIDs reads the "Uid" value from status and splits it into four values --
  373. // real, effective, saved set, and file system UIDs.
  374. func getUIDs(status map[string]string) ([]string, error) {
  375. uidLine, ok := status["Uid"]
  376. if !ok {
  377. return nil, fmt.Errorf("Uid not found in proc status")
  378. }
  379. uidStrs := strings.Fields(uidLine)
  380. if len(uidStrs) != 4 {
  381. return nil, fmt.Errorf("Uid line ('%s') did not contain four values", uidLine)
  382. }
  383. return uidStrs, nil
  384. }