kmem.py: account for kernel data structures Account for seL4 kernel objects. Detailed accounting shows kernel objects using the mangled capDL names (modulo some stripping of component prefixes to make display more compact). Initially this accounting a riscv32 target architecture with MCS enabled. While here fix assumption data structure sizes are at leat 4KB (was true when only page frames were being tracked). Add --verbose option to enable objects skipped for various reasons (e.g. IRQ's that have no associated memory). Add --kernel option to enable kernel data structure analysis. Change-Id: Iddab2d9a71af3e11de00fa67c582212dfe9f7e7b
diff --git a/kmem-tool/kmem.py b/kmem-tool/kmem.py index 867e920..e141ac8 100644 --- a/kmem-tool/kmem.py +++ b/kmem-tool/kmem.py
@@ -28,11 +28,24 @@ "($ROOTDIR/cantrip/projects/capdl/python-capdl-tool)") raise -from capdl.Object import Frame # pylint: disable=import-error +from capdl.Object import Frame, register_object_sizes # pylint: disable=import-error logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) +# TODO(sleffler): does pickle have target cpu? +riscv32_object_sizes = { + 'seL4_TCBObject': 9, + 'seL4_RTReplyObject': 4, + 'seL4_EndpointObject': 4, + 'seL4_NotificationObject': 5, # MCS, !MCS = 4 + 'seL4_SmallPageObject': 12, + 'seL4_LargePageObject': 22, + 'seL4_ASID_Pool': 12, + 'seL4_Slot': 4, + 'seL4_PageTableObject': 12, + 'seL4_PageDirectoryObject': 12, +} def main(): parser = argparse.ArgumentParser(description='') @@ -44,17 +57,29 @@ The file is generated as part of the cantrip build process. ''') + parser.add_argument('--kernel', + default=False, + action='store_true', + help='''Include kernel data structures in analysis.''') parser.add_argument('--details', default=False, action='store_true', help='''Change the output format to have a per frame type breakdown.''') + parser.add_argument('--verbose', + default=False, + action='store_true', + help='''Enable messages during analysis phase.''') args = parser.parse_args() allocator_state = pickle.load(args.object_state) - sizes = accumulate_frame_sizes(allocator_state) + register_object_sizes(riscv32_object_sizes) + + sizes = accumulate_obj_sizes(allocator_state, + kernel=args.kernel, + verbose=args.verbose) print_summary(sizes, details=args.details) return 0 @@ -70,32 +95,51 @@ MMIO = enum.auto() -def accumulate_frame_sizes(allocator_state) -> Dict: +def accumulate_obj_sizes(allocator_state, kernel=False, verbose=False) -> Dict: ret_sizes = {} for component in allocator_state.obj_space.labels.keys(): - for frame in allocator_state.obj_space.labels.get(component): - if not isinstance(frame, Frame): + for obj in allocator_state.obj_space.labels.get(component): + if component is None: + # e.g. BOOTINFO_FRAME + if verbose: + print("SKIP", obj.name) continue - ft = frame_type(frame.name, frame.fill, frame.paddr) + component_name = component - if ft == FrameType.COPYREGION or component is None: - component_name = get_copy_region_component(frame.name) + if isinstance(obj, Frame): + # Page frame object + ft = frame_type(obj.name, obj.fill, obj.paddr) + if ft == FrameType.COPYREGION: + # NB: for C templates, Rust templates have no backing frames + component_name = get_copy_region_component(obj.name) + obj_name = ft + size = obj.size else: - component_name = component + # seL4 kernel object + if not kernel: + continue + obj_name = obj.name + + size = obj.get_size_bits() + if size is None: + if verbose: + print("SKIP", obj.name) + continue + size = 1 << size if component_name not in ret_sizes: ret_sizes[component_name] = {} - if ft not in ret_sizes[component_name]: - ret_sizes[component_name][ft] = 0 - ret_sizes[component_name][ft] += frame.size + if obj_name not in ret_sizes[component_name]: + ret_sizes[component_name][obj_name] = 0 + ret_sizes[component_name][obj_name] += size return ret_sizes def print_summary(sizes: Dict, details=False) -> None: col_widths = ( 25, # component name - 20, # frame type - 15, # size in KiB + 28, # object type + 12, # size in KiB 20, # size in bytes ) grand_total = 0 @@ -118,15 +162,15 @@ pad_left(f"{to_kib(total)} KiB", col_widths[2]) + pad_left(str(total), col_widths[3])) if details: - for unused_ft_name, ft in sorted(FrameType.__members__.items(), - key=lambda x: x[0]): - if ft in sizes[component_name]: - size = sizes[component_name][ft] - print( - pad_right("", col_widths[0]) + - pad_right(str(ft), col_widths[1]) + - pad_left(f"{to_kib(size)} KiB", col_widths[2]) + - pad_left(str(size), col_widths[3])) + for key, size in sizes[component_name].items(): + obj_name = str(key) + while obj_name.startswith(component_name + '_'): + obj_name = obj_name[len(component_name) + 1:] + print( + pad_right("", col_widths[0]) + + pad_right(str(obj_name), col_widths[1]) + + pad_left(f"{to_kib(size)} KiB", col_widths[2]) + + pad_left(str(size), col_widths[3])) print( pad_right("GRAND TOTAL", col_widths[0]) + pad_right("", col_widths[1]) + @@ -164,6 +208,8 @@ def to_kib(byte_count: int) -> int: + if 0 < byte_count < (1 << 10): + return 1 return byte_count >> 10