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