Development mbed library for MAX32630FTHR

Dependents:   blinky_max32630fthr

Committer:
switches
Date:
Fri Nov 11 20:59:50 2016 +0000
Revision:
0:5c4d7b2438d3
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
switches 0:5c4d7b2438d3 1 #!/usr/bin/env python
switches 0:5c4d7b2438d3 2
switches 0:5c4d7b2438d3 3 """Memory Map File Analyser for ARM mbed"""
switches 0:5c4d7b2438d3 4
switches 0:5c4d7b2438d3 5 import sys
switches 0:5c4d7b2438d3 6 import os
switches 0:5c4d7b2438d3 7 import re
switches 0:5c4d7b2438d3 8 import csv
switches 0:5c4d7b2438d3 9 import json
switches 0:5c4d7b2438d3 10 import argparse
switches 0:5c4d7b2438d3 11 from prettytable import PrettyTable
switches 0:5c4d7b2438d3 12
switches 0:5c4d7b2438d3 13 from utils import argparse_filestring_type, \
switches 0:5c4d7b2438d3 14 argparse_lowercase_hyphen_type, argparse_uppercase_type
switches 0:5c4d7b2438d3 15
switches 0:5c4d7b2438d3 16 DEBUG = False
switches 0:5c4d7b2438d3 17
switches 0:5c4d7b2438d3 18 RE_ARMCC = re.compile(
switches 0:5c4d7b2438d3 19 r'^\s+0x(\w{8})\s+0x(\w{8})\s+(\w+)\s+(\w+)\s+(\d+)\s+[*]?.+\s+(.+)$')
switches 0:5c4d7b2438d3 20 RE_IAR = re.compile(
switches 0:5c4d7b2438d3 21 r'^\s+(.+)\s+(zero|const|ro code|inited|uninit)\s'
switches 0:5c4d7b2438d3 22 r'+0x(\w{8})\s+0x(\w+)\s+(.+)\s.+$')
switches 0:5c4d7b2438d3 23
switches 0:5c4d7b2438d3 24 class MemapParser(object):
switches 0:5c4d7b2438d3 25 """An object that represents parsed results, parses the memory map files,
switches 0:5c4d7b2438d3 26 and writes out different file types of memory results
switches 0:5c4d7b2438d3 27 """
switches 0:5c4d7b2438d3 28
switches 0:5c4d7b2438d3 29 print_sections = ('.text', '.data', '.bss')
switches 0:5c4d7b2438d3 30
switches 0:5c4d7b2438d3 31 misc_flash_sections = ('.interrupts', '.flash_config')
switches 0:5c4d7b2438d3 32
switches 0:5c4d7b2438d3 33 other_sections = ('.interrupts_ram', '.init', '.ARM.extab',
switches 0:5c4d7b2438d3 34 '.ARM.exidx', '.ARM.attributes', '.eh_frame',
switches 0:5c4d7b2438d3 35 '.init_array', '.fini_array', '.jcr', '.stab',
switches 0:5c4d7b2438d3 36 '.stabstr', '.ARM.exidx', '.ARM')
switches 0:5c4d7b2438d3 37
switches 0:5c4d7b2438d3 38 # sections to print info (generic for all toolchains)
switches 0:5c4d7b2438d3 39 sections = ('.text', '.data', '.bss', '.heap', '.stack')
switches 0:5c4d7b2438d3 40
switches 0:5c4d7b2438d3 41 def __init__(self, detailed_misc=False):
switches 0:5c4d7b2438d3 42 """ General initialization
switches 0:5c4d7b2438d3 43 """
switches 0:5c4d7b2438d3 44 #
switches 0:5c4d7b2438d3 45 self.detailed_misc = detailed_misc
switches 0:5c4d7b2438d3 46
switches 0:5c4d7b2438d3 47 # list of all modules and their sections
switches 0:5c4d7b2438d3 48 self.modules = dict()
switches 0:5c4d7b2438d3 49
switches 0:5c4d7b2438d3 50 # sections must be defined in this order to take irrelevant out
switches 0:5c4d7b2438d3 51 self.all_sections = self.sections + self.other_sections + \
switches 0:5c4d7b2438d3 52 self.misc_flash_sections + ('unknown', 'OUTPUT')
switches 0:5c4d7b2438d3 53
switches 0:5c4d7b2438d3 54 # list of all object files and mappting to module names
switches 0:5c4d7b2438d3 55 self.object_to_module = dict()
switches 0:5c4d7b2438d3 56
switches 0:5c4d7b2438d3 57 # Memory report (sections + summary)
switches 0:5c4d7b2438d3 58 self.mem_report = []
switches 0:5c4d7b2438d3 59
switches 0:5c4d7b2438d3 60 # Just the memory summary section
switches 0:5c4d7b2438d3 61 self.mem_summary = dict()
switches 0:5c4d7b2438d3 62
switches 0:5c4d7b2438d3 63 self.subtotal = dict()
switches 0:5c4d7b2438d3 64
switches 0:5c4d7b2438d3 65 def module_add(self, module_name, size, section):
switches 0:5c4d7b2438d3 66 """ Adds a module / section to the list
switches 0:5c4d7b2438d3 67
switches 0:5c4d7b2438d3 68 Positional arguments:
switches 0:5c4d7b2438d3 69 module_name - name of the module to add
switches 0:5c4d7b2438d3 70 size - the size of the module being added
switches 0:5c4d7b2438d3 71 section - the section the module contributes to
switches 0:5c4d7b2438d3 72 """
switches 0:5c4d7b2438d3 73
switches 0:5c4d7b2438d3 74 if module_name in self.modules:
switches 0:5c4d7b2438d3 75 self.modules[module_name][section] += size
switches 0:5c4d7b2438d3 76 else:
switches 0:5c4d7b2438d3 77 temp_dic = dict()
switches 0:5c4d7b2438d3 78 for section_idx in self.all_sections:
switches 0:5c4d7b2438d3 79 temp_dic[section_idx] = 0
switches 0:5c4d7b2438d3 80 temp_dic[section] = size
switches 0:5c4d7b2438d3 81 self.modules[module_name] = temp_dic
switches 0:5c4d7b2438d3 82
switches 0:5c4d7b2438d3 83 def check_new_section_gcc(self, line):
switches 0:5c4d7b2438d3 84 """ Check whether a new section in a map file has been detected (only
switches 0:5c4d7b2438d3 85 applies to gcc)
switches 0:5c4d7b2438d3 86
switches 0:5c4d7b2438d3 87 Positional arguments:
switches 0:5c4d7b2438d3 88 line - the line to check for a new section
switches 0:5c4d7b2438d3 89 """
switches 0:5c4d7b2438d3 90
switches 0:5c4d7b2438d3 91 for i in self.all_sections:
switches 0:5c4d7b2438d3 92 if line.startswith(i):
switches 0:5c4d7b2438d3 93 # should name of the section (assuming it's a known one)
switches 0:5c4d7b2438d3 94 return i
switches 0:5c4d7b2438d3 95
switches 0:5c4d7b2438d3 96 if line.startswith('.'):
switches 0:5c4d7b2438d3 97 return 'unknown' # all others are classified are unknown
switches 0:5c4d7b2438d3 98 else:
switches 0:5c4d7b2438d3 99 return False # everything else, means no change in section
switches 0:5c4d7b2438d3 100
switches 0:5c4d7b2438d3 101
switches 0:5c4d7b2438d3 102 def path_object_to_module_name(self, txt):
switches 0:5c4d7b2438d3 103 """ Parse a path to object file to extract it's module and object data
switches 0:5c4d7b2438d3 104
switches 0:5c4d7b2438d3 105 Positional arguments:
switches 0:5c4d7b2438d3 106 txt - the path to parse the object and module name from
switches 0:5c4d7b2438d3 107 """
switches 0:5c4d7b2438d3 108
switches 0:5c4d7b2438d3 109 txt = txt.replace('\\', '/')
switches 0:5c4d7b2438d3 110 rex_mbed_os_name = r'^.+mbed-os\/(.+)\/(.+\.o)$'
switches 0:5c4d7b2438d3 111 test_rex_mbed_os_name = re.match(rex_mbed_os_name, txt)
switches 0:5c4d7b2438d3 112
switches 0:5c4d7b2438d3 113 if test_rex_mbed_os_name:
switches 0:5c4d7b2438d3 114
switches 0:5c4d7b2438d3 115 object_name = test_rex_mbed_os_name.group(2)
switches 0:5c4d7b2438d3 116 data = test_rex_mbed_os_name.group(1).split('/')
switches 0:5c4d7b2438d3 117 ndata = len(data)
switches 0:5c4d7b2438d3 118
switches 0:5c4d7b2438d3 119 if ndata == 1:
switches 0:5c4d7b2438d3 120 module_name = data[0]
switches 0:5c4d7b2438d3 121 else:
switches 0:5c4d7b2438d3 122 module_name = data[0] + '/' + data[1]
switches 0:5c4d7b2438d3 123
switches 0:5c4d7b2438d3 124 return [module_name, object_name]
switches 0:5c4d7b2438d3 125
switches 0:5c4d7b2438d3 126 elif self.detailed_misc:
switches 0:5c4d7b2438d3 127 rex_obj_name = r'^.+\/(.+\.o\)*)$'
switches 0:5c4d7b2438d3 128 test_rex_obj_name = re.match(rex_obj_name, txt)
switches 0:5c4d7b2438d3 129 if test_rex_obj_name:
switches 0:5c4d7b2438d3 130 object_name = test_rex_obj_name.group(1)
switches 0:5c4d7b2438d3 131 return ['Misc/' + object_name, ""]
switches 0:5c4d7b2438d3 132
switches 0:5c4d7b2438d3 133 return ['Misc', ""]
switches 0:5c4d7b2438d3 134 else:
switches 0:5c4d7b2438d3 135 return ['Misc', ""]
switches 0:5c4d7b2438d3 136
switches 0:5c4d7b2438d3 137 def parse_section_gcc(self, line):
switches 0:5c4d7b2438d3 138 """ Parse data from a section of gcc map file
switches 0:5c4d7b2438d3 139
switches 0:5c4d7b2438d3 140 examples:
switches 0:5c4d7b2438d3 141 0x00004308 0x7c ./.build/K64F/GCC_ARM/mbed-os/hal/targets/hal/TARGET_Freescale/TARGET_KPSDK_MCUS/spi_api.o
switches 0:5c4d7b2438d3 142 .text 0x00000608 0x198 ./.build/K64F/GCC_ARM/mbed-os/core/mbed-rtos/rtx/TARGET_CORTEX_M/TARGET_RTOS_M4_M7/TOOLCHAIN_GCC/HAL_CM4.o
switches 0:5c4d7b2438d3 143
switches 0:5c4d7b2438d3 144 Positional arguments:
switches 0:5c4d7b2438d3 145 line - the line to parse a section from
switches 0:5c4d7b2438d3 146 """
switches 0:5c4d7b2438d3 147 rex_address_len_name = re.compile(
switches 0:5c4d7b2438d3 148 r'^\s+.*0x(\w{8,16})\s+0x(\w+)\s(.+)$')
switches 0:5c4d7b2438d3 149
switches 0:5c4d7b2438d3 150 test_address_len_name = re.match(rex_address_len_name, line)
switches 0:5c4d7b2438d3 151
switches 0:5c4d7b2438d3 152 if test_address_len_name:
switches 0:5c4d7b2438d3 153
switches 0:5c4d7b2438d3 154 if int(test_address_len_name.group(2), 16) == 0: # size == 0
switches 0:5c4d7b2438d3 155 return ["", 0] # no valid entry
switches 0:5c4d7b2438d3 156 else:
switches 0:5c4d7b2438d3 157 m_name, _ = self.path_object_to_module_name(
switches 0:5c4d7b2438d3 158 test_address_len_name.group(3))
switches 0:5c4d7b2438d3 159 m_size = int(test_address_len_name.group(2), 16)
switches 0:5c4d7b2438d3 160 return [m_name, m_size]
switches 0:5c4d7b2438d3 161
switches 0:5c4d7b2438d3 162 else: # special corner case for *fill* sections
switches 0:5c4d7b2438d3 163 # example
switches 0:5c4d7b2438d3 164 # *fill* 0x0000abe4 0x4
switches 0:5c4d7b2438d3 165 rex_address_len = r'^\s+\*fill\*\s+0x(\w{8,16})\s+0x(\w+).*$'
switches 0:5c4d7b2438d3 166 test_address_len = re.match(rex_address_len, line)
switches 0:5c4d7b2438d3 167
switches 0:5c4d7b2438d3 168 if test_address_len:
switches 0:5c4d7b2438d3 169 if int(test_address_len.group(2), 16) == 0: # size == 0
switches 0:5c4d7b2438d3 170 return ["", 0] # no valid entry
switches 0:5c4d7b2438d3 171 else:
switches 0:5c4d7b2438d3 172 m_name = 'Fill'
switches 0:5c4d7b2438d3 173 m_size = int(test_address_len.group(2), 16)
switches 0:5c4d7b2438d3 174 return [m_name, m_size]
switches 0:5c4d7b2438d3 175 else:
switches 0:5c4d7b2438d3 176 return ["", 0] # no valid entry
switches 0:5c4d7b2438d3 177
switches 0:5c4d7b2438d3 178 def parse_map_file_gcc(self, file_desc):
switches 0:5c4d7b2438d3 179 """ Main logic to decode gcc map files
switches 0:5c4d7b2438d3 180
switches 0:5c4d7b2438d3 181 Positional arguments:
switches 0:5c4d7b2438d3 182 file_desc - a stream object to parse as a gcc map file
switches 0:5c4d7b2438d3 183 """
switches 0:5c4d7b2438d3 184
switches 0:5c4d7b2438d3 185 current_section = 'unknown'
switches 0:5c4d7b2438d3 186
switches 0:5c4d7b2438d3 187 with file_desc as infile:
switches 0:5c4d7b2438d3 188
switches 0:5c4d7b2438d3 189 # Search area to parse
switches 0:5c4d7b2438d3 190 for line in infile:
switches 0:5c4d7b2438d3 191 if line.startswith('Linker script and memory map'):
switches 0:5c4d7b2438d3 192 current_section = "unknown"
switches 0:5c4d7b2438d3 193 break
switches 0:5c4d7b2438d3 194
switches 0:5c4d7b2438d3 195 # Start decoding the map file
switches 0:5c4d7b2438d3 196 for line in infile:
switches 0:5c4d7b2438d3 197
switches 0:5c4d7b2438d3 198 change_section = self.check_new_section_gcc(line)
switches 0:5c4d7b2438d3 199
switches 0:5c4d7b2438d3 200 if change_section == "OUTPUT": # finish parsing file: exit
switches 0:5c4d7b2438d3 201 break
switches 0:5c4d7b2438d3 202 elif change_section != False:
switches 0:5c4d7b2438d3 203 current_section = change_section
switches 0:5c4d7b2438d3 204
switches 0:5c4d7b2438d3 205 [module_name, module_size] = self.parse_section_gcc(line)
switches 0:5c4d7b2438d3 206
switches 0:5c4d7b2438d3 207 if module_size == 0 or module_name == "":
switches 0:5c4d7b2438d3 208 pass
switches 0:5c4d7b2438d3 209 else:
switches 0:5c4d7b2438d3 210 self.module_add(module_name, module_size, current_section)
switches 0:5c4d7b2438d3 211
switches 0:5c4d7b2438d3 212 if DEBUG:
switches 0:5c4d7b2438d3 213 print "Line: %s" % line,
switches 0:5c4d7b2438d3 214 print "Module: %s\tSection: %s\tSize: %s" % \
switches 0:5c4d7b2438d3 215 (module_name, current_section, module_size)
switches 0:5c4d7b2438d3 216 raw_input("----------")
switches 0:5c4d7b2438d3 217
switches 0:5c4d7b2438d3 218 def parse_section_armcc(self, line):
switches 0:5c4d7b2438d3 219 """ Parse data from an armcc map file
switches 0:5c4d7b2438d3 220
switches 0:5c4d7b2438d3 221 Examples of armcc map file:
switches 0:5c4d7b2438d3 222 Base_Addr Size Type Attr Idx E Section Name Object
switches 0:5c4d7b2438d3 223 0x00000000 0x00000400 Data RO 11222 RESET startup_MK64F12.o
switches 0:5c4d7b2438d3 224 0x00000410 0x00000008 Code RO 49364 * !!!main c_w.l(__main.o)
switches 0:5c4d7b2438d3 225
switches 0:5c4d7b2438d3 226 Positional arguments:
switches 0:5c4d7b2438d3 227 line - the line to parse the section data from
switches 0:5c4d7b2438d3 228 """
switches 0:5c4d7b2438d3 229
switches 0:5c4d7b2438d3 230 test_rex_armcc = re.match(RE_ARMCC, line)
switches 0:5c4d7b2438d3 231
switches 0:5c4d7b2438d3 232 if test_rex_armcc:
switches 0:5c4d7b2438d3 233
switches 0:5c4d7b2438d3 234 size = int(test_rex_armcc.group(2), 16)
switches 0:5c4d7b2438d3 235
switches 0:5c4d7b2438d3 236 if test_rex_armcc.group(4) == 'RO':
switches 0:5c4d7b2438d3 237 section = '.text'
switches 0:5c4d7b2438d3 238 else:
switches 0:5c4d7b2438d3 239 if test_rex_armcc.group(3) == 'Data':
switches 0:5c4d7b2438d3 240 section = '.data'
switches 0:5c4d7b2438d3 241 elif test_rex_armcc.group(3) == 'Zero':
switches 0:5c4d7b2438d3 242 section = '.bss'
switches 0:5c4d7b2438d3 243 else:
switches 0:5c4d7b2438d3 244 print "BUG armcc map parser"
switches 0:5c4d7b2438d3 245 raw_input()
switches 0:5c4d7b2438d3 246
switches 0:5c4d7b2438d3 247 # lookup object in dictionary and return module name
switches 0:5c4d7b2438d3 248 object_name = test_rex_armcc.group(6)
switches 0:5c4d7b2438d3 249 if object_name in self.object_to_module:
switches 0:5c4d7b2438d3 250 module_name = self.object_to_module[object_name]
switches 0:5c4d7b2438d3 251 else:
switches 0:5c4d7b2438d3 252 module_name = 'Misc'
switches 0:5c4d7b2438d3 253
switches 0:5c4d7b2438d3 254 return [module_name, size, section]
switches 0:5c4d7b2438d3 255
switches 0:5c4d7b2438d3 256 else:
switches 0:5c4d7b2438d3 257 return ["", 0, ""] # no valid entry
switches 0:5c4d7b2438d3 258
switches 0:5c4d7b2438d3 259 def parse_section_iar(self, line):
switches 0:5c4d7b2438d3 260 """ Parse data from an IAR map file
switches 0:5c4d7b2438d3 261
switches 0:5c4d7b2438d3 262 Examples of IAR map file:
switches 0:5c4d7b2438d3 263 Section Kind Address Size Object
switches 0:5c4d7b2438d3 264 .intvec ro code 0x00000000 0x198 startup_MK64F12.o [15]
switches 0:5c4d7b2438d3 265 .rodata const 0x00000198 0x0 zero_init3.o [133]
switches 0:5c4d7b2438d3 266 .iar.init_table const 0x00008384 0x2c - Linker created -
switches 0:5c4d7b2438d3 267 Initializer bytes const 0x00000198 0xb2 <for P3 s0>
switches 0:5c4d7b2438d3 268 .data inited 0x20000000 0xd4 driverAtmelRFInterface.o [70]
switches 0:5c4d7b2438d3 269 .bss zero 0x20000598 0x318 RTX_Conf_CM.o [4]
switches 0:5c4d7b2438d3 270 .iar.dynexit uninit 0x20001448 0x204 <Block tail>
switches 0:5c4d7b2438d3 271 HEAP uninit 0x20001650 0x10000 <Block tail>
switches 0:5c4d7b2438d3 272
switches 0:5c4d7b2438d3 273 Positional_arguments:
switches 0:5c4d7b2438d3 274 line - the line to parse section data from
switches 0:5c4d7b2438d3 275 """
switches 0:5c4d7b2438d3 276
switches 0:5c4d7b2438d3 277 test_rex_iar = re.match(RE_IAR, line)
switches 0:5c4d7b2438d3 278
switches 0:5c4d7b2438d3 279 if test_rex_iar:
switches 0:5c4d7b2438d3 280
switches 0:5c4d7b2438d3 281 size = int(test_rex_iar.group(4), 16)
switches 0:5c4d7b2438d3 282
switches 0:5c4d7b2438d3 283 if test_rex_iar.group(2) == 'const' or \
switches 0:5c4d7b2438d3 284 test_rex_iar.group(2) == 'ro code':
switches 0:5c4d7b2438d3 285 section = '.text'
switches 0:5c4d7b2438d3 286 elif test_rex_iar.group(2) == 'zero' or \
switches 0:5c4d7b2438d3 287 test_rex_iar.group(2) == 'uninit':
switches 0:5c4d7b2438d3 288 if test_rex_iar.group(1)[0:4] == 'HEAP':
switches 0:5c4d7b2438d3 289 section = '.heap'
switches 0:5c4d7b2438d3 290 elif test_rex_iar.group(1)[0:6] == 'CSTACK':
switches 0:5c4d7b2438d3 291 section = '.stack'
switches 0:5c4d7b2438d3 292 else:
switches 0:5c4d7b2438d3 293 section = '.bss' # default section
switches 0:5c4d7b2438d3 294
switches 0:5c4d7b2438d3 295 elif test_rex_iar.group(2) == 'inited':
switches 0:5c4d7b2438d3 296 section = '.data'
switches 0:5c4d7b2438d3 297 else:
switches 0:5c4d7b2438d3 298 print "BUG IAR map parser"
switches 0:5c4d7b2438d3 299 raw_input()
switches 0:5c4d7b2438d3 300
switches 0:5c4d7b2438d3 301 # lookup object in dictionary and return module name
switches 0:5c4d7b2438d3 302 object_name = test_rex_iar.group(5)
switches 0:5c4d7b2438d3 303 if object_name in self.object_to_module:
switches 0:5c4d7b2438d3 304 module_name = self.object_to_module[object_name]
switches 0:5c4d7b2438d3 305 else:
switches 0:5c4d7b2438d3 306 module_name = 'Misc'
switches 0:5c4d7b2438d3 307
switches 0:5c4d7b2438d3 308 return [module_name, size, section]
switches 0:5c4d7b2438d3 309
switches 0:5c4d7b2438d3 310 else:
switches 0:5c4d7b2438d3 311 return ["", 0, ""] # no valid entry
switches 0:5c4d7b2438d3 312
switches 0:5c4d7b2438d3 313 def parse_map_file_armcc(self, file_desc):
switches 0:5c4d7b2438d3 314 """ Main logic to decode armc5 map files
switches 0:5c4d7b2438d3 315
switches 0:5c4d7b2438d3 316 Positional arguments:
switches 0:5c4d7b2438d3 317 file_desc - a file like object to parse as an armc5 map file
switches 0:5c4d7b2438d3 318 """
switches 0:5c4d7b2438d3 319
switches 0:5c4d7b2438d3 320 with file_desc as infile:
switches 0:5c4d7b2438d3 321
switches 0:5c4d7b2438d3 322 # Search area to parse
switches 0:5c4d7b2438d3 323 for line in infile:
switches 0:5c4d7b2438d3 324 if line.startswith(' Base Addr Size'):
switches 0:5c4d7b2438d3 325 break
switches 0:5c4d7b2438d3 326
switches 0:5c4d7b2438d3 327 # Start decoding the map file
switches 0:5c4d7b2438d3 328 for line in infile:
switches 0:5c4d7b2438d3 329
switches 0:5c4d7b2438d3 330 [name, size, section] = self.parse_section_armcc(line)
switches 0:5c4d7b2438d3 331
switches 0:5c4d7b2438d3 332 if size == 0 or name == "" or section == "":
switches 0:5c4d7b2438d3 333 pass
switches 0:5c4d7b2438d3 334 else:
switches 0:5c4d7b2438d3 335 self.module_add(name, size, section)
switches 0:5c4d7b2438d3 336
switches 0:5c4d7b2438d3 337 def parse_map_file_iar(self, file_desc):
switches 0:5c4d7b2438d3 338 """ Main logic to decode IAR map files
switches 0:5c4d7b2438d3 339
switches 0:5c4d7b2438d3 340 Positional arguments:
switches 0:5c4d7b2438d3 341 file_desc - a file like object to parse as an IAR map file
switches 0:5c4d7b2438d3 342 """
switches 0:5c4d7b2438d3 343
switches 0:5c4d7b2438d3 344 with file_desc as infile:
switches 0:5c4d7b2438d3 345
switches 0:5c4d7b2438d3 346 # Search area to parse
switches 0:5c4d7b2438d3 347 for line in infile:
switches 0:5c4d7b2438d3 348 if line.startswith(' Section '):
switches 0:5c4d7b2438d3 349 break
switches 0:5c4d7b2438d3 350
switches 0:5c4d7b2438d3 351 # Start decoding the map file
switches 0:5c4d7b2438d3 352 for line in infile:
switches 0:5c4d7b2438d3 353
switches 0:5c4d7b2438d3 354 [name, size, section] = self.parse_section_iar(line)
switches 0:5c4d7b2438d3 355
switches 0:5c4d7b2438d3 356 if size == 0 or name == "" or section == "":
switches 0:5c4d7b2438d3 357 pass
switches 0:5c4d7b2438d3 358 else:
switches 0:5c4d7b2438d3 359 self.module_add(name, size, section)
switches 0:5c4d7b2438d3 360
switches 0:5c4d7b2438d3 361 def search_objects(self, path):
switches 0:5c4d7b2438d3 362 """ Searches for object files and creates mapping: object --> module
switches 0:5c4d7b2438d3 363
switches 0:5c4d7b2438d3 364 Positional arguments:
switches 0:5c4d7b2438d3 365 path - the path to an object file
switches 0:5c4d7b2438d3 366 """
switches 0:5c4d7b2438d3 367
switches 0:5c4d7b2438d3 368 path = path.replace('\\', '/')
switches 0:5c4d7b2438d3 369
switches 0:5c4d7b2438d3 370 # check location of map file
switches 0:5c4d7b2438d3 371 rex = r'^(.+)' + r'\/(.+\.map)$'
switches 0:5c4d7b2438d3 372 test_rex = re.match(rex, path)
switches 0:5c4d7b2438d3 373
switches 0:5c4d7b2438d3 374 if test_rex:
switches 0:5c4d7b2438d3 375 search_path = test_rex.group(1) + '/mbed-os/'
switches 0:5c4d7b2438d3 376 else:
switches 0:5c4d7b2438d3 377 print "Warning: this doesn't look like an mbed project"
switches 0:5c4d7b2438d3 378 return
switches 0:5c4d7b2438d3 379
switches 0:5c4d7b2438d3 380 for root, _, obj_files in os.walk(search_path):
switches 0:5c4d7b2438d3 381 for obj_file in obj_files:
switches 0:5c4d7b2438d3 382 if obj_file.endswith(".o"):
switches 0:5c4d7b2438d3 383 module_name, object_name = self.path_object_to_module_name(
switches 0:5c4d7b2438d3 384 os.path.join(root, obj_file))
switches 0:5c4d7b2438d3 385
switches 0:5c4d7b2438d3 386 if object_name in self.object_to_module:
switches 0:5c4d7b2438d3 387 if DEBUG:
switches 0:5c4d7b2438d3 388 print "WARNING: multiple usages of object file: %s"\
switches 0:5c4d7b2438d3 389 % object_name
switches 0:5c4d7b2438d3 390 print " Current: %s" % \
switches 0:5c4d7b2438d3 391 self.object_to_module[object_name]
switches 0:5c4d7b2438d3 392 print " New: %s" % module_name
switches 0:5c4d7b2438d3 393 print " "
switches 0:5c4d7b2438d3 394 else:
switches 0:5c4d7b2438d3 395 self.object_to_module.update({object_name:module_name})
switches 0:5c4d7b2438d3 396
switches 0:5c4d7b2438d3 397 export_formats = ["json", "csv-ci", "table"]
switches 0:5c4d7b2438d3 398
switches 0:5c4d7b2438d3 399 def generate_output(self, export_format, file_output=None):
switches 0:5c4d7b2438d3 400 """ Generates summary of memory map data
switches 0:5c4d7b2438d3 401
switches 0:5c4d7b2438d3 402 Positional arguments:
switches 0:5c4d7b2438d3 403 export_format - the format to dump
switches 0:5c4d7b2438d3 404
switches 0:5c4d7b2438d3 405 Keyword arguments:
switches 0:5c4d7b2438d3 406 file_desc - descriptor (either stdout or file)
switches 0:5c4d7b2438d3 407
switches 0:5c4d7b2438d3 408 Returns: generated string for the 'table' format, otherwise None
switches 0:5c4d7b2438d3 409 """
switches 0:5c4d7b2438d3 410
switches 0:5c4d7b2438d3 411 try:
switches 0:5c4d7b2438d3 412 if file_output:
switches 0:5c4d7b2438d3 413 file_desc = open(file_output, 'wb')
switches 0:5c4d7b2438d3 414 else:
switches 0:5c4d7b2438d3 415 file_desc = sys.stdout
switches 0:5c4d7b2438d3 416 except IOError as error:
switches 0:5c4d7b2438d3 417 print "I/O error({0}): {1}".format(error.errno, error.strerror)
switches 0:5c4d7b2438d3 418 return False
switches 0:5c4d7b2438d3 419
switches 0:5c4d7b2438d3 420 to_call = {'json': self.generate_json,
switches 0:5c4d7b2438d3 421 'csv-ci': self.generate_csv,
switches 0:5c4d7b2438d3 422 'table': self.generate_table}[export_format]
switches 0:5c4d7b2438d3 423 output = to_call(file_desc)
switches 0:5c4d7b2438d3 424
switches 0:5c4d7b2438d3 425 if file_desc is not sys.stdout:
switches 0:5c4d7b2438d3 426 file_desc.close()
switches 0:5c4d7b2438d3 427
switches 0:5c4d7b2438d3 428 return output
switches 0:5c4d7b2438d3 429
switches 0:5c4d7b2438d3 430 def generate_json(self, file_desc):
switches 0:5c4d7b2438d3 431 """Generate a json file from a memory map
switches 0:5c4d7b2438d3 432
switches 0:5c4d7b2438d3 433 Positional arguments:
switches 0:5c4d7b2438d3 434 file_desc - the file to write out the final report to
switches 0:5c4d7b2438d3 435 """
switches 0:5c4d7b2438d3 436 file_desc.write(json.dumps(self.mem_report, indent=4))
switches 0:5c4d7b2438d3 437 file_desc.write('\n')
switches 0:5c4d7b2438d3 438
switches 0:5c4d7b2438d3 439 return None
switches 0:5c4d7b2438d3 440
switches 0:5c4d7b2438d3 441 def generate_csv(self, file_desc):
switches 0:5c4d7b2438d3 442 """Generate a CSV file from a memoy map
switches 0:5c4d7b2438d3 443
switches 0:5c4d7b2438d3 444 Positional arguments:
switches 0:5c4d7b2438d3 445 file_desc - the file to write out the final report to
switches 0:5c4d7b2438d3 446 """
switches 0:5c4d7b2438d3 447 csv_writer = csv.writer(file_desc, delimiter=',',
switches 0:5c4d7b2438d3 448 quoting=csv.QUOTE_MINIMAL)
switches 0:5c4d7b2438d3 449
switches 0:5c4d7b2438d3 450 csv_module_section = []
switches 0:5c4d7b2438d3 451 csv_sizes = []
switches 0:5c4d7b2438d3 452 for i in sorted(self.modules):
switches 0:5c4d7b2438d3 453 for k in self.print_sections:
switches 0:5c4d7b2438d3 454 csv_module_section += [i+k]
switches 0:5c4d7b2438d3 455 csv_sizes += [self.modules[i][k]]
switches 0:5c4d7b2438d3 456
switches 0:5c4d7b2438d3 457 csv_module_section += ['static_ram']
switches 0:5c4d7b2438d3 458 csv_sizes += [self.mem_summary['static_ram']]
switches 0:5c4d7b2438d3 459
switches 0:5c4d7b2438d3 460 csv_module_section += ['heap']
switches 0:5c4d7b2438d3 461 if self.mem_summary['heap'] == 0:
switches 0:5c4d7b2438d3 462 csv_sizes += ['unknown']
switches 0:5c4d7b2438d3 463 else:
switches 0:5c4d7b2438d3 464 csv_sizes += [self.mem_summary['heap']]
switches 0:5c4d7b2438d3 465
switches 0:5c4d7b2438d3 466 csv_module_section += ['stack']
switches 0:5c4d7b2438d3 467 if self.mem_summary['stack'] == 0:
switches 0:5c4d7b2438d3 468 csv_sizes += ['unknown']
switches 0:5c4d7b2438d3 469 else:
switches 0:5c4d7b2438d3 470 csv_sizes += [self.mem_summary['stack']]
switches 0:5c4d7b2438d3 471
switches 0:5c4d7b2438d3 472 csv_module_section += ['total_ram']
switches 0:5c4d7b2438d3 473 csv_sizes += [self.mem_summary['total_ram']]
switches 0:5c4d7b2438d3 474
switches 0:5c4d7b2438d3 475 csv_module_section += ['total_flash']
switches 0:5c4d7b2438d3 476 csv_sizes += [self.mem_summary['total_flash']]
switches 0:5c4d7b2438d3 477
switches 0:5c4d7b2438d3 478 csv_writer.writerow(csv_module_section)
switches 0:5c4d7b2438d3 479 csv_writer.writerow(csv_sizes)
switches 0:5c4d7b2438d3 480
switches 0:5c4d7b2438d3 481 return None
switches 0:5c4d7b2438d3 482
switches 0:5c4d7b2438d3 483 def generate_table(self, file_desc):
switches 0:5c4d7b2438d3 484 """Generate a table from a memoy map
switches 0:5c4d7b2438d3 485
switches 0:5c4d7b2438d3 486 Positional arguments:
switches 0:5c4d7b2438d3 487 file_desc - the file to write out the final report to
switches 0:5c4d7b2438d3 488
switches 0:5c4d7b2438d3 489 Returns: string of the generated table
switches 0:5c4d7b2438d3 490 """
switches 0:5c4d7b2438d3 491 # Create table
switches 0:5c4d7b2438d3 492 columns = ['Module']
switches 0:5c4d7b2438d3 493 columns.extend(self.print_sections)
switches 0:5c4d7b2438d3 494
switches 0:5c4d7b2438d3 495 table = PrettyTable(columns)
switches 0:5c4d7b2438d3 496 table.align["Module"] = "l"
switches 0:5c4d7b2438d3 497 for col in self.print_sections:
switches 0:5c4d7b2438d3 498 table.align[col] = 'r'
switches 0:5c4d7b2438d3 499
switches 0:5c4d7b2438d3 500 for i in list(self.print_sections):
switches 0:5c4d7b2438d3 501 table.align[i] = 'r'
switches 0:5c4d7b2438d3 502
switches 0:5c4d7b2438d3 503 for i in sorted(self.modules):
switches 0:5c4d7b2438d3 504 row = [i]
switches 0:5c4d7b2438d3 505
switches 0:5c4d7b2438d3 506 for k in self.print_sections:
switches 0:5c4d7b2438d3 507 row.append(self.modules[i][k])
switches 0:5c4d7b2438d3 508
switches 0:5c4d7b2438d3 509 table.add_row(row)
switches 0:5c4d7b2438d3 510
switches 0:5c4d7b2438d3 511 subtotal_row = ['Subtotals']
switches 0:5c4d7b2438d3 512 for k in self.print_sections:
switches 0:5c4d7b2438d3 513 subtotal_row.append(self.subtotal[k])
switches 0:5c4d7b2438d3 514
switches 0:5c4d7b2438d3 515 table.add_row(subtotal_row)
switches 0:5c4d7b2438d3 516
switches 0:5c4d7b2438d3 517 output = table.get_string()
switches 0:5c4d7b2438d3 518 output += '\n'
switches 0:5c4d7b2438d3 519
switches 0:5c4d7b2438d3 520 if self.mem_summary['heap'] == 0:
switches 0:5c4d7b2438d3 521 output += "Allocated Heap: unknown\n"
switches 0:5c4d7b2438d3 522 else:
switches 0:5c4d7b2438d3 523 output += "Allocated Heap: %s bytes\n" % \
switches 0:5c4d7b2438d3 524 str(self.mem_summary['heap'])
switches 0:5c4d7b2438d3 525
switches 0:5c4d7b2438d3 526 if self.mem_summary['stack'] == 0:
switches 0:5c4d7b2438d3 527 output += "Allocated Stack: unknown\n"
switches 0:5c4d7b2438d3 528 else:
switches 0:5c4d7b2438d3 529 output += "Allocated Stack: %s bytes\n" % \
switches 0:5c4d7b2438d3 530 str(self.mem_summary['stack'])
switches 0:5c4d7b2438d3 531
switches 0:5c4d7b2438d3 532 output += "Total Static RAM memory (data + bss): %s bytes\n" % \
switches 0:5c4d7b2438d3 533 str(self.mem_summary['static_ram'])
switches 0:5c4d7b2438d3 534 output += "Total RAM memory (data + bss + heap + stack): %s bytes\n" % \
switches 0:5c4d7b2438d3 535 str(self.mem_summary['total_ram'])
switches 0:5c4d7b2438d3 536 output += "Total Flash memory (text + data + misc): %s bytes\n" % \
switches 0:5c4d7b2438d3 537 str(self.mem_summary['total_flash'])
switches 0:5c4d7b2438d3 538
switches 0:5c4d7b2438d3 539 return output
switches 0:5c4d7b2438d3 540
switches 0:5c4d7b2438d3 541 toolchains = ["ARM", "ARM_STD", "ARM_MICRO", "GCC_ARM", "IAR"]
switches 0:5c4d7b2438d3 542
switches 0:5c4d7b2438d3 543 def compute_report(self):
switches 0:5c4d7b2438d3 544 for k in self.sections:
switches 0:5c4d7b2438d3 545 self.subtotal[k] = 0
switches 0:5c4d7b2438d3 546
switches 0:5c4d7b2438d3 547 for i in sorted(self.modules):
switches 0:5c4d7b2438d3 548 for k in self.sections:
switches 0:5c4d7b2438d3 549 self.subtotal[k] += self.modules[i][k]
switches 0:5c4d7b2438d3 550
switches 0:5c4d7b2438d3 551 # Calculate misc flash sections
switches 0:5c4d7b2438d3 552 self.misc_flash_mem = 0
switches 0:5c4d7b2438d3 553 for i in self.modules:
switches 0:5c4d7b2438d3 554 for k in self.misc_flash_sections:
switches 0:5c4d7b2438d3 555 if self.modules[i][k]:
switches 0:5c4d7b2438d3 556 self.misc_flash_mem += self.modules[i][k]
switches 0:5c4d7b2438d3 557
switches 0:5c4d7b2438d3 558 self.mem_summary = {
switches 0:5c4d7b2438d3 559 'static_ram': (self.subtotal['.data'] + self.subtotal['.bss']),
switches 0:5c4d7b2438d3 560 'heap': (self.subtotal['.heap']),
switches 0:5c4d7b2438d3 561 'stack': (self.subtotal['.stack']),
switches 0:5c4d7b2438d3 562 'total_ram': (self.subtotal['.data'] + self.subtotal['.bss'] +
switches 0:5c4d7b2438d3 563 self.subtotal['.heap']+self.subtotal['.stack']),
switches 0:5c4d7b2438d3 564 'total_flash': (self.subtotal['.text'] + self.subtotal['.data'] +
switches 0:5c4d7b2438d3 565 self.misc_flash_mem),
switches 0:5c4d7b2438d3 566 }
switches 0:5c4d7b2438d3 567
switches 0:5c4d7b2438d3 568 self.mem_report = []
switches 0:5c4d7b2438d3 569 for i in sorted(self.modules):
switches 0:5c4d7b2438d3 570 self.mem_report.append({
switches 0:5c4d7b2438d3 571 "module":i,
switches 0:5c4d7b2438d3 572 "size":{
switches 0:5c4d7b2438d3 573 k:self.modules[i][k] for k in self.print_sections
switches 0:5c4d7b2438d3 574 }
switches 0:5c4d7b2438d3 575 })
switches 0:5c4d7b2438d3 576
switches 0:5c4d7b2438d3 577 self.mem_report.append({
switches 0:5c4d7b2438d3 578 'summary': self.mem_summary
switches 0:5c4d7b2438d3 579 })
switches 0:5c4d7b2438d3 580
switches 0:5c4d7b2438d3 581 def parse(self, mapfile, toolchain):
switches 0:5c4d7b2438d3 582 """ Parse and decode map file depending on the toolchain
switches 0:5c4d7b2438d3 583
switches 0:5c4d7b2438d3 584 Positional arguments:
switches 0:5c4d7b2438d3 585 mapfile - the file name of the memory map file
switches 0:5c4d7b2438d3 586 toolchain - the toolchain used to create the file
switches 0:5c4d7b2438d3 587 """
switches 0:5c4d7b2438d3 588
switches 0:5c4d7b2438d3 589 result = True
switches 0:5c4d7b2438d3 590 try:
switches 0:5c4d7b2438d3 591 with open(mapfile, 'r') as file_input:
switches 0:5c4d7b2438d3 592 if toolchain == "ARM" or toolchain == "ARM_STD" or\
switches 0:5c4d7b2438d3 593 toolchain == "ARM_MICRO":
switches 0:5c4d7b2438d3 594 self.search_objects(os.path.abspath(mapfile))
switches 0:5c4d7b2438d3 595 self.parse_map_file_armcc(file_input)
switches 0:5c4d7b2438d3 596 elif toolchain == "GCC_ARM":
switches 0:5c4d7b2438d3 597 self.parse_map_file_gcc(file_input)
switches 0:5c4d7b2438d3 598 elif toolchain == "IAR":
switches 0:5c4d7b2438d3 599 self.search_objects(os.path.abspath(mapfile))
switches 0:5c4d7b2438d3 600 self.parse_map_file_iar(file_input)
switches 0:5c4d7b2438d3 601 else:
switches 0:5c4d7b2438d3 602 result = False
switches 0:5c4d7b2438d3 603
switches 0:5c4d7b2438d3 604 self.compute_report()
switches 0:5c4d7b2438d3 605
switches 0:5c4d7b2438d3 606 except IOError as error:
switches 0:5c4d7b2438d3 607 print "I/O error({0}): {1}".format(error.errno, error.strerror)
switches 0:5c4d7b2438d3 608 result = False
switches 0:5c4d7b2438d3 609 return result
switches 0:5c4d7b2438d3 610
switches 0:5c4d7b2438d3 611 def main():
switches 0:5c4d7b2438d3 612 """Entry Point"""
switches 0:5c4d7b2438d3 613
switches 0:5c4d7b2438d3 614 version = '0.3.12'
switches 0:5c4d7b2438d3 615
switches 0:5c4d7b2438d3 616 # Parser handling
switches 0:5c4d7b2438d3 617 parser = argparse.ArgumentParser(
switches 0:5c4d7b2438d3 618 description="Memory Map File Analyser for ARM mbed\nversion %s" %
switches 0:5c4d7b2438d3 619 version)
switches 0:5c4d7b2438d3 620
switches 0:5c4d7b2438d3 621 parser.add_argument(
switches 0:5c4d7b2438d3 622 'file', type=argparse_filestring_type, help='memory map file')
switches 0:5c4d7b2438d3 623
switches 0:5c4d7b2438d3 624 parser.add_argument(
switches 0:5c4d7b2438d3 625 '-t', '--toolchain', dest='toolchain',
switches 0:5c4d7b2438d3 626 help='select a toolchain used to build the memory map file (%s)' %
switches 0:5c4d7b2438d3 627 ", ".join(MemapParser.toolchains),
switches 0:5c4d7b2438d3 628 required=True,
switches 0:5c4d7b2438d3 629 type=argparse_uppercase_type(MemapParser.toolchains, "toolchain"))
switches 0:5c4d7b2438d3 630
switches 0:5c4d7b2438d3 631 parser.add_argument(
switches 0:5c4d7b2438d3 632 '-o', '--output', help='output file name', required=False)
switches 0:5c4d7b2438d3 633
switches 0:5c4d7b2438d3 634 parser.add_argument(
switches 0:5c4d7b2438d3 635 '-e', '--export', dest='export', required=False, default='table',
switches 0:5c4d7b2438d3 636 type=argparse_lowercase_hyphen_type(MemapParser.export_formats,
switches 0:5c4d7b2438d3 637 'export format'),
switches 0:5c4d7b2438d3 638 help="export format (examples: %s: default)" %
switches 0:5c4d7b2438d3 639 ", ".join(MemapParser.export_formats))
switches 0:5c4d7b2438d3 640
switches 0:5c4d7b2438d3 641 parser.add_argument('-v', '--version', action='version', version=version)
switches 0:5c4d7b2438d3 642
switches 0:5c4d7b2438d3 643 parser.add_argument('-d', '--detailed', action='store_true', help='Displays the elements in "Misc" in a detailed fashion', required=False)
switches 0:5c4d7b2438d3 644
switches 0:5c4d7b2438d3 645 # Parse/run command
switches 0:5c4d7b2438d3 646 if len(sys.argv) <= 1:
switches 0:5c4d7b2438d3 647 parser.print_help()
switches 0:5c4d7b2438d3 648 sys.exit(1)
switches 0:5c4d7b2438d3 649
switches 0:5c4d7b2438d3 650
switches 0:5c4d7b2438d3 651 args = parser.parse_args()
switches 0:5c4d7b2438d3 652
switches 0:5c4d7b2438d3 653 # Create memap object
switches 0:5c4d7b2438d3 654 memap = MemapParser(detailed_misc=args.detailed)
switches 0:5c4d7b2438d3 655
switches 0:5c4d7b2438d3 656 # Parse and decode a map file
switches 0:5c4d7b2438d3 657 if args.file and args.toolchain:
switches 0:5c4d7b2438d3 658 if memap.parse(args.file, args.toolchain) is False:
switches 0:5c4d7b2438d3 659 sys.exit(0)
switches 0:5c4d7b2438d3 660
switches 0:5c4d7b2438d3 661 returned_string = None
switches 0:5c4d7b2438d3 662 # Write output in file
switches 0:5c4d7b2438d3 663 if args.output != None:
switches 0:5c4d7b2438d3 664 returned_string = memap.generate_output(args.export, args.output)
switches 0:5c4d7b2438d3 665 else: # Write output in screen
switches 0:5c4d7b2438d3 666 returned_string = memap.generate_output(args.export)
switches 0:5c4d7b2438d3 667
switches 0:5c4d7b2438d3 668 if args.export == 'table' and returned_string:
switches 0:5c4d7b2438d3 669 print returned_string
switches 0:5c4d7b2438d3 670
switches 0:5c4d7b2438d3 671 sys.exit(0)
switches 0:5c4d7b2438d3 672
switches 0:5c4d7b2438d3 673 if __name__ == "__main__":
switches 0:5c4d7b2438d3 674 main()