Merge "kmem: capDL memory analyzer"
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;
+  }
+}