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