@@ -17,6 +17,7 @@ package libcontainer
1717import (
1818 "bufio"
1919 "encoding/json"
20+ "flag"
2021 "fmt"
2122 "io"
2223 "io/ioutil"
@@ -38,16 +39,27 @@ import (
3839 "k8s.io/klog/v2"
3940)
4041
42+ var (
43+ whitelistedUlimits = [... ]string {"max_open_files" }
44+ referencedResetInterval = flag .Uint64 ("referenced_reset_interval" , 0 ,
45+ "Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)" )
46+
47+ smapsFilePathPattern = "/proc/%d/smaps"
48+ clearRefsFilePathPattern = "/proc/%d/clear_refs"
49+
50+ referencedRegexp = regexp .MustCompile (`Referenced:\s*([0-9]+)\s*kB` )
51+ isDigitRegExp = regexp .MustCompile ("\\ d+" )
52+ )
53+
4154type Handler struct {
4255 cgroupManager cgroups.Manager
4356 rootFs string
4457 pid int
4558 includedMetrics container.MetricSet
4659 pidMetricsCache map [int ]* info.CpuSchedstat
60+ cycles uint64
4761}
4862
49- var whitelistedUlimits = [... ]string {"max_open_files" }
50-
5163func NewHandler (cgroupManager cgroups.Manager , rootFs string , pid int , includedMetrics container.MetricSet ) * Handler {
5264 return & Handler {
5365 cgroupManager : cgroupManager ,
@@ -81,6 +93,19 @@ func (h *Handler) GetStats() (*info.ContainerStats, error) {
8193 }
8294 }
8395
96+ if h .includedMetrics .Has (container .ReferencedMemoryMetrics ) {
97+ h .cycles ++
98+ pids , err := h .cgroupManager .GetPids ()
99+ if err != nil {
100+ klog .V (4 ).Infof ("Could not get PIDs for container %d: %v" , h .pid , err )
101+ } else {
102+ stats .ReferencedMemory , err = referencedBytesStat (pids , h .cycles , * referencedResetInterval )
103+ if err != nil {
104+ klog .V (4 ).Infof ("Unable to get referenced bytes: %v" , err )
105+ }
106+ }
107+ }
108+
84109 // If we know the pid then get network stats from /proc/<pid>/net/dev
85110 if h .pid == 0 {
86111 return stats , nil
@@ -318,6 +343,92 @@ func schedulerStatsFromProcs(rootFs string, pids []int, pidMetricsCache map[int]
318343 return schedstats , nil
319344}
320345
346+ // referencedBytesStat gets and clears referenced bytes
347+ // see: https://github.com/brendangregg/wss#wsspl-referenced-page-flag
348+ func referencedBytesStat (pids []int , cycles uint64 , resetInterval uint64 ) (uint64 , error ) {
349+ referencedKBytes , err := getReferencedKBytes (pids )
350+ if err != nil {
351+ return uint64 (0 ), err
352+ }
353+
354+ err = clearReferencedBytes (pids , cycles , resetInterval )
355+ if err != nil {
356+ return uint64 (0 ), err
357+ }
358+ return referencedKBytes * 1024 , nil
359+ }
360+
361+ func getReferencedKBytes (pids []int ) (uint64 , error ) {
362+ referencedKBytes := uint64 (0 )
363+ readSmapsContent := false
364+ foundMatch := false
365+ for _ , pid := range pids {
366+ smapsFilePath := fmt .Sprintf (smapsFilePathPattern , pid )
367+ smapsContent , err := ioutil .ReadFile (smapsFilePath )
368+ if err != nil {
369+ klog .V (5 ).Infof ("Cannot read %s file, err: %s" , smapsFilePath , err )
370+ if os .IsNotExist (err ) {
371+ continue //smaps file does not exists for all PIDs
372+ }
373+ return 0 , err
374+ }
375+ readSmapsContent = true
376+
377+ allMatches := referencedRegexp .FindAllSubmatch (smapsContent , - 1 )
378+ if len (allMatches ) == 0 {
379+ klog .V (5 ).Infof ("Not found any information about referenced bytes in %s file" , smapsFilePath )
380+ continue // referenced bytes may not exist in smaps file
381+ }
382+
383+ for _ , matches := range allMatches {
384+ if len (matches ) != 2 {
385+ return 0 , fmt .Errorf ("failed to match regexp in output: %s" , string (smapsContent ))
386+ }
387+ foundMatch = true
388+ referenced , err := strconv .ParseUint (string (matches [1 ]), 10 , 64 )
389+ if err != nil {
390+ return 0 , err
391+ }
392+ referencedKBytes += referenced
393+ }
394+ }
395+
396+ if len (pids ) != 0 {
397+ if ! readSmapsContent {
398+ klog .Warningf ("Cannot read smaps files for any PID from %s" , "CONTAINER" )
399+ } else if ! foundMatch {
400+ klog .Warningf ("Not found any information about referenced bytes in smaps files for any PID from %s" , "CONTAINER" )
401+ }
402+ }
403+ return referencedKBytes , nil
404+ }
405+
406+ func clearReferencedBytes (pids []int , cycles uint64 , resetInterval uint64 ) error {
407+ if resetInterval == 0 {
408+ return nil
409+ }
410+
411+ if cycles % resetInterval == 0 {
412+ for _ , pid := range pids {
413+ clearRefsFilePath := fmt .Sprintf (clearRefsFilePathPattern , pid )
414+ clerRefsFile , err := os .OpenFile (clearRefsFilePath , os .O_WRONLY , 0644 )
415+ if err != nil {
416+ // clear_refs file may not exist for all PIDs
417+ continue
418+ }
419+ _ , err = clerRefsFile .WriteString ("1\n " )
420+ if err != nil {
421+ return err
422+ }
423+ err = clerRefsFile .Close ()
424+ if err != nil {
425+ return err
426+ }
427+ }
428+ }
429+ return nil
430+ }
431+
321432func networkStatsFromProc (rootFs string , pid int ) ([]info.InterfaceStats , error ) {
322433 netStatsFile := path .Join (rootFs , "proc" , strconv .Itoa (pid ), "/net/dev" )
323434
0 commit comments