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.
 
 

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