Add pager options to mem_usage.py

Adds options to report the size of the init, paged and unpaged areas
in pager builds (CFG_WITH_PAGER=y). This is helpful to monitor the
evolution of the code size, and identify issues such as when useless
functions get pulled into the unpaged area by mistake.
For instance, here is what we can get with 'mem_usage.py -nUr' and a
few lines of script (shows changes in the size of unpaged code and
data for a non-debug qemu_virt build since 2.5.0):

2.5.0                   67600
2.5.0-2-g0b8e6e78       67632    +32  core: abort: fix get_fault_type()
2.5.0-11-g87d96185      67728    +96  core: pager: avoid page faults with pager lock held
2.5.0-37-g0073c9a8      67792    +64  core: pager: optimize tee_pager_handle_fault()
2.5.0-40-g935ac3ec      67888    +96  core: arm32: handle aborts in system mode
2.5.0-53-g2e4e94bf      67920    +32  core: default enable program flow prediction on ARMv7 cores
2.5.0-57-ge84e1fec      67704   -216  core: clean and unwind arm32_aeabi_[l]divmod_a32.S
2.5.0-63-g23346f16      67736    +32  Add support for several user TA stores
2.5.0-76-gb2087a20      67608   -128  core: pager: deal with large CFG_TEE_RAM_VA_SIZE
2.5.0-77-g00aa47a4      67688    +80  core: plat-vexpress: increase CFG_TEE_RAM_VA_SIZE
2.5.0-112-g86e50a60     67696     +8  core: arm: psci: add suspend resume common functions
2.5.0-120-g13b3ee90     67768    +72  core: print rwx flags for each MMU region when a user TA aborts
2.5.0-122-ge61644fb     83408 +15640  core: make reset_secondary() unpaged
2.5.0-123-g1506eb6f     83424    +16  core: plat-vexpress: init gic on secondary cores
2.5.0-124-ge2b68c87     91368  +7944  core: plat-vextpress-qemu_virt: update num cores
2.5.0-126-g5402a9fe     92808  +1440  qemu_virt: enable smp boot
2.5.0-132-gda51216b     92840    +32  dts: pass PA of reserved region
2.5.0-173-gb4121bfb     92848     +8  pl011: prevent putc() and flush() function from blocking indefinitely
2.5.0-176-g55d6853c     92912    +64  core: add registered shared memory support
2.5.0-182-g93d3c451    163936 +71024  core: pager: ltc: prng: add entropy to the AE key for paged TAs
2.5.0-202-ga71af55e    163984    +48  core: mobj: add mobj_get_phys_offs()
2.5.0-203-g5c7a19bb    163968    -16  core: mobj: remove double physical offset
2.5.0-205-g430dcbd8    163944    -24  core: reimplement mobj_mapped_shm_alloc()
2.5.0-208-gbbed97b6    163976    +32  core:mmu: fix userland va2pa conversion
2.5.0-209-g42d91b4b    164040    +64  core:mmu: fix userland pa2va conversion
2.6.0-rc1-1-gb6449075  164136    +96  thread.c: free rpc arg mobj during cache disabling
2.6.0-7-g8473540d       77144 -86992  Keep assembly functions in separate sections
2.6.0-8-g64113fca       77112    -32  core: arm32: replace _start with reset() function
2.6.0-9-g486754e8       77176    +64  core: arm32: reset_secondary() set reset vector

Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Reviewed-by: Volodymyr Babchuk <vlad.babchuk@gmail.com>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Acked-by Etienne Carriere <etienne.carriere@linaro.org>
diff --git a/scripts/mem_usage.py b/scripts/mem_usage.py
index 4309314..8b7faf0 100755
--- a/scripts/mem_usage.py
+++ b/scripts/mem_usage.py
@@ -12,10 +12,31 @@
 
 
 def get_args():
-    parser = argparse.ArgumentParser(
-        description='Shows the memory layout of the TEE ELF file. '
-        'Size is provided for each section.')
-    parser.add_argument('tee_elf', help='The OP-TEE ELF file (tee.elf)')
+    parser = argparse.ArgumentParser(description='Shows the memory usage '
+                                     'of an OP-TEE based on ELF sections')
+    parser.add_argument('tee_elf', help='the OP-TEE ELF file (tee.elf)')
+    parser.add_argument('-a', '--all', action='store_true',
+                        help=' same as -i -p -u -U')
+    parser.add_argument('-n', '--no-map', action='store_true',
+                        help=' do not show the detailed section mappings and '
+                        'RAM usage')
+    parser.add_argument('-i', '--init', action='store_true',
+                        help='report the total size of the .*_init sections')
+    parser.add_argument('-p', '--paged', action='store_true',
+                        help='report the total size of the .*_pageable '
+                        'sections')
+    parser.add_argument('-u', '--unpaged', action='store_true',
+                        help='report the total size of the unpaged sections, '
+                        'that is, all sections but the ones in --init or '
+                        '--paged')
+    parser.add_argument('-U', '--unpaged-no-heap', action='store_true',
+                        help='report the size of all unpaged sections '
+                        'excluding heap space. Reflects the size of unpaged '
+                        'code and data (.text, .rodata, .data, .bss, .nozi '
+                        'and possibly unwind tables)')
+    parser.add_argument('-r', '--raw', action='store_true',
+                        help='when processing -i, -p, -u, or -U, show only '
+                        'the size (in decimal) and no other text')
     return parser.parse_args()
 
 
@@ -24,6 +45,8 @@
 
 
 def print_sect(name, addr, size, round_up=False, print_num_pages=False):
+    if args.no_map:
+        return
     if size == 0:
         size_kib = 0
         num_pages = 0
@@ -41,13 +64,27 @@
     printf('\n')
 
 
+def print_pager_stat(name, size):
+    size_kib = size / 1024
+    if args.raw:
+        printf('%d ', size)
+    else:
+        printf('%-36s size %.8X %3d KiB\n', name, size, size_kib)
+
+
 def readelf_cmd():
     return os.getenv('CROSS_COMPILE', '') + 'readelf'
 
 
 def main():
+    global args
+
     in_shdr = False
     sects = []
+    init_size = 0
+    paged_size = 0
+    unpaged_size = 0
+    unpaged_no_heap_size = 0
 
     args = get_args()
     env = os.environ.copy()
@@ -96,9 +133,30 @@
             print_sect('*hole*', last_addr + last_size,
                        addr - (last_addr + last_size))
         print_sect(name, addr, size)
+        if name.endswith('_init'):
+            init_size += size
+        elif name.endswith('_pageable'):
+            paged_size += size
+        else:
+            if not name.startswith('.heap'):
+                unpaged_no_heap_size += size
+            unpaged_size += size
         last_addr = addr
         last_size = size
 
+    if args.all or args.init:
+        print_pager_stat('Init sections (.*_init)', init_size)
+    if args.all or args.paged:
+        print_pager_stat('Paged sections (.*_pageable)', paged_size)
+    if args.all or args.unpaged:
+        print_pager_stat('Unpaged sections ', unpaged_size)
+    if args.all or args.unpaged_no_heap:
+        print_pager_stat('Unpaged sections (heap excluded)',
+                         unpaged_no_heap_size)
+    if (args.raw and (args.all or args.init or args.paged or
+                      args.unpaged or args.unpaged_no_heap)):
+        printf('\n')
+
 
 if __name__ == "__main__":
     main()