...

Source file src/github.com/prometheus/procfs/proc_maps.go

Documentation: github.com/prometheus/procfs

     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  	i := strings.Index(s, ":")
    67  	if i == -1 {
    68  		return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, s)
    69  	}
    70  
    71  	major, err := strconv.ParseUint(s[0:i], 16, 0)
    72  	if err != nil {
    73  		return 0, err
    74  	}
    75  
    76  	minor, err := strconv.ParseUint(s[i+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  	idx := strings.Index(s, "-")
    97  	if idx == -1 {
    98  		return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, s)
    99  	}
   100  
   101  	saddr, err := parseAddress(s[0:idx])
   102  	if err != nil {
   103  		return 0, 0, err
   104  	}
   105  
   106  	eaddr, err := parseAddress(s[idx+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("%w: invalid permissions token", ErrFileParse)
   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("%w: truncated procmap entry", ErrFileParse)
   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  }
   212  

View as plain text