Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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()
Generated on Tue Jul 12 2022 13:24:58 by
1.7.2