@@ -102,6 +102,12 @@ func statMemory(dirPath string, stats *cgroups.Stats) error {
102102 // cgroup v2 is always hierarchical.
103103 stats .MemoryStats .UseHierarchy = true
104104
105+ pagesByNUMA , err := getPageUsageByNUMAV2 (dirPath )
106+ if err != nil {
107+ return err
108+ }
109+ stats .MemoryStats .PageUsageByNUMA = pagesByNUMA
110+
105111 memoryUsage , err := getMemoryDataV2 (dirPath , "" )
106112 if err != nil {
107113 if errors .Is (err , unix .ENOENT ) && dirPath == UnifiedMountpoint {
@@ -219,3 +225,117 @@ func statsFromMeminfo(stats *cgroups.Stats) error {
219225
220226 return nil
221227}
228+
229+ func getPageUsageByNUMAV2 (path string ) (cgroups.PageUsageByNUMA , error ) {
230+ const (
231+ maxColumns = math .MaxUint8 + 1
232+ file = "memory.numa_stat"
233+ )
234+ stats := cgroups.PageUsageByNUMA {}
235+
236+ fd , err := cgroups .OpenFile (path , file , os .O_RDONLY )
237+ if os .IsNotExist (err ) {
238+ return stats , nil
239+ } else if err != nil {
240+ return stats , err
241+ }
242+ defer fd .Close ()
243+
244+ // anon N0=139022336 N1=2760704
245+ // file N0=449581056 N1=0
246+ // kernel_stack N0=3670016 N1=0
247+ // pagetables N0=4116480 N1=0
248+ // sec_pagetables N0=0 N1=0
249+ // shmem N0=0 N1=0
250+ // file_mapped N0=55029760 N1=0
251+ // file_dirty N0=0 N1=0
252+ // file_writeback N0=0 N1=0
253+ // swapcached N0=0 N1=0
254+ // anon_thp N0=0 N1=0
255+ // file_thp N0=0 N1=0
256+ // shmem_thp N0=0 N1=0
257+ // inactive_anon N0=138956800 N1=2752512
258+ // active_anon N0=65536 N1=8192
259+ // inactive_file N0=14770176 N1=0
260+ // active_file N0=434810880 N1=0
261+ // unevictable N0=0 N1=0
262+ // slab_reclaimable N0=2358224 N1=11088
263+ // slab_unreclaimable N0=2672352 N1=544144
264+ // workingset_refault_anon N0=0 N1=0
265+ // workingset_refault_file N0=0 N1=0
266+ // workingset_activate_anon N0=0 N1=0
267+ // workingset_activate_file N0=0 N1=0
268+ // workingset_restore_anon N0=0 N1=0
269+ // workingset_restore_file N0=0 N1=0
270+ // workingset_nodereclaim N0=0 N1=0
271+
272+ scanner := bufio .NewScanner (fd )
273+ for scanner .Scan () {
274+ var field * cgroups.PageStats
275+
276+ line := scanner .Text ()
277+ columns := strings .SplitN (line , " " , maxColumns )
278+ for i , column := range columns {
279+ byNode := strings .SplitN (column , "=" , 2 )
280+ key := byNode [0 ]
281+ if i == 0 { // First column: key is name, val is total.
282+ field = getNUMAFieldV2 (& stats , key )
283+ if field == nil { // unknown field (new kernel?)
284+ break
285+ }
286+ field .Nodes = map [uint8 ]uint64 {}
287+ } else { // Subsequent columns: key is N<id>, val is usage.
288+ val := byNode [1 ]
289+ if len (key ) < 2 || key [0 ] != 'N' {
290+ // This is definitely an error.
291+ return stats , malformedLine (path , file , line )
292+ }
293+
294+ n , err := strconv .ParseUint (key [1 :], 10 , 8 )
295+ if err != nil {
296+ return stats , & parseError {Path : path , File : file , Err : err }
297+ }
298+
299+ usage , err := strconv .ParseUint (val , 10 , 64 )
300+ if err != nil {
301+ return stats , & parseError {Path : path , File : file , Err : err }
302+ }
303+ if ! strings .HasSuffix (key , "thp" ) {
304+ field .Nodes [uint8 (n )] += usage
305+ field .Total += usage
306+ } else {
307+ field .Nodes [uint8 (n )] += (usage >> 5 )
308+ field .Total += (usage >> 5 )
309+ }
310+ }
311+
312+ }
313+ }
314+ if err := scanner .Err (); err != nil {
315+ return cgroups.PageUsageByNUMA {}, & parseError {Path : path , File : file , Err : err }
316+ }
317+
318+ return stats , nil
319+ }
320+
321+ func getNUMAFieldV2 (stats * cgroups.PageUsageByNUMA , name string ) * cgroups.PageStats {
322+ switch name {
323+ case "pagetable" :
324+ return & stats .Total
325+ case "anon" :
326+ return & stats .Anon
327+ case "anon_thp" :
328+ return & stats .Anon
329+ case "file_mapped" :
330+ return & stats .File
331+ case "file_dirty" :
332+ return & stats .File
333+ case "file_writeback" :
334+ return & stats .File
335+ case "file_thp" :
336+ return & stats .File
337+ case "unevictable" :
338+ return & stats .Unevictable
339+ }
340+ return nil
341+ }
0 commit comments