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// +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} 210