You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
4.6 KiB

  1. // Copyright 2019 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. //go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !js
  14. // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
  15. // +build !js
  16. package procfs
  17. import (
  18. "bufio"
  19. "fmt"
  20. "os"
  21. "strconv"
  22. "strings"
  23. "golang.org/x/sys/unix"
  24. )
  25. // ProcMapPermissions contains permission settings read from `/proc/[pid]/maps`.
  26. type ProcMapPermissions struct {
  27. // mapping has the [R]ead flag set
  28. Read bool
  29. // mapping has the [W]rite flag set
  30. Write bool
  31. // mapping has the [X]ecutable flag set
  32. Execute bool
  33. // mapping has the [S]hared flag set
  34. Shared bool
  35. // mapping is marked as [P]rivate (copy on write)
  36. Private bool
  37. }
  38. // ProcMap contains the process memory-mappings of the process
  39. // read from `/proc/[pid]/maps`.
  40. type ProcMap struct {
  41. // The start address of current mapping.
  42. StartAddr uintptr
  43. // The end address of the current mapping
  44. EndAddr uintptr
  45. // The permissions for this mapping
  46. Perms *ProcMapPermissions
  47. // The current offset into the file/fd (e.g., shared libs)
  48. Offset int64
  49. // Device owner of this mapping (major:minor) in Mkdev format.
  50. Dev uint64
  51. // The inode of the device above
  52. Inode uint64
  53. // The file or psuedofile (or empty==anonymous)
  54. Pathname string
  55. }
  56. // parseDevice parses the device token of a line and converts it to a dev_t
  57. // (mkdev) like structure.
  58. func parseDevice(s string) (uint64, error) {
  59. toks := strings.Split(s, ":")
  60. if len(toks) < 2 {
  61. return 0, fmt.Errorf("unexpected number of fields")
  62. }
  63. major, err := strconv.ParseUint(toks[0], 16, 0)
  64. if err != nil {
  65. return 0, err
  66. }
  67. minor, err := strconv.ParseUint(toks[1], 16, 0)
  68. if err != nil {
  69. return 0, err
  70. }
  71. return unix.Mkdev(uint32(major), uint32(minor)), nil
  72. }
  73. // parseAddress converts a hex-string to a uintptr.
  74. func parseAddress(s string) (uintptr, error) {
  75. a, err := strconv.ParseUint(s, 16, 0)
  76. if err != nil {
  77. return 0, err
  78. }
  79. return uintptr(a), nil
  80. }
  81. // parseAddresses parses the start-end address.
  82. func parseAddresses(s string) (uintptr, uintptr, error) {
  83. toks := strings.Split(s, "-")
  84. if len(toks) < 2 {
  85. return 0, 0, fmt.Errorf("invalid address")
  86. }
  87. saddr, err := parseAddress(toks[0])
  88. if err != nil {
  89. return 0, 0, err
  90. }
  91. eaddr, err := parseAddress(toks[1])
  92. if err != nil {
  93. return 0, 0, err
  94. }
  95. return saddr, eaddr, nil
  96. }
  97. // parsePermissions parses a token and returns any that are set.
  98. func parsePermissions(s string) (*ProcMapPermissions, error) {
  99. if len(s) < 4 {
  100. return nil, fmt.Errorf("invalid permissions token")
  101. }
  102. perms := ProcMapPermissions{}
  103. for _, ch := range s {
  104. switch ch {
  105. case 'r':
  106. perms.Read = true
  107. case 'w':
  108. perms.Write = true
  109. case 'x':
  110. perms.Execute = true
  111. case 'p':
  112. perms.Private = true
  113. case 's':
  114. perms.Shared = true
  115. }
  116. }
  117. return &perms, nil
  118. }
  119. // parseProcMap will attempt to parse a single line within a proc/[pid]/maps
  120. // buffer.
  121. func parseProcMap(text string) (*ProcMap, error) {
  122. fields := strings.Fields(text)
  123. if len(fields) < 5 {
  124. return nil, fmt.Errorf("truncated procmap entry")
  125. }
  126. saddr, eaddr, err := parseAddresses(fields[0])
  127. if err != nil {
  128. return nil, err
  129. }
  130. perms, err := parsePermissions(fields[1])
  131. if err != nil {
  132. return nil, err
  133. }
  134. offset, err := strconv.ParseInt(fields[2], 16, 0)
  135. if err != nil {
  136. return nil, err
  137. }
  138. device, err := parseDevice(fields[3])
  139. if err != nil {
  140. return nil, err
  141. }
  142. inode, err := strconv.ParseUint(fields[4], 10, 0)
  143. if err != nil {
  144. return nil, err
  145. }
  146. pathname := ""
  147. if len(fields) >= 5 {
  148. pathname = strings.Join(fields[5:], " ")
  149. }
  150. return &ProcMap{
  151. StartAddr: saddr,
  152. EndAddr: eaddr,
  153. Perms: perms,
  154. Offset: offset,
  155. Dev: device,
  156. Inode: inode,
  157. Pathname: pathname,
  158. }, nil
  159. }
  160. // ProcMaps reads from /proc/[pid]/maps to get the memory-mappings of the
  161. // process.
  162. func (p Proc) ProcMaps() ([]*ProcMap, error) {
  163. file, err := os.Open(p.path("maps"))
  164. if err != nil {
  165. return nil, err
  166. }
  167. defer file.Close()
  168. maps := []*ProcMap{}
  169. scan := bufio.NewScanner(file)
  170. for scan.Scan() {
  171. m, err := parseProcMap(scan.Text())
  172. if err != nil {
  173. return nil, err
  174. }
  175. maps = append(maps, m)
  176. }
  177. return maps, nil
  178. }