Clone of official tools

Revision:
31:8ea194f6145b
Parent:
30:f12ce67666d0
Child:
36:96847d42f010
--- a/memap.py	Mon Aug 29 11:56:59 2016 +0100
+++ b/memap.py	Wed Jan 04 11:58:24 2017 -0600
@@ -10,10 +10,11 @@
 import argparse
 from prettytable import PrettyTable
 
-from tools.utils import argparse_filestring_type, \
+from utils import argparse_filestring_type, \
     argparse_lowercase_hyphen_type, argparse_uppercase_type
 
 DEBUG = False
+
 RE_ARMCC = re.compile(
     r'^\s+0x(\w{8})\s+0x(\w{8})\s+(\w+)\s+(\w+)\s+(\d+)\s+[*]?.+\s+(.+)$')
 RE_IAR = re.compile(
@@ -37,10 +38,12 @@
     # sections to print info (generic for all toolchains)
     sections = ('.text', '.data', '.bss', '.heap', '.stack')
 
-    def __init__(self):
+    def __init__(self, detailed_misc=False):
         """ General initialization
         """
-
+        # 
+        self.detailed_misc = detailed_misc
+        
         # list of all modules and their sections
         self.modules = dict()
 
@@ -51,9 +54,14 @@
         # list of all object files and mappting to module names
         self.object_to_module = dict()
 
-        # Memory usage summary structure
+        # Memory report (sections + summary)
+        self.mem_report = []
+
+        # Just the memory summary section
         self.mem_summary = dict()
 
+        self.subtotal = dict()
+
     def module_add(self, module_name, size, section):
         """ Adds a module / section to the list
 
@@ -90,8 +98,8 @@
         else:
             return False         # everything else, means no change in section
 
-    @staticmethod
-    def path_object_to_module_name(txt):
+    
+    def path_object_to_module_name(self, txt):
         """ Parse a path to object file to extract it's module and object data
 
         Positional arguments:
@@ -114,16 +122,24 @@
                 module_name = data[0] + '/' + data[1]
 
             return [module_name, object_name]
-        else:
+            
+        elif self.detailed_misc:           
+            rex_obj_name = r'^.+\/(.+\.o\)*)$'
+            test_rex_obj_name = re.match(rex_obj_name, txt)
+            if test_rex_obj_name:
+                object_name = test_rex_obj_name.group(1)
+                return ['Misc/' + object_name, ""]        
+                
             return ['Misc', ""]
-
+        else: 
+            return ['Misc', ""]
 
     def parse_section_gcc(self, line):
         """ Parse data from a section of gcc map file
 
         examples:
-                        0x00004308       0x7c ./.build/K64F/GCC_ARM/mbed-os/hal/targets/hal/TARGET_Freescale/TARGET_KPSDK_MCUS/spi_api.o
-         .text          0x00000608      0x198 ./.build/K64F/GCC_ARM/mbed-os/core/mbed-rtos/rtx/TARGET_CORTEX_M/TARGET_RTOS_M4_M7/TOOLCHAIN_GCC/HAL_CM4.o
+                        0x00004308       0x7c ./BUILD/K64F/GCC_ARM/mbed-os/hal/targets/hal/TARGET_Freescale/TARGET_KPSDK_MCUS/spi_api.o
+         .text          0x00000608      0x198 ./BUILD/K64F/GCC_ARM/mbed-os/core/mbed-rtos/rtx/TARGET_CORTEX_M/TARGET_RTOS_M4_M7/TOOLCHAIN_GCC/HAL_CM4.o
 
         Positional arguments:
         line - the line to parse a section from
@@ -342,28 +358,23 @@
                 else:
                     self.module_add(name, size, section)
 
-    def search_objects(self, path, toolchain):
-        """ Check whether the specified map file matches with the toolchain.
-        Searches for object files and creates mapping: object --> module
+    def search_objects(self, path):
+        """ Searches for object files and creates mapping: object --> module
 
         Positional arguments:
         path - the path to an object file
-        toolchain - the toolchain used to build the object file
         """
 
         path = path.replace('\\', '/')
 
         # check location of map file
-        rex = r'^(.+\/)' + re.escape(toolchain) + r'\/(.+\.map)$'
+        rex = r'^(.+)' + r'\/(.+\.map)$'
         test_rex = re.match(rex, path)
 
         if test_rex:
-            search_path = test_rex.group(1) + toolchain + '/mbed-os/'
+            search_path = test_rex.group(1) + '/mbed-os/'
         else:
-            # It looks this is not an mbed project
-            # object-to-module mapping cannot be generated
-            print "Warning: specified toolchain doesn't match with"\
-                " path to the memory map file."
+            print "Warning: this doesn't look like an mbed project"
             return
 
         for root, _, obj_files in os.walk(search_path):
@@ -393,6 +404,8 @@
 
         Keyword arguments:
         file_desc - descriptor (either stdout or file)
+
+        Returns: generated string for the 'table' format, otherwise None
         """
 
         try:
@@ -404,79 +417,35 @@
             print "I/O error({0}): {1}".format(error.errno, error.strerror)
             return False
 
-        subtotal = dict()
-        for k in self.sections:
-            subtotal[k] = 0
-
-        # Calculate misc flash sections
-        misc_flash_mem = 0
-        for i in self.modules:
-            for k in self.misc_flash_sections:
-                if self.modules[i][k]:
-                    misc_flash_mem += self.modules[i][k]
-
-        json_obj = []
-        for i in sorted(self.modules):
-
-            row = []
-            row.append(i)
-
-            for k in self.sections:
-                subtotal[k] += self.modules[i][k]
-
-            for k in self.print_sections:
-                row.append(self.modules[i][k])
-
-            json_obj.append({
-                "module":i,
-                "size":{
-                    k:self.modules[i][k] for k in self.print_sections
-                }
-            })
-
-        summary = {
-            'summary':{
-                'static_ram': (subtotal['.data'] + subtotal['.bss']),
-                'heap': (subtotal['.heap']),
-                'stack': (subtotal['.stack']),
-                'total_ram': (subtotal['.data'] + subtotal['.bss'] +
-                              subtotal['.heap']+subtotal['.stack']),
-                'total_flash': (subtotal['.text'] + subtotal['.data'] +
-                                misc_flash_mem),
-            }
-        }
-
-        self.mem_summary = json_obj + [summary]
-
         to_call = {'json': self.generate_json,
                    'csv-ci': self.generate_csv,
                    'table': self.generate_table}[export_format]
-        to_call(subtotal, misc_flash_mem, file_desc)
+        output = to_call(file_desc)
 
         if file_desc is not sys.stdout:
             file_desc.close()
 
-    def generate_json(self, _, dummy, file_desc):
+        return output
+
+    def generate_json(self, file_desc):
         """Generate a json file from a memory map
 
         Positional arguments:
-        subtotal - total sizes for each module
-        misc_flash_mem - size of misc flash sections
         file_desc - the file to write out the final report to
         """
-        file_desc.write(json.dumps(self.mem_summary, indent=4))
+        file_desc.write(json.dumps(self.mem_report, indent=4))
         file_desc.write('\n')
 
-    def generate_csv(self, subtotal, misc_flash_mem, file_desc):
+        return None
+
+    def generate_csv(self, file_desc):
         """Generate a CSV file from a memoy map
 
         Positional arguments:
-        subtotal - total sizes for each module
-        misc_flash_mem - size of misc flash sections
         file_desc - the file to write out the final report to
         """
         csv_writer = csv.writer(file_desc, delimiter=',',
-                                quoting=csv.QUOTE_NONE)
+                                quoting=csv.QUOTE_MINIMAL)
 
         csv_module_section = []
         csv_sizes = []
@@ -486,37 +455,38 @@
                 csv_sizes += [self.modules[i][k]]
 
         csv_module_section += ['static_ram']
-        csv_sizes += [subtotal['.data']+subtotal['.bss']]
+        csv_sizes += [self.mem_summary['static_ram']]
 
         csv_module_section += ['heap']
-        if subtotal['.heap'] == 0:
+        if self.mem_summary['heap'] == 0:
             csv_sizes += ['unknown']
         else:
-            csv_sizes += [subtotal['.heap']]
+            csv_sizes += [self.mem_summary['heap']]
 
         csv_module_section += ['stack']
-        if subtotal['.stack'] == 0:
+        if self.mem_summary['stack'] == 0:
             csv_sizes += ['unknown']
         else:
-            csv_sizes += [subtotal['.stack']]
+            csv_sizes += [self.mem_summary['stack']]
 
         csv_module_section += ['total_ram']
-        csv_sizes += [subtotal['.data'] + subtotal['.bss'] +
-                      subtotal['.heap'] + subtotal['.stack']]
+        csv_sizes += [self.mem_summary['total_ram']]
 
         csv_module_section += ['total_flash']
-        csv_sizes += [subtotal['.text']+subtotal['.data']+misc_flash_mem]
+        csv_sizes += [self.mem_summary['total_flash']]
 
         csv_writer.writerow(csv_module_section)
         csv_writer.writerow(csv_sizes)
 
-    def generate_table(self, subtotal, misc_flash_mem, file_desc):
+        return None
+
+    def generate_table(self, file_desc):
         """Generate a table from a memoy map
 
         Positional arguments:
-        subtotal - total sizes for each module
-        misc_flash_mem - size of misc flash sections
         file_desc - the file to write out the final report to
+
+        Returns: string of the generated table
         """
         # Create table
         columns = ['Module']
@@ -530,40 +500,84 @@
         for i in list(self.print_sections):
             table.align[i] = 'r'
 
+        for i in sorted(self.modules):
+            row = [i]
+
+            for k in self.print_sections:
+                row.append(self.modules[i][k])
+
+            table.add_row(row)
 
         subtotal_row = ['Subtotals']
         for k in self.print_sections:
-            subtotal_row.append(subtotal[k])
+            subtotal_row.append(self.subtotal[k])
 
         table.add_row(subtotal_row)
 
-        file_desc.write(table.get_string())
-        file_desc.write('\n')
+        output = table.get_string()
+        output += '\n'
 
-        if subtotal['.heap'] == 0:
-            file_desc.write("Allocated Heap: unknown\n")
+        if self.mem_summary['heap'] == 0:
+            output += "Allocated Heap: unknown\n"
         else:
-            file_desc.write("Allocated Heap: %s bytes\n" %
-                            str(subtotal['.heap']))
+            output += "Allocated Heap: %s bytes\n" % \
+                        str(self.mem_summary['heap'])
 
-        if subtotal['.stack'] == 0:
-            file_desc.write("Allocated Stack: unknown\n")
+        if self.mem_summary['stack'] == 0:
+            output += "Allocated Stack: unknown\n"
         else:
-            file_desc.write("Allocated Stack: %s bytes\n" %
-                            str(subtotal['.stack']))
+            output += "Allocated Stack: %s bytes\n" % \
+                        str(self.mem_summary['stack'])
 
-        file_desc.write("Total Static RAM memory (data + bss): %s bytes\n" %
-                        (str(subtotal['.data'] + subtotal['.bss'])))
-        file_desc.write(
-            "Total RAM memory (data + bss + heap + stack): %s bytes\n"
-            % (str(subtotal['.data'] + subtotal['.bss'] + subtotal['.heap'] +
-                   subtotal['.stack'])))
-        file_desc.write("Total Flash memory (text + data + misc): %s bytes\n" %
-                        (str(subtotal['.text'] + subtotal['.data'] +
-                             misc_flash_mem)))
+        output += "Total Static RAM memory (data + bss): %s bytes\n" % \
+                        str(self.mem_summary['static_ram'])
+        output += "Total RAM memory (data + bss + heap + stack): %s bytes\n" % \
+                        str(self.mem_summary['total_ram'])
+        output += "Total Flash memory (text + data + misc): %s bytes\n" % \
+                        str(self.mem_summary['total_flash'])
+
+        return output
 
     toolchains = ["ARM", "ARM_STD", "ARM_MICRO", "GCC_ARM", "IAR"]
 
+    def compute_report(self):
+        for k in self.sections:
+            self.subtotal[k] = 0
+
+        for i in sorted(self.modules):
+            for k in self.sections:
+                self.subtotal[k] += self.modules[i][k]
+
+        # Calculate misc flash sections
+        self.misc_flash_mem = 0
+        for i in self.modules:
+            for k in self.misc_flash_sections:
+                if self.modules[i][k]:
+                    self.misc_flash_mem += self.modules[i][k]
+
+        self.mem_summary = {
+            'static_ram': (self.subtotal['.data'] + self.subtotal['.bss']),
+            'heap': (self.subtotal['.heap']),
+            'stack': (self.subtotal['.stack']),
+            'total_ram': (self.subtotal['.data'] + self.subtotal['.bss'] +
+                          self.subtotal['.heap']+self.subtotal['.stack']),
+            'total_flash': (self.subtotal['.text'] + self.subtotal['.data'] +
+                            self.misc_flash_mem),
+        }
+
+        self.mem_report = []
+        for i in sorted(self.modules):
+            self.mem_report.append({
+                "module":i,
+                "size":{
+                    k:self.modules[i][k] for k in self.print_sections
+                }
+            })
+
+        self.mem_report.append({
+            'summary': self.mem_summary
+        })
+
     def parse(self, mapfile, toolchain):
         """ Parse and decode map file depending on the toolchain
 
@@ -577,15 +591,18 @@
             with open(mapfile, 'r') as file_input:
                 if toolchain == "ARM" or toolchain == "ARM_STD" or\
                    toolchain == "ARM_MICRO":
-                    self.search_objects(os.path.abspath(mapfile), "ARM")
+                    self.search_objects(os.path.abspath(mapfile))
                     self.parse_map_file_armcc(file_input)
                 elif toolchain == "GCC_ARM":
                     self.parse_map_file_gcc(file_input)
                 elif toolchain == "IAR":
-                    self.search_objects(os.path.abspath(mapfile), toolchain)
+                    self.search_objects(os.path.abspath(mapfile))
                     self.parse_map_file_iar(file_input)
                 else:
                     result = False
+            
+            self.compute_report()
+        
         except IOError as error:
             print "I/O error({0}): {1}".format(error.errno, error.strerror)
             result = False
@@ -594,7 +611,7 @@
 def main():
     """Entry Point"""
 
-    version = '0.3.11'
+    version = '0.3.12'
 
     # Parser handling
     parser = argparse.ArgumentParser(
@@ -622,6 +639,8 @@
         ", ".join(MemapParser.export_formats))
 
     parser.add_argument('-v', '--version', action='version', version=version)
+    
+    parser.add_argument('-d', '--detailed', action='store_true', help='Displays the elements in "Misc" in a detailed fashion', required=False)
 
     # Parse/run command
     if len(sys.argv) <= 1:
@@ -632,18 +651,22 @@
     args = parser.parse_args()
 
     # Create memap object
-    memap = MemapParser()
+    memap = MemapParser(detailed_misc=args.detailed)
 
     # Parse and decode a map file
     if args.file and args.toolchain:
         if memap.parse(args.file, args.toolchain) is False:
             sys.exit(0)
 
+    returned_string = None
     # Write output in file
     if args.output != None:
-        memap.generate_output(args.export, args.output)
+        returned_string = memap.generate_output(args.export, args.output)
     else: # Write output in screen
-        memap.generate_output(args.export)
+        returned_string = memap.generate_output(args.export)
+
+    if args.export == 'table' and returned_string:
+        print returned_string
 
     sys.exit(0)