Clone of official tools

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