| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | // Copyright 2015 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 04:35:24 -07:00
										 |  |  | //go:build !nomeminfo_numa
 | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | // +build !nomeminfo_numa
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package collector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	memInfoNumaSubsystem = "memory_numa" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | var meminfoNodeRE = regexp.MustCompile(`.*devices/system/node/node([0-9]*)`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type meminfoMetric struct { | 
					
						
							|  |  |  | 	metricName string | 
					
						
							|  |  |  | 	metricType prometheus.ValueType | 
					
						
							|  |  |  | 	numaNode   string | 
					
						
							|  |  |  | 	value      float64 | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type meminfoNumaCollector struct { | 
					
						
							|  |  |  | 	metricDescs map[string]*prometheus.Desc | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	logger      *slog.Logger | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 	registerCollector("meminfo_numa", defaultDisabled, NewMeminfoNumaCollector) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 08:44:53 -08:00
										 |  |  | // NewMeminfoNumaCollector returns a new Collector exposing memory stats.
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | func NewMeminfoNumaCollector(logger *slog.Logger) (Collector, error) { | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	return &meminfoNumaCollector{ | 
					
						
							|  |  |  | 		metricDescs: map[string]*prometheus.Desc{}, | 
					
						
							| 
									
										
										
										
											2019-12-31 08:19:37 -08:00
										 |  |  | 		logger:      logger, | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 10:47:20 -08:00
										 |  |  | func (c *meminfoNumaCollector) Update(ch chan<- prometheus.Metric) error { | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 	metrics, err := getMemInfoNuma() | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-15 13:27:14 -07:00
										 |  |  | 		return fmt.Errorf("couldn't get NUMA meminfo: %w", err) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 	for _, v := range metrics { | 
					
						
							|  |  |  | 		desc, ok := c.metricDescs[v.metricName] | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		if !ok { | 
					
						
							|  |  |  | 			desc = prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 				prometheus.BuildFQName(namespace, memInfoNumaSubsystem, v.metricName), | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 				fmt.Sprintf("Memory information field %s.", v.metricName), | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 				[]string{"node"}, nil) | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 			c.metricDescs[v.metricName] = desc | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		ch <- prometheus.MustNewConstMetric(desc, v.metricType, v.value, v.numaNode) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | func getMemInfoNuma() ([]meminfoMetric, error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		metrics []meminfoMetric | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	nodes, err := filepath.Glob(sysFilePath("devices/system/node/node[0-9]*")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, node := range nodes { | 
					
						
							| 
									
										
										
										
											2019-02-05 07:37:27 -08:00
										 |  |  | 		meminfoFile, err := os.Open(filepath.Join(node, "meminfo")) | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer meminfoFile.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		numaInfo, err := parseMemInfoNuma(meminfoFile) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		metrics = append(metrics, numaInfo...) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 07:37:27 -08:00
										 |  |  | 		numastatFile, err := os.Open(filepath.Join(node, "numastat")) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		defer numastatFile.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		nodeNumber := meminfoNodeRE.FindStringSubmatch(node) | 
					
						
							|  |  |  | 		if nodeNumber == nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("device node string didn't match regexp: %s", node) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		numaStat, err := parseMemInfoNumaStat(numastatFile, nodeNumber[1]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		metrics = append(metrics, numaStat...) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 	return metrics, nil | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | func parseMemInfoNuma(r io.Reader) ([]meminfoMetric, error) { | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		memInfo []meminfoMetric | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		scanner = bufio.NewScanner(r) | 
					
						
							| 
									
										
										
										
											2017-02-28 09:32:41 -08:00
										 |  |  | 		re      = regexp.MustCompile(`\((.*)\)`) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for scanner.Scan() { | 
					
						
							| 
									
										
										
										
											2016-02-06 04:32:25 -08:00
										 |  |  | 		line := strings.TrimSpace(scanner.Text()) | 
					
						
							|  |  |  | 		if line == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-28 09:42:43 -08:00
										 |  |  | 		parts := strings.Fields(line) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		fv, err := strconv.ParseFloat(parts[3], 64) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-15 13:27:14 -07:00
										 |  |  | 			return nil, fmt.Errorf("invalid value in meminfo: %w", err) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		switch l := len(parts); { | 
					
						
							|  |  |  | 		case l == 4: // no unit
 | 
					
						
							|  |  |  | 		case l == 5 && parts[4] == "kB": // has unit
 | 
					
						
							|  |  |  | 			fv *= 1024 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("invalid line in meminfo: %s", line) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		metric := strings.TrimRight(parts[2], ":") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Active(anon) -> Active_anon
 | 
					
						
							|  |  |  | 		metric = re.ReplaceAllString(metric, "_${1}") | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		memInfo = append(memInfo, meminfoMetric{metric, prometheus.GaugeValue, parts[1], fv}) | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 	return memInfo, scanner.Err() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseMemInfoNumaStat(r io.Reader, nodeNumber string) ([]meminfoMetric, error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		numaStat []meminfoMetric | 
					
						
							|  |  |  | 		scanner  = bufio.NewScanner(r) | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for scanner.Scan() { | 
					
						
							|  |  |  | 		line := strings.TrimSpace(scanner.Text()) | 
					
						
							|  |  |  | 		if line == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-28 09:42:43 -08:00
										 |  |  | 		parts := strings.Fields(line) | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		if len(parts) != 2 { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("line scan did not return 2 fields: %s", line) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fv, err := strconv.ParseFloat(parts[1], 64) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-15 13:27:14 -07:00
										 |  |  | 			return nil, fmt.Errorf("invalid value in numastat: %w", err) | 
					
						
							| 
									
										
										
										
											2016-10-12 04:07:49 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		numaStat = append(numaStat, meminfoMetric{parts[0] + "_total", prometheus.CounterValue, nodeNumber, fv}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return numaStat, scanner.Err() | 
					
						
							| 
									
										
										
										
											2015-11-13 01:23:21 -08:00
										 |  |  | } |