sanitycheck: binary size calculation improvements

- Several recently added binary sections were not being taken
  into consideration, unknown sections now trigger test case
  failure
- Use same VMA/LMA terminology that objdump uses and show both
  addresses

Change-Id: I641eef64aaed6612f62e5aa092f66baaa2797c70
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
diff --git a/scripts/sanitycheck b/scripts/sanitycheck
index aab96eb..2272e35 100755
--- a/scripts/sanitycheck
+++ b/scripts/sanitycheck
@@ -296,6 +296,13 @@
 
 
 class SizeCalculator:
+
+    alloc_sections = ["bss", "noinit"]
+    rw_sections = ["datas", "initlevel", "_k_mem_map_ptr", "_k_pipe_ptr",
+                   "_k_task_ptr", "_k_task_list", "initlevel"]
+    # These get copied into RAM only on non-XIP
+    ro_sections = ["text", "ctors", "rodata", "devconfig"]
+
     def __init__(self, filename):
         """Constructor
 
@@ -316,9 +323,8 @@
         self.is_xip = (len(is_xip_output) != 0)
 
         self.filename = filename
-        self.sections = {}
-        self.xip_rom_size = 0
-        self.xip_ram_size = 0
+        self.sections = []
+        self.rom_size = 0
         self.ram_size = 0
 
         self._calculate_sizes()
@@ -328,17 +334,14 @@
 
         @return amount of RAM, in bytes
         """
-        if self.is_xip:
-            return self.xip_ram_size
-        else:
-            return self.ram_size
+        return self.ram_size
 
     def get_rom_size(self):
         """Get the size of the data that this application uses on device's flash
 
         @return amount of ROM, in bytes
         """
-        return self.xip_rom_size
+        return self.rom_size
 
     def unrecognized_sections(self):
         """Get a list of sections inside the binary that weren't recognized
@@ -346,9 +349,9 @@
         @return list of unrecogized section names
         """
         slist = []
-        for k, v in self.sections.iteritems():
+        for v in self.sections:
             if not v["recognized"]:
-                slist.append(k)
+                slist.append(v["name"])
         return slist
 
     def _calculate_sizes(self):
@@ -371,35 +374,35 @@
             if (name[0] == '.'):                # starting with '.'
                 continue
 
+            # TODO this doesn't actually reflect the size in flash or RAM as
+            # it doesn't include linker-imposed padding between sections.
+            # It is close though.
             size = int(words[2], 16)
-            phys_addr = int(words[4], 16)
+            load_addr = int(words[4], 16)
+            virt_addr = int(words[3], 16)
 
             # Add section to memory use totals (for both non-XIP and XIP scenarios)
-            #
-            # In an XIP image, the following sections are placed into ROM:
-            #     text, ctors, rodata and datas
-            # In an XIP image, the following sections are placed into RAM:
-            #     datas, bss and noinit
-            # In a non-XIP image, the following sections are placed into RAM
-            #     text, ctors, rodata, datas, bss and noinit
             # Unrecognized section names are not included in the calculations.
-
-            self.ram_size += size
             recognized = True
-
-            if ((name == "text") or (name == "ctors") or (name == "rodata")):
-                self.xip_rom_size += size
-            elif (name == "datas"):
-                self.xip_rom_size += size
-                self.xip_ram_size += size
-            elif ((name == "bss") or (name == "noinit")):
-                self.xip_ram_size += size
+            if name in SizeCalculator.alloc_sections:
+                self.ram_size += size
+                stype = "alloc"
+            elif name in SizeCalculator.rw_sections:
+                self.ram_size += size
+                self.rom_size += size
+                stype = "rw"
+            elif name in SizeCalculator.ro_sections:
+                self.rom_size += size
+                if not self.is_xip:
+                    self.ram_size += size
+                stype = "ro"
             else:
+                stype = "unknown"
                 recognized = False
-                self.ram_size -= size   # Undo the calculation
 
-            self.sections[name] = {"phys_addr" : phys_addr, "size" : size,
-                                   "recognized" : recognized}
+            self.sections.append({"name" : name, "load_addr" : load_addr,
+                                  "size" : size, "virt_addr" : virt_addr,
+                                  "type" : stype, "recognized" : recognized})
 
 
 class MakeGoal:
@@ -1209,6 +1212,7 @@
             sc = i.calculate_sizes()
             goal.metrics["ram_size"] = sc.get_ram_size()
             goal.metrics["rom_size"] = sc.get_rom_size()
+            goal.metrics["unrecognized"] = sc.unrecognized_sections()
         return self.goals
 
     def discard_report(self, filename):
@@ -1450,18 +1454,13 @@
 
 def size_report(sc):
     info(sc.filename)
-    info("SECTION NAME         ADDRESS     SIZE   HEX")
-    for k, v in sc.sections.iteritems():
-        if not v["recognized"]:
-            k += "*"
-
-        info("%-17s 0x%08x %8d %5x" %
-             (k, v["phys_addr"], v["size"], v["size"]))
-    if sc.is_xip:
-        info("Total: %d bytes (ROM) + %d bytes (RAM)" %
-             (sc.xip_rom_size, sc.xip_ram_size))
-    else:
-        info("Total: %d bytes (RAM)" % sc.ram_size)
+    info("SECTION NAME             VMA        LMA     SIZE  HEX SZ TYPE")
+    for v in sc.sections:
+        info("%-17s 0x%08x 0x%08x %8d 0x%05x %-7s" %
+             (v["name"], v["virt_addr"], v["load_addr"], v["size"], v["size"],
+              v["type"]))
+    info("Totals: %d bytes (ROM), %d bytes (RAM)" %
+             (sc.rom_size, sc.ram_size))
     info("")
 
 
@@ -1538,6 +1537,11 @@
     for name, goal in goals.iteritems():
         if goal.failed:
             failed += 1
+        elif goal.metrics["unrecognized"]:
+            info("%sFAILED%s: %s has unrecognized binary sections: %s" %
+                 (COLOR_RED, COLOR_NORMAL, goal.name,
+                  str(goal.metrics["unrecognized"])))
+            failed += 1
 
     info("%s%d of %d%s tests passed with %s%d%s warnings in %d seconds" %
           (COLOR_RED if failed else COLOR_GREEN, len(goals) - failed,