proc.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Copyright 2018 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package procfs
  14. import (
  15. "bytes"
  16. "fmt"
  17. "io"
  18. "os"
  19. "strconv"
  20. "strings"
  21. "github.com/prometheus/procfs/internal/util"
  22. )
  23. // Proc provides information about a running process.
  24. type Proc struct {
  25. // The process ID.
  26. PID int
  27. fs FS
  28. }
  29. // Procs represents a list of Proc structs.
  30. type Procs []Proc
  31. func (p Procs) Len() int { return len(p) }
  32. func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  33. func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
  34. // Self returns a process for the current process read via /proc/self.
  35. func Self() (Proc, error) {
  36. fs, err := NewFS(DefaultMountPoint)
  37. if err != nil {
  38. return Proc{}, err
  39. }
  40. return fs.Self()
  41. }
  42. // NewProc returns a process for the given pid under /proc.
  43. func NewProc(pid int) (Proc, error) {
  44. fs, err := NewFS(DefaultMountPoint)
  45. if err != nil {
  46. return Proc{}, err
  47. }
  48. return fs.Proc(pid)
  49. }
  50. // AllProcs returns a list of all currently available processes under /proc.
  51. func AllProcs() (Procs, error) {
  52. fs, err := NewFS(DefaultMountPoint)
  53. if err != nil {
  54. return Procs{}, err
  55. }
  56. return fs.AllProcs()
  57. }
  58. // Self returns a process for the current process.
  59. func (fs FS) Self() (Proc, error) {
  60. p, err := os.Readlink(fs.proc.Path("self"))
  61. if err != nil {
  62. return Proc{}, err
  63. }
  64. pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
  65. if err != nil {
  66. return Proc{}, err
  67. }
  68. return fs.Proc(pid)
  69. }
  70. // NewProc returns a process for the given pid.
  71. //
  72. // Deprecated: Use fs.Proc() instead.
  73. func (fs FS) NewProc(pid int) (Proc, error) {
  74. return fs.Proc(pid)
  75. }
  76. // Proc returns a process for the given pid.
  77. func (fs FS) Proc(pid int) (Proc, error) {
  78. if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
  79. return Proc{}, err
  80. }
  81. return Proc{PID: pid, fs: fs}, nil
  82. }
  83. // AllProcs returns a list of all currently available processes.
  84. func (fs FS) AllProcs() (Procs, error) {
  85. d, err := os.Open(fs.proc.Path())
  86. if err != nil {
  87. return Procs{}, err
  88. }
  89. defer d.Close()
  90. names, err := d.Readdirnames(-1)
  91. if err != nil {
  92. return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err)
  93. }
  94. p := Procs{}
  95. for _, n := range names {
  96. pid, err := strconv.ParseInt(n, 10, 64)
  97. if err != nil {
  98. continue
  99. }
  100. p = append(p, Proc{PID: int(pid), fs: fs})
  101. }
  102. return p, nil
  103. }
  104. // CmdLine returns the command line of a process.
  105. func (p Proc) CmdLine() ([]string, error) {
  106. data, err := util.ReadFileNoStat(p.path("cmdline"))
  107. if err != nil {
  108. return nil, err
  109. }
  110. if len(data) < 1 {
  111. return []string{}, nil
  112. }
  113. return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
  114. }
  115. // Wchan returns the wchan (wait channel) of a process.
  116. func (p Proc) Wchan() (string, error) {
  117. f, err := os.Open(p.path("wchan"))
  118. if err != nil {
  119. return "", err
  120. }
  121. defer f.Close()
  122. data, err := io.ReadAll(f)
  123. if err != nil {
  124. return "", err
  125. }
  126. wchan := string(data)
  127. if wchan == "" || wchan == "0" {
  128. return "", nil
  129. }
  130. return wchan, nil
  131. }
  132. // Comm returns the command name of a process.
  133. func (p Proc) Comm() (string, error) {
  134. data, err := util.ReadFileNoStat(p.path("comm"))
  135. if err != nil {
  136. return "", err
  137. }
  138. return strings.TrimSpace(string(data)), nil
  139. }
  140. // Executable returns the absolute path of the executable command of a process.
  141. func (p Proc) Executable() (string, error) {
  142. exe, err := os.Readlink(p.path("exe"))
  143. if os.IsNotExist(err) {
  144. return "", nil
  145. }
  146. return exe, err
  147. }
  148. // Cwd returns the absolute path to the current working directory of the process.
  149. func (p Proc) Cwd() (string, error) {
  150. wd, err := os.Readlink(p.path("cwd"))
  151. if os.IsNotExist(err) {
  152. return "", nil
  153. }
  154. return wd, err
  155. }
  156. // RootDir returns the absolute path to the process's root directory (as set by chroot).
  157. func (p Proc) RootDir() (string, error) {
  158. rdir, err := os.Readlink(p.path("root"))
  159. if os.IsNotExist(err) {
  160. return "", nil
  161. }
  162. return rdir, err
  163. }
  164. // FileDescriptors returns the currently open file descriptors of a process.
  165. func (p Proc) FileDescriptors() ([]uintptr, error) {
  166. names, err := p.fileDescriptors()
  167. if err != nil {
  168. return nil, err
  169. }
  170. fds := make([]uintptr, len(names))
  171. for i, n := range names {
  172. fd, err := strconv.ParseInt(n, 10, 32)
  173. if err != nil {
  174. return nil, fmt.Errorf("could not parse fd %q: %w", n, err)
  175. }
  176. fds[i] = uintptr(fd)
  177. }
  178. return fds, nil
  179. }
  180. // FileDescriptorTargets returns the targets of all file descriptors of a process.
  181. // If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
  182. func (p Proc) FileDescriptorTargets() ([]string, error) {
  183. names, err := p.fileDescriptors()
  184. if err != nil {
  185. return nil, err
  186. }
  187. targets := make([]string, len(names))
  188. for i, name := range names {
  189. target, err := os.Readlink(p.path("fd", name))
  190. if err == nil {
  191. targets[i] = target
  192. }
  193. }
  194. return targets, nil
  195. }
  196. // FileDescriptorsLen returns the number of currently open file descriptors of
  197. // a process.
  198. func (p Proc) FileDescriptorsLen() (int, error) {
  199. // Use fast path if available (Linux v6.2): https://github.com/torvalds/linux/commit/f1f1f2569901
  200. if p.fs.real {
  201. stat, err := os.Stat(p.path("fd"))
  202. if err != nil {
  203. return 0, err
  204. }
  205. size := stat.Size()
  206. if size > 0 {
  207. return int(size), nil
  208. }
  209. }
  210. fds, err := p.fileDescriptors()
  211. if err != nil {
  212. return 0, err
  213. }
  214. return len(fds), nil
  215. }
  216. // MountStats retrieves statistics and configuration for mount points in a
  217. // process's namespace.
  218. func (p Proc) MountStats() ([]*Mount, error) {
  219. f, err := os.Open(p.path("mountstats"))
  220. if err != nil {
  221. return nil, err
  222. }
  223. defer f.Close()
  224. return parseMountStats(f)
  225. }
  226. // MountInfo retrieves mount information for mount points in a
  227. // process's namespace.
  228. // It supplies information missing in `/proc/self/mounts` and
  229. // fixes various other problems with that file too.
  230. func (p Proc) MountInfo() ([]*MountInfo, error) {
  231. data, err := util.ReadFileNoStat(p.path("mountinfo"))
  232. if err != nil {
  233. return nil, err
  234. }
  235. return parseMountInfo(data)
  236. }
  237. func (p Proc) fileDescriptors() ([]string, error) {
  238. d, err := os.Open(p.path("fd"))
  239. if err != nil {
  240. return nil, err
  241. }
  242. defer d.Close()
  243. names, err := d.Readdirnames(-1)
  244. if err != nil {
  245. return nil, fmt.Errorf("could not read %q: %w", d.Name(), err)
  246. }
  247. return names, nil
  248. }
  249. func (p Proc) path(pa ...string) string {
  250. return p.fs.proc.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
  251. }
  252. // FileDescriptorsInfo retrieves information about all file descriptors of
  253. // the process.
  254. func (p Proc) FileDescriptorsInfo() (ProcFDInfos, error) {
  255. names, err := p.fileDescriptors()
  256. if err != nil {
  257. return nil, err
  258. }
  259. var fdinfos ProcFDInfos
  260. for _, n := range names {
  261. fdinfo, err := p.FDInfo(n)
  262. if err != nil {
  263. continue
  264. }
  265. fdinfos = append(fdinfos, *fdinfo)
  266. }
  267. return fdinfos, nil
  268. }
  269. // Schedstat returns task scheduling information for the process.
  270. func (p Proc) Schedstat() (ProcSchedstat, error) {
  271. contents, err := os.ReadFile(p.path("schedstat"))
  272. if err != nil {
  273. return ProcSchedstat{}, err
  274. }
  275. return parseProcSchedstat(string(contents))
  276. }