Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-sdk-tools by
Diff: memap.py
- Revision:
- 32:8ea194f6145b
- Parent:
- 30:f12ce67666d0
--- 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)