Knight KE / Mbed OS Game_Master
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers memap.py Source File

memap.py

00001 #!/usr/bin/env python
00002 
00003 """Memory Map File Analyser for ARM mbed"""
00004 from __future__ import print_function, division, absolute_import
00005 
00006 from abc import abstractmethod, ABCMeta
00007 from sys import stdout, exit, argv
00008 from os import sep
00009 from os.path import (basename, dirname, join, relpath, abspath, commonprefix,
00010                      splitext)
00011 import re
00012 import csv
00013 import json
00014 from argparse import ArgumentParser
00015 from copy import deepcopy
00016 from prettytable import PrettyTable
00017 from jinja2 import FileSystemLoader, StrictUndefined
00018 from jinja2.environment import Environment
00019 
00020 from .utils import (argparse_filestring_type, argparse_lowercase_hyphen_type,
00021                     argparse_uppercase_type)
00022 
00023 
00024 class _Parser (object):
00025     """Internal interface for parsing"""
00026     __metaclass__ = ABCMeta
00027     SECTIONS = ('.text', '.data', '.bss', '.heap', '.stack')
00028     MISC_FLASH_SECTIONS = ('.interrupts', '.flash_config')
00029     OTHER_SECTIONS = ('.interrupts_ram', '.init', '.ARM.extab',
00030                       '.ARM.exidx', '.ARM.attributes', '.eh_frame',
00031                       '.init_array', '.fini_array', '.jcr', '.stab',
00032                       '.stabstr', '.ARM.exidx', '.ARM')
00033 
00034     def __init__(self):
00035         self.modules  = dict()
00036 
00037     def module_add (self, object_name, size, section):
00038         """ Adds a module or section to the list
00039 
00040         Positional arguments:
00041         object_name - name of the entry to add
00042         size - the size of the module being added
00043         section - the section the module contributes to
00044         """
00045         if not object_name or not size or not section:
00046             return
00047 
00048         if object_name in self.modules :
00049             self.modules [object_name].setdefault(section, 0)
00050             self.modules [object_name][section] += size
00051             return
00052 
00053         obj_split = sep + basename(object_name)
00054         for module_path, contents in self.modules .items():
00055             if module_path.endswith(obj_split) or module_path == object_name:
00056                 contents.setdefault(section, 0)
00057                 contents[section] += size
00058                 return
00059 
00060         new_module = {section: size}
00061         self.modules [object_name] = new_module
00062 
00063     def module_replace (self, old_object, new_object):
00064         """ Replaces an object name with a new one
00065         """
00066         if old_object in self.modules :
00067             self.modules [new_object] = self.modules [old_object]
00068             del self.modules [old_object]
00069 
00070     @abstractmethod
00071     def parse_mapfile (self, mapfile):
00072         """Parse a given file object pointing to a map file
00073 
00074         Positional arguments:
00075         mapfile - an open file object that reads a map file
00076 
00077         return value - a dict mapping from object names to section dicts,
00078                        where a section dict maps from sections to sizes
00079         """
00080         raise NotImplemented
00081 
00082 
00083 class _GccParser(_Parser ):
00084     RE_OBJECT_FILE = re.compile(r'^(.+\/.+\.o)$')
00085     RE_LIBRARY_OBJECT = re.compile(r'^.+' + r''.format(sep) + r'lib((.+\.a)\((.+\.o)\))$')
00086     RE_STD_SECTION = re.compile(r'^\s+.*0x(\w{8,16})\s+0x(\w+)\s(.+)$')
00087     RE_FILL_SECTION = re.compile(r'^\s*\*fill\*\s+0x(\w{8,16})\s+0x(\w+).*$')
00088 
00089     ALL_SECTIONS = _Parser.SECTIONS + _Parser.OTHER_SECTIONS + \
00090                    _Parser.MISC_FLASH_SECTIONS + ('unknown', 'OUTPUT')
00091 
00092     def check_new_section(self, line):
00093         """ Check whether a new section in a map file has been detected 
00094 
00095         Positional arguments:
00096         line - the line to check for a new section
00097 
00098         return value - A section name, if a new section was found, False
00099                        otherwise
00100         """
00101         for i in self.ALL_SECTIONS:
00102             if line.startswith(i):
00103                 # should name of the section (assuming it's a known one)
00104                 return i
00105 
00106         if line.startswith('.'):
00107             return 'unknown'     # all others are classified are unknown
00108         else:
00109             return False         # everything else, means no change in section
00110 
00111 
00112     def parse_object_name(self, line):
00113         """ Parse a path to object file
00114 
00115         Positional arguments:
00116         line - the path to parse the object and module name from
00117 
00118         return value - an object file name
00119         """
00120         test_re_mbed_os_name = re.match(self.RE_OBJECT_FILE, line)
00121 
00122         if test_re_mbed_os_name:
00123             object_name = test_re_mbed_os_name.group(1)
00124 
00125             # corner case: certain objects are provided by the GCC toolchain
00126             if 'arm-none-eabi' in line:
00127                 return join('[lib]', 'misc', basename(object_name))
00128             return object_name
00129 
00130         else:
00131             test_re_obj_name = re.match(self.RE_LIBRARY_OBJECT, line)
00132 
00133             if test_re_obj_name:
00134                 return join('[lib]', test_re_obj_name.group(2),
00135                             test_re_obj_name.group(3))
00136             else:
00137                 print("Unknown object name found in GCC map file: %s" % line)
00138                 return '[misc]'
00139 
00140     def parse_section(self, line):
00141         """ Parse data from a section of gcc map file
00142 
00143         examples:
00144                         0x00004308       0x7c ./BUILD/K64F/GCC_ARM/mbed-os/hal/targets/hal/TARGET_Freescale/TARGET_KPSDK_MCUS/spi_api.o
00145          .text          0x00000608      0x198 ./BUILD/K64F/GCC_ARM/mbed-os/core/mbed-rtos/rtx/TARGET_CORTEX_M/TARGET_RTOS_M4_M7/TOOLCHAIN/HAL_CM4.o
00146 
00147         Positional arguments:
00148         line - the line to parse a section from
00149         """
00150         is_fill = re.match(self.RE_FILL_SECTION, line)
00151         if is_fill:
00152             o_name = '[fill]'
00153             o_size = int(is_fill.group(2), 16)
00154             return [o_name, o_size]
00155 
00156         is_section = re.match(self.RE_STD_SECTION, line)
00157         if is_section:
00158             o_size = int(is_section.group(2), 16)
00159             if o_size:
00160                 o_name = self.parse_object_name(is_section.group(3))
00161                 return [o_name, o_size]
00162 
00163         return ["", 0]
00164 
00165     def parse_mapfile (self, file_desc):
00166         """ Main logic to decode gcc map files
00167 
00168         Positional arguments:
00169         file_desc - a stream object to parse as a gcc map file
00170         """
00171         current_section = 'unknown'
00172 
00173         with file_desc as infile:
00174             for line in infile:
00175                 if line.startswith('Linker script and memory map'):
00176                     current_section = "unknown"
00177                     break
00178 
00179             for line in infile:
00180                 next_section = self.check_new_section(line)
00181 
00182                 if next_section == "OUTPUT":
00183                     break
00184                 elif next_section:
00185                     current_section = next_section
00186 
00187                 object_name, object_size = self.parse_section(line)
00188                 self.module_add (object_name, object_size, current_section)
00189 
00190         common_prefix = dirname(commonprefix([
00191             o for o in self.modules .keys() if (o.endswith(".o") and not o.startswith("[lib]"))]))
00192         new_modules = {}
00193         for name, stats in self.modules .items():
00194             if name.startswith("[lib]"):
00195                 new_modules[name] = stats
00196             elif name.endswith(".o"):
00197                 new_modules[relpath(name, common_prefix)] = stats
00198             else:
00199                 new_modules[name] = stats
00200         return new_modules
00201 
00202 
00203 class _ArmccParser(_Parser ):
00204     RE = re.compile(
00205         r'^\s+0x(\w{8})\s+0x(\w{8})\s+(\w+)\s+(\w+)\s+(\d+)\s+[*]?.+\s+(.+)$')
00206     RE_OBJECT = re.compile(r'(.+\.(l|ar))\((.+\.o)\)')
00207 
00208     def parse_object_name(self, line):
00209         """ Parse object file
00210 
00211         Positional arguments:
00212         line - the line containing the object or library
00213         """
00214         if line.endswith(".o"):
00215             return line
00216 
00217         else:
00218             is_obj = re.match(self.RE_OBJECT, line)
00219             if is_obj:
00220                 return join('[lib]', basename(is_obj.group(1)), is_obj.group(3))
00221             else:
00222                 print("Malformed input found when parsing ARMCC map: %s" % line)
00223                 return '[misc]'
00224 
00225     def parse_section(self, line):
00226         """ Parse data from an armcc map file
00227 
00228         Examples of armcc map file:
00229             Base_Addr    Size         Type   Attr      Idx    E Section Name        Object
00230             0x00000000   0x00000400   Data   RO        11222    self.RESET               startup_MK64F12.o
00231             0x00000410   0x00000008   Code   RO        49364  * !!!main             c_w.l(__main.o)
00232 
00233         Positional arguments:
00234         line - the line to parse the section data from
00235         """
00236         test_re = re.match(self.RE, line)
00237 
00238         if test_re:
00239             size = int(test_re.group(2), 16)
00240 
00241             if test_re.group(4) == 'RO':
00242                 section = '.text'
00243             else:
00244                 if test_re.group(3) == 'Data':
00245                     section = '.data'
00246                 elif test_re.group(3) == 'Zero':
00247                     section = '.bss'
00248                 elif test_re.group(3) == 'Code':
00249                     section = '.text'
00250                 else:
00251                     print("Malformed input found when parsing armcc map: %s, %r"
00252                           % (line, test_re.groups()))
00253 
00254                     return ["", 0, ""]
00255 
00256             # check name of object or library
00257             object_name = self.parse_object_name(
00258                 test_re.group(6))
00259 
00260             return [object_name, size, section]
00261 
00262         else:
00263             return ["", 0, ""]
00264 
00265     def parse_mapfile (self, file_desc):
00266         """ Main logic to decode armc5 map files
00267 
00268         Positional arguments:
00269         file_desc - a file like object to parse as an armc5 map file
00270         """
00271         with file_desc as infile:
00272             # Search area to parse
00273             for line in infile:
00274                 if line.startswith('    Base Addr    Size'):
00275                     break
00276 
00277             # Start decoding the map file
00278             for line in infile:
00279                 self.module_add (*self.parse_section(line))
00280 
00281         common_prefix = dirname(commonprefix([
00282             o for o in self.modules .keys() if (o.endswith(".o") and o != "anon$$obj.o" and not o.startswith("[lib]"))]))
00283         new_modules = {}
00284         for name, stats in self.modules .items():
00285             if name == "anon$$obj.o" or name.startswith("[lib]"):
00286                 new_modules[name] = stats
00287             elif name.endswith(".o"):
00288                 new_modules[relpath(name, common_prefix)] = stats
00289             else:
00290                 new_modules[name] = stats
00291         return new_modules
00292 
00293 
00294 class _IarParser(_Parser ):
00295     RE = re.compile(
00296         r'^\s+(.+)\s+(zero|const|ro code|inited|uninit)\s'
00297         r'+0x(\w{8})\s+0x(\w+)\s+(.+)\s.+$')
00298 
00299     RE_CMDLINE_FILE = re.compile(r'^#\s+(.+\.o)')
00300     RE_LIBRARY = re.compile(r'^(.+\.a)\:.+$')
00301     RE_OBJECT_LIBRARY = re.compile(r'^\s+(.+\.o)\s.*')
00302 
00303     def __init__(self):
00304         _Parser.__init__(self)
00305         # Modules passed to the linker on the command line
00306         # this is a dict because modules are looked up by their basename
00307         self.cmd_modules = {}
00308 
00309     def parse_object_name(self, object_name):
00310         """ Parse object file
00311 
00312         Positional arguments:
00313         line - the line containing the object or library
00314         """
00315         if object_name.endswith(".o"):
00316             try:
00317                 return self.cmd_modules[object_name]
00318             except KeyError:
00319                 return object_name
00320         else:
00321             return '[misc]'
00322 
00323     def parse_section(self, line):
00324         """ Parse data from an IAR map file
00325 
00326         Examples of IAR map file:
00327          Section             Kind        Address     Size  Object
00328          .intvec             ro code  0x00000000    0x198  startup_MK64F12.o [15]
00329          .rodata             const    0x00000198      0x0  zero_init3.o [133]
00330          .iar.init_table     const    0x00008384     0x2c  - Linker created -
00331          Initializer bytes   const    0x00000198     0xb2  <for P3 s0>
00332          .data               inited   0x20000000     0xd4  driverAtmelRFInterface.o [70]
00333          .bss                zero     0x20000598    0x318  RTX_Conf_CM.o [4]
00334          .iar.dynexit        uninit   0x20001448    0x204  <Block tail>
00335            HEAP              uninit   0x20001650  0x10000  <Block tail>
00336 
00337         Positional_arguments:
00338         line - the line to parse section data from
00339         """
00340         test_re = re.match(self.RE, line)
00341         if test_re:
00342             if (test_re.group(2) == 'const' or
00343                 test_re.group(2) == 'ro code'):
00344                 section = '.text'
00345             elif (test_re.group(2) == 'zero' or
00346                   test_re.group(2) == 'uninit'):
00347                 if test_re.group(1)[0:4] == 'HEAP':
00348                     section = '.heap'
00349                 elif test_re.group(1)[0:6] == 'CSTACK':
00350                     section = '.stack'
00351                 else:
00352                     section = '.bss' #  default section
00353 
00354             elif test_re.group(2) == 'inited':
00355                 section = '.data'
00356             else:
00357                 print("Malformed input found when parsing IAR map: %s" % line)
00358                 return ["", 0, ""]
00359 
00360             # lookup object in dictionary and return module name
00361             object_name = self.parse_object_name(test_re.group(5))
00362 
00363             size = int(test_re.group(4), 16)
00364             return [object_name, size, section]
00365 
00366         else:
00367             return ["", 0, ""]
00368 
00369     def check_new_library(self, line):
00370         """
00371         Searches for libraries and returns name. Example:
00372         m7M_tls.a: [43]
00373 
00374         """
00375         test_address_line = re.match(self.RE_LIBRARY, line)
00376         if test_address_line:
00377             return test_address_line.group(1)
00378         else:
00379             return ""
00380 
00381     def check_new_object_lib(self, line):
00382         """
00383         Searches for objects within a library section and returns name. Example:
00384         rt7M_tl.a: [44]
00385             ABImemclr4.o                 6
00386             ABImemcpy_unaligned.o      118
00387             ABImemset48.o               50
00388             I64DivMod.o                238
00389             I64DivZer.o                  2
00390 
00391         """
00392         test_address_line = re.match(self.RE_OBJECT_LIBRARY, line)
00393         if test_address_line:
00394             return test_address_line.group(1)
00395         else:
00396             return ""
00397 
00398     def parse_command_line(self, lines):
00399         """Parse the files passed on the command line to the iar linker
00400 
00401         Positional arguments:
00402         lines -- an iterator over the lines within a file
00403         """
00404         for line in lines:
00405             if line.startswith("*"):
00406                 break
00407             for arg in line.split(" "):
00408                 arg = arg.rstrip(" \n")
00409                 if (not arg.startswith("-")) and arg.endswith(".o"):
00410                     self.cmd_modules[basename(arg)] = arg
00411 
00412         common_prefix = dirname(commonprefix(list(self.cmd_modules.values())))
00413         self.cmd_modules = {s: relpath(f, common_prefix)
00414                             for s, f in self.cmd_modules.items()}
00415 
00416     def parse_mapfile (self, file_desc):
00417         """ Main logic to decode IAR map files
00418 
00419         Positional arguments:
00420         file_desc - a file like object to parse as an IAR map file
00421         """
00422         with file_desc as infile:
00423             self.parse_command_line(infile)
00424 
00425             for line in infile:
00426                 if line.startswith('  Section  '):
00427                     break
00428 
00429             for line in infile:
00430                 self.module_add (*self.parse_section(line))
00431 
00432                 if line.startswith('*** MODULE SUMMARY'): # finish section
00433                     break
00434 
00435             current_library = ""
00436             for line in infile:
00437                 library = self.check_new_library(line)
00438 
00439                 if library:
00440                     current_library = library
00441 
00442                 object_name = self.check_new_object_lib(line)
00443 
00444                 if object_name and current_library:
00445                     temp = join('[lib]', current_library, object_name)
00446                     self.module_replace (object_name, temp)
00447         return self.modules 
00448 
00449 
00450 class MemapParser (object):
00451     """An object that represents parsed results, parses the memory map files,
00452     and writes out different file types of memory results
00453     """
00454 
00455     print_sections = ('.text', '.data', '.bss')
00456 
00457 
00458     # sections to print info (generic for all toolchains)
00459     sections = _Parser.SECTIONS
00460     misc_flash_sections = _Parser.MISC_FLASH_SECTIONS
00461     other_sections = _Parser.OTHER_SECTIONS
00462 
00463     def __init__(self):
00464         # list of all modules and their sections
00465         # full list - doesn't change with depth
00466         self.modules  = dict()
00467         # short version with specific depth
00468         self.short_modules  = dict()
00469 
00470 
00471         # Memory report (sections + summary)
00472         self.mem_report  = []
00473 
00474         # Memory summary
00475         self.mem_summary  = dict()
00476 
00477         # Totals of ".text", ".data" and ".bss"
00478         self.subtotal  = dict()
00479 
00480         # Flash no associated with a module
00481         self.misc_flash_mem  = 0
00482 
00483         # Name of the toolchain, for better headings
00484         self.tc_name  = None
00485 
00486     def reduce_depth (self, depth):
00487         """
00488         populates the short_modules attribute with a truncated module list
00489 
00490         (1) depth = 1:
00491         main.o
00492         mbed-os
00493 
00494         (2) depth = 2:
00495         main.o
00496         mbed-os/test.o
00497         mbed-os/drivers
00498 
00499         """
00500         if depth == 0 or depth == None:
00501             self.short_modules  = deepcopy(self.modules )
00502         else:
00503             self.short_modules  = dict()
00504             for module_name, v in self.modules .items():
00505                 split_name = module_name.split(sep)
00506                 if split_name[0] == '':
00507                     split_name = split_name[1:]
00508                 new_name = join(*split_name[:depth])
00509                 self.short_modules .setdefault(new_name, {})
00510                 for section_idx, value in v.items():
00511                     self.short_modules [new_name].setdefault(section_idx, 0)
00512                     self.short_modules [new_name][section_idx] += self.modules [module_name][section_idx]
00513 
00514     export_formats = ["json", "csv-ci", "html", "table"]
00515 
00516     def generate_output (self, export_format, depth, file_output=None):
00517         """ Generates summary of memory map data
00518 
00519         Positional arguments:
00520         export_format - the format to dump
00521 
00522         Keyword arguments:
00523         file_desc - descriptor (either stdout or file)
00524         depth - directory depth on report
00525 
00526         Returns: generated string for the 'table' format, otherwise None
00527         """
00528         self.reduce_depth (depth)
00529         self.compute_report ()
00530         try:
00531             if file_output:
00532                 file_desc = open(file_output, 'w')
00533             else:
00534                 file_desc = stdout
00535         except IOError as error:
00536             print("I/O error({0}): {1}".format(error.errno, error.strerror))
00537             return False
00538 
00539         to_call = {'json': self.generate_json ,
00540                    'html': self.generate_html ,
00541                    'csv-ci': self.generate_csv ,
00542                    'table': self.generate_table }[export_format]
00543         output = to_call(file_desc)
00544 
00545         if file_desc is not stdout:
00546             file_desc.close()
00547 
00548         return output
00549 
00550     @staticmethod
00551     def _move_up_tree(tree, next_module):
00552         tree.setdefault("children", [])
00553         for child in tree["children"]:
00554             if child["name"] == next_module:
00555                 return child
00556         else:
00557             new_module = {"name": next_module, "value": 0}
00558             tree["children"].append(new_module)
00559             return new_module
00560 
00561     def generate_html (self, file_desc):
00562         """Generate a json file from a memory map for D3
00563 
00564         Positional arguments:
00565         file_desc - the file to write out the final report to
00566         """
00567         tree_text = {"name": ".text", "value": 0}
00568         tree_bss = {"name": ".bss", "value": 0}
00569         tree_data = {"name": ".data", "value": 0}
00570         for name, dct in self.modules .items():
00571             cur_text = tree_text
00572             cur_bss = tree_bss
00573             cur_data = tree_data
00574             modules = name.split(sep)
00575             while True:
00576                 try:
00577                     cur_text["value"] += dct['.text']
00578                 except KeyError:
00579                     pass
00580                 try:
00581                     cur_bss["value"] += dct['.bss']
00582                 except KeyError:
00583                     pass
00584                 try:
00585                     cur_data["value"] += dct['.data']
00586                 except KeyError:
00587                     pass
00588                 if not modules:
00589                     break
00590                 next_module = modules.pop(0)
00591                 cur_text = self._move_up_tree (cur_text, next_module)
00592                 cur_data = self._move_up_tree (cur_data, next_module)
00593                 cur_bss = self._move_up_tree (cur_bss, next_module)
00594 
00595         tree_rom = {
00596             "name": "ROM",
00597             "value": tree_text["value"] + tree_data["value"],
00598             "children": [tree_text, tree_data]
00599         }
00600         tree_ram = {
00601             "name": "RAM",
00602             "value": tree_bss["value"] + tree_data["value"],
00603             "children": [tree_bss, tree_data]
00604         }
00605 
00606         jinja_loader = FileSystemLoader(dirname(abspath(__file__)))
00607         jinja_environment = Environment(loader=jinja_loader,
00608                                         undefined=StrictUndefined)
00609 
00610         template = jinja_environment.get_template("memap_flamegraph.html")
00611         name, _ = splitext(basename(file_desc.name))
00612         if name.endswith("_map"):
00613             name = name[:-4]
00614         if self.tc_name :
00615             name = "%s %s" % (name, self.tc_name )
00616         data = {
00617             "name": name,
00618             "rom": json.dumps(tree_rom),
00619             "ram": json.dumps(tree_ram),
00620         }
00621         file_desc.write(template.render(data))
00622         return None
00623 
00624     def generate_json (self, file_desc):
00625         """Generate a json file from a memory map
00626 
00627         Positional arguments:
00628         file_desc - the file to write out the final report to
00629         """
00630         file_desc.write(json.dumps(self.mem_report , indent=4))
00631         file_desc.write('\n')
00632         return None
00633 
00634     def generate_csv (self, file_desc):
00635         """Generate a CSV file from a memoy map
00636 
00637         Positional arguments:
00638         file_desc - the file to write out the final report to
00639         """
00640         writer = csv.writer(file_desc, delimiter=',',
00641                             quoting=csv.QUOTE_MINIMAL)
00642 
00643         module_section = []
00644         sizes = []
00645         for i in sorted(self.short_modules ):
00646             for k in self.print_sections :
00647                 module_section.append((i + k))
00648                 sizes += [self.short_modules [i][k]]
00649 
00650         module_section.append('static_ram')
00651         sizes.append(self.mem_summary ['static_ram'])
00652 
00653         module_section.append('total_flash')
00654         sizes.append(self.mem_summary ['total_flash'])
00655 
00656         writer.writerow(module_section)
00657         writer.writerow(sizes)
00658         return None
00659 
00660     def generate_table (self, file_desc):
00661         """Generate a table from a memoy map
00662 
00663         Returns: string of the generated table
00664         """
00665         # Create table
00666         columns = ['Module']
00667         columns.extend(self.print_sections )
00668 
00669         table = PrettyTable(columns)
00670         table.align["Module"] = "l"
00671         for col in self.print_sections :
00672             table.align[col] = 'r'
00673 
00674         for i in list(self.print_sections ):
00675             table.align[i] = 'r'
00676 
00677         for i in sorted(self.short_modules ):
00678             row = [i]
00679 
00680             for k in self.print_sections :
00681                 row.append(self.short_modules [i][k])
00682 
00683             table.add_row(row)
00684 
00685         subtotal_row = ['Subtotals']
00686         for k in self.print_sections :
00687             subtotal_row.append(self.subtotal [k])
00688 
00689         table.add_row(subtotal_row)
00690 
00691         output = table.get_string()
00692         output += '\n'
00693 
00694         output += "Total Static RAM memory (data + bss): %s bytes\n" % \
00695                         str(self.mem_summary ['static_ram'])
00696         output += "Total Flash memory (text + data): %s bytes\n" % \
00697                         str(self.mem_summary ['total_flash'])
00698 
00699         return output
00700 
00701     toolchains = ["ARM", "ARM_STD", "ARM_MICRO", "GCC_ARM", "GCC_CR", "IAR"]
00702 
00703     def compute_report (self):
00704         """ Generates summary of memory usage for main areas
00705         """
00706         for k in self.sections :
00707             self.subtotal [k] = 0
00708 
00709         for i in self.short_modules :
00710             for k in self.sections :
00711                 self.short_modules [i].setdefault(k, 0)
00712                 self.subtotal [k] += self.short_modules [i][k]
00713 
00714         self.mem_summary  = {
00715             'static_ram': (self.subtotal ['.data'] + self.subtotal ['.bss']),
00716             'total_flash': (self.subtotal ['.text'] + self.subtotal ['.data']),
00717         }
00718 
00719         self.mem_report  = []
00720         for i in sorted(self.short_modules ):
00721             self.mem_report .append({
00722                 "module":i,
00723                 "size":{
00724                     k: self.short_modules [i][k] for k in self.print_sections 
00725                 }
00726             })
00727 
00728         self.mem_report .append({
00729             'summary': self.mem_summary 
00730         })
00731 
00732     def parse (self, mapfile, toolchain):
00733         """ Parse and decode map file depending on the toolchain
00734 
00735         Positional arguments:
00736         mapfile - the file name of the memory map file
00737         toolchain - the toolchain used to create the file
00738         """
00739         self.tc_name  = toolchain.title()
00740         if toolchain in ("ARM", "ARM_STD", "ARM_MICRO", "ARMC6"):
00741             parser = _ArmccParser()
00742         elif toolchain == "GCC_ARM" or toolchain == "GCC_CR":
00743             parser = _GccParser()
00744         elif toolchain == "IAR":
00745             parser = _IarParser()
00746         else:
00747             return False
00748         try:
00749             with open(mapfile, 'r') as file_input:
00750                 self.modules  = parser.parse_mapfile(file_input)
00751             return True
00752 
00753         except IOError as error:
00754             print("I/O error({0}): {1}".format(error.errno, error.strerror))
00755             return False
00756 
00757 def main():
00758     """Entry Point"""
00759     version = '0.4.0'
00760 
00761     # Parser handling
00762     parser = ArgumentParser(
00763         description="Memory Map File Analyser for ARM mbed\nversion %s" %
00764         version)
00765 
00766     parser.add_argument(
00767         'file', type=argparse_filestring_type, help='memory map file')
00768 
00769     parser.add_argument(
00770         '-t', '--toolchain', dest='toolchain',
00771         help='select a toolchain used to build the memory map file (%s)' %
00772         ", ".join(MemapParser.toolchains),
00773         required=True,
00774         type=argparse_uppercase_type(MemapParser.toolchains, "toolchain"))
00775 
00776     parser.add_argument(
00777         '-d', '--depth', dest='depth', type=int,
00778         help='specify directory depth level to display report', required=False)
00779 
00780     parser.add_argument(
00781         '-o', '--output', help='output file name', required=False)
00782 
00783     parser.add_argument(
00784         '-e', '--export', dest='export', required=False, default='table',
00785         type=argparse_lowercase_hyphen_type(MemapParser.export_formats,
00786                                             'export format'),
00787         help="export format (examples: %s: default)" %
00788         ", ".join(MemapParser.export_formats))
00789 
00790     parser.add_argument('-v', '--version', action='version', version=version)
00791 
00792     # Parse/run command
00793     if len(argv) <= 1:
00794         parser.print_help()
00795         exit(1)
00796 
00797     args = parser.parse_args()
00798 
00799     # Create memap object
00800     memap = MemapParser()
00801 
00802     # Parse and decode a map file
00803     if args.file and args.toolchain:
00804         if memap.parse(args.file, args.toolchain) is False:
00805             exit(0)
00806 
00807     if args.depth is None:
00808         depth = 2  # default depth level
00809     else:
00810         depth = args.depth
00811 
00812     returned_string = None
00813     # Write output in file
00814     if args.output != None:
00815         returned_string = memap.generate_output(args.export, \
00816             depth, args.output)
00817     else: # Write output in screen
00818         returned_string = memap.generate_output(args.export, depth)
00819 
00820     if args.export == 'table' and returned_string:
00821         print(returned_string)
00822 
00823     exit(0)
00824 
00825 if __name__ == "__main__":
00826     main()