From 3ba15c1ddbfb0e7d9761397128ceaf84ba4e7bde Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Mon, 6 Feb 2017 09:56:58 -0600 Subject: [PATCH 1/7] 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 --- collector/buddyinfo.go | 114 ++++++++++++++++++++++++++++++ collector/buddyinfo_test.go | 40 +++++++++++ collector/fixtures/proc/buddyinfo | 3 + 3 files changed, 157 insertions(+) create mode 100644 collector/buddyinfo.go create mode 100644 collector/buddyinfo_test.go create mode 100644 collector/fixtures/proc/buddyinfo diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go new file mode 100644 index 00000000..07cc8753 --- /dev/null +++ b/collector/buddyinfo.go @@ -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 +} diff --git a/collector/buddyinfo_test.go b/collector/buddyinfo_test.go new file mode 100644 index 00000000..e884130e --- /dev/null +++ b/collector/buddyinfo_test.go @@ -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) + } +} diff --git a/collector/fixtures/proc/buddyinfo b/collector/fixtures/proc/buddyinfo new file mode 100644 index 00000000..f90594a8 --- /dev/null +++ b/collector/fixtures/proc/buddyinfo @@ -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 From 492c96f6b633415fbbf0d65afe9b08d3562af58c Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 7 Feb 2017 08:09:08 -0600 Subject: [PATCH 2/7] Moving buddyinfo_test.go to procfs library --- collector/buddyinfo_test.go | 40 ------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 collector/buddyinfo_test.go diff --git a/collector/buddyinfo_test.go b/collector/buddyinfo_test.go deleted file mode 100644 index e884130e..00000000 --- a/collector/buddyinfo_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// 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) - } -} From 2f16f6b84e3f7f3a0916df38a3b1f47aad917b20 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 7 Feb 2017 08:29:50 -0600 Subject: [PATCH 3/7] Adding buddyinfo to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 33ab0a6f..d70928fc 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ zfs | Exposes [ZFS](http://open-zfs.org/) performance statistics. | [Linux](http Name | Description | OS ---------|-------------|---- bonding | Exposes the number of configured and active slaves of Linux bonding interfaces. | Linux +buddyinfo | Exposes statistics of memory fragments as reported by /proc/buddyinfo. | Linux devstat | Exposes device statistics | Dragonfly, FreeBSD drbd | Exposes Distributed Replicated Block Device statistics | Linux interrupts | Exposes detailed interrupts statistics. | Linux, OpenBSD From 55417d768820ec44c5f995182cc808640dfd1667 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 7 Feb 2017 08:30:49 -0600 Subject: [PATCH 4/7] Moving buddyinfo logic to procfs --- collector/buddyinfo.go | 56 +++++++----------------------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go index 07cc8753..2d4b5115 100644 --- a/collector/buddyinfo.go +++ b/collector/buddyinfo.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Prometheus Authors +// Copyright 2017 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 @@ -17,19 +17,14 @@ package collector import ( - "bufio" "fmt" - "io" - "os" "strconv" - "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" + "github.com/prometheus/procfs" ) -type buddyInfo map[string]map[string][]float64 - const ( buddyInfoSubsystem = "buddyinfo" ) @@ -55,10 +50,16 @@ func NewBuddyinfoCollector() (Collector, error) { // 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() + fs, err := procfs.NewFS(*procPath) + if err != nil { + return fmt.Errorf("failed to open procfs: %v", err) + } + + buddyInfo, err := fs.NewBuddyInfo() 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 { @@ -73,42 +74,3 @@ func (c *buddyinfoCollector) Update(ch chan<- prometheus.Metric) (err error) { } 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 -} From 5ab285e0989257de7d50b2ddc15fe2df16d80616 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 7 Feb 2017 09:11:22 -0600 Subject: [PATCH 5/7] Adding buddyinfo to end to end test. --- collector/fixtures/e2e-output.txt | 35 +++++++++++++++++++++++++++++++ end-to-end-test.sh | 1 + 2 files changed, 36 insertions(+) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 9a08a561..b54f35d1 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -82,6 +82,41 @@ node_bonding_slaves{master="int"} 2 # HELP node_boot_time Node boot time, in unixtime. # TYPE node_boot_time gauge node_boot_time 1.418183276e+09 +# HELP node_buddyinfo_count Count of free blocks according to size. +# TYPE node_buddyinfo_count gauge +node_buddyinfo_count{node="0",size="0",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="0",zone="DMA32"} 759 +node_buddyinfo_count{node="0",size="0",zone="Normal"} 4381 +node_buddyinfo_count{node="0",size="1",zone="DMA"} 0 +node_buddyinfo_count{node="0",size="1",zone="DMA32"} 572 +node_buddyinfo_count{node="0",size="1",zone="Normal"} 1093 +node_buddyinfo_count{node="0",size="10",zone="DMA"} 3 +node_buddyinfo_count{node="0",size="10",zone="DMA32"} 0 +node_buddyinfo_count{node="0",size="10",zone="Normal"} 0 +node_buddyinfo_count{node="0",size="2",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="2",zone="DMA32"} 791 +node_buddyinfo_count{node="0",size="2",zone="Normal"} 185 +node_buddyinfo_count{node="0",size="3",zone="DMA"} 0 +node_buddyinfo_count{node="0",size="3",zone="DMA32"} 475 +node_buddyinfo_count{node="0",size="3",zone="Normal"} 1530 +node_buddyinfo_count{node="0",size="4",zone="DMA"} 2 +node_buddyinfo_count{node="0",size="4",zone="DMA32"} 194 +node_buddyinfo_count{node="0",size="4",zone="Normal"} 567 +node_buddyinfo_count{node="0",size="5",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="5",zone="DMA32"} 45 +node_buddyinfo_count{node="0",size="5",zone="Normal"} 102 +node_buddyinfo_count{node="0",size="6",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="6",zone="DMA32"} 12 +node_buddyinfo_count{node="0",size="6",zone="Normal"} 4 +node_buddyinfo_count{node="0",size="7",zone="DMA"} 0 +node_buddyinfo_count{node="0",size="7",zone="DMA32"} 0 +node_buddyinfo_count{node="0",size="7",zone="Normal"} 0 +node_buddyinfo_count{node="0",size="8",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="8",zone="DMA32"} 0 +node_buddyinfo_count{node="0",size="8",zone="Normal"} 0 +node_buddyinfo_count{node="0",size="9",zone="DMA"} 1 +node_buddyinfo_count{node="0",size="9",zone="DMA32"} 0 +node_buddyinfo_count{node="0",size="9",zone="Normal"} 0 # HELP node_context_switches Total number of context switches. # TYPE node_context_switches counter node_context_switches 3.8014093e+07 diff --git a/end-to-end-test.sh b/end-to-end-test.sh index 2e92dad5..39e55413 100755 --- a/end-to-end-test.sh +++ b/end-to-end-test.sh @@ -3,6 +3,7 @@ set -euf -o pipefail collectors=$(cat << COLLECTORS + buddyinfo conntrack diskstats drbd From 19813d3e02a344c61b417824e8066becdde2a9da Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Tue, 7 Feb 2017 10:48:07 -0600 Subject: [PATCH 6/7] Changing datastructure for BuddyInfo --- collector/buddyinfo.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go index 2d4b5115..782aceaf 100644 --- a/collector/buddyinfo.go +++ b/collector/buddyinfo.go @@ -61,15 +61,13 @@ func (c *buddyinfoCollector) Update(ch chan<- prometheus.Metric) (err error) { } 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), - ) - } + for _, entry := range buddyInfo { + for size, value := range entry.Sizes { + ch <- prometheus.MustNewConstMetric( + c.desc, + prometheus.GaugeValue, value, + entry.Node, entry.Zone, strconv.Itoa(size), + ) } } return nil From 8d5e0aeba0af10e2e3742937516a9744e61f4a10 Mon Sep 17 00:00:00 2001 From: Thorhallur Sverrisson Date: Wed, 15 Feb 2017 16:10:43 -0600 Subject: [PATCH 7/7] Adding self to AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 030d6171..0fafa505 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -35,5 +35,6 @@ The following individuals have contributed code to this repository * Siavash Safi * Stephen Shirley * Steve Durrheimer +* Thorhallur Sverrisson * Tobias Schmidt * Will Rouesnel