blob: 867e9206504fce9015c2ead5bb6b665c3d3bb743 [file]
# Copyright 2022 Google LLC
#
# Licensed 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
#
# https://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.
import argparse
import enum
import logging
import pickle
import re
import sys
from typing import Dict, List, Optional
try:
import capdl # pylint: disable=unused-import
except ModuleNotFoundError as e:
e.msg += (". The PYTHONPATH environment variable should include the path"
"to the CAPDL module "
"($ROOTDIR/cantrip/projects/capdl/python-capdl-tool)")
raise
from capdl.Object import Frame # pylint: disable=import-error
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def main():
parser = argparse.ArgumentParser(description='')
parser.add_argument('--object-state',
type=argparse.FileType('rb'),
required=True,
help='''The allocator state exported from
capdl_linker.py
The file is generated as part of the cantrip build
process.
''')
parser.add_argument('--details',
default=False,
action='store_true',
help='''Change the output format to have a per frame
type breakdown.''')
args = parser.parse_args()
allocator_state = pickle.load(args.object_state)
sizes = accumulate_frame_sizes(allocator_state)
print_summary(sizes, details=args.details)
return 0
class FrameType(enum.Enum):
ELF = enum.auto()
BSS = enum.auto()
IPC_BUFFER = enum.auto()
STACK = enum.auto()
COPYREGION = enum.auto()
BOOTINFO = enum.auto()
MMIO = enum.auto()
def accumulate_frame_sizes(allocator_state) -> 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):
continue
ft = frame_type(frame.name, frame.fill, frame.paddr)
if ft == FrameType.COPYREGION or component is None:
component_name = get_copy_region_component(frame.name)
else:
component_name = component
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
return ret_sizes
def print_summary(sizes: Dict, details=False) -> None:
col_widths = (
25, # component name
20, # frame type
15, # size in KiB
20, # size in bytes
)
grand_total = 0
for component_name in sorted(list(sizes.keys())):
total = sum(list(sizes[component_name].values()))
# MMIO is mapped but doesn't use physical memory
total -= sizes[component_name].get(FrameType.MMIO, 0)
# Copy Regions are immidately freed to create a hole in virtual memory
total -= sizes[component_name].get(FrameType.COPYREGION, 0)
if total == 0 and not details:
continue
grand_total += total
print(
pad_right(component_name, col_widths[0]) +
pad_right("", col_widths[1]) +
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]))
print(
pad_right("GRAND TOTAL", col_widths[0]) +
pad_right("", col_widths[1]) +
pad_left(f"{to_kib(grand_total)} KiB", col_widths[2]) +
pad_left(str(grand_total), col_widths[3]))
def frame_type(frame_name: str, fill: List[str],
paddr: Optional[int]) -> FrameType:
assert len(fill) in (
0,
1,
)
if "_copy_region_" in frame_name:
return FrameType.COPYREGION
if "_frame__camkes_ipc_buffer_" in frame_name:
return FrameType.IPC_BUFFER
if frame_name.startswith("stack__camkes_stack_"):
return FrameType.STACK
if fill and "CDL_FrameFill_BootInfo" in fill[0]:
return FrameType.BOOTINFO
if fill and "CDL_FrameFill_FileData" in fill[0]:
return FrameType.ELF
if re.search(r"_data_[0-9]_obj", frame_name):
if paddr:
return FrameType.MMIO
return FrameType.BSS
if paddr:
return FrameType.MMIO
return FrameType.BSS
def get_copy_region_component(frame_name: str) -> str:
return re.sub(r"_copy_region.*", "", frame_name)
def to_kib(byte_count: int) -> int:
return byte_count >> 10
def pad_right(string: str, length: int) -> str:
return string + " " * (length - len(string))
def pad_left(string: str, length: int) -> str:
return " " * (length - len(string)) + string
if __name__ == '__main__':
sys.exit(main())