proc_maps.go 4.4 KB

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