mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	Adding support for /proc/buddyinfo for linux free memory fragmentation.
/prod/buddyinfo returns data on the free blocks fragments available for use from the kernel. This data is useful when diagnosing possible memory fragmentation. More info can be found in: * https://lwn.net/Articles/7868/ * https://andorian.blogspot.com/2014/03/making-sense-of-procbuddyinfo.html
This commit is contained in:
		
							parent
							
								
									dc863551bd
								
							
						
					
					
						commit
						3ba15c1ddb
					
				
							
								
								
									
										114
									
								
								collector/buddyinfo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								collector/buddyinfo.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,114 @@ | ||||||
|  | // 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.
 | ||||||
|  | 
 | ||||||
|  | // +build !nobuddyinfo
 | ||||||
|  | // +build !windows,!netbsd
 | ||||||
|  | 
 | ||||||
|  | package collector | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/prometheus/client_golang/prometheus" | ||||||
|  | 	"github.com/prometheus/common/log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type buddyInfo map[string]map[string][]float64 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	buddyInfoSubsystem = "buddyinfo" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type buddyinfoCollector struct { | ||||||
|  | 	desc *prometheus.Desc | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	Factories["buddyinfo"] = NewBuddyinfoCollector | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewBuddyinfoCollector returns a new Collector exposing buddyinfo stats.
 | ||||||
|  | func NewBuddyinfoCollector() (Collector, error) { | ||||||
|  | 	desc := prometheus.NewDesc( | ||||||
|  | 		prometheus.BuildFQName(Namespace, buddyInfoSubsystem, "count"), | ||||||
|  | 		"Count of free blocks according to size.", | ||||||
|  | 		[]string{"node", "zone", "size"}, nil, | ||||||
|  | 	) | ||||||
|  | 	return &buddyinfoCollector{desc}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Update calls (*buddyinfoCollector).getBuddyInfo to get the platform specific
 | ||||||
|  | // buddyinfo metrics.
 | ||||||
|  | func (c *buddyinfoCollector) Update(ch chan<- prometheus.Metric) (err error) { | ||||||
|  | 	buddyInfo, err := c.getBuddyInfo() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("couldn't get buddyinfo: %s", err) | ||||||
|  | 	} | ||||||
|  | 	log.Debugf("Set node_buddy: %#v", buddyInfo) | ||||||
|  | 	for node, zones := range buddyInfo { | ||||||
|  | 		for zone, values := range zones { | ||||||
|  | 			for size, value := range values { | ||||||
|  | 				ch <- prometheus.MustNewConstMetric( | ||||||
|  | 					c.desc, | ||||||
|  | 					prometheus.GaugeValue, value, | ||||||
|  | 					node, zone, strconv.Itoa(size), | ||||||
|  | 				) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | func (c *buddyinfoCollector) getBuddyInfo() (buddyInfo, error) { | ||||||
|  | 	file, err := os.Open(procFilePath("buddyinfo")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  | 
 | ||||||
|  | 	return parseBuddyInfo(file) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func parseBuddyInfo(r io.Reader) (buddyInfo, error) { | ||||||
|  | 	var ( | ||||||
|  | 		buddyInfo = buddyInfo{} | ||||||
|  | 		scanner   = bufio.NewScanner(r) | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	for scanner.Scan() { | ||||||
|  | 		var err error | ||||||
|  | 		line := scanner.Text() | ||||||
|  | 		parts := strings.Fields(string(line)) | ||||||
|  | 		node := strings.TrimRight(parts[1], ",") | ||||||
|  | 		zone := strings.TrimRight(parts[3], ",") | ||||||
|  | 		arraySize := len(parts[4:]) | ||||||
|  | 		sizes := make([]float64, arraySize) | ||||||
|  | 		for i := 0; i < arraySize; i++ { | ||||||
|  | 			sizes[i], err = strconv.ParseFloat(parts[i+4], 64) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, fmt.Errorf("invalid value in buddyinfo: %s", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if _, ok := buddyInfo[node]; !ok { | ||||||
|  | 			buddyInfo[node] = make(map[string][]float64) | ||||||
|  | 		} | ||||||
|  | 		buddyInfo[node][zone] = sizes | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return buddyInfo, nil | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								collector/buddyinfo_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								collector/buddyinfo_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | // 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.
 | ||||||
|  | 
 | ||||||
|  | package collector | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestBuddyInfo(t *testing.T) { | ||||||
|  | 	file, err := os.Open("fixtures/proc/buddyinfo") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	defer file.Close() | ||||||
|  | 
 | ||||||
|  | 	buddyInfo, err := parseBuddyInfo(file) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if want, got := 4381.0, buddyInfo["0"]["Normal"][0]; want != got { | ||||||
|  | 		t.Errorf("want Node 0, Zone Normal %f, got %f", want, got) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if want, got := 572.0, buddyInfo["0"]["DMA32"][1]; want != got { | ||||||
|  | 		t.Errorf("want Node 0, Zone DMA32 %f, got %f", want, got) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								collector/fixtures/proc/buddyinfo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								collector/fixtures/proc/buddyinfo
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3  | ||||||
|  | Node 0, zone    DMA32    759    572    791    475    194     45     12      0      0      0      0  | ||||||
|  | Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0      0  | ||||||
		Loading…
	
		Reference in a new issue