Skip to content

Commit 1431af1

Browse files
Dan Schatzbergfacebook-github-bot
authored andcommitted
Add data collection for memory.numa_stat
Summary: As titled, this file has valuable data for numa memory consumption Reviewed By: lnyng Differential Revision: D40514647 fbshipit-source-id: 8a210f1e62b6aeea6242c432b90a304661498958
1 parent 9fe7f58 commit 1431af1

File tree

3 files changed

+312
-7
lines changed

3 files changed

+312
-7
lines changed

below/cgroupfs/src/lib.rs

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ macro_rules! impl_read_pressure {
9494
}};
9595
}
9696

97+
macro_rules! parse_and_set_fields {
98+
($struct:expr; $key:expr; $value:expr; [ $($field:ident,)+ ]) => (
99+
match $key {
100+
$(stringify!($field) => $struct.$field = Some($value),)*
101+
_ => (),
102+
}
103+
)
104+
}
105+
97106
impl CgroupReader {
98107
pub fn new(root: PathBuf) -> Result<CgroupReader> {
99108
CgroupReader::new_with_relative_path(root, PathBuf::from(OsStr::new("")))
@@ -248,6 +257,84 @@ impl CgroupReader {
248257
})
249258
}
250259

260+
// Reads memory.numa_stat - the return value is a map from numa_node_id ->
261+
// memory breakdown
262+
pub fn read_memory_numa_stat(&self) -> Result<BTreeMap<u32, MemoryNumaStat>> {
263+
let mut s: BTreeMap<u32, MemoryNumaStat> = BTreeMap::new();
264+
let file_name = "memory.numa_stat";
265+
let file = self
266+
.dir
267+
.open_file(file_name)
268+
.map_err(|e| self.io_error(file_name, e))?;
269+
let buf_reader = BufReader::new(file);
270+
for line in buf_reader.lines() {
271+
let line = line.map_err(|e| self.io_error(file_name, e))?;
272+
let items = line.split_whitespace().collect::<Vec<_>>();
273+
// Need to have at least the field name + at least one N0=val item
274+
if items.len() < 2 {
275+
return Err(self.unexpected_line(file_name, line));
276+
}
277+
let field = items[0];
278+
for item in items.iter().skip(1) {
279+
let kv = item.split('=').collect::<Vec<_>>();
280+
if kv.len() != 2 || kv[0].len() < 2 || !kv[0].starts_with('N') {
281+
return Err(self.unexpected_line(file_name, line));
282+
}
283+
let mut kchars = kv[0].chars();
284+
kchars.next();
285+
let numa_node = kchars
286+
.as_str()
287+
.parse::<u32>()
288+
.map_err(|_| self.unexpected_line(file_name, line.clone()))?;
289+
parse_and_set_fields!(
290+
s.entry(numa_node).or_default();
291+
field;
292+
kv[1].parse().map_err(|_| self.unexpected_line(file_name, line.clone()))?;
293+
[
294+
anon,
295+
file,
296+
kernel_stack,
297+
pagetables,
298+
shmem,
299+
file_mapped,
300+
file_dirty,
301+
file_writeback,
302+
swapcached,
303+
anon_thp,
304+
file_thp,
305+
shmem_thp,
306+
inactive_anon,
307+
active_anon,
308+
inactive_file,
309+
active_file,
310+
unevictable,
311+
slab_reclaimable,
312+
slab_unreclaimable,
313+
workingset_refault_anon,
314+
workingset_refault_file,
315+
workingset_activate_anon,
316+
workingset_activate_file,
317+
workingset_restore_anon,
318+
workingset_restore_file,
319+
workingset_nodereclaim,
320+
]
321+
);
322+
}
323+
}
324+
325+
if s.is_empty() {
326+
return Err(self.invalid_file_format(file_name));
327+
}
328+
329+
for (_node, memory_numa_stat) in s.iter() {
330+
if *memory_numa_stat == MemoryNumaStat::default() {
331+
return Err(self.invalid_file_format(file_name));
332+
}
333+
}
334+
335+
Ok(s)
336+
}
337+
251338
/// Return an iterator over child cgroups
252339
pub fn child_cgroup_iter(&self) -> Result<impl Iterator<Item = CgroupReader> + '_> {
253340
Ok(self
@@ -428,13 +515,13 @@ macro_rules! name_key_equal_value_format {
428515
}
429516
// Certain keys such as cost.usage can not be struct fields so must use cost_usage
430517
let key = kv[0].replace(".", "_");
431-
match key.as_ref() {
432-
$(stringify!($field) => s.$field = Some(
433-
kv[1].parse().map_err(|_| r.unexpected_line(file_name.clone(), line.clone()))?
434-
),)*
435-
_ => (),
436-
};
437-
}
518+
parse_and_set_fields!(
519+
s;
520+
key.as_ref();
521+
kv[1].parse().map_err(|_| r.unexpected_line(file_name.clone(), line.clone()))?;
522+
[ $($field,)* ]
523+
)
524+
};
438525
if s == $struct::default() {
439526
return Err(r.invalid_file_format(file_name))
440527
}

below/cgroupfs/src/test.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use tempfile::TempDir;
2323

2424
use crate::CgroupReader;
2525
use crate::Error;
26+
use crate::MemoryNumaStat;
2627

2728
struct TestCgroup {
2829
tempdir: TempDir,
@@ -620,3 +621,190 @@ fn test_cgroup_stat_invalid_format() {
620621
_ => panic!("Got unexpected error type: {}", err),
621622
}
622623
}
624+
625+
#[test]
626+
fn test_memory_numa_stat_success() {
627+
let cgroup = TestCgroup::new();
628+
cgroup.create_file_with_content(
629+
"memory.numa_stat",
630+
b"anon N0=133948178432 N1=85731622912 N2=56469581824 N3=67508137984
631+
file N0=29022474240 N1=28619689984 N2=27863502848 N3=20205821952
632+
kernel_stack N0=139689984 N1=93978624 N2=104693760 N3=145391616
633+
pagetables N0=464572416 N1=392798208 N2=332378112 N3=352788480
634+
shmem N0=27244945408 N1=27178311680 N2=27170930688 N3=13595549696
635+
file_mapped N0=27685949440 N1=27733299200 N2=27522891776 N3=15582023680
636+
file_dirty N0=6488064 N1=17436672 N2=85155840 N3=165040128
637+
file_writeback N0=0 N1=0 N2=38522880 N3=123408384
638+
swapcached N0=0 N1=0 N2=0 N3=0
639+
anon_thp N0=1419771904 N1=673185792 N2=536870912 N3=681574400
640+
file_thp N0=48234496 N1=14680064 N2=0 N3=48234496
641+
shmem_thp N0=48234496 N1=8388608 N2=0 N3=8388608
642+
inactive_anon N0=160479961088 N1=112294313984 N2=83386806272 N3=80141840384
643+
active_anon N0=466096128 N1=398712832 N2=8605696 N3=599347200
644+
inactive_file N0=1189363712 N1=913510400 N2=448503808 N3=2159505408
645+
active_file N0=522350592 N1=460431360 N2=206303232 N3=4300206080
646+
unevictable N0=405504 N1=135168 N2=0 N3=0
647+
slab_reclaimable N0=663340528 N1=563089336 N2=514239048 N3=647222000
648+
slab_unreclaimable N0=686272088 N1=472630728 N2=556250640 N3=693263576
649+
workingset_refault_anon N0=3497864 N1=2225803 N2=2410565 N3=1468001
650+
workingset_refault_file N0=214724399 N1=172943243 N2=2094241456 N3=155295239
651+
workingset_activate_anon N0=477507 N1=238415 N2=318245 N3=235467
652+
workingset_activate_file N0=96231899 N1=75825820 N2=674636976 N3=61718859
653+
workingset_restore_anon N0=182593 N1=55793 N2=96984 N3=53780
654+
workingset_restore_file N0=74008297 N1=63719159 N2=528595708 N3=48463497
655+
workingset_nodereclaim N0=266941 N1=176289 N2=1260264 N3=638641",
656+
);
657+
658+
let cgroup_reader = cgroup.get_reader();
659+
let val = cgroup_reader
660+
.read_memory_numa_stat()
661+
.expect("Failed to read numa memory stat");
662+
663+
assert_eq!(val.len(), 4);
664+
let node0 = MemoryNumaStat {
665+
anon: Some(133948178432),
666+
file: Some(29022474240),
667+
kernel_stack: Some(139689984),
668+
pagetables: Some(464572416),
669+
shmem: Some(27244945408),
670+
file_mapped: Some(27685949440),
671+
file_dirty: Some(6488064),
672+
file_writeback: Some(0),
673+
swapcached: Some(0),
674+
anon_thp: Some(1419771904),
675+
file_thp: Some(48234496),
676+
shmem_thp: Some(48234496),
677+
inactive_anon: Some(160479961088),
678+
active_anon: Some(466096128),
679+
inactive_file: Some(1189363712),
680+
active_file: Some(522350592),
681+
unevictable: Some(405504),
682+
slab_reclaimable: Some(663340528),
683+
slab_unreclaimable: Some(686272088),
684+
workingset_refault_anon: Some(3497864),
685+
workingset_refault_file: Some(214724399),
686+
workingset_activate_anon: Some(477507),
687+
workingset_activate_file: Some(96231899),
688+
workingset_restore_anon: Some(182593),
689+
workingset_restore_file: Some(74008297),
690+
workingset_nodereclaim: Some(266941),
691+
};
692+
let node1 = MemoryNumaStat {
693+
anon: Some(85731622912),
694+
file: Some(28619689984),
695+
kernel_stack: Some(93978624),
696+
pagetables: Some(392798208),
697+
shmem: Some(27178311680),
698+
file_mapped: Some(27733299200),
699+
file_dirty: Some(17436672),
700+
file_writeback: Some(0),
701+
swapcached: Some(0),
702+
anon_thp: Some(673185792),
703+
file_thp: Some(14680064),
704+
shmem_thp: Some(8388608),
705+
inactive_anon: Some(112294313984),
706+
active_anon: Some(398712832),
707+
inactive_file: Some(913510400),
708+
active_file: Some(460431360),
709+
unevictable: Some(135168),
710+
slab_reclaimable: Some(563089336),
711+
slab_unreclaimable: Some(472630728),
712+
workingset_refault_anon: Some(2225803),
713+
workingset_refault_file: Some(172943243),
714+
workingset_activate_anon: Some(238415),
715+
workingset_activate_file: Some(75825820),
716+
workingset_restore_anon: Some(55793),
717+
workingset_restore_file: Some(63719159),
718+
workingset_nodereclaim: Some(176289),
719+
};
720+
let node2 = MemoryNumaStat {
721+
anon: Some(56469581824),
722+
file: Some(27863502848),
723+
kernel_stack: Some(104693760),
724+
pagetables: Some(332378112),
725+
shmem: Some(27170930688),
726+
file_mapped: Some(27522891776),
727+
file_dirty: Some(85155840),
728+
file_writeback: Some(38522880),
729+
swapcached: Some(0),
730+
anon_thp: Some(536870912),
731+
file_thp: Some(0),
732+
shmem_thp: Some(0),
733+
inactive_anon: Some(83386806272),
734+
active_anon: Some(8605696),
735+
inactive_file: Some(448503808),
736+
active_file: Some(206303232),
737+
unevictable: Some(0),
738+
slab_reclaimable: Some(514239048),
739+
slab_unreclaimable: Some(556250640),
740+
workingset_refault_anon: Some(2410565),
741+
workingset_refault_file: Some(2094241456),
742+
workingset_activate_anon: Some(318245),
743+
workingset_activate_file: Some(674636976),
744+
workingset_restore_anon: Some(96984),
745+
workingset_restore_file: Some(528595708),
746+
workingset_nodereclaim: Some(1260264),
747+
};
748+
let node3 = MemoryNumaStat {
749+
anon: Some(67508137984),
750+
file: Some(20205821952),
751+
kernel_stack: Some(145391616),
752+
pagetables: Some(352788480),
753+
shmem: Some(13595549696),
754+
file_mapped: Some(15582023680),
755+
file_dirty: Some(165040128),
756+
file_writeback: Some(123408384),
757+
swapcached: Some(0),
758+
anon_thp: Some(681574400),
759+
file_thp: Some(48234496),
760+
shmem_thp: Some(8388608),
761+
inactive_anon: Some(80141840384),
762+
active_anon: Some(599347200),
763+
inactive_file: Some(2159505408),
764+
active_file: Some(4300206080),
765+
unevictable: Some(0),
766+
slab_reclaimable: Some(647222000),
767+
slab_unreclaimable: Some(693263576),
768+
workingset_refault_anon: Some(1468001),
769+
workingset_refault_file: Some(155295239),
770+
workingset_activate_anon: Some(235467),
771+
workingset_activate_file: Some(61718859),
772+
workingset_restore_anon: Some(53780),
773+
workingset_restore_file: Some(48463497),
774+
workingset_nodereclaim: Some(638641),
775+
};
776+
assert_eq!(val[&0], node0);
777+
assert_eq!(val[&1], node1);
778+
assert_eq!(val[&2], node2);
779+
assert_eq!(val[&3], node3);
780+
}
781+
782+
#[test]
783+
fn test_memory_numa_stat_parse_failure() {
784+
let cgroup = TestCgroup::new();
785+
cgroup.create_file_with_content("memory.numa_stat", b"anon garbage\nfile garbage");
786+
787+
let cgroup_reader = cgroup.get_reader();
788+
let err = cgroup_reader
789+
.read_memory_numa_stat()
790+
.expect_err("Did not fail to read memory.numa.stat");
791+
match err {
792+
Error::UnexpectedLine(_, _) => {}
793+
_ => panic!("Got unexpected error type {}", err),
794+
}
795+
}
796+
797+
#[test]
798+
fn test_memory_numa_stat_invalid_format() {
799+
let cgroup = TestCgroup::new();
800+
cgroup.create_file_with_content("memory.numa_stat", b"");
801+
802+
let cgroup_reader = cgroup.get_reader();
803+
let err = cgroup_reader
804+
.read_memory_numa_stat()
805+
.expect_err("Did not fail to read memory.numa_stat");
806+
match err {
807+
Error::InvalidFileFormat(_) => {}
808+
_ => panic!("Got unexpected error type: {}", err),
809+
}
810+
}

below/cgroupfs/src/types.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,33 @@ pub struct CgroupStat {
121121
pub nr_descendants: Option<u32>,
122122
pub nr_dying_descendants: Option<u32>,
123123
}
124+
125+
#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
126+
pub struct MemoryNumaStat {
127+
pub anon: Option<u64>,
128+
pub file: Option<u64>,
129+
pub kernel_stack: Option<u64>,
130+
pub pagetables: Option<u64>,
131+
pub shmem: Option<u64>,
132+
pub file_mapped: Option<u64>,
133+
pub file_dirty: Option<u64>,
134+
pub file_writeback: Option<u64>,
135+
pub swapcached: Option<u64>,
136+
pub anon_thp: Option<u64>,
137+
pub file_thp: Option<u64>,
138+
pub shmem_thp: Option<u64>,
139+
pub inactive_anon: Option<u64>,
140+
pub active_anon: Option<u64>,
141+
pub inactive_file: Option<u64>,
142+
pub active_file: Option<u64>,
143+
pub unevictable: Option<u64>,
144+
pub slab_reclaimable: Option<u64>,
145+
pub slab_unreclaimable: Option<u64>,
146+
pub workingset_refault_anon: Option<u64>,
147+
pub workingset_refault_file: Option<u64>,
148+
pub workingset_activate_anon: Option<u64>,
149+
pub workingset_activate_file: Option<u64>,
150+
pub workingset_restore_anon: Option<u64>,
151+
pub workingset_restore_file: Option<u64>,
152+
pub workingset_nodereclaim: Option<u64>,
153+
}

0 commit comments

Comments
 (0)