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