blob: 5634b30259382af555edb83f6764e74cad108650 [file] [log] [blame]
# ------------------------------------------------------------------------------
# Copyright (c) 2022, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# ------------------------------------------------------------------------------
import sqlite3
from xlsxwriter.workbook import Workbook
class SQ(object):
"""
Class SQ is used to create a new sqlite3 database and search the information
of functions, data, sections, libraries, object files from map file. Then
store the result into the database for further usage.
- Methods:
- SQ().update() - Create and update the database.
- Variables:
- SQ().armcc - ARMCLANG option.
- SQ().gnuarm - GNUARM option.
- SQ().map_file - The map file path which detail information comes from.
- SQ().db_name - The database file name to be saved.
"""
def __init__(self):
"""
Initialize variables.
"""
self.gnuarm = False
self.armcc = False
self.map_file = ""
self.db_name = ""
self.__gnuarm_info = []
self.__sec_dict = {}
self.__excel_name = ""
def __new(self):
"""
Create tables in a new empty database.
"""
self.__con = sqlite3.connect(self.db_name)
self.__cur = self.__con.cursor()
self.__excel_name = self.db_name +'.xlsx'
self.__cur.execute('''create table Summary
(Code INT NOT NULL,
RO_data INT NOT NULL,
RW_data INT NOT NULL,
ZI_data INT NOT NULL,
EXTRA_FLASH INT NOT NULL,
EXTRA_RAM INT NOT NULL,
Flash INT NOT NULL,
RAM INT NOT NULL);''')
self.__cur.execute('''create table Section
(name TEXT NOT NULL,
size INT NOT NULL,
address TEXT NOT NULL,
pad_size INT NOT NULL);''')
self.__cur.execute('''create table Function
(name TEXT NOT NULL,
section TEXT NOT NULL,
size INT NOT NULL,
base_addr TEXT NOT NULL,
obj_file TEXT NOT_NULL,
lib_file TEXT NOT_NULL);''')
self.__cur.execute('''create table Data
(name TEXT NOT NULL,
section TEXT NOT NULL,
size INT NOT NULL,
base_addr TEXT NOT NULL,
type TEXT NOT NULL,
obj_file TEXT NOT_NULL,
lib_file TEXT NOT_NULL);''')
self.__cur.execute('''create table Library
(name TEXT NOT NULL,
size INT NOT NULL,
ramsize INT NOT NULL,
code INT NOT NULL,
rodata INT NOT NULL,
rwdata INT NOT NULL,
zidata INT NOT NULL,
incdata INT NOT_NULL,
Debug INT NOT NULL);''')
self.__cur.execute('''create table Object
(name TEXT NOT NULL,
lib_file TEXT NOT NULL,
size INT NOT NULL,
ramsize INT NOT NULL,
code INT NOT NULL,
rodata INT NOT NULL,
rwdata INT NOT NULL,
zidata INT NOT NULL,
incdata INT NOT_NULL,
Debug INT NOT NULL);''')
self.__cur.execute('''create table Compiler
(Compiler TEXT NOT NULL,
flag INT NOT NULL);''')
if self.gnuarm:
self.__cur.execute('''create table Unknown
(name TEXT NOT NULL,
section TEXT NOT NULL,
size INT NOT NULL,
base_addr TEXT NOT NULL,
type TEXT NOT NULL,
obj_file TEXT NOT_NULL,
lib_file TEXT NOT_NULL);''')
def __collect_compiler(self):
if self.gnuarm:
self.__cur.execute("insert into Compiler values (?, ?)", ("gnuarm", 1))
if self.armcc:
self.__cur.execute("insert into Compiler values (?, ?)", ("armcc", 0))
def __collect_summary(self):
code_size = ro_data = rw_data = zi_data = flash_size = ram_size = extra_ram = extra_flash = 0
if self.gnuarm:
max_ram_addr = max_flash_addr = max_addr_ram_sym_size = max_addr_flash_sym_size = 0
flash_start_addr, x, ram_start_addr, y = self.__get_ram_and_flash_start_addr()
for s in self.__gnuarm_info:
if s[3] == "text":
code_size += s[1]
if s[3] == "rodata":
ro_data += s[1]
if s[3] == "data":
rw_data += s[1]
if s[3] == "bss":
zi_data += s[1]
if s[3] == "unknown_ram":
extra_ram += s[1]
if s[3] == "unknown_flash":
extra_flash += s[1]
for s in self.__sec_dict.keys():
if self.__sec_dict[s]['type'] == 'ram':
max_ram_addr = max(max_ram_addr, int(self.__sec_dict[s]['addr'], 16))
max_addr_ram_sym_size = self.__sec_dict[s]['size']
ram_size = max_ram_addr - ram_start_addr + max_addr_ram_sym_size
"""
Some special sections like 'psp_stack' or 'heap' are pre-allocated,
and filled with zero. They are belonging to bss/ZI part.
"""
if self.__sec_dict[s]['size'] == self.__sec_dict[s]['fill']:
zi_data += self.__sec_dict[s]['size']
if self.__sec_dict[s]['type'] == 'flash':
max_flash_addr = max(max_flash_addr, int(self.__sec_dict[s]['addr'], 16))
max_addr_flash_sym_size = self.__sec_dict[s]['size']
flash_size = max_flash_addr - flash_start_addr + max_addr_flash_sym_size
"""
For Secure image, the TFM_DATA part is loaded from Flash.
"""
for line in open(self.map_file, "r"):
if line.find("load address") >= 0:
extra_flash_addr = int(line.split()[-1] ,16)
extra_flash_data = int(line.split()[-4], 16)
if extra_flash_addr == max_flash_addr + max_addr_flash_sym_size:
flash_size += extra_flash_data
elif self.armcc:
for line in open(self.map_file, "r"):
if line.find("gram Size: Code=") > 0:
content = line.split()
code_size = int(content[2].split('=')[1])
ro_data = int(content[3].split('=')[1])
rw_data = int(content[4].split('=')[1])
zi_data = int(content[5].split('=')[1])
flash_size = code_size + ro_data + rw_data
ram_size = rw_data + zi_data
self.__cur.execute("insert into Summary values (?,?,?,?,?,?,?,?)",
(code_size,
ro_data,
rw_data,
zi_data,
extra_flash,
extra_ram,
flash_size,
ram_size))
def __collect_section(self):
if self.gnuarm:
for s in self.__gnuarm_info:
if s[3] == "text":
self.__cur.execute("insert into Function values (?,?,?,?,?,?)",
(s[0], s[6], s[1], s[2], s[4], s[5]))
elif s[3] == "rodata":
self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
(s[0], s[6], s[1], s[2], "RO", s[4], s[5]))
elif s[3] == "data":
self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
(s[0], s[6], s[1], s[2], "RW", s[4], s[5]))
elif s[3] == "bss":
self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
(s[0], s[6], s[1], s[2], "ZI", s[4], s[5]))
else:
self.__cur.execute("insert into Unknown values (?,?,?,?,?,?,?)",
(s[0], s[6], s[1], s[2], s[3], s[4], s[5]))
for s in self.__sec_dict.keys():
self.__cur.execute("insert into Section values (?,?,?,?)",
(self.__sec_dict[s]['name'],
self.__sec_dict[s]['size'],
self.__sec_dict[s]['addr'],
self.__sec_dict[s]['fill']))
elif self.armcc:
line_idx, line_start = 0, 0
section_name = ""
section_addr = ""
section_size = 0
section_pad_size = 0
for line in open(self.map_file, "r"):
line_idx += 1
if line.find("Execution Region") > 0:
if len(section_name) > 0:
self.__cur.execute("insert into Section values (?,?,?,?)",
(section_name,
section_size,
section_addr,
section_pad_size))
line_start = line_idx + 1
content = line.split()
if len(content) >= 10:
section_name = content[2]
section_addr = content[4][:-1]
section_size = int(content[6][:-1], 16)
section_pad_size = 0
if line.find("PAD\n") > 0 and line_idx > line_start and line_start > 0:
section_pad_size += int(line.split()[1], 16)
if line.find(" Code ") > 0:
content = line.split()
if len(content) >= 7:
if line.find(" * ") > 0:
content.remove("*")
func_name = content[5].strip().split('.')[-1]
if content[6].find('(') > 0:
object_file = content[6][content[6].find('(') + 1: -1]
lib_file = content[6][:content[6].find('(')]
else:
object_file = lib_file = content[6]
self.__cur.execute("insert into Function values (?,?,?,?,?,?)",
(func_name,
section_name,
int(content[1].strip(), 16),
content[0].strip(),
object_file,
lib_file))
if line.find(" Data ") > 0 or line.find(" Zero ") > 0:
content = line.split()
if len(content) == 7:
if content[2] == "Zero":
data_type = "ZI"
else:
data_type = content[3]
data_name = content[5].strip()
if content[6].find('(') > 0:
object_file = content[6][content[6].find('(') + 1: -1]
lib_file = content[6][:content[6].find('(')]
else:
object_file = lib_file = content[6]
self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
(data_name,
section_name,
int(content[1].strip(), 16),
content[0].strip(),
data_type,
object_file,
lib_file))
def __collect_library(self):
if self.gnuarm:
lib_detail_list = self.__collect_set(5)
for s in lib_detail_list:
self.__cur.execute("insert into Library values (?,?,?,?,?,?,?,?,?)",
(s['name'],
s['Code']+ s['RO'] + s['RW'],
s['RW'] + s['ZI'],
s['Code'],
s['RO'],
s['RW'],
s['ZI'],
0, 0))
elif self.armcc:
line_idx, line_start = 0, 0
for line in open(self.map_file, "r"):
line_idx += 1
if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Library Name") > 0:
line_start = line_idx + 1
if line_idx > line_start and line_start > 0:
content = line.split()
if len(content) == 7:
self.__cur.execute("insert into Library values (?,?,?,?,?,?,?,?,?)",
(content[6],
int(content[0]) + int(content[2]) + int(content[3]),
int(content[3]) + int(content[4]),
content[0],
content[2],
content[3],
content[4],
content[1],
content[5]))
else:
break
def __collect_obj(self):
if self.gnuarm:
obj_lib = ""
obj_detail_list = self.__collect_set(4)
for s in obj_detail_list:
for t in self.__gnuarm_info:
if t[4] == s['name']:
obj_lib = t[5]
break
self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
(s['name'],
obj_lib,
s['Code']+ s['RO'] + s['RW'],
s['RW'] + s['ZI'],
s['Code'],
s['RO'],
s['RW'],
s['ZI'],
0, 0))
elif self.armcc:
line_idx, line_start = 0, 0
for line in open(self.map_file, "r"):
line_idx += 1
if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Object Name") > 0:
line_start = line_idx + 1
if line_idx > line_start and line_start > 0:
content = line.split()
if len(content) == 7:
self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
(content[6],
"no library",
int(content[0]) + int(content[2]) + int(content[3]),
int(content[3]) + int(content[4]),
content[0],
content[2],
content[3],
content[4],
content[1],
content[5]))
else:
break
line_idx, line_start = 0, 0
for line in open(self.map_file, "r"):
line_idx += 1
if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name") > 0:
line_start = line_idx + 1
if line_idx > line_start and line_start > 0:
content = line.split()
if len(content) == 7:
obj_name = content[6]
library_file = ""
for line in open(self.map_file, "r"):
if line.find(obj_name) > 0:
ch_r = line[line.find(obj_name) + len(obj_name)]
ch_l = line[line.find(obj_name) - 1]
if ch_l == '(' and ch_r == ')':
library_file = line.split()[6][:line.split()[6].find('(')]
if len(library_file) == 0:
library_file = "no library"
self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
(content[6],
library_file,
int(content[0]) + int(content[2]) + int(content[3]),
int(content[3]) + int(content[4]),
content[0],
content[2],
content[3],
content[4],
content[1],
content[5]))
else:
break
def __get_ram_and_flash_start_addr(self):
start = False
for line in open(self.map_file, "r"):
if line.find('Memory Configuration') >= 0:
start = True
if line.find('Linker script and memory map') == 0:
break
if start:
if line.find('FLASH') == 0:
flash_start_addr = int(line.split()[1].strip(), 16)
flash_end_addr = int(line.split()[1].strip(), 16) + int(line.split()[2].strip(), 16)
if line.find('RAM') == 0:
ram_start_addr = int(line.split()[1].strip(), 16)
ram_end_addr = int(line.split()[1].strip(), 16) + int(line.split()[2].strip(), 16)
return flash_start_addr, flash_end_addr, ram_start_addr, ram_end_addr
def __get_info_from_gnuarm_map(self):
def get_key_content():
start, end, real_start = False, False, False
content = ""
for line in open(self.map_file, "r"):
if line.find('Linker script and memory map') >= 0:
start = True
if line.find('OUTPUT(') == 0:
end = True
if start and not real_start:
if(line[0] == '.'):
real_start = True
if real_start and not end:
content += line
return content.split('\n')[:-2]
def output_to_gnuarm_info():
type_founded = False
info = [cur_sym_name, cur_sym_size, cur_sym_addr, "type", obj, lib, cur_sec_name]
for type in ["text", "data", "bss", "rodata"]:
info[3] = type
if cur_sym_name.find('.' + type) == 0:
if cur_sym_name.find('.' + type + '.') == 0:
info[0] = cur_sym_name[len(type)+2:]
self.__gnuarm_info.append(info)
type_founded = True
break
self.__gnuarm_info.append(info)
type_founded = True
if not type_founded:
if int(cur_sym_addr,16) >= flash_start_addr and int(cur_sym_addr,16) <= flash_end_addr:
info[3] = "unknown_flash"
self.__gnuarm_info.append(info)
if int(cur_sym_addr,16) >= ram_start_addr and int(cur_sym_addr,16) <= ram_end_addr:
info[3] = "unknown_ram"
self.__gnuarm_info.append(info)
def get_obj_and_lib(line):
lib = line[line.rfind('/') + 1:line.rfind('.o') + 2]
obj = lib[lib.find("(") + 1:lib.rfind('.o')] + '.o'
if lib.find('(') > 0:
lib = lib[:lib.find("(")]
else:
lib = "no library"
return lib, obj
def get_part_list(idx):
ret = []
while len(lines[idx]) > 0 and lines[idx].split()[0].find('0x0000') >= 0:
if len(lines[idx].split()) == 2:
cur_addr = lines[idx].split()[0].strip()
cur_name = lines[idx].split()[1].strip()
if cur_sym_name.find(cur_name) >= 0:
break
else:
delta = -1
cur_idx = idx + 1
next_addr = ''
while len(lines[cur_idx]) >= 0:
if lines[cur_idx].find('0x0000') >= 0:
for s in lines[cur_idx].split():
if s.find('0x0000') >= 0:
next_addr = s.strip()
delta = int(next_addr, 16) - int(cur_addr, 16)
if delta >= 0:
break
cur_idx += 1
ret.append(['0x' + cur_addr[-8:], cur_sym_name+'.'+cur_name, delta])
else:
break
idx += 1
return ret
lines = get_key_content()
i_max = len(lines)
part_list = []
cur_sym_name, cur_sym_addr, cur_sym_size, lib, obj = "", "", "", "", ""
flash_start_addr , flash_end_addr , ram_start_addr , ram_end_addr = self.__get_ram_and_flash_start_addr()
for i in range(i_max):
lib, obj = "no_library", "no_object"
if len(lines[i]) > 0 and lines[i][0] == '.':
cur_sec_name = lines[i].split()[0][1:]
if len(lines[i].split()) > 1:
cur_sec_addr = lines[i].split()[1]
cur_sec_size = lines[i].split()[2]
else:
cur_sec_addr = lines[i+1].split()[0]
cur_sec_size = lines[i+1].split()[1]
if cur_sec_addr.find('0x00') < 0:
continue
if int(cur_sec_addr,16) >= flash_start_addr and int(cur_sec_addr,16) <= flash_end_addr:
self.__sec_dict[cur_sec_name] = {'name': cur_sec_name,
'addr': '0x' + cur_sec_addr[-8:],
'fill': 0,
'size': int(cur_sec_size, 16),
'type': 'flash'}
if int(cur_sec_addr,16) >= ram_start_addr and int(cur_sec_addr,16) <= ram_end_addr:
self.__sec_dict[cur_sec_name] = {'name': cur_sec_name,
'addr': '0x' + cur_sec_addr[-8:],
'fill': 0,
'size': int(cur_sec_size, 16),
'type': 'ram'}
if len(lines[i]) > 0 and lines[i][0] == ' ' and lines[i][1] != ' ':
cur_sym_name = lines[i].split()[0].strip()
if len(lines[i].split()) > 2:
cur_sym_addr = lines[i].split()[1].strip()
cur_sym_size = lines[i].split()[2].strip()
if cur_sym_addr.find('0x00') == 0 and cur_sym_size.find('0x') == 0:
cur_sym_addr = '0x' + cur_sym_addr[-8:]
cur_sym_size = int(cur_sym_size, 16)
if lines[i].find('/') > 0:
lib, obj = get_obj_and_lib(lines[i])
part_list = get_part_list(i+1)
else:
continue
elif len(lines[i+1]) > 0 and lines[i+1].split()[0].find('0x00') >= 0:
cur_sym_addr = lines[i+1].split()[0].strip()
cur_sym_size = lines[i+1].split()[1].strip()
if cur_sym_addr.find('0x00') == 0 and cur_sym_size.find('0x') == 0:
cur_sym_addr = '0x' + cur_sym_addr[-8:]
cur_sym_size = int(cur_sym_size, 16)
if lines[i+1].find('/') > 0:
lib, obj = get_obj_and_lib(lines[i+1])
part_list = get_part_list(i+2)
else:
continue
else:
continue
if cur_sym_name.find("*fill*") >= 0 and cur_sec_name in self.__sec_dict.keys():
self.__sec_dict[cur_sec_name]['fill'] += cur_sym_size
continue
if len(part_list) > 0:
for s in part_list:
cur_sym_addr = s[0]
cur_sym_name = s[1]
cur_sym_size = s[2]
output_to_gnuarm_info()
else:
output_to_gnuarm_info()
def __output_to_excel(self):
def transform_db_2_excel(table_name):
worksheet = workbook.add_worksheet(name=table_name)
cursor = self.__cur.execute('select * from {}'.format(table_name))
names = list(map(lambda x: x[0], cursor.description))
len_list = []
for s in names:
len_list.append(max(15, len(s)))
for i in range(len(names)):
worksheet.write(0, i, names[i], title_m)
mysel = self.__cur.execute('select * from {}'.format(table_name))
for i, row in enumerate(mysel):
for j, value in enumerate(row):
len_list[j] = max(len_list[j], len(str(value)))
worksheet.write(i + 1, j, value, page_m)
worksheet.set_column(j, j, width=len_list[j])
table_list = ["Summary", "Section", "Library", "Object", "Function", "Data"]
if self.gnuarm:
table_list.append("Unknown")
workbook = Workbook(self.__excel_name)
title_m = workbook.add_format({'bold': True,
'align': 'left',
'font_size': 12})
page_m = workbook.add_format({'align': 'left',
'font_size': 10})
for table in table_list:
transform_db_2_excel(table)
workbook.close()
def __collect_set(self, index):
name_list = []
detail_list = []
for s in self.__gnuarm_info:
if s[index] not in name_list:
name_list.append(s[index])
if s[3] == "text":
detail_list.append({'name': s[index], 'Code':s[1],'ZI':0, 'RO':0,'RW':0})
if s[3] == "rodata":
detail_list.append({'name': s[index], 'Code':0,'ZI':0, 'RO':s[1],'RW':0})
if s[3] == "data":
detail_list.append({'name': s[index], 'Code':0,'ZI':0, 'RO':0,'RW':s[1]})
if s[3] == "bss":
detail_list.append({'name': s[index], 'Code':0,'ZI':s[1], 'RO':0,'RW':0})
else:
for t in detail_list:
if t['name'] == s[index]:
if s[3] == "text":
t['Code'] += s[1]
if s[3] == "rodata":
t['RO'] += s[1]
if s[3] == "data":
t['RW'] += s[1]
if s[3] == "bss":
t['ZI'] += s[1]
return detail_list
def update(self):
"""
Create the database and collect information from map file, then store
it into the database, and write to excel file for further usage.
"""
self.__new()
if self.gnuarm:
self.__get_info_from_gnuarm_map()
self.__collect_compiler()
self.__collect_summary()
self.__collect_section()
self.__collect_library()
self.__collect_obj()
self.__con.commit()
self.__output_to_excel()
self.__con.close()