github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/util/process/collector.go (about)

     1  package process
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  
    12  	"github.com/prometheus/client_golang/prometheus"
    13  )
    14  
    15  const (
    16  	// DefaultProcMountPoint is the common mount point of the proc filesystem.
    17  	DefaultProcMountPoint = "/proc"
    18  )
    19  
    20  var (
    21  	ErrUnsupportedCollector = errors.New("unsupported platform")
    22  )
    23  
    24  type processCollector struct {
    25  	pid            int
    26  	procMountPoint string
    27  
    28  	// Metrics.
    29  	currMaps *prometheus.Desc
    30  	maxMaps  *prometheus.Desc
    31  }
    32  
    33  // NewProcessCollector makes a new custom process collector used to collect process metrics the
    34  // default instrumentation doesn't support.
    35  func NewProcessCollector() (prometheus.Collector, error) {
    36  	return newProcessCollector(os.Getpid(), DefaultProcMountPoint)
    37  }
    38  
    39  func newProcessCollector(pid int, procMountPoint string) (prometheus.Collector, error) {
    40  	// Check whether it's supported on this platform.
    41  	if !isSupported(procMountPoint) {
    42  		return nil, ErrUnsupportedCollector
    43  	}
    44  
    45  	return &processCollector{
    46  		pid:            pid,
    47  		procMountPoint: procMountPoint,
    48  		currMaps: prometheus.NewDesc(
    49  			"process_memory_map_areas",
    50  			"Number of memory map areas allocated by the process.",
    51  			nil, nil,
    52  		),
    53  		maxMaps: prometheus.NewDesc(
    54  			"process_memory_map_areas_limit",
    55  			"Maximum number of memory map ares the process can allocate.",
    56  			nil, nil,
    57  		),
    58  	}, nil
    59  }
    60  
    61  // Describe returns all descriptions of the collector.
    62  func (c *processCollector) Describe(ch chan<- *prometheus.Desc) {
    63  	ch <- c.currMaps
    64  	ch <- c.maxMaps
    65  }
    66  
    67  // Collect returns the current state of all metrics of the collector.
    68  func (c *processCollector) Collect(ch chan<- prometheus.Metric) {
    69  	if value, err := c.getMapsCount(); err == nil {
    70  		ch <- prometheus.MustNewConstMetric(c.currMaps, prometheus.GaugeValue, value)
    71  	}
    72  
    73  	if value, err := c.getMapsCountLimit(); err == nil {
    74  		ch <- prometheus.MustNewConstMetric(c.maxMaps, prometheus.GaugeValue, value)
    75  	}
    76  }
    77  
    78  // getMapsCount returns the number of memory map ares the process has allocated.
    79  func (c *processCollector) getMapsCount() (float64, error) {
    80  	file, err := os.Open(processMapsPath(c.procMountPoint, c.pid))
    81  	if err != nil {
    82  		return 0, err
    83  	}
    84  	defer file.Close()
    85  
    86  	count := 0
    87  	scan := bufio.NewScanner(file)
    88  	for scan.Scan() {
    89  		count++
    90  	}
    91  
    92  	return float64(count), scan.Err()
    93  }
    94  
    95  // getMapsCountLimit returns the maximum of memory map ares the process can allocate.
    96  func (c *processCollector) getMapsCountLimit() (float64, error) {
    97  	file, err := os.Open(vmMapsLimitPath(c.procMountPoint))
    98  	if err != nil {
    99  		return 0, err
   100  	}
   101  	defer file.Close()
   102  
   103  	content, err := ioutil.ReadAll(file)
   104  	if err != nil {
   105  		return 0, err
   106  	}
   107  
   108  	content = bytes.TrimSpace(content)
   109  
   110  	// A base value of zero makes ParseInt infer the correct base using the
   111  	// string's prefix, if any.
   112  	const base = 0
   113  	value, err := strconv.ParseInt(string(content), base, 64)
   114  	if err != nil {
   115  		return 0, err
   116  	}
   117  
   118  	return float64(value), nil
   119  }
   120  
   121  func isSupported(procPath string) bool {
   122  	_, err := os.Stat(vmMapsLimitPath(procPath))
   123  	return err == nil
   124  }
   125  
   126  func processMapsPath(procPath string, pid int) string {
   127  	return filepath.Join(procPath, strconv.Itoa(pid), "maps")
   128  }
   129  
   130  func vmMapsLimitPath(procPath string) string {
   131  	return filepath.Join(procPath, "sys", "vm", "max_map_count")
   132  }