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