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