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
14package procfs
15
16import (
17	"bufio"
18	"bytes"
19	"fmt"
20	"io"
21	"strconv"
22	"strings"
23
24	"github.com/prometheus/procfs/internal/util"
25)
26
27// Meminfo represents memory statistics.
28type Meminfo struct {
29	// Total usable ram (i.e. physical ram minus a few reserved
30	// bits and the kernel binary code)
31	MemTotal *uint64
32	// The sum of LowFree+HighFree
33	MemFree *uint64
34	// An estimate of how much memory is available for starting
35	// new applications, without swapping. Calculated from
36	// MemFree, SReclaimable, the size of the file LRU lists, and
37	// the low watermarks in each zone.  The estimate takes into
38	// account that the system needs some page cache to function
39	// well, and that not all reclaimable slab will be
40	// reclaimable, due to items being in use. The impact of those
41	// factors will vary from system to system.
42	MemAvailable *uint64
43	// Relatively temporary storage for raw disk blocks shouldn't
44	// get tremendously large (20MB or so)
45	Buffers *uint64
46	Cached  *uint64
47	// Memory that once was swapped out, is swapped back in but
48	// still also is in the swapfile (if memory is needed it
49	// doesn't need to be swapped out AGAIN because it is already
50	// in the swapfile. This saves I/O)
51	SwapCached *uint64
52	// Memory that has been used more recently and usually not
53	// reclaimed unless absolutely necessary.
54	Active *uint64
55	// Memory which has been less recently used.  It is more
56	// eligible to be reclaimed for other purposes
57	Inactive     *uint64
58	ActiveAnon   *uint64
59	InactiveAnon *uint64
60	ActiveFile   *uint64
61	InactiveFile *uint64
62	Unevictable  *uint64
63	Mlocked      *uint64
64	// total amount of swap space available
65	SwapTotal *uint64
66	// Memory which has been evicted from RAM, and is temporarily
67	// on the disk
68	SwapFree *uint64
69	// Memory which is waiting to get written back to the disk
70	Dirty *uint64
71	// Memory which is actively being written back to the disk
72	Writeback *uint64
73	// Non-file backed pages mapped into userspace page tables
74	AnonPages *uint64
75	// files which have been mapped, such as libraries
76	Mapped *uint64
77	Shmem  *uint64
78	// in-kernel data structures cache
79	Slab *uint64
80	// Part of Slab, that might be reclaimed, such as caches
81	SReclaimable *uint64
82	// Part of Slab, that cannot be reclaimed on memory pressure
83	SUnreclaim  *uint64
84	KernelStack *uint64
85	// amount of memory dedicated to the lowest level of page
86	// tables.
87	PageTables *uint64
88	// NFS pages sent to the server, but not yet committed to
89	// stable storage
90	NFSUnstable *uint64
91	// Memory used for block device "bounce buffers"
92	Bounce *uint64
93	// Memory used by FUSE for temporary writeback buffers
94	WritebackTmp *uint64
95	// Based on the overcommit ratio ('vm.overcommit_ratio'),
96	// this is the total amount of  memory currently available to
97	// be allocated on the system. This limit is only adhered to
98	// if strict overcommit accounting is enabled (mode 2 in
99	// 'vm.overcommit_memory').
100	// The CommitLimit is calculated with the following formula:
101	// CommitLimit = ([total RAM pages] - [total huge TLB pages]) *
102	//                overcommit_ratio / 100 + [total swap pages]
103	// For example, on a system with 1G of physical RAM and 7G
104	// of swap with a `vm.overcommit_ratio` of 30 it would
105	// yield a CommitLimit of 7.3G.
106	// For more details, see the memory overcommit documentation
107	// in vm/overcommit-accounting.
108	CommitLimit *uint64
109	// The amount of memory presently allocated on the system.
110	// The committed memory is a sum of all of the memory which
111	// has been allocated by processes, even if it has not been
112	// "used" by them as of yet. A process which malloc()'s 1G
113	// of memory, but only touches 300M of it will show up as
114	// using 1G. This 1G is memory which has been "committed" to
115	// by the VM and can be used at any time by the allocating
116	// application. With strict overcommit enabled on the system
117	// (mode 2 in 'vm.overcommit_memory'),allocations which would
118	// exceed the CommitLimit (detailed above) will not be permitted.
119	// This is useful if one needs to guarantee that processes will
120	// not fail due to lack of memory once that memory has been
121	// successfully allocated.
122	CommittedAS *uint64
123	// total size of vmalloc memory area
124	VmallocTotal *uint64
125	// amount of vmalloc area which is used
126	VmallocUsed *uint64
127	// largest contiguous block of vmalloc area which is free
128	VmallocChunk      *uint64
129	HardwareCorrupted *uint64
130	AnonHugePages     *uint64
131	ShmemHugePages    *uint64
132	ShmemPmdMapped    *uint64
133	CmaTotal          *uint64
134	CmaFree           *uint64
135	HugePagesTotal    *uint64
136	HugePagesFree     *uint64
137	HugePagesRsvd     *uint64
138	HugePagesSurp     *uint64
139	Hugepagesize      *uint64
140	DirectMap4k       *uint64
141	DirectMap2M       *uint64
142	DirectMap1G       *uint64
143}
144
145// Meminfo returns an information about current kernel/system memory statistics.
146// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
147func (fs FS) Meminfo() (Meminfo, error) {
148	b, err := util.ReadFileNoStat(fs.proc.Path("meminfo"))
149	if err != nil {
150		return Meminfo{}, err
151	}
152
153	m, err := parseMemInfo(bytes.NewReader(b))
154	if err != nil {
155		return Meminfo{}, fmt.Errorf("failed to parse meminfo: %w", err)
156	}
157
158	return *m, nil
159}
160
161func parseMemInfo(r io.Reader) (*Meminfo, error) {
162	var m Meminfo
163	s := bufio.NewScanner(r)
164	for s.Scan() {
165		// Each line has at least a name and value; we ignore the unit.
166		fields := strings.Fields(s.Text())
167		if len(fields) < 2 {
168			return nil, fmt.Errorf("malformed meminfo line: %q", s.Text())
169		}
170
171		v, err := strconv.ParseUint(fields[1], 0, 64)
172		if err != nil {
173			return nil, err
174		}
175
176		switch fields[0] {
177		case "MemTotal:":
178			m.MemTotal = &v
179		case "MemFree:":
180			m.MemFree = &v
181		case "MemAvailable:":
182			m.MemAvailable = &v
183		case "Buffers:":
184			m.Buffers = &v
185		case "Cached:":
186			m.Cached = &v
187		case "SwapCached:":
188			m.SwapCached = &v
189		case "Active:":
190			m.Active = &v
191		case "Inactive:":
192			m.Inactive = &v
193		case "Active(anon):":
194			m.ActiveAnon = &v
195		case "Inactive(anon):":
196			m.InactiveAnon = &v
197		case "Active(file):":
198			m.ActiveFile = &v
199		case "Inactive(file):":
200			m.InactiveFile = &v
201		case "Unevictable:":
202			m.Unevictable = &v
203		case "Mlocked:":
204			m.Mlocked = &v
205		case "SwapTotal:":
206			m.SwapTotal = &v
207		case "SwapFree:":
208			m.SwapFree = &v
209		case "Dirty:":
210			m.Dirty = &v
211		case "Writeback:":
212			m.Writeback = &v
213		case "AnonPages:":
214			m.AnonPages = &v
215		case "Mapped:":
216			m.Mapped = &v
217		case "Shmem:":
218			m.Shmem = &v
219		case "Slab:":
220			m.Slab = &v
221		case "SReclaimable:":
222			m.SReclaimable = &v
223		case "SUnreclaim:":
224			m.SUnreclaim = &v
225		case "KernelStack:":
226			m.KernelStack = &v
227		case "PageTables:":
228			m.PageTables = &v
229		case "NFS_Unstable:":
230			m.NFSUnstable = &v
231		case "Bounce:":
232			m.Bounce = &v
233		case "WritebackTmp:":
234			m.WritebackTmp = &v
235		case "CommitLimit:":
236			m.CommitLimit = &v
237		case "Committed_AS:":
238			m.CommittedAS = &v
239		case "VmallocTotal:":
240			m.VmallocTotal = &v
241		case "VmallocUsed:":
242			m.VmallocUsed = &v
243		case "VmallocChunk:":
244			m.VmallocChunk = &v
245		case "HardwareCorrupted:":
246			m.HardwareCorrupted = &v
247		case "AnonHugePages:":
248			m.AnonHugePages = &v
249		case "ShmemHugePages:":
250			m.ShmemHugePages = &v
251		case "ShmemPmdMapped:":
252			m.ShmemPmdMapped = &v
253		case "CmaTotal:":
254			m.CmaTotal = &v
255		case "CmaFree:":
256			m.CmaFree = &v
257		case "HugePages_Total:":
258			m.HugePagesTotal = &v
259		case "HugePages_Free:":
260			m.HugePagesFree = &v
261		case "HugePages_Rsvd:":
262			m.HugePagesRsvd = &v
263		case "HugePages_Surp:":
264			m.HugePagesSurp = &v
265		case "Hugepagesize:":
266			m.Hugepagesize = &v
267		case "DirectMap4k:":
268			m.DirectMap4k = &v
269		case "DirectMap2M:":
270			m.DirectMap2M = &v
271		case "DirectMap1G:":
272			m.DirectMap1G = &v
273		}
274	}
275
276	return &m, nil
277}
278