init

Dependencies:   mbed

Committer:
Nathan Yonkee
Date:
Fri Mar 02 07:16:49 2018 -0700
Revision:
10:46a4cf51ee38
Parent:
9:d58e77ebd769
remove mbed-os

Who changed what in which revision?

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