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 }