// Copyright 2019 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package procfs import ( "bufio" "bytes" "fmt" "io" "strconv" "strings" "github.com/prometheus/procfs/internal/util" ) // Fscacheinfo represents fscache statistics. type Fscacheinfo struct { // Number of index cookies allocated IndexCookiesAllocated uint64 // data storage cookies allocated DataStorageCookiesAllocated uint64 // Number of special cookies allocated SpecialCookiesAllocated uint64 // Number of objects allocated ObjectsAllocated uint64 // Number of object allocation failures ObjectAllocationsFailure uint64 // Number of objects that reached the available state ObjectsAvailable uint64 // Number of objects that reached the dead state ObjectsDead uint64 // Number of objects that didn't have a coherency check ObjectsWithoutCoherencyCheck uint64 // Number of objects that passed a coherency check ObjectsWithCoherencyCheck uint64 // Number of objects that needed a coherency data update ObjectsNeedCoherencyCheckUpdate uint64 // Number of objects that were declared obsolete ObjectsDeclaredObsolete uint64 // Number of pages marked as being cached PagesMarkedAsBeingCached uint64 // Number of uncache page requests seen UncachePagesRequestSeen uint64 // Number of acquire cookie requests seen AcquireCookiesRequestSeen uint64 // Number of acq reqs given a NULL parent AcquireRequestsWithNullParent uint64 // Number of acq reqs rejected due to no cache available AcquireRequestsRejectedNoCacheAvailable uint64 // Number of acq reqs succeeded AcquireRequestsSucceeded uint64 // Number of acq reqs rejected due to error AcquireRequestsRejectedDueToError uint64 // Number of acq reqs failed on ENOMEM AcquireRequestsFailedDueToEnomem uint64 // Number of lookup calls made on cache backends LookupsNumber uint64 // Number of negative lookups made LookupsNegative uint64 // Number of positive lookups made LookupsPositive uint64 // Number of objects created by lookup ObjectsCreatedByLookup uint64 // Number of lookups timed out and requeued LookupsTimedOutAndRequed uint64 InvalidationsNumber uint64 InvalidationsRunning uint64 // Number of update cookie requests seen UpdateCookieRequestSeen uint64 // Number of upd reqs given a NULL parent UpdateRequestsWithNullParent uint64 // Number of upd reqs granted CPU time UpdateRequestsRunning uint64 // Number of relinquish cookie requests seen RelinquishCookiesRequestSeen uint64 // Number of rlq reqs given a NULL parent RelinquishCookiesWithNullParent uint64 // Number of rlq reqs waited on completion of creation RelinquishRequestsWaitingCompleteCreation uint64 // Relinqs rtr RelinquishRetries uint64 // Number of attribute changed requests seen AttributeChangedRequestsSeen uint64 // Number of attr changed requests queued AttributeChangedRequestsQueued uint64 // Number of attr changed rejected -ENOBUFS AttributeChangedRejectDueToEnobufs uint64 // Number of attr changed failed -ENOMEM AttributeChangedFailedDueToEnomem uint64 // Number of attr changed ops given CPU time AttributeChangedOps uint64 // Number of allocation requests seen AllocationRequestsSeen uint64 // Number of successful alloc reqs AllocationOkRequests uint64 // Number of alloc reqs that waited on lookup completion AllocationWaitingOnLookup uint64 // Number of alloc reqs rejected -ENOBUFS AllocationsRejectedDueToEnobufs uint64 // Number of alloc reqs aborted -ERESTARTSYS AllocationsAbortedDueToErestartsys uint64 // Number of alloc reqs submitted AllocationOperationsSubmitted uint64 // Number of alloc reqs waited for CPU time AllocationsWaitedForCPU uint64 // Number of alloc reqs aborted due to object death AllocationsAbortedDueToObjectDeath uint64 // Number of retrieval (read) requests seen RetrievalsReadRequests uint64 // Number of successful retr reqs RetrievalsOk uint64 // Number of retr reqs that waited on lookup completion RetrievalsWaitingLookupCompletion uint64 // Number of retr reqs returned -ENODATA RetrievalsReturnedEnodata uint64 // Number of retr reqs rejected -ENOBUFS RetrievalsRejectedDueToEnobufs uint64 // Number of retr reqs aborted -ERESTARTSYS RetrievalsAbortedDueToErestartsys uint64 // Number of retr reqs failed -ENOMEM RetrievalsFailedDueToEnomem uint64 // Number of retr reqs submitted RetrievalsRequests uint64 // Number of retr reqs waited for CPU time RetrievalsWaitingCPU uint64 // Number of retr reqs aborted due to object death RetrievalsAbortedDueToObjectDeath uint64 // Number of storage (write) requests seen StoreWriteRequests uint64 // Number of successful store reqs StoreSuccessfulRequests uint64 // Number of store reqs on a page already pending storage StoreRequestsOnPendingStorage uint64 // Number of store reqs rejected -ENOBUFS StoreRequestsRejectedDueToEnobufs uint64 // Number of store reqs failed -ENOMEM StoreRequestsFailedDueToEnomem uint64 // Number of store reqs submitted StoreRequestsSubmitted uint64 // Number of store reqs granted CPU time StoreRequestsRunning uint64 // Number of pages given store req processing time StorePagesWithRequestsProcessing uint64 // Number of store reqs deleted from tracking tree StoreRequestsDeleted uint64 // Number of store reqs over store limit StoreRequestsOverStoreLimit uint64 // Number of release reqs against pages with no pending store ReleaseRequestsAgainstPagesWithNoPendingStorage uint64 // Number of release reqs against pages stored by time lock granted ReleaseRequestsAgainstPagesStoredByTimeLockGranted uint64 // Number of release reqs ignored due to in-progress store ReleaseRequestsIgnoredDueToInProgressStore uint64 // Number of page stores cancelled due to release req PageStoresCancelledByReleaseRequests uint64 VmscanWaiting uint64 // Number of times async ops added to pending queues OpsPending uint64 // Number of times async ops given CPU time OpsRunning uint64 // Number of times async ops queued for processing OpsEnqueued uint64 // Number of async ops cancelled OpsCancelled uint64 // Number of async ops rejected due to object lookup/create failure OpsRejected uint64 // Number of async ops initialised OpsInitialised uint64 // Number of async ops queued for deferred release OpsDeferred uint64 // Number of async ops released (should equal ini=N when idle) OpsReleased uint64 // Number of deferred-release async ops garbage collected OpsGarbageCollected uint64 // Number of in-progress alloc_object() cache ops CacheopAllocationsinProgress uint64 // Number of in-progress lookup_object() cache ops CacheopLookupObjectInProgress uint64 // Number of in-progress lookup_complete() cache ops CacheopLookupCompleteInPorgress uint64 // Number of in-progress grab_object() cache ops CacheopGrabObjectInProgress uint64 CacheopInvalidations uint64 // Number of in-progress update_object() cache ops CacheopUpdateObjectInProgress uint64 // Number of in-progress drop_object() cache ops CacheopDropObjectInProgress uint64 // Number of in-progress put_object() cache ops CacheopPutObjectInProgress uint64 // Number of in-progress attr_changed() cache ops CacheopAttributeChangeInProgress uint64 // Number of in-progress sync_cache() cache ops CacheopSyncCacheInProgress uint64 // Number of in-progress read_or_alloc_page() cache ops CacheopReadOrAllocPageInProgress uint64 // Number of in-progress read_or_alloc_pages() cache ops CacheopReadOrAllocPagesInProgress uint64 // Number of in-progress allocate_page() cache ops CacheopAllocatePageInProgress uint64 // Number of in-progress allocate_pages() cache ops CacheopAllocatePagesInProgress uint64 // Number of in-progress write_page() cache ops CacheopWritePagesInProgress uint64 // Number of in-progress uncache_page() cache ops CacheopUncachePagesInProgress uint64 // Number of in-progress dissociate_pages() cache ops CacheopDissociatePagesInProgress uint64 // Number of object lookups/creations rejected due to lack of space CacheevLookupsAndCreationsRejectedLackSpace uint64 // Number of stale objects deleted CacheevStaleObjectsDeleted uint64 // Number of objects retired when relinquished CacheevRetiredWhenReliquished uint64 // Number of objects culled CacheevObjectsCulled uint64 } // Fscacheinfo returns information about current fscache statistics. // See https://www.kernel.org/doc/Documentation/filesystems/caching/fscache.txt func (fs FS) Fscacheinfo() (Fscacheinfo, error) { b, err := util.ReadFileNoStat(fs.proc.Path("fs/fscache/stats")) if err != nil { return Fscacheinfo{}, err } m, err := parseFscacheinfo(bytes.NewReader(b)) if err != nil { return Fscacheinfo{}, fmt.Errorf("failed to parse Fscacheinfo: %v", err) } return *m, nil } func setFSCacheFields(fields []string, setFields ...*uint64) error { var err error if len(fields) < len(setFields) { return fmt.Errorf("Insufficient number of fields, expected %v, got %v", len(setFields), len(fields)) } for i := range setFields { *setFields[i], err = strconv.ParseUint(strings.Split(fields[i], "=")[1], 0, 64) if err != nil { return err } } return nil } func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) { var m Fscacheinfo s := bufio.NewScanner(r) for s.Scan() { fields := strings.Fields(s.Text()) if len(fields) < 2 { return nil, fmt.Errorf("malformed Fscacheinfo line: %q", s.Text()) } switch fields[0] { case "Cookies:": err := setFSCacheFields(fields[1:], &m.IndexCookiesAllocated, &m.DataStorageCookiesAllocated, &m.SpecialCookiesAllocated) if err != nil { return &m, err } case "Objects:": err := setFSCacheFields(fields[1:], &m.ObjectsAllocated, &m.ObjectAllocationsFailure, &m.ObjectsAvailable, &m.ObjectsDead) if err != nil { return &m, err } case "ChkAux": err := setFSCacheFields(fields[2:], &m.ObjectsWithoutCoherencyCheck, &m.ObjectsWithCoherencyCheck, &m.ObjectsNeedCoherencyCheckUpdate, &m.ObjectsDeclaredObsolete) if err != nil { return &m, err } case "Pages": err := setFSCacheFields(fields[2:], &m.PagesMarkedAsBeingCached, &m.UncachePagesRequestSeen) if err != nil { return &m, err } case "Acquire:": err := setFSCacheFields(fields[1:], &m.AcquireCookiesRequestSeen, &m.AcquireRequestsWithNullParent, &m.AcquireRequestsRejectedNoCacheAvailable, &m.AcquireRequestsSucceeded, &m.AcquireRequestsRejectedDueToError, &m.AcquireRequestsFailedDueToEnomem) if err != nil { return &m, err } case "Lookups:": err := setFSCacheFields(fields[1:], &m.LookupsNumber, &m.LookupsNegative, &m.LookupsPositive, &m.ObjectsCreatedByLookup, &m.LookupsTimedOutAndRequed) if err != nil { return &m, err } case "Invals": err := setFSCacheFields(fields[2:], &m.InvalidationsNumber, &m.InvalidationsRunning) if err != nil { return &m, err } case "Updates:": err := setFSCacheFields(fields[1:], &m.UpdateCookieRequestSeen, &m.UpdateRequestsWithNullParent, &m.UpdateRequestsRunning) if err != nil { return &m, err } case "Relinqs:": err := setFSCacheFields(fields[1:], &m.RelinquishCookiesRequestSeen, &m.RelinquishCookiesWithNullParent, &m.RelinquishRequestsWaitingCompleteCreation, &m.RelinquishRetries) if err != nil { return &m, err } case "AttrChg:": err := setFSCacheFields(fields[1:], &m.AttributeChangedRequestsSeen, &m.AttributeChangedRequestsQueued, &m.AttributeChangedRejectDueToEnobufs, &m.AttributeChangedFailedDueToEnomem, &m.AttributeChangedOps) if err != nil { return &m, err } case "Allocs": if strings.Split(fields[2], "=")[0] == "n" { err := setFSCacheFields(fields[2:], &m.AllocationRequestsSeen, &m.AllocationOkRequests, &m.AllocationWaitingOnLookup, &m.AllocationsRejectedDueToEnobufs, &m.AllocationsAbortedDueToErestartsys) if err != nil { return &m, err } } else { err := setFSCacheFields(fields[2:], &m.AllocationOperationsSubmitted, &m.AllocationsWaitedForCPU, &m.AllocationsAbortedDueToObjectDeath) if err != nil { return &m, err } } case "Retrvls:": if strings.Split(fields[1], "=")[0] == "n" { err := setFSCacheFields(fields[1:], &m.RetrievalsReadRequests, &m.RetrievalsOk, &m.RetrievalsWaitingLookupCompletion, &m.RetrievalsReturnedEnodata, &m.RetrievalsRejectedDueToEnobufs, &m.RetrievalsAbortedDueToErestartsys, &m.RetrievalsFailedDueToEnomem) if err != nil { return &m, err } } else { err := setFSCacheFields(fields[1:], &m.RetrievalsRequests, &m.RetrievalsWaitingCPU, &m.RetrievalsAbortedDueToObjectDeath) if err != nil { return &m, err } } case "Stores": if strings.Split(fields[2], "=")[0] == "n" { err := setFSCacheFields(fields[2:], &m.StoreWriteRequests, &m.StoreSuccessfulRequests, &m.StoreRequestsOnPendingStorage, &m.StoreRequestsRejectedDueToEnobufs, &m.StoreRequestsFailedDueToEnomem) if err != nil { return &m, err } } else { err := setFSCacheFields(fields[2:], &m.StoreRequestsSubmitted, &m.StoreRequestsRunning, &m.StorePagesWithRequestsProcessing, &m.StoreRequestsDeleted, &m.StoreRequestsOverStoreLimit) if err != nil { return &m, err } } case "VmScan": err := setFSCacheFields(fields[2:], &m.ReleaseRequestsAgainstPagesWithNoPendingStorage, &m.ReleaseRequestsAgainstPagesStoredByTimeLockGranted, &m.ReleaseRequestsIgnoredDueToInProgressStore, &m.PageStoresCancelledByReleaseRequests, &m.VmscanWaiting) if err != nil { return &m, err } case "Ops": if strings.Split(fields[2], "=")[0] == "pend" { err := setFSCacheFields(fields[2:], &m.OpsPending, &m.OpsRunning, &m.OpsEnqueued, &m.OpsCancelled, &m.OpsRejected) if err != nil { return &m, err } } else { err := setFSCacheFields(fields[2:], &m.OpsInitialised, &m.OpsDeferred, &m.OpsReleased, &m.OpsGarbageCollected) if err != nil { return &m, err } } case "CacheOp:": if strings.Split(fields[1], "=")[0] == "alo" { err := setFSCacheFields(fields[1:], &m.CacheopAllocationsinProgress, &m.CacheopLookupObjectInProgress, &m.CacheopLookupCompleteInPorgress, &m.CacheopGrabObjectInProgress) if err != nil { return &m, err } } else if strings.Split(fields[1], "=")[0] == "inv" { err := setFSCacheFields(fields[1:], &m.CacheopInvalidations, &m.CacheopUpdateObjectInProgress, &m.CacheopDropObjectInProgress, &m.CacheopPutObjectInProgress, &m.CacheopAttributeChangeInProgress, &m.CacheopSyncCacheInProgress) if err != nil { return &m, err } } else { err := setFSCacheFields(fields[1:], &m.CacheopReadOrAllocPageInProgress, &m.CacheopReadOrAllocPagesInProgress, &m.CacheopAllocatePageInProgress, &m.CacheopAllocatePagesInProgress, &m.CacheopWritePagesInProgress, &m.CacheopUncachePagesInProgress, &m.CacheopDissociatePagesInProgress) if err != nil { return &m, err } } case "CacheEv:": err := setFSCacheFields(fields[1:], &m.CacheevLookupsAndCreationsRejectedLackSpace, &m.CacheevStaleObjectsDeleted, &m.CacheevRetiredWhenReliquished, &m.CacheevObjectsCulled) if err != nil { return &m, err } } } return &m, nil }