kmem: capDL memory analyzer Script that analyzes the CAmkES-generated capDL spec for memory use. By default the memory footprint of each "component" is displayed. (Shared memory regions are also displayed separately as it's unclear which of multiple components to charge.) The -d option gives each component a breakdown by memory type: - "elf": data loadeed from .text or .data ELF segments, - "bss": zero-initialized memory, - "ipc_buffer": CAmkES-generated per-thread IPC buffers, - "stack": CAmkES-generated thread stacks, - "copyregion": unmapped VSpace regions, and - "mmio": device-backed memory, typically used to access MMIO registers but also used to reach Renode-loaded data like the cpio archive of builtin applications & models (NB: mmio + copyregion sections do not count against memory use as they are allocated from dedicated memory that does not have physical memory backing). By default the last release build will be analyzed (also selected with the -R option). The -D option can be used to select the last debug build. Change-Id: I8ed1d6249fca13e5dfd2a10159e2399463901229
diff --git a/kmem.sh b/kmem.sh new file mode 100755 index 0000000..c55dbdc --- /dev/null +++ b/kmem.sh
@@ -0,0 +1,96 @@ +#! /bin/bash +# +# 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. + +# CAmkES system image memory analyzer. + +# Analyze the CAmkES-generated capDL spec for memory use. +# By default the memory foottprint of each component is displayed. +# The -d option will give a breakdown by memory type: +# elf .text + .data +# bss .bss +# ipc_buffer CAmkES per-thread ipc_buffer's +# stack CAmkES per-thread stack +# bootinfo Bootinfo page passed by the rooteserver +# mmio MMIO region (backed by devivce memory) +# copyregion VSpace region (w/o backing memory) +# +# Note mmio + copyregion sections do not count against memory usage as +# they are allocated from dedicated memory that does not have physical +# memory backing. +# +# TODO: account for system resources +# +# ROOTDIR must be set to the top of the shodan development tree +# (as done by build/setup.sh). + +# Usage: kmem [-d] + +if [[ -z "${ROOTDIR}" ]]; then + echo "Source build/setup.sh first" + exit 1 +fi + +TARGET=${TARGET:-riscv32-unknown-elf} + +# Default is a summary of release build. +DETAILS="0" +BUILD="release" + +function parseargv { + local usage="Usage: kmem.sh [-h|--help] [-d|--details] [-D|--debug] [-R|--release] [-s|--summary]" + local args=$(getopt -o dDRs --long details,debug,release,summary,help -n kmem.sh -- "$@") + + set -- $args + + for i; do + case "$1" in + -d|--details) + DETAILS="1" + shift + ;; + + -s|--summary) + DETAILS="0" + shift + ;; + + -D|--debug) + BUILD="wdebug" + shift + ;; + + -R|--release) + BUILD="release" + shift + ;; + + --) + shift + break + ;; + + -h|--help|*) + echo "$usage" >/dev/stderr + exit 1 + ;; + esac + done +} + +parseargv "$@" + +KATA_OUT="${ROOTDIR}/out/kata/${TARGET}/${BUILD}" +exec awk -f "${ROOTDIR}/scripts/mem.awk" "${KATA_OUT}/system.cdl" DETAILS="${DETAILS}"
diff --git a/mem.awk b/mem.awk new file mode 100644 index 0000000..429da6c --- /dev/null +++ b/mem.awk
@@ -0,0 +1,127 @@ +# +# 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. + +# seL4 capDL spec memory analyzer. + +BEGIN { + in_objects = 0; + frame_types[1] = "elf"; + frame_types[2] = "bss"; + frame_types[3] = "ipc_buffer"; + frame_types[4] = "stack"; + frame_types[5] = "copyregion"; + frame_types[6] = "bootinfo"; + frame_types[7] = "mmio"; + DETAILS = 0; +} +/^objects.*{/ { in_objects = 1; } +in_objects && $3 == "frame" { + component = $1; + size = 0; + switch (substr($4, 2)) { + case /4k/: size = 4096; break; + case /4M/: size = 4*1024*1024; break; + default: + print "Unknown frame size", substr($4, 2); + break; + } + + switch ($8) { + case "CDL_FrameFill_BootInfo": + split(component, a, "_"); + component = a[3] "_" a[4]; + frame_type = "bootinfo"; + break; + case "CDL_FrameFill_FileData": + sub(".*frame_", "", component); + sub("_group_bin.*", "", component); + frame_type = "elf"; + break; + default: { + switch (component) { + case /_copy_region_/: + sub("_copy_region.*", "", component); + frame_type = "copyregion"; + break; + case /_frame__camkes_ipc_buffer_/: + sub("_frame__camkes_ipc_buffer.*", "", component); + frame_type = "ipc_buffer"; + break; + case /^stack__camkes_stack_/: + sub("^stack__camkes_stack_", "", component); + split(component, a, "_"); + component = a[1] "_" a[2]; + frame_type = "stack"; + break; + case /_data_[0-9]_obj/: + sub("_?[0-9]*_data_[0-9]_obj", "", component); + sub("_mmio.*", "", component); + frame_type = ($5 == "paddr:" ? "mmio" : "bss"); + break; + default: + if ($5 == "paddr:") { + sub("_mmio.*", "", component); + sub("_csr_.*", "", component); + frame_type = "mmio"; + } else { + sub(".*frame_", "", component); + sub("_group_bin.*", "", component); + frame_type = "bss"; + } + break; + } + break; + } + } + cur_component = component; + if (frame_type != "copyregion" && frame_type != "mmio") { + # NB: copyregion's are holes in the VSpace + components[cur_component] += size;; + } + memory[cur_component, frame_type] += size; +} +in_objects && /^}/ { in_objects = 0; } + +END { + asorti(components, comps, "@ind_str_asc"); + total = 0; + for (i in comps) { + c = comps[i]; + ntypes = 0; + # per-component breakdown by frame type + for (j in frame_types) { + t = frame_types[j]; + if (memory[c, t] != "") { + if (DETAILS) { + printf "%-36.36s %-14s %5.0f KiB\n", (ntypes > 0 ? "" : c), t, memory[c, t] / 1024.; + } + ntypes++; + } + } + total += components[c]; + # per-component totals + if (!DETAILS) { + printf "%-36.36s %5.0f KiB (%s)\n", c, components[c] / 1024., components[c]; + } else if (ntypes > 1) { + printf "%-36.36s %-14s %5.0f KiB (%s)\n", c, "total", components[c] / 1024., components[c]; + } + } + # overall total + if (DETAILS) { + printf "%-36.36s %-14s %5.0f KiB (%s)\n", "Total", "", total / 1024., total; + } else { + printf "%-36.36s %5.0f KiB (%s)\n", "Total", total / 1024., total; + } +}