Nathan Yonkee / Mbed 2 deprecated Nucleo_sinewave_output_copy

Dependencies:   mbed

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