Clone of official tools
memap.py@43:2a7da56ebd24, 2018-09-25 (annotated)
- Committer:
- theotherjimmy
- Date:
- Tue Sep 25 13:43:09 2018 -0500
- Revision:
- 43:2a7da56ebd24
- Parent:
- 41:2a77626a4c21
Release 5.10.0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 1 | #!/usr/bin/env python |
screamer | 8:a8ac6ed29081 | 2 | |
screamer | 29:1210849dba19 | 3 | """Memory Map File Analyser for ARM mbed""" |
theotherjimmy |
43:2a7da56ebd24 | 4 | from __future__ import print_function, division, absolute_import |
screamer | 8:a8ac6ed29081 | 5 | |
theotherjimmy |
43:2a7da56ebd24 | 6 | from abc import abstractmethod, ABCMeta |
theotherjimmy |
43:2a7da56ebd24 | 7 | from sys import stdout, exit, argv, path |
theotherjimmy |
43:2a7da56ebd24 | 8 | from os import sep, rename, remove |
theotherjimmy |
43:2a7da56ebd24 | 9 | from os.path import (basename, dirname, join, relpath, abspath, commonprefix, |
theotherjimmy |
43:2a7da56ebd24 | 10 | splitext, exists) |
theotherjimmy |
43:2a7da56ebd24 | 11 | |
theotherjimmy |
43:2a7da56ebd24 | 12 | # Be sure that the tools directory is in the search path |
theotherjimmy |
43:2a7da56ebd24 | 13 | ROOT = abspath(join(dirname(__file__), "..")) |
theotherjimmy |
43:2a7da56ebd24 | 14 | path.insert(0, ROOT) |
theotherjimmy |
43:2a7da56ebd24 | 15 | |
screamer | 8:a8ac6ed29081 | 16 | import re |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 17 | import csv |
screamer | 8:a8ac6ed29081 | 18 | import json |
theotherjimmy |
43:2a7da56ebd24 | 19 | from argparse import ArgumentParser |
theotherjimmy |
40:7d3fa6b99b2b | 20 | from copy import deepcopy |
theotherjimmy |
43:2a7da56ebd24 | 21 | from collections import defaultdict |
theotherjimmy |
43:2a7da56ebd24 | 22 | from prettytable import PrettyTable, HEADER |
theotherjimmy |
43:2a7da56ebd24 | 23 | from jinja2 import FileSystemLoader, StrictUndefined |
theotherjimmy |
43:2a7da56ebd24 | 24 | from jinja2.environment import Environment |
screamer | 29:1210849dba19 | 25 | |
theotherjimmy |
43:2a7da56ebd24 | 26 | from tools.utils import (argparse_filestring_type, argparse_lowercase_hyphen_type, |
theotherjimmy |
43:2a7da56ebd24 | 27 | argparse_uppercase_type) |
theotherjimmy |
43:2a7da56ebd24 | 28 | from tools.settings import COMPARE_FIXED |
theotherjimmy |
40:7d3fa6b99b2b | 29 | |
theotherjimmy |
40:7d3fa6b99b2b | 30 | |
theotherjimmy |
43:2a7da56ebd24 | 31 | class _Parser(object): |
theotherjimmy |
43:2a7da56ebd24 | 32 | """Internal interface for parsing""" |
theotherjimmy |
43:2a7da56ebd24 | 33 | __metaclass__ = ABCMeta |
theotherjimmy |
43:2a7da56ebd24 | 34 | SECTIONS = ('.text', '.data', '.bss', '.heap', '.stack') |
theotherjimmy |
43:2a7da56ebd24 | 35 | MISC_FLASH_SECTIONS = ('.interrupts', '.flash_config') |
theotherjimmy |
43:2a7da56ebd24 | 36 | OTHER_SECTIONS = ('.interrupts_ram', '.init', '.ARM.extab', |
screamer | 29:1210849dba19 | 37 | '.ARM.exidx', '.ARM.attributes', '.eh_frame', |
screamer | 29:1210849dba19 | 38 | '.init_array', '.fini_array', '.jcr', '.stab', |
screamer | 29:1210849dba19 | 39 | '.stabstr', '.ARM.exidx', '.ARM') |
screamer | 29:1210849dba19 | 40 | |
theotherjimmy |
40:7d3fa6b99b2b | 41 | def __init__(self): |
theotherjimmy |
43:2a7da56ebd24 | 42 | self.modules = dict() |
theotherjimmy |
40:7d3fa6b99b2b | 43 | |
theotherjimmy |
40:7d3fa6b99b2b | 44 | def module_add(self, object_name, size, section): |
theotherjimmy |
43:2a7da56ebd24 | 45 | """ Adds a module or section to the list |
screamer | 29:1210849dba19 | 46 | |
screamer | 29:1210849dba19 | 47 | Positional arguments: |
theotherjimmy |
40:7d3fa6b99b2b | 48 | object_name - name of the entry to add |
screamer | 29:1210849dba19 | 49 | size - the size of the module being added |
screamer | 29:1210849dba19 | 50 | section - the section the module contributes to |
screamer | 8:a8ac6ed29081 | 51 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 52 | if not object_name or not size or not section: |
theotherjimmy |
40:7d3fa6b99b2b | 53 | return |
theotherjimmy |
40:7d3fa6b99b2b | 54 | |
theotherjimmy |
40:7d3fa6b99b2b | 55 | if object_name in self.modules: |
theotherjimmy |
40:7d3fa6b99b2b | 56 | self.modules[object_name].setdefault(section, 0) |
theotherjimmy |
40:7d3fa6b99b2b | 57 | self.modules[object_name][section] += size |
theotherjimmy |
40:7d3fa6b99b2b | 58 | return |
theotherjimmy |
40:7d3fa6b99b2b | 59 | |
theotherjimmy |
43:2a7da56ebd24 | 60 | obj_split = sep + basename(object_name) |
theotherjimmy |
40:7d3fa6b99b2b | 61 | for module_path, contents in self.modules.items(): |
theotherjimmy |
40:7d3fa6b99b2b | 62 | if module_path.endswith(obj_split) or module_path == object_name: |
theotherjimmy |
40:7d3fa6b99b2b | 63 | contents.setdefault(section, 0) |
theotherjimmy |
40:7d3fa6b99b2b | 64 | contents[section] += size |
theotherjimmy |
40:7d3fa6b99b2b | 65 | return |
theotherjimmy |
40:7d3fa6b99b2b | 66 | |
theotherjimmy |
43:2a7da56ebd24 | 67 | new_module = defaultdict(int) |
theotherjimmy |
43:2a7da56ebd24 | 68 | new_module[section] = size |
theotherjimmy |
40:7d3fa6b99b2b | 69 | self.modules[object_name] = new_module |
theotherjimmy |
40:7d3fa6b99b2b | 70 | |
theotherjimmy |
40:7d3fa6b99b2b | 71 | def module_replace(self, old_object, new_object): |
theotherjimmy |
40:7d3fa6b99b2b | 72 | """ Replaces an object name with a new one |
theotherjimmy |
40:7d3fa6b99b2b | 73 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 74 | if old_object in self.modules: |
theotherjimmy |
40:7d3fa6b99b2b | 75 | self.modules[new_object] = self.modules[old_object] |
theotherjimmy |
40:7d3fa6b99b2b | 76 | del self.modules[old_object] |
screamer | 8:a8ac6ed29081 | 77 | |
theotherjimmy |
43:2a7da56ebd24 | 78 | @abstractmethod |
theotherjimmy |
43:2a7da56ebd24 | 79 | def parse_mapfile(self, mapfile): |
theotherjimmy |
43:2a7da56ebd24 | 80 | """Parse a given file object pointing to a map file |
theotherjimmy |
43:2a7da56ebd24 | 81 | |
theotherjimmy |
43:2a7da56ebd24 | 82 | Positional arguments: |
theotherjimmy |
43:2a7da56ebd24 | 83 | mapfile - an open file object that reads a map file |
theotherjimmy |
43:2a7da56ebd24 | 84 | |
theotherjimmy |
43:2a7da56ebd24 | 85 | return value - a dict mapping from object names to section dicts, |
theotherjimmy |
43:2a7da56ebd24 | 86 | where a section dict maps from sections to sizes |
theotherjimmy |
43:2a7da56ebd24 | 87 | """ |
theotherjimmy |
43:2a7da56ebd24 | 88 | raise NotImplemented |
theotherjimmy |
43:2a7da56ebd24 | 89 | |
theotherjimmy |
43:2a7da56ebd24 | 90 | |
theotherjimmy |
43:2a7da56ebd24 | 91 | class _GccParser(_Parser): |
theotherjimmy |
43:2a7da56ebd24 | 92 | RE_OBJECT_FILE = re.compile(r'^(.+\/.+\.o)$') |
theotherjimmy |
43:2a7da56ebd24 | 93 | RE_LIBRARY_OBJECT = re.compile(r'^.+' + r''.format(sep) + r'lib((.+\.a)\((.+\.o)\))$') |
theotherjimmy |
43:2a7da56ebd24 | 94 | RE_STD_SECTION = re.compile(r'^\s+.*0x(\w{8,16})\s+0x(\w+)\s(.+)$') |
theotherjimmy |
43:2a7da56ebd24 | 95 | RE_FILL_SECTION = re.compile(r'^\s*\*fill\*\s+0x(\w{8,16})\s+0x(\w+).*$') |
theotherjimmy |
43:2a7da56ebd24 | 96 | |
theotherjimmy |
43:2a7da56ebd24 | 97 | ALL_SECTIONS = _Parser.SECTIONS + _Parser.OTHER_SECTIONS + \ |
theotherjimmy |
43:2a7da56ebd24 | 98 | _Parser.MISC_FLASH_SECTIONS + ('unknown', 'OUTPUT') |
theotherjimmy |
43:2a7da56ebd24 | 99 | |
theotherjimmy |
43:2a7da56ebd24 | 100 | def check_new_section(self, line): |
theotherjimmy |
43:2a7da56ebd24 | 101 | """ Check whether a new section in a map file has been detected |
screamer | 29:1210849dba19 | 102 | |
screamer | 29:1210849dba19 | 103 | Positional arguments: |
screamer | 29:1210849dba19 | 104 | line - the line to check for a new section |
theotherjimmy |
43:2a7da56ebd24 | 105 | |
theotherjimmy |
43:2a7da56ebd24 | 106 | return value - A section name, if a new section was found, False |
theotherjimmy |
43:2a7da56ebd24 | 107 | otherwise |
screamer | 8:a8ac6ed29081 | 108 | """ |
theotherjimmy |
43:2a7da56ebd24 | 109 | for i in self.ALL_SECTIONS: |
screamer | 8:a8ac6ed29081 | 110 | if line.startswith(i): |
screamer | 29:1210849dba19 | 111 | # should name of the section (assuming it's a known one) |
screamer | 29:1210849dba19 | 112 | return i |
screamer | 8:a8ac6ed29081 | 113 | |
screamer | 8:a8ac6ed29081 | 114 | if line.startswith('.'): |
screamer | 22:9e85236d8716 | 115 | return 'unknown' # all others are classified are unknown |
screamer | 8:a8ac6ed29081 | 116 | else: |
screamer | 8:a8ac6ed29081 | 117 | return False # everything else, means no change in section |
screamer | 8:a8ac6ed29081 | 118 | |
theotherjimmy |
40:7d3fa6b99b2b | 119 | |
theotherjimmy |
43:2a7da56ebd24 | 120 | def parse_object_name(self, line): |
theotherjimmy |
40:7d3fa6b99b2b | 121 | """ Parse a path to object file |
screamer | 29:1210849dba19 | 122 | |
screamer | 29:1210849dba19 | 123 | Positional arguments: |
theotherjimmy |
43:2a7da56ebd24 | 124 | line - the path to parse the object and module name from |
screamer | 8:a8ac6ed29081 | 125 | |
theotherjimmy |
43:2a7da56ebd24 | 126 | return value - an object file name |
theotherjimmy |
43:2a7da56ebd24 | 127 | """ |
theotherjimmy |
43:2a7da56ebd24 | 128 | test_re_mbed_os_name = re.match(self.RE_OBJECT_FILE, line) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 129 | |
theotherjimmy |
40:7d3fa6b99b2b | 130 | if test_re_mbed_os_name: |
theotherjimmy |
40:7d3fa6b99b2b | 131 | object_name = test_re_mbed_os_name.group(1) |
screamer | 8:a8ac6ed29081 | 132 | |
theotherjimmy |
40:7d3fa6b99b2b | 133 | # corner case: certain objects are provided by the GCC toolchain |
theotherjimmy |
40:7d3fa6b99b2b | 134 | if 'arm-none-eabi' in line: |
theotherjimmy |
43:2a7da56ebd24 | 135 | return join('[lib]', 'misc', basename(object_name)) |
theotherjimmy |
40:7d3fa6b99b2b | 136 | return object_name |
screamer | 8:a8ac6ed29081 | 137 | |
theotherjimmy |
40:7d3fa6b99b2b | 138 | else: |
theotherjimmy |
43:2a7da56ebd24 | 139 | test_re_obj_name = re.match(self.RE_LIBRARY_OBJECT, line) |
theotherjimmy |
40:7d3fa6b99b2b | 140 | |
theotherjimmy |
40:7d3fa6b99b2b | 141 | if test_re_obj_name: |
theotherjimmy |
43:2a7da56ebd24 | 142 | return join('[lib]', test_re_obj_name.group(2), |
theotherjimmy |
43:2a7da56ebd24 | 143 | test_re_obj_name.group(3)) |
The Other Jimmy |
36:96847d42f010 | 144 | else: |
theotherjimmy |
43:2a7da56ebd24 | 145 | print("Unknown object name found in GCC map file: %s" % line) |
theotherjimmy |
40:7d3fa6b99b2b | 146 | return '[misc]' |
screamer | 8:a8ac6ed29081 | 147 | |
theotherjimmy |
43:2a7da56ebd24 | 148 | def parse_section(self, line): |
screamer | 29:1210849dba19 | 149 | """ Parse data from a section of gcc map file |
screamer | 29:1210849dba19 | 150 | |
screamer | 29:1210849dba19 | 151 | examples: |
The Other Jimmy |
31:8ea194f6145b | 152 | 0x00004308 0x7c ./BUILD/K64F/GCC_ARM/mbed-os/hal/targets/hal/TARGET_Freescale/TARGET_KPSDK_MCUS/spi_api.o |
theotherjimmy |
43:2a7da56ebd24 | 153 | .text 0x00000608 0x198 ./BUILD/K64F/GCC_ARM/mbed-os/core/mbed-rtos/rtx/TARGET_CORTEX_M/TARGET_RTOS_M4_M7/TOOLCHAIN/HAL_CM4.o |
screamer | 29:1210849dba19 | 154 | |
screamer | 29:1210849dba19 | 155 | Positional arguments: |
screamer | 29:1210849dba19 | 156 | line - the line to parse a section from |
screamer | 8:a8ac6ed29081 | 157 | """ |
theotherjimmy |
43:2a7da56ebd24 | 158 | is_fill = re.match(self.RE_FILL_SECTION, line) |
theotherjimmy |
40:7d3fa6b99b2b | 159 | if is_fill: |
theotherjimmy |
40:7d3fa6b99b2b | 160 | o_name = '[fill]' |
theotherjimmy |
40:7d3fa6b99b2b | 161 | o_size = int(is_fill.group(2), 16) |
theotherjimmy |
40:7d3fa6b99b2b | 162 | return [o_name, o_size] |
screamer | 8:a8ac6ed29081 | 163 | |
theotherjimmy |
43:2a7da56ebd24 | 164 | is_section = re.match(self.RE_STD_SECTION, line) |
theotherjimmy |
40:7d3fa6b99b2b | 165 | if is_section: |
theotherjimmy |
40:7d3fa6b99b2b | 166 | o_size = int(is_section.group(2), 16) |
theotherjimmy |
40:7d3fa6b99b2b | 167 | if o_size: |
theotherjimmy |
43:2a7da56ebd24 | 168 | o_name = self.parse_object_name(is_section.group(3)) |
theotherjimmy |
40:7d3fa6b99b2b | 169 | return [o_name, o_size] |
screamer | 8:a8ac6ed29081 | 170 | |
theotherjimmy |
40:7d3fa6b99b2b | 171 | return ["", 0] |
screamer | 8:a8ac6ed29081 | 172 | |
theotherjimmy |
43:2a7da56ebd24 | 173 | def parse_mapfile(self, file_desc): |
screamer | 29:1210849dba19 | 174 | """ Main logic to decode gcc map files |
screamer | 29:1210849dba19 | 175 | |
screamer | 29:1210849dba19 | 176 | Positional arguments: |
screamer | 29:1210849dba19 | 177 | file_desc - a stream object to parse as a gcc map file |
screamer | 8:a8ac6ed29081 | 178 | """ |
screamer | 8:a8ac6ed29081 | 179 | current_section = 'unknown' |
screamer | 8:a8ac6ed29081 | 180 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 181 | with file_desc as infile: |
screamer | 8:a8ac6ed29081 | 182 | for line in infile: |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 183 | if line.startswith('Linker script and memory map'): |
screamer | 8:a8ac6ed29081 | 184 | current_section = "unknown" |
screamer | 8:a8ac6ed29081 | 185 | break |
screamer | 8:a8ac6ed29081 | 186 | |
screamer | 8:a8ac6ed29081 | 187 | for line in infile: |
theotherjimmy |
43:2a7da56ebd24 | 188 | next_section = self.check_new_section(line) |
screamer | 8:a8ac6ed29081 | 189 | |
theotherjimmy |
40:7d3fa6b99b2b | 190 | if next_section == "OUTPUT": |
screamer | 8:a8ac6ed29081 | 191 | break |
theotherjimmy |
40:7d3fa6b99b2b | 192 | elif next_section: |
theotherjimmy |
40:7d3fa6b99b2b | 193 | current_section = next_section |
theotherjimmy |
40:7d3fa6b99b2b | 194 | |
theotherjimmy |
43:2a7da56ebd24 | 195 | object_name, object_size = self.parse_section(line) |
theotherjimmy |
40:7d3fa6b99b2b | 196 | self.module_add(object_name, object_size, current_section) |
screamer | 8:a8ac6ed29081 | 197 | |
theotherjimmy |
43:2a7da56ebd24 | 198 | common_prefix = dirname(commonprefix([ |
theotherjimmy |
40:7d3fa6b99b2b | 199 | o for o in self.modules.keys() if (o.endswith(".o") and not o.startswith("[lib]"))])) |
theotherjimmy |
40:7d3fa6b99b2b | 200 | new_modules = {} |
theotherjimmy |
40:7d3fa6b99b2b | 201 | for name, stats in self.modules.items(): |
theotherjimmy |
40:7d3fa6b99b2b | 202 | if name.startswith("[lib]"): |
theotherjimmy |
40:7d3fa6b99b2b | 203 | new_modules[name] = stats |
theotherjimmy |
40:7d3fa6b99b2b | 204 | elif name.endswith(".o"): |
theotherjimmy |
43:2a7da56ebd24 | 205 | new_modules[relpath(name, common_prefix)] = stats |
theotherjimmy |
40:7d3fa6b99b2b | 206 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 207 | new_modules[name] = stats |
theotherjimmy |
43:2a7da56ebd24 | 208 | return new_modules |
theotherjimmy |
43:2a7da56ebd24 | 209 | |
theotherjimmy |
40:7d3fa6b99b2b | 210 | |
theotherjimmy |
43:2a7da56ebd24 | 211 | class _ArmccParser(_Parser): |
theotherjimmy |
43:2a7da56ebd24 | 212 | RE = re.compile( |
theotherjimmy |
43:2a7da56ebd24 | 213 | r'^\s+0x(\w{8})\s+0x(\w{8})\s+(\w+)\s+(\w+)\s+(\d+)\s+[*]?.+\s+(.+)$') |
theotherjimmy |
43:2a7da56ebd24 | 214 | RE_OBJECT = re.compile(r'(.+\.(l|ar))\((.+\.o)\)') |
theotherjimmy |
43:2a7da56ebd24 | 215 | |
theotherjimmy |
43:2a7da56ebd24 | 216 | def parse_object_name(self, line): |
theotherjimmy |
40:7d3fa6b99b2b | 217 | """ Parse object file |
screamer | 8:a8ac6ed29081 | 218 | |
theotherjimmy |
40:7d3fa6b99b2b | 219 | Positional arguments: |
theotherjimmy |
40:7d3fa6b99b2b | 220 | line - the line containing the object or library |
theotherjimmy |
40:7d3fa6b99b2b | 221 | """ |
theotherjimmy |
43:2a7da56ebd24 | 222 | if line.endswith(".o"): |
theotherjimmy |
40:7d3fa6b99b2b | 223 | return line |
screamer | 8:a8ac6ed29081 | 224 | |
theotherjimmy |
40:7d3fa6b99b2b | 225 | else: |
theotherjimmy |
43:2a7da56ebd24 | 226 | is_obj = re.match(self.RE_OBJECT, line) |
theotherjimmy |
40:7d3fa6b99b2b | 227 | if is_obj: |
theotherjimmy |
43:2a7da56ebd24 | 228 | return join('[lib]', basename(is_obj.group(1)), is_obj.group(3)) |
theotherjimmy |
40:7d3fa6b99b2b | 229 | else: |
theotherjimmy |
43:2a7da56ebd24 | 230 | print("Malformed input found when parsing ARMCC map: %s" % line) |
theotherjimmy |
40:7d3fa6b99b2b | 231 | return '[misc]' |
theotherjimmy |
40:7d3fa6b99b2b | 232 | |
theotherjimmy |
43:2a7da56ebd24 | 233 | def parse_section(self, line): |
screamer | 29:1210849dba19 | 234 | """ Parse data from an armcc map file |
screamer | 29:1210849dba19 | 235 | |
screamer | 29:1210849dba19 | 236 | Examples of armcc map file: |
screamer | 29:1210849dba19 | 237 | Base_Addr Size Type Attr Idx E Section Name Object |
theotherjimmy |
43:2a7da56ebd24 | 238 | 0x00000000 0x00000400 Data RO 11222 self.RESET startup_MK64F12.o |
screamer | 29:1210849dba19 | 239 | 0x00000410 0x00000008 Code RO 49364 * !!!main c_w.l(__main.o) |
screamer | 29:1210849dba19 | 240 | |
screamer | 29:1210849dba19 | 241 | Positional arguments: |
screamer | 29:1210849dba19 | 242 | line - the line to parse the section data from |
screamer | 8:a8ac6ed29081 | 243 | """ |
theotherjimmy |
43:2a7da56ebd24 | 244 | test_re = re.match(self.RE, line) |
screamer | 8:a8ac6ed29081 | 245 | |
theotherjimmy |
43:2a7da56ebd24 | 246 | if test_re: |
theotherjimmy |
43:2a7da56ebd24 | 247 | size = int(test_re.group(2), 16) |
screamer | 8:a8ac6ed29081 | 248 | |
theotherjimmy |
43:2a7da56ebd24 | 249 | if test_re.group(4) == 'RO': |
screamer | 8:a8ac6ed29081 | 250 | section = '.text' |
screamer | 8:a8ac6ed29081 | 251 | else: |
theotherjimmy |
43:2a7da56ebd24 | 252 | if test_re.group(3) == 'Data': |
screamer | 8:a8ac6ed29081 | 253 | section = '.data' |
theotherjimmy |
43:2a7da56ebd24 | 254 | elif test_re.group(3) == 'Zero': |
screamer | 8:a8ac6ed29081 | 255 | section = '.bss' |
theotherjimmy |
43:2a7da56ebd24 | 256 | elif test_re.group(3) == 'Code': |
theotherjimmy |
43:2a7da56ebd24 | 257 | section = '.text' |
screamer | 8:a8ac6ed29081 | 258 | else: |
theotherjimmy |
43:2a7da56ebd24 | 259 | print("Malformed input found when parsing armcc map: %s, %r" |
theotherjimmy |
43:2a7da56ebd24 | 260 | % (line, test_re.groups())) |
theotherjimmy |
43:2a7da56ebd24 | 261 | |
theotherjimmy |
43:2a7da56ebd24 | 262 | return ["", 0, ""] |
screamer | 8:a8ac6ed29081 | 263 | |
theotherjimmy |
40:7d3fa6b99b2b | 264 | # check name of object or library |
theotherjimmy |
43:2a7da56ebd24 | 265 | object_name = self.parse_object_name( |
theotherjimmy |
43:2a7da56ebd24 | 266 | test_re.group(6)) |
screamer | 8:a8ac6ed29081 | 267 | |
theotherjimmy |
40:7d3fa6b99b2b | 268 | return [object_name, size, section] |
screamer | 8:a8ac6ed29081 | 269 | |
screamer | 8:a8ac6ed29081 | 270 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 271 | return ["", 0, ""] |
theotherjimmy |
40:7d3fa6b99b2b | 272 | |
theotherjimmy |
43:2a7da56ebd24 | 273 | def parse_mapfile(self, file_desc): |
theotherjimmy |
43:2a7da56ebd24 | 274 | """ Main logic to decode armc5 map files |
theotherjimmy |
43:2a7da56ebd24 | 275 | |
theotherjimmy |
43:2a7da56ebd24 | 276 | Positional arguments: |
theotherjimmy |
43:2a7da56ebd24 | 277 | file_desc - a file like object to parse as an armc5 map file |
theotherjimmy |
43:2a7da56ebd24 | 278 | """ |
theotherjimmy |
43:2a7da56ebd24 | 279 | with file_desc as infile: |
theotherjimmy |
43:2a7da56ebd24 | 280 | # Search area to parse |
theotherjimmy |
43:2a7da56ebd24 | 281 | for line in infile: |
theotherjimmy |
43:2a7da56ebd24 | 282 | if line.startswith(' Base Addr Size'): |
theotherjimmy |
43:2a7da56ebd24 | 283 | break |
theotherjimmy |
43:2a7da56ebd24 | 284 | |
theotherjimmy |
43:2a7da56ebd24 | 285 | # Start decoding the map file |
theotherjimmy |
43:2a7da56ebd24 | 286 | for line in infile: |
theotherjimmy |
43:2a7da56ebd24 | 287 | self.module_add(*self.parse_section(line)) |
theotherjimmy |
43:2a7da56ebd24 | 288 | |
theotherjimmy |
43:2a7da56ebd24 | 289 | common_prefix = dirname(commonprefix([ |
theotherjimmy |
43:2a7da56ebd24 | 290 | o for o in self.modules.keys() if (o.endswith(".o") and o != "anon$$obj.o" and not o.startswith("[lib]"))])) |
theotherjimmy |
43:2a7da56ebd24 | 291 | new_modules = {} |
theotherjimmy |
43:2a7da56ebd24 | 292 | for name, stats in self.modules.items(): |
theotherjimmy |
43:2a7da56ebd24 | 293 | if name == "anon$$obj.o" or name.startswith("[lib]"): |
theotherjimmy |
43:2a7da56ebd24 | 294 | new_modules[name] = stats |
theotherjimmy |
43:2a7da56ebd24 | 295 | elif name.endswith(".o"): |
theotherjimmy |
43:2a7da56ebd24 | 296 | new_modules[relpath(name, common_prefix)] = stats |
theotherjimmy |
43:2a7da56ebd24 | 297 | else: |
theotherjimmy |
43:2a7da56ebd24 | 298 | new_modules[name] = stats |
theotherjimmy |
43:2a7da56ebd24 | 299 | return new_modules |
theotherjimmy |
43:2a7da56ebd24 | 300 | |
theotherjimmy |
43:2a7da56ebd24 | 301 | |
theotherjimmy |
43:2a7da56ebd24 | 302 | class _IarParser(_Parser): |
theotherjimmy |
43:2a7da56ebd24 | 303 | RE = re.compile( |
theotherjimmy |
43:2a7da56ebd24 | 304 | r'^\s+(.+)\s+(zero|const|ro code|inited|uninit)\s' |
theotherjimmy |
43:2a7da56ebd24 | 305 | r'+0x(\w{8})\s+0x(\w+)\s+(.+)\s.+$') |
theotherjimmy |
43:2a7da56ebd24 | 306 | |
theotherjimmy |
43:2a7da56ebd24 | 307 | RE_CMDLINE_FILE = re.compile(r'^#\s+(.+\.o)') |
theotherjimmy |
43:2a7da56ebd24 | 308 | RE_LIBRARY = re.compile(r'^(.+\.a)\:.+$') |
theotherjimmy |
43:2a7da56ebd24 | 309 | RE_OBJECT_LIBRARY = re.compile(r'^\s+(.+\.o)\s.*') |
theotherjimmy |
43:2a7da56ebd24 | 310 | |
theotherjimmy |
43:2a7da56ebd24 | 311 | def __init__(self): |
theotherjimmy |
43:2a7da56ebd24 | 312 | _Parser.__init__(self) |
theotherjimmy |
43:2a7da56ebd24 | 313 | # Modules passed to the linker on the command line |
theotherjimmy |
43:2a7da56ebd24 | 314 | # this is a dict because modules are looked up by their basename |
theotherjimmy |
43:2a7da56ebd24 | 315 | self.cmd_modules = {} |
theotherjimmy |
43:2a7da56ebd24 | 316 | |
theotherjimmy |
43:2a7da56ebd24 | 317 | def parse_object_name(self, object_name): |
theotherjimmy |
40:7d3fa6b99b2b | 318 | """ Parse object file |
theotherjimmy |
40:7d3fa6b99b2b | 319 | |
theotherjimmy |
40:7d3fa6b99b2b | 320 | Positional arguments: |
theotherjimmy |
40:7d3fa6b99b2b | 321 | line - the line containing the object or library |
theotherjimmy |
40:7d3fa6b99b2b | 322 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 323 | if object_name.endswith(".o"): |
theotherjimmy |
40:7d3fa6b99b2b | 324 | try: |
theotherjimmy |
40:7d3fa6b99b2b | 325 | return self.cmd_modules[object_name] |
theotherjimmy |
40:7d3fa6b99b2b | 326 | except KeyError: |
theotherjimmy |
40:7d3fa6b99b2b | 327 | return object_name |
theotherjimmy |
40:7d3fa6b99b2b | 328 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 329 | return '[misc]' |
theotherjimmy |
40:7d3fa6b99b2b | 330 | |
theotherjimmy |
43:2a7da56ebd24 | 331 | def parse_section(self, line): |
screamer | 29:1210849dba19 | 332 | """ Parse data from an IAR map file |
screamer | 29:1210849dba19 | 333 | |
screamer | 29:1210849dba19 | 334 | Examples of IAR map file: |
screamer | 29:1210849dba19 | 335 | Section Kind Address Size Object |
screamer | 29:1210849dba19 | 336 | .intvec ro code 0x00000000 0x198 startup_MK64F12.o [15] |
screamer | 29:1210849dba19 | 337 | .rodata const 0x00000198 0x0 zero_init3.o [133] |
screamer | 29:1210849dba19 | 338 | .iar.init_table const 0x00008384 0x2c - Linker created - |
screamer | 29:1210849dba19 | 339 | Initializer bytes const 0x00000198 0xb2 <for P3 s0> |
screamer | 29:1210849dba19 | 340 | .data inited 0x20000000 0xd4 driverAtmelRFInterface.o [70] |
screamer | 29:1210849dba19 | 341 | .bss zero 0x20000598 0x318 RTX_Conf_CM.o [4] |
screamer | 29:1210849dba19 | 342 | .iar.dynexit uninit 0x20001448 0x204 <Block tail> |
screamer | 29:1210849dba19 | 343 | HEAP uninit 0x20001650 0x10000 <Block tail> |
screamer | 29:1210849dba19 | 344 | |
screamer | 29:1210849dba19 | 345 | Positional_arguments: |
screamer | 29:1210849dba19 | 346 | line - the line to parse section data from |
screamer | 8:a8ac6ed29081 | 347 | """ |
theotherjimmy |
43:2a7da56ebd24 | 348 | test_re = re.match(self.RE, line) |
theotherjimmy |
43:2a7da56ebd24 | 349 | if test_re: |
theotherjimmy |
43:2a7da56ebd24 | 350 | if (test_re.group(2) == 'const' or |
theotherjimmy |
43:2a7da56ebd24 | 351 | test_re.group(2) == 'ro code'): |
screamer | 8:a8ac6ed29081 | 352 | section = '.text' |
theotherjimmy |
43:2a7da56ebd24 | 353 | elif (test_re.group(2) == 'zero' or |
theotherjimmy |
43:2a7da56ebd24 | 354 | test_re.group(2) == 'uninit'): |
theotherjimmy |
43:2a7da56ebd24 | 355 | if test_re.group(1)[0:4] == 'HEAP': |
screamer | 8:a8ac6ed29081 | 356 | section = '.heap' |
theotherjimmy |
43:2a7da56ebd24 | 357 | elif test_re.group(1)[0:6] == 'CSTACK': |
screamer | 8:a8ac6ed29081 | 358 | section = '.stack' |
screamer | 8:a8ac6ed29081 | 359 | else: |
screamer | 8:a8ac6ed29081 | 360 | section = '.bss' # default section |
screamer | 8:a8ac6ed29081 | 361 | |
theotherjimmy |
43:2a7da56ebd24 | 362 | elif test_re.group(2) == 'inited': |
screamer | 8:a8ac6ed29081 | 363 | section = '.data' |
screamer | 8:a8ac6ed29081 | 364 | else: |
theotherjimmy |
43:2a7da56ebd24 | 365 | print("Malformed input found when parsing IAR map: %s" % line) |
theotherjimmy |
43:2a7da56ebd24 | 366 | return ["", 0, ""] |
screamer | 8:a8ac6ed29081 | 367 | |
screamer | 8:a8ac6ed29081 | 368 | # lookup object in dictionary and return module name |
theotherjimmy |
43:2a7da56ebd24 | 369 | object_name = self.parse_object_name(test_re.group(5)) |
screamer | 8:a8ac6ed29081 | 370 | |
theotherjimmy |
43:2a7da56ebd24 | 371 | size = int(test_re.group(4), 16) |
theotherjimmy |
40:7d3fa6b99b2b | 372 | return [object_name, size, section] |
screamer | 8:a8ac6ed29081 | 373 | |
screamer | 8:a8ac6ed29081 | 374 | else: |
theotherjimmy |
43:2a7da56ebd24 | 375 | return ["", 0, ""] |
screamer | 8:a8ac6ed29081 | 376 | |
theotherjimmy |
43:2a7da56ebd24 | 377 | def check_new_library(self, line): |
theotherjimmy |
40:7d3fa6b99b2b | 378 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 379 | Searches for libraries and returns name. Example: |
theotherjimmy |
40:7d3fa6b99b2b | 380 | m7M_tls.a: [43] |
theotherjimmy |
40:7d3fa6b99b2b | 381 | |
theotherjimmy |
40:7d3fa6b99b2b | 382 | """ |
theotherjimmy |
43:2a7da56ebd24 | 383 | test_address_line = re.match(self.RE_LIBRARY, line) |
theotherjimmy |
40:7d3fa6b99b2b | 384 | if test_address_line: |
theotherjimmy |
40:7d3fa6b99b2b | 385 | return test_address_line.group(1) |
theotherjimmy |
40:7d3fa6b99b2b | 386 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 387 | return "" |
screamer | 8:a8ac6ed29081 | 388 | |
theotherjimmy |
43:2a7da56ebd24 | 389 | def check_new_object_lib(self, line): |
theotherjimmy |
40:7d3fa6b99b2b | 390 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 391 | Searches for objects within a library section and returns name. Example: |
theotherjimmy |
40:7d3fa6b99b2b | 392 | rt7M_tl.a: [44] |
theotherjimmy |
40:7d3fa6b99b2b | 393 | ABImemclr4.o 6 |
theotherjimmy |
40:7d3fa6b99b2b | 394 | ABImemcpy_unaligned.o 118 |
theotherjimmy |
40:7d3fa6b99b2b | 395 | ABImemset48.o 50 |
theotherjimmy |
40:7d3fa6b99b2b | 396 | I64DivMod.o 238 |
theotherjimmy |
40:7d3fa6b99b2b | 397 | I64DivZer.o 2 |
theotherjimmy |
40:7d3fa6b99b2b | 398 | |
theotherjimmy |
40:7d3fa6b99b2b | 399 | """ |
theotherjimmy |
43:2a7da56ebd24 | 400 | test_address_line = re.match(self.RE_OBJECT_LIBRARY, line) |
theotherjimmy |
40:7d3fa6b99b2b | 401 | if test_address_line: |
theotherjimmy |
40:7d3fa6b99b2b | 402 | return test_address_line.group(1) |
theotherjimmy |
40:7d3fa6b99b2b | 403 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 404 | return "" |
theotherjimmy |
40:7d3fa6b99b2b | 405 | |
theotherjimmy |
43:2a7da56ebd24 | 406 | def parse_command_line(self, lines): |
theotherjimmy |
40:7d3fa6b99b2b | 407 | """Parse the files passed on the command line to the iar linker |
theotherjimmy |
40:7d3fa6b99b2b | 408 | |
theotherjimmy |
40:7d3fa6b99b2b | 409 | Positional arguments: |
theotherjimmy |
40:7d3fa6b99b2b | 410 | lines -- an iterator over the lines within a file |
theotherjimmy |
40:7d3fa6b99b2b | 411 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 412 | for line in lines: |
theotherjimmy |
40:7d3fa6b99b2b | 413 | if line.startswith("*"): |
theotherjimmy |
40:7d3fa6b99b2b | 414 | break |
theotherjimmy |
43:2a7da56ebd24 | 415 | for arg in line.split(" "): |
theotherjimmy |
43:2a7da56ebd24 | 416 | arg = arg.rstrip(" \n") |
theotherjimmy |
43:2a7da56ebd24 | 417 | if (not arg.startswith("-")) and arg.endswith(".o"): |
theotherjimmy |
43:2a7da56ebd24 | 418 | self.cmd_modules[basename(arg)] = arg |
theotherjimmy |
40:7d3fa6b99b2b | 419 | |
theotherjimmy |
43:2a7da56ebd24 | 420 | common_prefix = dirname(commonprefix(list(self.cmd_modules.values()))) |
theotherjimmy |
43:2a7da56ebd24 | 421 | self.cmd_modules = {s: relpath(f, common_prefix) |
theotherjimmy |
40:7d3fa6b99b2b | 422 | for s, f in self.cmd_modules.items()} |
theotherjimmy |
40:7d3fa6b99b2b | 423 | |
theotherjimmy |
43:2a7da56ebd24 | 424 | def parse_mapfile(self, file_desc): |
screamer | 29:1210849dba19 | 425 | """ Main logic to decode IAR map files |
screamer | 29:1210849dba19 | 426 | |
screamer | 29:1210849dba19 | 427 | Positional arguments: |
screamer | 29:1210849dba19 | 428 | file_desc - a file like object to parse as an IAR map file |
screamer | 8:a8ac6ed29081 | 429 | """ |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 430 | with file_desc as infile: |
theotherjimmy |
43:2a7da56ebd24 | 431 | self.parse_command_line(infile) |
screamer | 8:a8ac6ed29081 | 432 | |
screamer | 8:a8ac6ed29081 | 433 | for line in infile: |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 434 | if line.startswith(' Section '): |
screamer | 8:a8ac6ed29081 | 435 | break |
screamer | 8:a8ac6ed29081 | 436 | |
theotherjimmy |
40:7d3fa6b99b2b | 437 | for line in infile: |
theotherjimmy |
43:2a7da56ebd24 | 438 | self.module_add(*self.parse_section(line)) |
theotherjimmy |
40:7d3fa6b99b2b | 439 | |
theotherjimmy |
40:7d3fa6b99b2b | 440 | if line.startswith('*** MODULE SUMMARY'): # finish section |
theotherjimmy |
40:7d3fa6b99b2b | 441 | break |
theotherjimmy |
40:7d3fa6b99b2b | 442 | |
theotherjimmy |
40:7d3fa6b99b2b | 443 | current_library = "" |
screamer | 8:a8ac6ed29081 | 444 | for line in infile: |
theotherjimmy |
43:2a7da56ebd24 | 445 | library = self.check_new_library(line) |
screamer | 8:a8ac6ed29081 | 446 | |
theotherjimmy |
40:7d3fa6b99b2b | 447 | if library: |
theotherjimmy |
40:7d3fa6b99b2b | 448 | current_library = library |
screamer | 8:a8ac6ed29081 | 449 | |
theotherjimmy |
43:2a7da56ebd24 | 450 | object_name = self.check_new_object_lib(line) |
screamer | 29:1210849dba19 | 451 | |
theotherjimmy |
40:7d3fa6b99b2b | 452 | if object_name and current_library: |
theotherjimmy |
43:2a7da56ebd24 | 453 | temp = join('[lib]', current_library, object_name) |
theotherjimmy |
40:7d3fa6b99b2b | 454 | self.module_replace(object_name, temp) |
theotherjimmy |
43:2a7da56ebd24 | 455 | return self.modules |
theotherjimmy |
40:7d3fa6b99b2b | 456 | |
screamer | 8:a8ac6ed29081 | 457 | |
theotherjimmy |
43:2a7da56ebd24 | 458 | class MemapParser(object): |
theotherjimmy |
43:2a7da56ebd24 | 459 | """An object that represents parsed results, parses the memory map files, |
theotherjimmy |
43:2a7da56ebd24 | 460 | and writes out different file types of memory results |
theotherjimmy |
43:2a7da56ebd24 | 461 | """ |
theotherjimmy |
43:2a7da56ebd24 | 462 | |
theotherjimmy |
43:2a7da56ebd24 | 463 | print_sections = ('.text', '.data', '.bss') |
theotherjimmy |
43:2a7da56ebd24 | 464 | delta_sections = ('.text-delta', '.data-delta', '.bss-delta') |
theotherjimmy |
43:2a7da56ebd24 | 465 | |
theotherjimmy |
43:2a7da56ebd24 | 466 | |
theotherjimmy |
43:2a7da56ebd24 | 467 | # sections to print info (generic for all toolchains) |
theotherjimmy |
43:2a7da56ebd24 | 468 | sections = _Parser.SECTIONS |
theotherjimmy |
43:2a7da56ebd24 | 469 | misc_flash_sections = _Parser.MISC_FLASH_SECTIONS |
theotherjimmy |
43:2a7da56ebd24 | 470 | other_sections = _Parser.OTHER_SECTIONS |
theotherjimmy |
43:2a7da56ebd24 | 471 | |
theotherjimmy |
43:2a7da56ebd24 | 472 | def __init__(self): |
theotherjimmy |
43:2a7da56ebd24 | 473 | # list of all modules and their sections |
theotherjimmy |
43:2a7da56ebd24 | 474 | # full list - doesn't change with depth |
theotherjimmy |
43:2a7da56ebd24 | 475 | self.modules = dict() |
theotherjimmy |
43:2a7da56ebd24 | 476 | self.old_modules = None |
theotherjimmy |
43:2a7da56ebd24 | 477 | # short version with specific depth |
theotherjimmy |
43:2a7da56ebd24 | 478 | self.short_modules = dict() |
theotherjimmy |
43:2a7da56ebd24 | 479 | |
theotherjimmy |
43:2a7da56ebd24 | 480 | |
theotherjimmy |
43:2a7da56ebd24 | 481 | # Memory report (sections + summary) |
theotherjimmy |
43:2a7da56ebd24 | 482 | self.mem_report = [] |
theotherjimmy |
43:2a7da56ebd24 | 483 | |
theotherjimmy |
43:2a7da56ebd24 | 484 | # Memory summary |
theotherjimmy |
43:2a7da56ebd24 | 485 | self.mem_summary = dict() |
theotherjimmy |
43:2a7da56ebd24 | 486 | |
theotherjimmy |
43:2a7da56ebd24 | 487 | # Totals of ".text", ".data" and ".bss" |
theotherjimmy |
43:2a7da56ebd24 | 488 | self.subtotal = dict() |
theotherjimmy |
43:2a7da56ebd24 | 489 | |
theotherjimmy |
43:2a7da56ebd24 | 490 | # Flash no associated with a module |
theotherjimmy |
43:2a7da56ebd24 | 491 | self.misc_flash_mem = 0 |
theotherjimmy |
43:2a7da56ebd24 | 492 | |
theotherjimmy |
43:2a7da56ebd24 | 493 | # Name of the toolchain, for better headings |
theotherjimmy |
43:2a7da56ebd24 | 494 | self.tc_name = None |
theotherjimmy |
43:2a7da56ebd24 | 495 | |
theotherjimmy |
40:7d3fa6b99b2b | 496 | def reduce_depth(self, depth): |
theotherjimmy |
40:7d3fa6b99b2b | 497 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 498 | populates the short_modules attribute with a truncated module list |
screamer | 8:a8ac6ed29081 | 499 | |
theotherjimmy |
40:7d3fa6b99b2b | 500 | (1) depth = 1: |
theotherjimmy |
40:7d3fa6b99b2b | 501 | main.o |
theotherjimmy |
40:7d3fa6b99b2b | 502 | mbed-os |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 503 | |
theotherjimmy |
40:7d3fa6b99b2b | 504 | (2) depth = 2: |
theotherjimmy |
40:7d3fa6b99b2b | 505 | main.o |
theotherjimmy |
40:7d3fa6b99b2b | 506 | mbed-os/test.o |
theotherjimmy |
40:7d3fa6b99b2b | 507 | mbed-os/drivers |
screamer | 8:a8ac6ed29081 | 508 | |
theotherjimmy |
40:7d3fa6b99b2b | 509 | """ |
theotherjimmy |
40:7d3fa6b99b2b | 510 | if depth == 0 or depth == None: |
theotherjimmy |
40:7d3fa6b99b2b | 511 | self.short_modules = deepcopy(self.modules) |
theotherjimmy |
40:7d3fa6b99b2b | 512 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 513 | self.short_modules = dict() |
theotherjimmy |
40:7d3fa6b99b2b | 514 | for module_name, v in self.modules.items(): |
theotherjimmy |
43:2a7da56ebd24 | 515 | split_name = module_name.split(sep) |
theotherjimmy |
40:7d3fa6b99b2b | 516 | if split_name[0] == '': |
theotherjimmy |
40:7d3fa6b99b2b | 517 | split_name = split_name[1:] |
theotherjimmy |
43:2a7da56ebd24 | 518 | new_name = join(*split_name[:depth]) |
theotherjimmy |
43:2a7da56ebd24 | 519 | self.short_modules.setdefault(new_name, defaultdict(int)) |
theotherjimmy |
40:7d3fa6b99b2b | 520 | for section_idx, value in v.items(): |
theotherjimmy |
40:7d3fa6b99b2b | 521 | self.short_modules[new_name][section_idx] += self.modules[module_name][section_idx] |
theotherjimmy |
43:2a7da56ebd24 | 522 | self.short_modules[new_name][section_idx + '-delta'] += self.modules[module_name][section_idx] |
theotherjimmy |
43:2a7da56ebd24 | 523 | if self.old_modules: |
theotherjimmy |
43:2a7da56ebd24 | 524 | for module_name, v in self.old_modules.items(): |
theotherjimmy |
43:2a7da56ebd24 | 525 | split_name = module_name.split(sep) |
theotherjimmy |
43:2a7da56ebd24 | 526 | if split_name[0] == '': |
theotherjimmy |
43:2a7da56ebd24 | 527 | split_name = split_name[1:] |
theotherjimmy |
43:2a7da56ebd24 | 528 | new_name = join(*split_name[:depth]) |
theotherjimmy |
43:2a7da56ebd24 | 529 | self.short_modules.setdefault(new_name, defaultdict(int)) |
theotherjimmy |
43:2a7da56ebd24 | 530 | for section_idx, value in v.items(): |
theotherjimmy |
43:2a7da56ebd24 | 531 | self.short_modules[new_name][section_idx + '-delta'] -= self.old_modules[module_name][section_idx] |
screamer | 8:a8ac6ed29081 | 532 | |
theotherjimmy |
43:2a7da56ebd24 | 533 | export_formats = ["json", "csv-ci", "html", "table"] |
screamer | 22:9e85236d8716 | 534 | |
theotherjimmy |
40:7d3fa6b99b2b | 535 | def generate_output(self, export_format, depth, file_output=None): |
screamer | 29:1210849dba19 | 536 | """ Generates summary of memory map data |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 537 | |
screamer | 29:1210849dba19 | 538 | Positional arguments: |
screamer | 29:1210849dba19 | 539 | export_format - the format to dump |
screamer | 29:1210849dba19 | 540 | |
screamer | 29:1210849dba19 | 541 | Keyword arguments: |
screamer | 29:1210849dba19 | 542 | file_desc - descriptor (either stdout or file) |
theotherjimmy |
40:7d3fa6b99b2b | 543 | depth - directory depth on report |
The Other Jimmy |
31:8ea194f6145b | 544 | |
The Other Jimmy |
31:8ea194f6145b | 545 | Returns: generated string for the 'table' format, otherwise None |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 546 | """ |
theotherjimmy |
43:2a7da56ebd24 | 547 | if depth is None or depth > 0: |
theotherjimmy |
43:2a7da56ebd24 | 548 | self.reduce_depth(depth) |
theotherjimmy |
40:7d3fa6b99b2b | 549 | self.compute_report() |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 550 | try: |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 551 | if file_output: |
theotherjimmy |
43:2a7da56ebd24 | 552 | file_desc = open(file_output, 'w') |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 553 | else: |
theotherjimmy |
43:2a7da56ebd24 | 554 | file_desc = stdout |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 555 | except IOError as error: |
theotherjimmy |
43:2a7da56ebd24 | 556 | print("I/O error({0}): {1}".format(error.errno, error.strerror)) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 557 | return False |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 558 | |
screamer | 29:1210849dba19 | 559 | to_call = {'json': self.generate_json, |
theotherjimmy |
43:2a7da56ebd24 | 560 | 'html': self.generate_html, |
screamer | 29:1210849dba19 | 561 | 'csv-ci': self.generate_csv, |
screamer | 29:1210849dba19 | 562 | 'table': self.generate_table}[export_format] |
The Other Jimmy |
31:8ea194f6145b | 563 | output = to_call(file_desc) |
screamer | 29:1210849dba19 | 564 | |
theotherjimmy |
43:2a7da56ebd24 | 565 | if file_desc is not stdout: |
screamer | 29:1210849dba19 | 566 | file_desc.close() |
screamer | 29:1210849dba19 | 567 | |
The Other Jimmy |
31:8ea194f6145b | 568 | return output |
The Other Jimmy |
31:8ea194f6145b | 569 | |
theotherjimmy |
43:2a7da56ebd24 | 570 | @staticmethod |
theotherjimmy |
43:2a7da56ebd24 | 571 | def _move_up_tree(tree, next_module): |
theotherjimmy |
43:2a7da56ebd24 | 572 | tree.setdefault("children", []) |
theotherjimmy |
43:2a7da56ebd24 | 573 | for child in tree["children"]: |
theotherjimmy |
43:2a7da56ebd24 | 574 | if child["name"] == next_module: |
theotherjimmy |
43:2a7da56ebd24 | 575 | return child |
theotherjimmy |
43:2a7da56ebd24 | 576 | else: |
theotherjimmy |
43:2a7da56ebd24 | 577 | new_module = {"name": next_module, "value": 0, "delta": 0} |
theotherjimmy |
43:2a7da56ebd24 | 578 | tree["children"].append(new_module) |
theotherjimmy |
43:2a7da56ebd24 | 579 | return new_module |
theotherjimmy |
43:2a7da56ebd24 | 580 | |
theotherjimmy |
43:2a7da56ebd24 | 581 | def generate_html(self, file_desc): |
theotherjimmy |
43:2a7da56ebd24 | 582 | """Generate a json file from a memory map for D3 |
theotherjimmy |
43:2a7da56ebd24 | 583 | |
theotherjimmy |
43:2a7da56ebd24 | 584 | Positional arguments: |
theotherjimmy |
43:2a7da56ebd24 | 585 | file_desc - the file to write out the final report to |
theotherjimmy |
43:2a7da56ebd24 | 586 | """ |
theotherjimmy |
43:2a7da56ebd24 | 587 | tree_text = {"name": ".text", "value": 0, "delta": 0} |
theotherjimmy |
43:2a7da56ebd24 | 588 | tree_bss = {"name": ".bss", "value": 0, "delta": 0} |
theotherjimmy |
43:2a7da56ebd24 | 589 | tree_data = {"name": ".data", "value": 0, "delta": 0} |
theotherjimmy |
43:2a7da56ebd24 | 590 | for name, dct in self.modules.items(): |
theotherjimmy |
43:2a7da56ebd24 | 591 | cur_text = tree_text |
theotherjimmy |
43:2a7da56ebd24 | 592 | cur_bss = tree_bss |
theotherjimmy |
43:2a7da56ebd24 | 593 | cur_data = tree_data |
theotherjimmy |
43:2a7da56ebd24 | 594 | modules = name.split(sep) |
theotherjimmy |
43:2a7da56ebd24 | 595 | while True: |
theotherjimmy |
43:2a7da56ebd24 | 596 | try: |
theotherjimmy |
43:2a7da56ebd24 | 597 | cur_text["value"] += dct['.text'] |
theotherjimmy |
43:2a7da56ebd24 | 598 | cur_text["delta"] += dct['.text'] |
theotherjimmy |
43:2a7da56ebd24 | 599 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 600 | pass |
theotherjimmy |
43:2a7da56ebd24 | 601 | try: |
theotherjimmy |
43:2a7da56ebd24 | 602 | cur_bss["value"] += dct['.bss'] |
theotherjimmy |
43:2a7da56ebd24 | 603 | cur_bss["delta"] += dct['.bss'] |
theotherjimmy |
43:2a7da56ebd24 | 604 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 605 | pass |
theotherjimmy |
43:2a7da56ebd24 | 606 | try: |
theotherjimmy |
43:2a7da56ebd24 | 607 | cur_data["value"] += dct['.data'] |
theotherjimmy |
43:2a7da56ebd24 | 608 | cur_data["delta"] += dct['.data'] |
theotherjimmy |
43:2a7da56ebd24 | 609 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 610 | pass |
theotherjimmy |
43:2a7da56ebd24 | 611 | if not modules: |
theotherjimmy |
43:2a7da56ebd24 | 612 | break |
theotherjimmy |
43:2a7da56ebd24 | 613 | next_module = modules.pop(0) |
theotherjimmy |
43:2a7da56ebd24 | 614 | cur_text = self._move_up_tree(cur_text, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 615 | cur_data = self._move_up_tree(cur_data, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 616 | cur_bss = self._move_up_tree(cur_bss, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 617 | if self.old_modules: |
theotherjimmy |
43:2a7da56ebd24 | 618 | for name, dct in self.old_modules.items(): |
theotherjimmy |
43:2a7da56ebd24 | 619 | cur_text = tree_text |
theotherjimmy |
43:2a7da56ebd24 | 620 | cur_bss = tree_bss |
theotherjimmy |
43:2a7da56ebd24 | 621 | cur_data = tree_data |
theotherjimmy |
43:2a7da56ebd24 | 622 | modules = name.split(sep) |
theotherjimmy |
43:2a7da56ebd24 | 623 | while True: |
theotherjimmy |
43:2a7da56ebd24 | 624 | try: |
theotherjimmy |
43:2a7da56ebd24 | 625 | cur_text["delta"] -= dct['.text'] |
theotherjimmy |
43:2a7da56ebd24 | 626 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 627 | pass |
theotherjimmy |
43:2a7da56ebd24 | 628 | try: |
theotherjimmy |
43:2a7da56ebd24 | 629 | cur_bss["delta"] -= dct['.bss'] |
theotherjimmy |
43:2a7da56ebd24 | 630 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 631 | pass |
theotherjimmy |
43:2a7da56ebd24 | 632 | try: |
theotherjimmy |
43:2a7da56ebd24 | 633 | cur_data["delta"] -= dct['.data'] |
theotherjimmy |
43:2a7da56ebd24 | 634 | except KeyError: |
theotherjimmy |
43:2a7da56ebd24 | 635 | pass |
theotherjimmy |
43:2a7da56ebd24 | 636 | if not modules: |
theotherjimmy |
43:2a7da56ebd24 | 637 | break |
theotherjimmy |
43:2a7da56ebd24 | 638 | next_module = modules.pop(0) |
theotherjimmy |
43:2a7da56ebd24 | 639 | if not any(cld['name'] == next_module for cld in cur_text['children']): |
theotherjimmy |
43:2a7da56ebd24 | 640 | break |
theotherjimmy |
43:2a7da56ebd24 | 641 | cur_text = self._move_up_tree(cur_text, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 642 | cur_data = self._move_up_tree(cur_data, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 643 | cur_bss = self._move_up_tree(cur_bss, next_module) |
theotherjimmy |
43:2a7da56ebd24 | 644 | |
theotherjimmy |
43:2a7da56ebd24 | 645 | tree_rom = { |
theotherjimmy |
43:2a7da56ebd24 | 646 | "name": "ROM", |
theotherjimmy |
43:2a7da56ebd24 | 647 | "value": tree_text["value"] + tree_data["value"], |
theotherjimmy |
43:2a7da56ebd24 | 648 | "delta": tree_text["delta"] + tree_data["delta"], |
theotherjimmy |
43:2a7da56ebd24 | 649 | "children": [tree_text, tree_data] |
theotherjimmy |
43:2a7da56ebd24 | 650 | } |
theotherjimmy |
43:2a7da56ebd24 | 651 | tree_ram = { |
theotherjimmy |
43:2a7da56ebd24 | 652 | "name": "RAM", |
theotherjimmy |
43:2a7da56ebd24 | 653 | "value": tree_bss["value"] + tree_data["value"], |
theotherjimmy |
43:2a7da56ebd24 | 654 | "delta": tree_bss["delta"] + tree_data["delta"], |
theotherjimmy |
43:2a7da56ebd24 | 655 | "children": [tree_bss, tree_data] |
theotherjimmy |
43:2a7da56ebd24 | 656 | } |
theotherjimmy |
43:2a7da56ebd24 | 657 | |
theotherjimmy |
43:2a7da56ebd24 | 658 | jinja_loader = FileSystemLoader(dirname(abspath(__file__))) |
theotherjimmy |
43:2a7da56ebd24 | 659 | jinja_environment = Environment(loader=jinja_loader, |
theotherjimmy |
43:2a7da56ebd24 | 660 | undefined=StrictUndefined) |
theotherjimmy |
43:2a7da56ebd24 | 661 | |
theotherjimmy |
43:2a7da56ebd24 | 662 | template = jinja_environment.get_template("memap_flamegraph.html") |
theotherjimmy |
43:2a7da56ebd24 | 663 | name, _ = splitext(basename(file_desc.name)) |
theotherjimmy |
43:2a7da56ebd24 | 664 | if name.endswith("_map"): |
theotherjimmy |
43:2a7da56ebd24 | 665 | name = name[:-4] |
theotherjimmy |
43:2a7da56ebd24 | 666 | if self.tc_name: |
theotherjimmy |
43:2a7da56ebd24 | 667 | name = "%s %s" % (name, self.tc_name) |
theotherjimmy |
43:2a7da56ebd24 | 668 | data = { |
theotherjimmy |
43:2a7da56ebd24 | 669 | "name": name, |
theotherjimmy |
43:2a7da56ebd24 | 670 | "rom": json.dumps(tree_rom), |
theotherjimmy |
43:2a7da56ebd24 | 671 | "ram": json.dumps(tree_ram), |
theotherjimmy |
43:2a7da56ebd24 | 672 | } |
theotherjimmy |
43:2a7da56ebd24 | 673 | file_desc.write(template.render(data)) |
theotherjimmy |
43:2a7da56ebd24 | 674 | return None |
theotherjimmy |
43:2a7da56ebd24 | 675 | |
The Other Jimmy |
31:8ea194f6145b | 676 | def generate_json(self, file_desc): |
screamer | 29:1210849dba19 | 677 | """Generate a json file from a memory map |
screamer | 29:1210849dba19 | 678 | |
screamer | 29:1210849dba19 | 679 | Positional arguments: |
screamer | 29:1210849dba19 | 680 | file_desc - the file to write out the final report to |
screamer | 29:1210849dba19 | 681 | """ |
The Other Jimmy |
31:8ea194f6145b | 682 | file_desc.write(json.dumps(self.mem_report, indent=4)) |
screamer | 29:1210849dba19 | 683 | file_desc.write('\n') |
theotherjimmy |
43:2a7da56ebd24 | 684 | return None |
screamer | 29:1210849dba19 | 685 | |
theotherjimmy |
43:2a7da56ebd24 | 686 | RAM_FORMAT_STR = ( |
theotherjimmy |
43:2a7da56ebd24 | 687 | "Total Static RAM memory (data + bss): {}({:+}) bytes\n" |
theotherjimmy |
43:2a7da56ebd24 | 688 | ) |
theotherjimmy |
43:2a7da56ebd24 | 689 | |
theotherjimmy |
43:2a7da56ebd24 | 690 | ROM_FORMAT_STR = ( |
theotherjimmy |
43:2a7da56ebd24 | 691 | "Total Flash memory (text + data): {}({:+}) bytes\n" |
theotherjimmy |
43:2a7da56ebd24 | 692 | ) |
The Other Jimmy |
31:8ea194f6145b | 693 | |
The Other Jimmy |
31:8ea194f6145b | 694 | def generate_csv(self, file_desc): |
screamer | 29:1210849dba19 | 695 | """Generate a CSV file from a memoy map |
screamer | 29:1210849dba19 | 696 | |
screamer | 29:1210849dba19 | 697 | Positional arguments: |
screamer | 29:1210849dba19 | 698 | file_desc - the file to write out the final report to |
screamer | 29:1210849dba19 | 699 | """ |
theotherjimmy |
43:2a7da56ebd24 | 700 | writer = csv.writer(file_desc, delimiter=',', |
theotherjimmy |
43:2a7da56ebd24 | 701 | quoting=csv.QUOTE_MINIMAL) |
screamer | 29:1210849dba19 | 702 | |
theotherjimmy |
43:2a7da56ebd24 | 703 | module_section = [] |
theotherjimmy |
43:2a7da56ebd24 | 704 | sizes = [] |
theotherjimmy |
43:2a7da56ebd24 | 705 | for i in sorted(self.short_modules): |
theotherjimmy |
43:2a7da56ebd24 | 706 | for k in self.print_sections + self.delta_sections: |
theotherjimmy |
43:2a7da56ebd24 | 707 | module_section.append((i + k)) |
theotherjimmy |
43:2a7da56ebd24 | 708 | sizes += [self.short_modules[i][k]] |
screamer | 29:1210849dba19 | 709 | |
theotherjimmy |
43:2a7da56ebd24 | 710 | module_section.append('static_ram') |
theotherjimmy |
43:2a7da56ebd24 | 711 | sizes.append(self.mem_summary['static_ram']) |
screamer | 29:1210849dba19 | 712 | |
theotherjimmy |
43:2a7da56ebd24 | 713 | module_section.append('total_flash') |
theotherjimmy |
43:2a7da56ebd24 | 714 | sizes.append(self.mem_summary['total_flash']) |
screamer | 29:1210849dba19 | 715 | |
theotherjimmy |
43:2a7da56ebd24 | 716 | writer.writerow(module_section) |
theotherjimmy |
43:2a7da56ebd24 | 717 | writer.writerow(sizes) |
The Other Jimmy |
31:8ea194f6145b | 718 | return None |
The Other Jimmy |
31:8ea194f6145b | 719 | |
The Other Jimmy |
31:8ea194f6145b | 720 | def generate_table(self, file_desc): |
screamer | 29:1210849dba19 | 721 | """Generate a table from a memoy map |
screamer | 29:1210849dba19 | 722 | |
The Other Jimmy |
31:8ea194f6145b | 723 | Returns: string of the generated table |
screamer | 29:1210849dba19 | 724 | """ |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 725 | # Create table |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 726 | columns = ['Module'] |
screamer | 22:9e85236d8716 | 727 | columns.extend(self.print_sections) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 728 | |
theotherjimmy |
43:2a7da56ebd24 | 729 | table = PrettyTable(columns, junction_char="|", hrules=HEADER) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 730 | table.align["Module"] = "l" |
screamer | 22:9e85236d8716 | 731 | for col in self.print_sections: |
screamer | 22:9e85236d8716 | 732 | table.align[col] = 'r' |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 733 | |
screamer | 13:ab47a20b66f0 | 734 | for i in list(self.print_sections): |
screamer | 13:ab47a20b66f0 | 735 | table.align[i] = 'r' |
screamer | 13:ab47a20b66f0 | 736 | |
theotherjimmy |
40:7d3fa6b99b2b | 737 | for i in sorted(self.short_modules): |
The Other Jimmy |
31:8ea194f6145b | 738 | row = [i] |
The Other Jimmy |
31:8ea194f6145b | 739 | |
The Other Jimmy |
31:8ea194f6145b | 740 | for k in self.print_sections: |
theotherjimmy |
43:2a7da56ebd24 | 741 | row.append("{}({:+})".format(self.short_modules[i][k], |
theotherjimmy |
43:2a7da56ebd24 | 742 | self.short_modules[i][k + "-delta"])) |
The Other Jimmy |
31:8ea194f6145b | 743 | |
The Other Jimmy |
31:8ea194f6145b | 744 | table.add_row(row) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 745 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 746 | subtotal_row = ['Subtotals'] |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 747 | for k in self.print_sections: |
theotherjimmy |
43:2a7da56ebd24 | 748 | subtotal_row.append("{}({:+})".format( |
theotherjimmy |
43:2a7da56ebd24 | 749 | self.subtotal[k], self.subtotal[k + '-delta'])) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 750 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 751 | table.add_row(subtotal_row) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 752 | |
The Other Jimmy |
31:8ea194f6145b | 753 | output = table.get_string() |
The Other Jimmy |
31:8ea194f6145b | 754 | output += '\n' |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 755 | |
theotherjimmy |
43:2a7da56ebd24 | 756 | output += self.RAM_FORMAT_STR.format( |
theotherjimmy |
43:2a7da56ebd24 | 757 | self.mem_summary['static_ram'], |
theotherjimmy |
43:2a7da56ebd24 | 758 | self.mem_summary['static_ram_delta'] |
theotherjimmy |
43:2a7da56ebd24 | 759 | ) |
theotherjimmy |
43:2a7da56ebd24 | 760 | output += self.ROM_FORMAT_STR.format( |
theotherjimmy |
43:2a7da56ebd24 | 761 | self.mem_summary['total_flash'], |
theotherjimmy |
43:2a7da56ebd24 | 762 | self.mem_summary['total_flash_delta'] |
theotherjimmy |
43:2a7da56ebd24 | 763 | ) |
The Other Jimmy |
31:8ea194f6145b | 764 | |
The Other Jimmy |
31:8ea194f6145b | 765 | return output |
screamer | 22:9e85236d8716 | 766 | |
The Other Jimmy |
36:96847d42f010 | 767 | toolchains = ["ARM", "ARM_STD", "ARM_MICRO", "GCC_ARM", "GCC_CR", "IAR"] |
screamer | 22:9e85236d8716 | 768 | |
The Other Jimmy |
31:8ea194f6145b | 769 | def compute_report(self): |
theotherjimmy |
40:7d3fa6b99b2b | 770 | """ Generates summary of memory usage for main areas |
theotherjimmy |
40:7d3fa6b99b2b | 771 | """ |
theotherjimmy |
43:2a7da56ebd24 | 772 | self.subtotal = defaultdict(int) |
The Other Jimmy |
31:8ea194f6145b | 773 | |
theotherjimmy |
43:2a7da56ebd24 | 774 | for mod in self.modules.values(): |
The Other Jimmy |
31:8ea194f6145b | 775 | for k in self.sections: |
theotherjimmy |
43:2a7da56ebd24 | 776 | self.subtotal[k] += mod[k] |
theotherjimmy |
43:2a7da56ebd24 | 777 | self.subtotal[k + '-delta'] += mod[k] |
theotherjimmy |
43:2a7da56ebd24 | 778 | if self.old_modules: |
theotherjimmy |
43:2a7da56ebd24 | 779 | for mod in self.old_modules.values(): |
theotherjimmy |
43:2a7da56ebd24 | 780 | for k in self.sections: |
theotherjimmy |
43:2a7da56ebd24 | 781 | self.subtotal[k + '-delta'] -= mod[k] |
The Other Jimmy |
31:8ea194f6145b | 782 | |
The Other Jimmy |
31:8ea194f6145b | 783 | self.mem_summary = { |
theotherjimmy |
43:2a7da56ebd24 | 784 | 'static_ram': self.subtotal['.data'] + self.subtotal['.bss'], |
theotherjimmy |
43:2a7da56ebd24 | 785 | 'static_ram_delta': |
theotherjimmy |
43:2a7da56ebd24 | 786 | self.subtotal['.data-delta'] + self.subtotal['.bss-delta'], |
theotherjimmy |
40:7d3fa6b99b2b | 787 | 'total_flash': (self.subtotal['.text'] + self.subtotal['.data']), |
theotherjimmy |
43:2a7da56ebd24 | 788 | 'total_flash_delta': |
theotherjimmy |
43:2a7da56ebd24 | 789 | self.subtotal['.text-delta'] + self.subtotal['.data-delta'], |
The Other Jimmy |
31:8ea194f6145b | 790 | } |
The Other Jimmy |
31:8ea194f6145b | 791 | |
The Other Jimmy |
31:8ea194f6145b | 792 | self.mem_report = [] |
theotherjimmy |
43:2a7da56ebd24 | 793 | if self.short_modules: |
theotherjimmy |
43:2a7da56ebd24 | 794 | for name, sizes in sorted(self.short_modules.items()): |
theotherjimmy |
43:2a7da56ebd24 | 795 | self.mem_report.append({ |
theotherjimmy |
43:2a7da56ebd24 | 796 | "module": name, |
theotherjimmy |
43:2a7da56ebd24 | 797 | "size":{ |
theotherjimmy |
43:2a7da56ebd24 | 798 | k: sizes.get(k, 0) for k in (self.print_sections + |
theotherjimmy |
43:2a7da56ebd24 | 799 | self.delta_sections) |
theotherjimmy |
43:2a7da56ebd24 | 800 | } |
theotherjimmy |
43:2a7da56ebd24 | 801 | }) |
The Other Jimmy |
31:8ea194f6145b | 802 | |
The Other Jimmy |
31:8ea194f6145b | 803 | self.mem_report.append({ |
The Other Jimmy |
31:8ea194f6145b | 804 | 'summary': self.mem_summary |
The Other Jimmy |
31:8ea194f6145b | 805 | }) |
The Other Jimmy |
31:8ea194f6145b | 806 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 807 | def parse(self, mapfile, toolchain): |
screamer | 29:1210849dba19 | 808 | """ Parse and decode map file depending on the toolchain |
screamer | 29:1210849dba19 | 809 | |
screamer | 29:1210849dba19 | 810 | Positional arguments: |
screamer | 29:1210849dba19 | 811 | mapfile - the file name of the memory map file |
screamer | 29:1210849dba19 | 812 | toolchain - the toolchain used to create the file |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 813 | """ |
theotherjimmy |
43:2a7da56ebd24 | 814 | self.tc_name = toolchain.title() |
theotherjimmy |
43:2a7da56ebd24 | 815 | if toolchain in ("ARM", "ARM_STD", "ARM_MICRO", "ARMC6"): |
theotherjimmy |
43:2a7da56ebd24 | 816 | parser = _ArmccParser |
theotherjimmy |
43:2a7da56ebd24 | 817 | elif toolchain == "GCC_ARM" or toolchain == "GCC_CR": |
theotherjimmy |
43:2a7da56ebd24 | 818 | parser = _GccParser |
theotherjimmy |
43:2a7da56ebd24 | 819 | elif toolchain == "IAR": |
theotherjimmy |
43:2a7da56ebd24 | 820 | parser = _IarParser |
theotherjimmy |
43:2a7da56ebd24 | 821 | else: |
theotherjimmy |
43:2a7da56ebd24 | 822 | return False |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 823 | try: |
screamer | 29:1210849dba19 | 824 | with open(mapfile, 'r') as file_input: |
theotherjimmy |
43:2a7da56ebd24 | 825 | self.modules = parser().parse_mapfile(file_input) |
theotherjimmy |
43:2a7da56ebd24 | 826 | try: |
theotherjimmy |
43:2a7da56ebd24 | 827 | with open("%s.old" % mapfile, 'r') as old_input: |
theotherjimmy |
43:2a7da56ebd24 | 828 | self.old_modules = parser().parse_mapfile(old_input) |
theotherjimmy |
43:2a7da56ebd24 | 829 | except IOError: |
theotherjimmy |
43:2a7da56ebd24 | 830 | self.old_modules = None |
theotherjimmy |
43:2a7da56ebd24 | 831 | if not COMPARE_FIXED: |
theotherjimmy |
43:2a7da56ebd24 | 832 | old_mapfile = "%s.old" % mapfile |
theotherjimmy |
43:2a7da56ebd24 | 833 | if exists(old_mapfile): |
theotherjimmy |
43:2a7da56ebd24 | 834 | remove(old_mapfile) |
theotherjimmy |
43:2a7da56ebd24 | 835 | rename(mapfile, old_mapfile) |
theotherjimmy |
43:2a7da56ebd24 | 836 | return True |
theotherjimmy |
40:7d3fa6b99b2b | 837 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 838 | except IOError as error: |
theotherjimmy |
43:2a7da56ebd24 | 839 | print("I/O error({0}): {1}".format(error.errno, error.strerror)) |
theotherjimmy |
43:2a7da56ebd24 | 840 | return False |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 841 | |
screamer | 8:a8ac6ed29081 | 842 | def main(): |
screamer | 29:1210849dba19 | 843 | """Entry Point""" |
theotherjimmy |
40:7d3fa6b99b2b | 844 | version = '0.4.0' |
screamer | 8:a8ac6ed29081 | 845 | |
screamer | 8:a8ac6ed29081 | 846 | # Parser handling |
theotherjimmy |
43:2a7da56ebd24 | 847 | parser = ArgumentParser( |
screamer | 29:1210849dba19 | 848 | description="Memory Map File Analyser for ARM mbed\nversion %s" % |
screamer | 29:1210849dba19 | 849 | version) |
screamer | 8:a8ac6ed29081 | 850 | |
screamer | 29:1210849dba19 | 851 | parser.add_argument( |
screamer | 29:1210849dba19 | 852 | 'file', type=argparse_filestring_type, help='memory map file') |
screamer | 8:a8ac6ed29081 | 853 | |
screamer | 29:1210849dba19 | 854 | parser.add_argument( |
screamer | 29:1210849dba19 | 855 | '-t', '--toolchain', dest='toolchain', |
screamer | 29:1210849dba19 | 856 | help='select a toolchain used to build the memory map file (%s)' % |
screamer | 29:1210849dba19 | 857 | ", ".join(MemapParser.toolchains), |
screamer | 29:1210849dba19 | 858 | required=True, |
screamer | 29:1210849dba19 | 859 | type=argparse_uppercase_type(MemapParser.toolchains, "toolchain")) |
screamer | 8:a8ac6ed29081 | 860 | |
screamer | 29:1210849dba19 | 861 | parser.add_argument( |
theotherjimmy |
40:7d3fa6b99b2b | 862 | '-d', '--depth', dest='depth', type=int, |
theotherjimmy |
40:7d3fa6b99b2b | 863 | help='specify directory depth level to display report', required=False) |
theotherjimmy |
40:7d3fa6b99b2b | 864 | |
theotherjimmy |
40:7d3fa6b99b2b | 865 | parser.add_argument( |
screamer | 29:1210849dba19 | 866 | '-o', '--output', help='output file name', required=False) |
screamer | 8:a8ac6ed29081 | 867 | |
screamer | 29:1210849dba19 | 868 | parser.add_argument( |
screamer | 29:1210849dba19 | 869 | '-e', '--export', dest='export', required=False, default='table', |
screamer | 29:1210849dba19 | 870 | type=argparse_lowercase_hyphen_type(MemapParser.export_formats, |
screamer | 29:1210849dba19 | 871 | 'export format'), |
screamer | 29:1210849dba19 | 872 | help="export format (examples: %s: default)" % |
screamer | 29:1210849dba19 | 873 | ", ".join(MemapParser.export_formats)) |
screamer | 8:a8ac6ed29081 | 874 | |
screamer | 8:a8ac6ed29081 | 875 | parser.add_argument('-v', '--version', action='version', version=version) |
screamer | 8:a8ac6ed29081 | 876 | |
screamer | 8:a8ac6ed29081 | 877 | # Parse/run command |
theotherjimmy |
43:2a7da56ebd24 | 878 | if len(argv) <= 1: |
screamer | 8:a8ac6ed29081 | 879 | parser.print_help() |
theotherjimmy |
43:2a7da56ebd24 | 880 | exit(1) |
screamer | 8:a8ac6ed29081 | 881 | |
screamer | 29:1210849dba19 | 882 | args = parser.parse_args() |
screamer | 8:a8ac6ed29081 | 883 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 884 | # Create memap object |
theotherjimmy |
40:7d3fa6b99b2b | 885 | memap = MemapParser() |
screamer | 8:a8ac6ed29081 | 886 | |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 887 | # Parse and decode a map file |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 888 | if args.file and args.toolchain: |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 889 | if memap.parse(args.file, args.toolchain) is False: |
theotherjimmy |
43:2a7da56ebd24 | 890 | exit(0) |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 891 | |
theotherjimmy |
40:7d3fa6b99b2b | 892 | if args.depth is None: |
theotherjimmy |
40:7d3fa6b99b2b | 893 | depth = 2 # default depth level |
theotherjimmy |
40:7d3fa6b99b2b | 894 | else: |
theotherjimmy |
40:7d3fa6b99b2b | 895 | depth = args.depth |
theotherjimmy |
40:7d3fa6b99b2b | 896 | |
The Other Jimmy |
31:8ea194f6145b | 897 | returned_string = None |
screamer | 8:a8ac6ed29081 | 898 | # Write output in file |
screamer | 8:a8ac6ed29081 | 899 | if args.output != None: |
theotherjimmy |
40:7d3fa6b99b2b | 900 | returned_string = memap.generate_output(args.export, \ |
theotherjimmy |
40:7d3fa6b99b2b | 901 | depth, args.output) |
screamer | 8:a8ac6ed29081 | 902 | else: # Write output in screen |
theotherjimmy |
40:7d3fa6b99b2b | 903 | returned_string = memap.generate_output(args.export, depth) |
The Other Jimmy |
31:8ea194f6145b | 904 | |
The Other Jimmy |
31:8ea194f6145b | 905 | if args.export == 'table' and returned_string: |
theotherjimmy |
43:2a7da56ebd24 | 906 | print(returned_string) |
screamer | 8:a8ac6ed29081 | 907 | |
theotherjimmy |
43:2a7da56ebd24 | 908 | exit(0) |
screamer | 8:a8ac6ed29081 | 909 | |
screamer | 8:a8ac6ed29081 | 910 | if __name__ == "__main__": |
Screamer@Y5070-M.virtuoso | 9:2d27d77ada5c | 911 | main() |