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: %v", 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