Clone of official tools

Committer:
screamer
Date:
Mon Aug 29 11:18:36 2016 +0100
Revision:
29:1210849dba19
Parent:
24:25bff2709c20
Child:
30:f12ce67666d0
Port the latest tools patches from https://github.com/ARMmbed/mbed-os

Who changed what in which revision?

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