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// Fscacheinfo represents fscache statistics.
28type Fscacheinfo struct {
29	// Number of index cookies allocated
30	IndexCookiesAllocated uint64
31	// data storage cookies allocated
32	DataStorageCookiesAllocated uint64
33	// Number of special cookies allocated
34	SpecialCookiesAllocated uint64
35	// Number of objects allocated
36	ObjectsAllocated uint64
37	// Number of object allocation failures
38	ObjectAllocationsFailure uint64
39	// Number of objects that reached the available state
40	ObjectsAvailable uint64
41	// Number of objects that reached the dead state
42	ObjectsDead uint64
43	// Number of objects that didn't have a coherency check
44	ObjectsWithoutCoherencyCheck uint64
45	// Number of objects that passed a coherency check
46	ObjectsWithCoherencyCheck uint64
47	// Number of objects that needed a coherency data update
48	ObjectsNeedCoherencyCheckUpdate uint64
49	// Number of objects that were declared obsolete
50	ObjectsDeclaredObsolete uint64
51	// Number of pages marked as being cached
52	PagesMarkedAsBeingCached uint64
53	// Number of uncache page requests seen
54	UncachePagesRequestSeen uint64
55	// Number of acquire cookie requests seen
56	AcquireCookiesRequestSeen uint64
57	// Number of acq reqs given a NULL parent
58	AcquireRequestsWithNullParent uint64
59	// Number of acq reqs rejected due to no cache available
60	AcquireRequestsRejectedNoCacheAvailable uint64
61	// Number of acq reqs succeeded
62	AcquireRequestsSucceeded uint64
63	// Number of acq reqs rejected due to error
64	AcquireRequestsRejectedDueToError uint64
65	// Number of acq reqs failed on ENOMEM
66	AcquireRequestsFailedDueToEnomem uint64
67	// Number of lookup calls made on cache backends
68	LookupsNumber uint64
69	// Number of negative lookups made
70	LookupsNegative uint64
71	// Number of positive lookups made
72	LookupsPositive uint64
73	// Number of objects created by lookup
74	ObjectsCreatedByLookup uint64
75	// Number of lookups timed out and requeued
76	LookupsTimedOutAndRequed uint64
77	InvalidationsNumber      uint64
78	InvalidationsRunning     uint64
79	// Number of update cookie requests seen
80	UpdateCookieRequestSeen uint64
81	// Number of upd reqs given a NULL parent
82	UpdateRequestsWithNullParent uint64
83	// Number of upd reqs granted CPU time
84	UpdateRequestsRunning uint64
85	// Number of relinquish cookie requests seen
86	RelinquishCookiesRequestSeen uint64
87	// Number of rlq reqs given a NULL parent
88	RelinquishCookiesWithNullParent uint64
89	// Number of rlq reqs waited on completion of creation
90	RelinquishRequestsWaitingCompleteCreation uint64
91	// Relinqs rtr
92	RelinquishRetries uint64
93	// Number of attribute changed requests seen
94	AttributeChangedRequestsSeen uint64
95	// Number of attr changed requests queued
96	AttributeChangedRequestsQueued uint64
97	// Number of attr changed rejected -ENOBUFS
98	AttributeChangedRejectDueToEnobufs uint64
99	// Number of attr changed failed -ENOMEM
100	AttributeChangedFailedDueToEnomem uint64
101	// Number of attr changed ops given CPU time
102	AttributeChangedOps uint64
103	// Number of allocation requests seen
104	AllocationRequestsSeen uint64
105	// Number of successful alloc reqs
106	AllocationOkRequests uint64
107	// Number of alloc reqs that waited on lookup completion
108	AllocationWaitingOnLookup uint64
109	// Number of alloc reqs rejected -ENOBUFS
110	AllocationsRejectedDueToEnobufs uint64
111	// Number of alloc reqs aborted -ERESTARTSYS
112	AllocationsAbortedDueToErestartsys uint64
113	// Number of alloc reqs submitted
114	AllocationOperationsSubmitted uint64
115	// Number of alloc reqs waited for CPU time
116	AllocationsWaitedForCPU uint64
117	// Number of alloc reqs aborted due to object death
118	AllocationsAbortedDueToObjectDeath uint64
119	// Number of retrieval (read) requests seen
120	RetrievalsReadRequests uint64
121	// Number of successful retr reqs
122	RetrievalsOk uint64
123	// Number of retr reqs that waited on lookup completion
124	RetrievalsWaitingLookupCompletion uint64
125	// Number of retr reqs returned -ENODATA
126	RetrievalsReturnedEnodata uint64
127	// Number of retr reqs rejected -ENOBUFS
128	RetrievalsRejectedDueToEnobufs uint64
129	// Number of retr reqs aborted -ERESTARTSYS
130	RetrievalsAbortedDueToErestartsys uint64
131	// Number of retr reqs failed -ENOMEM
132	RetrievalsFailedDueToEnomem uint64
133	// Number of retr reqs submitted
134	RetrievalsRequests uint64
135	// Number of retr reqs waited for CPU time
136	RetrievalsWaitingCPU uint64
137	// Number of retr reqs aborted due to object death
138	RetrievalsAbortedDueToObjectDeath uint64
139	// Number of storage (write) requests seen
140	StoreWriteRequests uint64
141	// Number of successful store reqs
142	StoreSuccessfulRequests uint64
143	// Number of store reqs on a page already pending storage
144	StoreRequestsOnPendingStorage uint64
145	// Number of store reqs rejected -ENOBUFS
146	StoreRequestsRejectedDueToEnobufs uint64
147	// Number of store reqs failed -ENOMEM
148	StoreRequestsFailedDueToEnomem uint64
149	// Number of store reqs submitted
150	StoreRequestsSubmitted uint64
151	// Number of store reqs granted CPU time
152	StoreRequestsRunning uint64
153	// Number of pages given store req processing time
154	StorePagesWithRequestsProcessing uint64
155	// Number of store reqs deleted from tracking tree
156	StoreRequestsDeleted uint64
157	// Number of store reqs over store limit
158	StoreRequestsOverStoreLimit uint64
159	// Number of release reqs against pages with no pending store
160	ReleaseRequestsAgainstPagesWithNoPendingStorage uint64
161	// Number of release reqs against pages stored by time lock granted
162	ReleaseRequestsAgainstPagesStoredByTimeLockGranted uint64
163	// Number of release reqs ignored due to in-progress store
164	ReleaseRequestsIgnoredDueToInProgressStore uint64
165	// Number of page stores cancelled due to release req
166	PageStoresCancelledByReleaseRequests uint64
167	VmscanWaiting                        uint64
168	// Number of times async ops added to pending queues
169	OpsPending uint64
170	// Number of times async ops given CPU time
171	OpsRunning uint64
172	// Number of times async ops queued for processing
173	OpsEnqueued uint64
174	// Number of async ops cancelled
175	OpsCancelled uint64
176	// Number of async ops rejected due to object lookup/create failure
177	OpsRejected uint64
178	// Number of async ops initialised
179	OpsInitialised uint64
180	// Number of async ops queued for deferred release
181	OpsDeferred uint64
182	// Number of async ops released (should equal ini=N when idle)
183	OpsReleased uint64
184	// Number of deferred-release async ops garbage collected
185	OpsGarbageCollected uint64
186	// Number of in-progress alloc_object() cache ops
187	CacheopAllocationsinProgress uint64
188	// Number of in-progress lookup_object() cache ops
189	CacheopLookupObjectInProgress uint64
190	// Number of in-progress lookup_complete() cache ops
191	CacheopLookupCompleteInPorgress uint64
192	// Number of in-progress grab_object() cache ops
193	CacheopGrabObjectInProgress uint64
194	CacheopInvalidations        uint64
195	// Number of in-progress update_object() cache ops
196	CacheopUpdateObjectInProgress uint64
197	// Number of in-progress drop_object() cache ops
198	CacheopDropObjectInProgress uint64
199	// Number of in-progress put_object() cache ops
200	CacheopPutObjectInProgress uint64
201	// Number of in-progress attr_changed() cache ops
202	CacheopAttributeChangeInProgress uint64
203	// Number of in-progress sync_cache() cache ops
204	CacheopSyncCacheInProgress uint64
205	// Number of in-progress read_or_alloc_page() cache ops
206	CacheopReadOrAllocPageInProgress uint64
207	// Number of in-progress read_or_alloc_pages() cache ops
208	CacheopReadOrAllocPagesInProgress uint64
209	// Number of in-progress allocate_page() cache ops
210	CacheopAllocatePageInProgress uint64
211	// Number of in-progress allocate_pages() cache ops
212	CacheopAllocatePagesInProgress uint64
213	// Number of in-progress write_page() cache ops
214	CacheopWritePagesInProgress uint64
215	// Number of in-progress uncache_page() cache ops
216	CacheopUncachePagesInProgress uint64
217	// Number of in-progress dissociate_pages() cache ops
218	CacheopDissociatePagesInProgress uint64
219	// Number of object lookups/creations rejected due to lack of space
220	CacheevLookupsAndCreationsRejectedLackSpace uint64
221	// Number of stale objects deleted
222	CacheevStaleObjectsDeleted uint64
223	// Number of objects retired when relinquished
224	CacheevRetiredWhenReliquished uint64
225	// Number of objects culled
226	CacheevObjectsCulled uint64
227}
228
229// Fscacheinfo returns information about current fscache statistics.
230// See https://www.kernel.org/doc/Documentation/filesystems/caching/fscache.txt
231func (fs FS) Fscacheinfo() (Fscacheinfo, error) {
232	b, err := util.ReadFileNoStat(fs.proc.Path("fs/fscache/stats"))
233	if err != nil {
234		return Fscacheinfo{}, err
235	}
236
237	m, err := parseFscacheinfo(bytes.NewReader(b))
238	if err != nil {
239		return Fscacheinfo{}, fmt.Errorf("failed to parse Fscacheinfo: %w", err)
240	}
241
242	return *m, nil
243}
244
245func setFSCacheFields(fields []string, setFields ...*uint64) error {
246	var err error
247	if len(fields) < len(setFields) {
248		return fmt.Errorf("Insufficient number of fields, expected %v, got %v", len(setFields), len(fields))
249	}
250
251	for i := range setFields {
252		*setFields[i], err = strconv.ParseUint(strings.Split(fields[i], "=")[1], 0, 64)
253		if err != nil {
254			return err
255		}
256	}
257	return nil
258}
259
260func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) {
261	var m Fscacheinfo
262	s := bufio.NewScanner(r)
263	for s.Scan() {
264		fields := strings.Fields(s.Text())
265		if len(fields) < 2 {
266			return nil, fmt.Errorf("malformed Fscacheinfo line: %q", s.Text())
267		}
268
269		switch fields[0] {
270		case "Cookies:":
271			err := setFSCacheFields(fields[1:], &m.IndexCookiesAllocated, &m.DataStorageCookiesAllocated,
272				&m.SpecialCookiesAllocated)
273			if err != nil {
274				return &m, err
275			}
276		case "Objects:":
277			err := setFSCacheFields(fields[1:], &m.ObjectsAllocated, &m.ObjectAllocationsFailure,
278				&m.ObjectsAvailable, &m.ObjectsDead)
279			if err != nil {
280				return &m, err
281			}
282		case "ChkAux":
283			err := setFSCacheFields(fields[2:], &m.ObjectsWithoutCoherencyCheck, &m.ObjectsWithCoherencyCheck,
284				&m.ObjectsNeedCoherencyCheckUpdate, &m.ObjectsDeclaredObsolete)
285			if err != nil {
286				return &m, err
287			}
288		case "Pages":
289			err := setFSCacheFields(fields[2:], &m.PagesMarkedAsBeingCached, &m.UncachePagesRequestSeen)
290			if err != nil {
291				return &m, err
292			}
293		case "Acquire:":
294			err := setFSCacheFields(fields[1:], &m.AcquireCookiesRequestSeen, &m.AcquireRequestsWithNullParent,
295				&m.AcquireRequestsRejectedNoCacheAvailable, &m.AcquireRequestsSucceeded, &m.AcquireRequestsRejectedDueToError,
296				&m.AcquireRequestsFailedDueToEnomem)
297			if err != nil {
298				return &m, err
299			}
300		case "Lookups:":
301			err := setFSCacheFields(fields[1:], &m.LookupsNumber, &m.LookupsNegative, &m.LookupsPositive,
302				&m.ObjectsCreatedByLookup, &m.LookupsTimedOutAndRequed)
303			if err != nil {
304				return &m, err
305			}
306		case "Invals":
307			err := setFSCacheFields(fields[2:], &m.InvalidationsNumber, &m.InvalidationsRunning)
308			if err != nil {
309				return &m, err
310			}
311		case "Updates:":
312			err := setFSCacheFields(fields[1:], &m.UpdateCookieRequestSeen, &m.UpdateRequestsWithNullParent,
313				&m.UpdateRequestsRunning)
314			if err != nil {
315				return &m, err
316			}
317		case "Relinqs:":
318			err := setFSCacheFields(fields[1:], &m.RelinquishCookiesRequestSeen, &m.RelinquishCookiesWithNullParent,
319				&m.RelinquishRequestsWaitingCompleteCreation, &m.RelinquishRetries)
320			if err != nil {
321				return &m, err
322			}
323		case "AttrChg:":
324			err := setFSCacheFields(fields[1:], &m.AttributeChangedRequestsSeen, &m.AttributeChangedRequestsQueued,
325				&m.AttributeChangedRejectDueToEnobufs, &m.AttributeChangedFailedDueToEnomem, &m.AttributeChangedOps)
326			if err != nil {
327				return &m, err
328			}
329		case "Allocs":
330			if strings.Split(fields[2], "=")[0] == "n" {
331				err := setFSCacheFields(fields[2:], &m.AllocationRequestsSeen, &m.AllocationOkRequests,
332					&m.AllocationWaitingOnLookup, &m.AllocationsRejectedDueToEnobufs, &m.AllocationsAbortedDueToErestartsys)
333				if err != nil {
334					return &m, err
335				}
336			} else {
337				err := setFSCacheFields(fields[2:], &m.AllocationOperationsSubmitted, &m.AllocationsWaitedForCPU,
338					&m.AllocationsAbortedDueToObjectDeath)
339				if err != nil {
340					return &m, err
341				}
342			}
343		case "Retrvls:":
344			if strings.Split(fields[1], "=")[0] == "n" {
345				err := setFSCacheFields(fields[1:], &m.RetrievalsReadRequests, &m.RetrievalsOk, &m.RetrievalsWaitingLookupCompletion,
346					&m.RetrievalsReturnedEnodata, &m.RetrievalsRejectedDueToEnobufs, &m.RetrievalsAbortedDueToErestartsys,
347					&m.RetrievalsFailedDueToEnomem)
348				if err != nil {
349					return &m, err
350				}
351			} else {
352				err := setFSCacheFields(fields[1:], &m.RetrievalsRequests, &m.RetrievalsWaitingCPU, &m.RetrievalsAbortedDueToObjectDeath)
353				if err != nil {
354					return &m, err
355				}
356			}
357		case "Stores":
358			if strings.Split(fields[2], "=")[0] == "n" {
359				err := setFSCacheFields(fields[2:], &m.StoreWriteRequests, &m.StoreSuccessfulRequests,
360					&m.StoreRequestsOnPendingStorage, &m.StoreRequestsRejectedDueToEnobufs, &m.StoreRequestsFailedDueToEnomem)
361				if err != nil {
362					return &m, err
363				}
364			} else {
365				err := setFSCacheFields(fields[2:], &m.StoreRequestsSubmitted, &m.StoreRequestsRunning,
366					&m.StorePagesWithRequestsProcessing, &m.StoreRequestsDeleted, &m.StoreRequestsOverStoreLimit)
367				if err != nil {
368					return &m, err
369				}
370			}
371		case "VmScan":
372			err := setFSCacheFields(fields[2:], &m.ReleaseRequestsAgainstPagesWithNoPendingStorage,
373				&m.ReleaseRequestsAgainstPagesStoredByTimeLockGranted, &m.ReleaseRequestsIgnoredDueToInProgressStore,
374				&m.PageStoresCancelledByReleaseRequests, &m.VmscanWaiting)
375			if err != nil {
376				return &m, err
377			}
378		case "Ops":
379			if strings.Split(fields[2], "=")[0] == "pend" {
380				err := setFSCacheFields(fields[2:], &m.OpsPending, &m.OpsRunning, &m.OpsEnqueued, &m.OpsCancelled, &m.OpsRejected)
381				if err != nil {
382					return &m, err
383				}
384			} else {
385				err := setFSCacheFields(fields[2:], &m.OpsInitialised, &m.OpsDeferred, &m.OpsReleased, &m.OpsGarbageCollected)
386				if err != nil {
387					return &m, err
388				}
389			}
390		case "CacheOp:":
391			if strings.Split(fields[1], "=")[0] == "alo" {
392				err := setFSCacheFields(fields[1:], &m.CacheopAllocationsinProgress, &m.CacheopLookupObjectInProgress,
393					&m.CacheopLookupCompleteInPorgress, &m.CacheopGrabObjectInProgress)
394				if err != nil {
395					return &m, err
396				}
397			} else if strings.Split(fields[1], "=")[0] == "inv" {
398				err := setFSCacheFields(fields[1:], &m.CacheopInvalidations, &m.CacheopUpdateObjectInProgress,
399					&m.CacheopDropObjectInProgress, &m.CacheopPutObjectInProgress, &m.CacheopAttributeChangeInProgress,
400					&m.CacheopSyncCacheInProgress)
401				if err != nil {
402					return &m, err
403				}
404			} else {
405				err := setFSCacheFields(fields[1:], &m.CacheopReadOrAllocPageInProgress, &m.CacheopReadOrAllocPagesInProgress,
406					&m.CacheopAllocatePageInProgress, &m.CacheopAllocatePagesInProgress, &m.CacheopWritePagesInProgress,
407					&m.CacheopUncachePagesInProgress, &m.CacheopDissociatePagesInProgress)
408				if err != nil {
409					return &m, err
410				}
411			}
412		case "CacheEv:":
413			err := setFSCacheFields(fields[1:], &m.CacheevLookupsAndCreationsRejectedLackSpace, &m.CacheevStaleObjectsDeleted,
414				&m.CacheevRetiredWhenReliquished, &m.CacheevObjectsCulled)
415			if err != nil {
416				return &m, err
417			}
418		}
419	}
420
421	return &m, nil
422}
423