1// Copyright 2020 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
14package sysfs
15
16import (
17	"bufio"
18	"bytes"
19	"fmt"
20	"path/filepath"
21	"regexp"
22	"strconv"
23	"strings"
24
25	"github.com/prometheus/procfs/internal/util"
26)
27
28var (
29	nodePattern      = "devices/system/node/node[0-9]*"
30	nodeNumberRegexp = regexp.MustCompile(`.*devices/system/node/node([0-9]*)`)
31)
32
33type VMStat struct {
34	NrFreePages                uint64
35	NrZoneInactiveAnon         uint64
36	NrZoneActiveAnon           uint64
37	NrZoneInactiveFile         uint64
38	NrZoneActiveFile           uint64
39	NrZoneUnevictable          uint64
40	NrZoneWritePending         uint64
41	NrMlock                    uint64
42	NrPageTablePages           uint64
43	NrKernelStack              uint64
44	NrBounce                   uint64
45	NrZspages                  uint64
46	NrFreeCma                  uint64
47	NumaHit                    uint64
48	NumaMiss                   uint64
49	NumaForeign                uint64
50	NumaInterleave             uint64
51	NumaLocal                  uint64
52	NumaOther                  uint64
53	NrInactiveAnon             uint64
54	NrActiveAnon               uint64
55	NrInactiveFile             uint64
56	NrActiveFile               uint64
57	NrUnevictable              uint64
58	NrSlabReclaimable          uint64
59	NrSlabUnreclaimable        uint64
60	NrIsolatedAnon             uint64
61	NrIsolatedFile             uint64
62	WorkingsetNodes            uint64
63	WorkingsetRefault          uint64
64	WorkingsetActivate         uint64
65	WorkingsetRestore          uint64
66	WorkingsetNodereclaim      uint64
67	NrAnonPages                uint64
68	NrMapped                   uint64
69	NrFilePages                uint64
70	NrDirty                    uint64
71	NrWriteback                uint64
72	NrWritebackTemp            uint64
73	NrShmem                    uint64
74	NrShmemHugepages           uint64
75	NrShmemPmdmapped           uint64
76	NrFileHugepages            uint64
77	NrFilePmdmapped            uint64
78	NrAnonTransparentHugepages uint64
79	NrVmscanWrite              uint64
80	NrVmscanImmediateReclaim   uint64
81	NrDirtied                  uint64
82	NrWritten                  uint64
83	NrKernelMiscReclaimable    uint64
84	NrFollPinAcquired          uint64
85	NrFollPinReleased          uint64
86}
87
88func (fs FS) VMStatNUMA() (map[int]VMStat, error) {
89	m := make(map[int]VMStat)
90	nodes, err := filepath.Glob(fs.sys.Path(nodePattern))
91	if err != nil {
92		return nil, err
93	}
94
95	for _, node := range nodes {
96		nodeNumbers := nodeNumberRegexp.FindStringSubmatch(node)
97		if len(nodeNumbers) != 2 {
98			continue
99		}
100		nodeNumber, err := strconv.Atoi(nodeNumbers[1])
101		if err != nil {
102			return nil, err
103		}
104		file, err := util.ReadFileNoStat(filepath.Join(node, "vmstat"))
105		if err != nil {
106			return nil, err
107		}
108		nodeStats, err := parseVMStatNUMA(file)
109		if err != nil {
110			return nil, err
111		}
112		m[nodeNumber] = nodeStats
113	}
114	return m, nil
115}
116
117func parseVMStatNUMA(r []byte) (VMStat, error) {
118	var (
119		vmStat  = VMStat{}
120		scanner = bufio.NewScanner(bytes.NewReader(r))
121	)
122
123	for scanner.Scan() {
124		line := strings.TrimSpace(scanner.Text())
125		if line == "" {
126			continue
127		}
128		parts := strings.Fields(line)
129		if len(parts) != 2 {
130			return vmStat, fmt.Errorf("line scan did not return 2 fields: %s", line)
131		}
132
133		fv, err := strconv.ParseUint(parts[1], 10, 64)
134		if err != nil {
135			return vmStat, fmt.Errorf("invalid value in vmstat: %w", err)
136		}
137		switch parts[0] {
138		case "nr_free_pages":
139			vmStat.NrFreePages = fv
140		case "nr_zone_inactive_anon":
141			vmStat.NrZoneInactiveAnon = fv
142		case "nr_zone_active_anon":
143			vmStat.NrZoneActiveAnon = fv
144		case "nr_zone_inactive_file":
145			vmStat.NrZoneActiveFile = fv
146		case "nr_zone_active_file":
147			vmStat.NrZoneActiveFile = fv
148		case "nr_zone_unevictable":
149			vmStat.NrZoneUnevictable = fv
150		case "nr_zone_write_pending":
151			vmStat.NrZoneWritePending = fv
152		case "nr_mlock":
153			vmStat.NrMlock = fv
154		case "nr_page_table_pages":
155			vmStat.NrPageTablePages = fv
156		case "nr_kernel_stack":
157			vmStat.NrKernelStack = fv
158		case "nr_bounce":
159			vmStat.NrBounce = fv
160		case "nr_zspages":
161			vmStat.NrZspages = fv
162		case "nr_free_cma":
163			vmStat.NrFreeCma = fv
164		case "numa_hit":
165			vmStat.NumaHit = fv
166		case "numa_miss":
167			vmStat.NumaMiss = fv
168		case "numa_foreign":
169			vmStat.NumaForeign = fv
170		case "numa_interleave":
171			vmStat.NumaInterleave = fv
172		case "numa_local":
173			vmStat.NumaLocal = fv
174		case "numa_other":
175			vmStat.NumaOther = fv
176		case "nr_inactive_anon":
177			vmStat.NrInactiveAnon = fv
178		case "nr_active_anon":
179			vmStat.NrActiveAnon = fv
180		case "nr_inactive_file":
181			vmStat.NrInactiveFile = fv
182		case "nr_active_file":
183			vmStat.NrActiveFile = fv
184		case "nr_unevictable":
185			vmStat.NrUnevictable = fv
186		case "nr_slab_reclaimable":
187			vmStat.NrSlabReclaimable = fv
188		case "nr_slab_unreclaimable":
189			vmStat.NrSlabUnreclaimable = fv
190		case "nr_isolated_anon":
191			vmStat.NrIsolatedAnon = fv
192		case "nr_isolated_file":
193			vmStat.NrIsolatedFile = fv
194		case "workingset_nodes":
195			vmStat.WorkingsetNodes = fv
196		case "workingset_refault":
197			vmStat.WorkingsetRefault = fv
198		case "workingset_activate":
199			vmStat.WorkingsetActivate = fv
200		case "workingset_restore":
201			vmStat.WorkingsetRestore = fv
202		case "workingset_nodereclaim":
203			vmStat.WorkingsetNodereclaim = fv
204		case "nr_anon_pages":
205			vmStat.NrAnonPages = fv
206		case "nr_mapped":
207			vmStat.NrMapped = fv
208		case "nr_file_pages":
209			vmStat.NrFilePages = fv
210		case "nr_dirty":
211			vmStat.NrDirty = fv
212		case "nr_writeback":
213			vmStat.NrWriteback = fv
214		case "nr_writeback_temp":
215			vmStat.NrWritebackTemp = fv
216		case "nr_shmem":
217			vmStat.NrShmem = fv
218		case "nr_shmem_hugepages":
219			vmStat.NrShmemHugepages = fv
220		case "nr_shmem_pmdmapped":
221			vmStat.NrShmemPmdmapped = fv
222		case "nr_file_hugepages":
223			vmStat.NrFileHugepages = fv
224		case "nr_file_pmdmapped":
225			vmStat.NrFilePmdmapped = fv
226		case "nr_anon_transparent_hugepages":
227			vmStat.NrAnonTransparentHugepages = fv
228		case "nr_vmscan_write":
229			vmStat.NrVmscanWrite = fv
230		case "nr_vmscan_immediate_reclaim":
231			vmStat.NrVmscanImmediateReclaim = fv
232		case "nr_dirtied":
233			vmStat.NrDirtied = fv
234		case "nr_written":
235			vmStat.NrWritten = fv
236		case "nr_kernel_misc_reclaimable":
237			vmStat.NrKernelMiscReclaimable = fv
238		case "nr_foll_pin_acquired":
239			vmStat.NrFollPinAcquired = fv
240		case "nr_foll_pin_released":
241			vmStat.NrFollPinReleased = fv
242		}
243
244	}
245	return vmStat, scanner.Err()
246}
247