1// Copyright 2019 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// +build !windows
15
16package procfs
17
18import (
19	"bytes"
20	"fmt"
21	"io/ioutil"
22	"regexp"
23	"strings"
24
25	"github.com/prometheus/procfs/internal/util"
26)
27
28// Zoneinfo holds info parsed from /proc/zoneinfo.
29type Zoneinfo struct {
30	Node                       string
31	Zone                       string
32	NrFreePages                *int64
33	Min                        *int64
34	Low                        *int64
35	High                       *int64
36	Scanned                    *int64
37	Spanned                    *int64
38	Present                    *int64
39	Managed                    *int64
40	NrActiveAnon               *int64
41	NrInactiveAnon             *int64
42	NrIsolatedAnon             *int64
43	NrAnonPages                *int64
44	NrAnonTransparentHugepages *int64
45	NrActiveFile               *int64
46	NrInactiveFile             *int64
47	NrIsolatedFile             *int64
48	NrFilePages                *int64
49	NrSlabReclaimable          *int64
50	NrSlabUnreclaimable        *int64
51	NrMlockStack               *int64
52	NrKernelStack              *int64
53	NrMapped                   *int64
54	NrDirty                    *int64
55	NrWriteback                *int64
56	NrUnevictable              *int64
57	NrShmem                    *int64
58	NrDirtied                  *int64
59	NrWritten                  *int64
60	NumaHit                    *int64
61	NumaMiss                   *int64
62	NumaForeign                *int64
63	NumaInterleave             *int64
64	NumaLocal                  *int64
65	NumaOther                  *int64
66	Protection                 []*int64
67}
68
69var nodeZoneRE = regexp.MustCompile(`(\d+), zone\s+(\w+)`)
70
71// Zoneinfo parses an zoneinfo-file (/proc/zoneinfo) and returns a slice of
72// structs containing the relevant info.  More information available here:
73// https://www.kernel.org/doc/Documentation/sysctl/vm.txt
74func (fs FS) Zoneinfo() ([]Zoneinfo, error) {
75	data, err := ioutil.ReadFile(fs.proc.Path("zoneinfo"))
76	if err != nil {
77		return nil, fmt.Errorf("error reading zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err)
78	}
79	zoneinfo, err := parseZoneinfo(data)
80	if err != nil {
81		return nil, fmt.Errorf("error parsing zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err)
82	}
83	return zoneinfo, nil
84}
85
86func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) {
87
88	zoneinfo := []Zoneinfo{}
89
90	zoneinfoBlocks := bytes.Split(zoneinfoData, []byte("\nNode"))
91	for _, block := range zoneinfoBlocks {
92		var zoneinfoElement Zoneinfo
93		lines := strings.Split(string(block), "\n")
94		for _, line := range lines {
95
96			if nodeZone := nodeZoneRE.FindStringSubmatch(line); nodeZone != nil {
97				zoneinfoElement.Node = nodeZone[1]
98				zoneinfoElement.Zone = nodeZone[2]
99				continue
100			}
101			if strings.HasPrefix(strings.TrimSpace(line), "per-node stats") {
102				zoneinfoElement.Zone = ""
103				continue
104			}
105			parts := strings.Fields(strings.TrimSpace(line))
106			if len(parts) < 2 {
107				continue
108			}
109			vp := util.NewValueParser(parts[1])
110			switch parts[0] {
111			case "nr_free_pages":
112				zoneinfoElement.NrFreePages = vp.PInt64()
113			case "min":
114				zoneinfoElement.Min = vp.PInt64()
115			case "low":
116				zoneinfoElement.Low = vp.PInt64()
117			case "high":
118				zoneinfoElement.High = vp.PInt64()
119			case "scanned":
120				zoneinfoElement.Scanned = vp.PInt64()
121			case "spanned":
122				zoneinfoElement.Spanned = vp.PInt64()
123			case "present":
124				zoneinfoElement.Present = vp.PInt64()
125			case "managed":
126				zoneinfoElement.Managed = vp.PInt64()
127			case "nr_active_anon":
128				zoneinfoElement.NrActiveAnon = vp.PInt64()
129			case "nr_inactive_anon":
130				zoneinfoElement.NrInactiveAnon = vp.PInt64()
131			case "nr_isolated_anon":
132				zoneinfoElement.NrIsolatedAnon = vp.PInt64()
133			case "nr_anon_pages":
134				zoneinfoElement.NrAnonPages = vp.PInt64()
135			case "nr_anon_transparent_hugepages":
136				zoneinfoElement.NrAnonTransparentHugepages = vp.PInt64()
137			case "nr_active_file":
138				zoneinfoElement.NrActiveFile = vp.PInt64()
139			case "nr_inactive_file":
140				zoneinfoElement.NrInactiveFile = vp.PInt64()
141			case "nr_isolated_file":
142				zoneinfoElement.NrIsolatedFile = vp.PInt64()
143			case "nr_file_pages":
144				zoneinfoElement.NrFilePages = vp.PInt64()
145			case "nr_slab_reclaimable":
146				zoneinfoElement.NrSlabReclaimable = vp.PInt64()
147			case "nr_slab_unreclaimable":
148				zoneinfoElement.NrSlabUnreclaimable = vp.PInt64()
149			case "nr_mlock_stack":
150				zoneinfoElement.NrMlockStack = vp.PInt64()
151			case "nr_kernel_stack":
152				zoneinfoElement.NrKernelStack = vp.PInt64()
153			case "nr_mapped":
154				zoneinfoElement.NrMapped = vp.PInt64()
155			case "nr_dirty":
156				zoneinfoElement.NrDirty = vp.PInt64()
157			case "nr_writeback":
158				zoneinfoElement.NrWriteback = vp.PInt64()
159			case "nr_unevictable":
160				zoneinfoElement.NrUnevictable = vp.PInt64()
161			case "nr_shmem":
162				zoneinfoElement.NrShmem = vp.PInt64()
163			case "nr_dirtied":
164				zoneinfoElement.NrDirtied = vp.PInt64()
165			case "nr_written":
166				zoneinfoElement.NrWritten = vp.PInt64()
167			case "numa_hit":
168				zoneinfoElement.NumaHit = vp.PInt64()
169			case "numa_miss":
170				zoneinfoElement.NumaMiss = vp.PInt64()
171			case "numa_foreign":
172				zoneinfoElement.NumaForeign = vp.PInt64()
173			case "numa_interleave":
174				zoneinfoElement.NumaInterleave = vp.PInt64()
175			case "numa_local":
176				zoneinfoElement.NumaLocal = vp.PInt64()
177			case "numa_other":
178				zoneinfoElement.NumaOther = vp.PInt64()
179			case "protection:":
180				protectionParts := strings.Split(line, ":")
181				protectionValues := strings.Replace(protectionParts[1], "(", "", 1)
182				protectionValues = strings.Replace(protectionValues, ")", "", 1)
183				protectionValues = strings.TrimSpace(protectionValues)
184				protectionStringMap := strings.Split(protectionValues, ", ")
185				val, err := util.ParsePInt64s(protectionStringMap)
186				if err == nil {
187					zoneinfoElement.Protection = val
188				}
189			}
190
191		}
192
193		zoneinfo = append(zoneinfo, zoneinfoElement)
194	}
195	return zoneinfo, nil
196}
197