metric/system/cgroup/cgv1/memory.go (135 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you 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 cgv1 import ( "bufio" "fmt" "os" "path/filepath" "github.com/elastic/elastic-agent-libs/opt" "github.com/elastic/elastic-agent-system-metrics/metric/system/cgroup/cgcommon" ) // MemorySubsystem contains the metrics and limits from the "memory" subsystem. type MemorySubsystem struct { ID string `json:"id,omitempty"` // ID of the cgroup. Path string `json:"path,omitempty"` // Path to the cgroup relative to the cgroup subsystem's mountpoint. Mem MemoryData `json:"mem" struct:"mem"` // Memory usage by tasks in this cgroup. MemSwap MemoryData `json:"memsw" struct:"memsw"` // Memory plus swap usage by tasks in this cgroup. Kernel MemoryData `json:"kmem" struct:"kmem"` // Kernel memory used by tasks in this cgroup. KernelTCP MemoryData `json:"kmem_tcp" struct:"kmem_tcp"` // Kernel TCP buffer memory used by tasks in this cgroup. Stats MemoryStat `json:"stats" struct:"stats"` // A wide range of memory statistics. } // MemoryData groups related memory usage metrics and limits. type MemoryData struct { Usage MemSubsystemUsage `json:"usage" struct:"usage"` // Usage in bytes. Limit opt.Bytes `json:"limit" struct:"limit"` // Limit in bytes. Failures uint64 `json:"failures" struct:"failures"` // Number of times the memory limit has been reached. } // MemSubsystemUsage groups fields used in memory.SUBSYSTEM.usage type MemSubsystemUsage struct { Bytes uint64 `json:"bytes" struct:"bytes"` Max opt.Bytes `json:"max" struct:"max"` } // MemoryStat contains various memory statistics and accounting information // associated with a cgroup. type MemoryStat struct { // Page cache, including tmpfs (shmem), in bytes. Cache opt.Bytes `json:"cache" struct:"cache"` // Anonymous and swap cache, not including tmpfs (shmem), in bytes. RSS opt.Bytes `json:"rss" struct:"rss"` // Anonymous transparent hugepages in bytes. RSSHuge opt.Bytes `json:"rss_huge" struct:"rss_huge"` // Size of memory-mapped mapped files, including tmpfs (shmem), in bytes. MappedFile opt.Bytes `json:"mapped_file" struct:"mapped_file"` // Number of pages paged into memory. PagesIn uint64 `json:"pages_in" struct:"pages_in"` // Number of pages paged out of memory. PagesOut uint64 `json:"pages_out" struct:"pages_out"` // Number of times a task in the cgroup triggered a page fault. PageFaults uint64 `json:"page_faults" struct:"page_faults"` // Number of times a task in the cgroup triggered a major page fault. MajorPageFaults uint64 `json:"major_page_faults" struct:"major_page_faults"` // Swap usage in bytes. Swap opt.Bytes `json:"swap"` // Anonymous and swap cache on active least-recently-used (LRU) list, including tmpfs (shmem), in bytes. ActiveAnon opt.Bytes `json:"active_anon" struct:"active_anon"` // Anonymous and swap cache on inactive LRU list, including tmpfs (shmem), in bytes. InactiveAnon opt.Bytes `json:"inactive_anon" struct:"inactive_anon"` // File-backed memory on active LRU list, in bytes. ActiveFile opt.Bytes `json:"active_file" struct:"active_file"` // File-backed memory on inactive LRU list, in bytes. InactiveFile opt.Bytes `json:"inactive_file" struct:"inactive_file"` // Memory that cannot be reclaimed, in bytes. Unevictable opt.Bytes `json:"unevictable" struct:"unevictable"` // Memory limit for the hierarchy that contains the memory cgroup, in bytes. HierarchicalMemoryLimit opt.Bytes `json:"hierarchical_memory_limit" struct:"hierarchical_memory_limit"` // Memory plus swap limit for the hierarchy that contains the memory cgroup, in bytes. HierarchicalMemswLimit opt.Bytes `json:"hierarchical_memsw_limit" struct:"hierarchical_memsw_limit"` } // Get reads metrics from the "memory" subsystem. path is the filepath to the // cgroup hierarchy to read. func (mem *MemorySubsystem) Get(path string) error { if err := memoryData(path, "memory", &mem.Mem); err != nil { return fmt.Errorf("error fetching memory stats: %w", err) } if err := memoryData(path, "memory.memsw", &mem.MemSwap); err != nil { return fmt.Errorf("error fetching memsw stats: %w", err) } if err := memoryData(path, "memory.kmem", &mem.Kernel); err != nil { return fmt.Errorf("error fetching kmem stats: %w", err) } if err := memoryData(path, "memory.kmem.tcp", &mem.KernelTCP); err != nil { return fmt.Errorf("error fetching kmem.tcp stats: %w", err) } if err := memoryStats(path, mem); err != nil { return fmt.Errorf("error fetching memory.stat metrics: %w", err) } return nil } func memoryData(path, prefix string, data *MemoryData) error { var err error data.Usage.Bytes, err = cgcommon.ParseUintFromFile(path, prefix+".usage_in_bytes") if err != nil { return fmt.Errorf("error fetching usage_in_bytes: %w", err) } data.Usage.Max.Bytes, err = cgcommon.ParseUintFromFile(path, prefix+".max_usage_in_bytes") if err != nil { return fmt.Errorf("error fetching max_usage_in_bytes: %w", err) } data.Limit.Bytes, err = cgcommon.ParseUintFromFile(path, prefix+".limit_in_bytes") if err != nil { return fmt.Errorf("error fetching limit_in_bytes: %w", err) } data.Failures, err = cgcommon.ParseUintFromFile(path, prefix+".failcnt") if err != nil { return fmt.Errorf("error fetching failcnt: %w", err) } return nil } func memoryStats(path string, mem *MemorySubsystem) error { f, err := os.Open(filepath.Join(path, "memory.stat")) if err != nil { if os.IsNotExist(err) { return nil } return err } defer f.Close() sc := bufio.NewScanner(f) for sc.Scan() { t, v, err := cgcommon.ParseCgroupParamKeyValue(sc.Text()) if err != nil { return err } switch t { case "cache": mem.Stats.Cache.Bytes = v case "rss": mem.Stats.RSS.Bytes = v case "rss_huge": mem.Stats.RSSHuge.Bytes = v case "mapped_file": mem.Stats.MappedFile.Bytes = v case "pgpgin": mem.Stats.PagesIn = v case "pgpgout": mem.Stats.PagesOut = v case "pgfault": mem.Stats.PageFaults = v case "pgmajfault": mem.Stats.MajorPageFaults = v case "swap": mem.Stats.Swap.Bytes = v case "active_anon": mem.Stats.ActiveAnon.Bytes = v case "inactive_anon": mem.Stats.InactiveAnon.Bytes = v case "active_file": mem.Stats.ActiveFile.Bytes = v case "inactive_file": mem.Stats.InactiveFile.Bytes = v case "unevictable": mem.Stats.Unevictable.Bytes = v case "hierarchical_memory_limit": mem.Stats.HierarchicalMemoryLimit.Bytes = v case "hierarchical_memsw_limit": mem.Stats.HierarchicalMemswLimit.Bytes = v } } return sc.Err() }