blob: 11f620a36cf4a3caddf01595df62457dfcf92224 [file] [log] [blame]
Jianliang Shen9b4ba112022-01-19 14:57:04 +08001# ------------------------------------------------------------------------------
2# Copyright (c) 2022, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6# ------------------------------------------------------------------------------
7
8import sqlite3
9import os
10import glob
11from xlsxwriter.workbook import Workbook
12
13class SQ(object):
14 """
15 Class SQ is used to create a new sqlite3 database and search the information
16 of functions, data, sections, libraries, object files from map file. Then
17 store the result into the database for further usage.
18
19 - Methods:
20 - SQ().update() - Create and update the database.
21
22 - Variables:
23 - SQ().armcc - ARMCLANG option.
24 - SQ().gnuarm - GNUARM option.
25 - SQ().file_path - The map file path which detail information comes from.
26 """
27 def __init__(self):
28 """
29 Initialize variables.
30 """
31 self.gnuarm = False
32 self.armcc = False
33 self.file_path = ""
34
35 self.__gnuarm_info = []
36 self.__sec_dict = {}
37 self.__delete()
38 self.__con = sqlite3.connect("data.db")
39 self.__cur = self.__con.cursor()
40
41 def __delete(self):
42 """
43 Search and delete the previous database file and excel file.
44 """
45 for infile in glob.glob(os.path.join(os.getcwd(), '*.db')):
46 os.remove(infile)
47 for infile in glob.glob(os.path.join(os.getcwd(), '*.xlsx')):
48 os.remove(infile)
49
50 def __new(self):
51 """
52 Create tables in a new empty database.
53 """
54 self.__cur.execute('''create table Summary
55 (Code INT NOT NULL,
56 RO_data INT NOT NULL,
57 RW_data INT NOT NULL,
58 ZI_data INT NOT NULL,
59 EXTRA_FLASH INT NOT NULL,
60 EXTRA_RAM INT NOT NULL,
61 Flash INT NOT NULL,
62 RAM INT NOT NULL);''')
63 self.__cur.execute('''create table Section
64 (name TEXT NOT NULL,
65 size INT NOT NULL,
66 address TEXT NOT NULL,
67 pad_size INT NOT NULL);''')
68 self.__cur.execute('''create table Function
69 (name TEXT NOT NULL,
70 section TEXT NOT NULL,
71 size INT NOT NULL,
72 base_addr TEXT NOT NULL,
73 obj_file TEXT NOT_NULL,
74 lib_file TEXT NOT_NULL);''')
75 self.__cur.execute('''create table Data
76 (name TEXT NOT NULL,
77 section TEXT NOT NULL,
78 size INT NOT NULL,
79 base_addr TEXT NOT NULL,
80 type TEXT NOT NULL,
81 obj_file TEXT NOT_NULL,
82 lib_file TEXT NOT_NULL);''')
83 self.__cur.execute('''create table Library
84 (name TEXT NOT NULL,
85 flashsize INT NOT NULL,
86 ramsize INT NOT NULL,
87 code INT NOT NULL,
88 rodata INT NOT NULL,
89 rwdata INT NOT NULL,
90 zidata INT NOT NULL,
91 incdata INT NOT_NULL,
92 Debug INT NOT NULL);''')
93 self.__cur.execute('''create table Object
94 (name TEXT NOT NULL,
95 library TEXT NOT NULL,
96 flashsize INT NOT NULL,
97 ramsize INT NOT NULL,
98 code INT NOT NULL,
99 rodata INT NOT NULL,
100 rwdata INT NOT NULL,
101 zidata INT NOT NULL,
102 incdata INT NOT_NULL,
103 Debug INT NOT NULL);''')
104 if self.gnuarm:
105 self.__cur.execute('''create table Unknown
106 (name TEXT NOT NULL,
107 section TEXT NOT NULL,
108 size INT NOT NULL,
109 base_addr TEXT NOT NULL,
110 type TEXT NOT NULL,
111 obj_file TEXT NOT_NULL,
112 lib_file TEXT NOT_NULL);''')
113
114 def __collect_summary(self):
115 code_size = ro_data = rw_data = zi_data = flash_size = ram_size = extra_ram = extra_flash = 0
116 if self.gnuarm:
117 max_ram_addr = max_flash_addr = max_addr_ram_sym_size = max_addr_flash_sym_size = 0
118 flash_start_addr, x, ram_start_addr, y = self.__get_ram_and_flash_start_addr()
119 for s in self.__gnuarm_info:
120 if s[3] == "text":
121 code_size += s[1]
122 if s[3] == "rodata":
123 ro_data += s[1]
124 if s[3] == "data":
125 rw_data += s[1]
126 if s[3] == "bss":
127 zi_data += s[1]
128 if s[3] == "unknown_ram":
129 extra_ram += s[1]
130 if s[3] == "unknown_flash":
131 extra_flash += s[1]
132
133 for s in self.__sec_dict.keys():
134 if self.__sec_dict[s]['type'] == 'ram':
135 max_ram_addr = max(max_ram_addr, int(self.__sec_dict[s]['addr'], 16))
136 max_addr_ram_sym_size = self.__sec_dict[s]['size']
137 ram_size = max_ram_addr - ram_start_addr + max_addr_ram_sym_size
138
139 """
140 Some special sections like 'psp_stack' or 'heap' are pre-allocated,
141 and filled with zero. They are belonging to bss/ZI part.
142 """
143 if self.__sec_dict[s]['size'] == self.__sec_dict[s]['fill']:
144 zi_data += self.__sec_dict[s]['size']
145 if self.__sec_dict[s]['type'] == 'flash':
146 max_flash_addr = max(max_flash_addr, int(self.__sec_dict[s]['addr'], 16))
147 max_addr_flash_sym_size = self.__sec_dict[s]['size']
148 flash_size = max_flash_addr - flash_start_addr + max_addr_flash_sym_size
149
150 """
151 For Secure image, the TFM_DATA part is loaded from Flash.
152 """
153 for line in open(self.file_path, "r"):
154 if line.find("load address") >= 0:
155 extra_flash_addr = int(line.split()[-1] ,16)
156 extra_flash_data = int(line.split()[-4], 16)
157 if extra_flash_addr == max_flash_addr + max_addr_flash_sym_size:
158 flash_size += extra_flash_data
159
160 elif self.armcc:
161 for line in open(self.file_path, "r"):
162 if line.find("gram Size: Code=") > 0:
163 content = line.split()
164 code_size = int(content[2].split('=')[1])
165 ro_data = int(content[3].split('=')[1])
166 rw_data = int(content[4].split('=')[1])
167 zi_data = int(content[5].split('=')[1])
168 flash_size = code_size + ro_data + rw_data
169 ram_size = rw_data + zi_data
170
171 self.__cur.execute("insert into Summary values (?,?,?,?,?,?,?,?)",
172 (code_size,
173 ro_data,
174 rw_data,
175 zi_data,
176 extra_flash,
177 extra_ram,
178 flash_size,
179 ram_size))
180
181 def __collect_section(self):
182 if self.gnuarm:
183 for s in self.__gnuarm_info:
184 if s[3] == "text":
185 self.__cur.execute("insert into Function values (?,?,?,?,?,?)",
186 (s[0], s[6], s[1], s[2], s[4], s[5]))
187 elif s[3] == "rodata":
188 self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
189 (s[0], s[6], s[1], s[2], "RO", s[4], s[5]))
190 elif s[3] == "data":
191 self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
192 (s[0], s[6], s[1], s[2], "RW", s[4], s[5]))
193 elif s[3] == "bss":
194 self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
195 (s[0], s[6], s[1], s[2], "ZI", s[4], s[5]))
196 else:
197 self.__cur.execute("insert into Unknown values (?,?,?,?,?,?,?)",
198 (s[0], s[6], s[1], s[2], s[3], s[4], s[5]))
199 for s in self.__sec_dict.keys():
200 self.__cur.execute("insert into Section values (?,?,?,?)",
201 (self.__sec_dict[s]['name'],
202 self.__sec_dict[s]['size'],
203 self.__sec_dict[s]['addr'],
204 self.__sec_dict[s]['fill']))
205 elif self.armcc:
206 line_idx, line_start = 0, 0
207 section_name = ""
208 section_addr = ""
209 section_size = 0
210 section_pad_size = 0
211 for line in open(self.file_path, "r"):
212 line_idx += 1
213
214 if line.find("Execution Region") > 0:
215 if len(section_name) > 0:
216 self.__cur.execute("insert into Section values (?,?,?,?)",
217 (section_name,
218 section_size,
219 section_addr,
220 section_pad_size))
221 line_start = line_idx + 1
222
223 content = line.split()
224 if len(content) >= 10:
225 section_name = content[2]
226 section_addr = content[4][:-1]
227 section_size = int(content[6][:-1], 16)
228 section_pad_size = 0
229 if line.find("PAD\n") > 0 and line_idx > line_start and line_start > 0:
230 section_pad_size += int(line.split()[1], 16)
231 if line.find(" Code ") > 0:
232 content = line.split()
233 if len(content) >= 7:
234 if line.find(" * ") > 0:
235 content.remove("*")
236 func_name = content[5].strip().split('.')[-1]
237 if content[6].find('(') > 0:
238 object_file = content[6][content[6].find('(') + 1: -1]
239 lib_file = content[6][:content[6].find('(')]
240 else:
241 object_file = lib_file = content[6]
242 self.__cur.execute("insert into Function values (?,?,?,?,?,?)",
243 (func_name,
244 section_name,
245 int(content[1].strip(), 16),
246 content[0].strip(),
247 object_file,
248 lib_file))
249 if line.find(" Data ") > 0 or line.find(" Zero ") > 0:
250 content = line.split()
251 if len(content) == 7:
252 if content[2] == "Zero":
253 data_type = "ZI"
254 else:
255 data_type = content[3]
256 data_name = content[5].strip()
257 if content[6].find('(') > 0:
258 object_file = content[6][content[6].find('(') + 1: -1]
259 lib_file = content[6][:content[6].find('(')]
260 else:
261 object_file = lib_file = content[6]
262 self.__cur.execute("insert into Data values (?,?,?,?,?,?,?)",
263 (data_name,
264 section_name,
265 int(content[1].strip(), 16),
266 content[0].strip(),
267 data_type,
268 object_file,
269 lib_file))
270
271 def __collect_library(self):
272 if self.gnuarm:
273 lib_detail_list = self.__collect_set(5)
274 for s in lib_detail_list:
275 self.__cur.execute("insert into Library values (?,?,?,?,?,?,?,?,?)",
276 (s['name'],
277 s['Code']+ s['RO'] + s['RW'],
278 s['RW'] + s['ZI'],
279 s['Code'],
280 s['RO'],
281 s['RW'],
282 s['ZI'],
283 0, 0))
284
285 elif self.armcc:
286 line_idx, line_start = 0, 0
287 for line in open(self.file_path, "r"):
288 line_idx += 1
289 if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Library Name") > 0:
290 line_start = line_idx + 1
291 if line_idx > line_start and line_start > 0:
292 content = line.split()
293 if len(content) == 7:
294 self.__cur.execute("insert into Library values (?,?,?,?,?,?,?,?,?)",
295 (content[6],
296 int(content[0]) + int(content[2]) + int(content[3]),
297 int(content[3]) + int(content[4]),
298 content[0],
299 content[2],
300 content[3],
301 content[4],
302 content[1],
303 content[5]))
304 else:
305 break
306
307 def __collect_obj(self):
308 if self.gnuarm:
309 obj_lib = ""
310 obj_detail_list = self.__collect_set(4)
311 for s in obj_detail_list:
312 for t in self.__gnuarm_info:
313 if t[4] == s['name']:
314 obj_lib = t[5]
315 break
316 self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
317 (s['name'],
318 obj_lib,
319 s['Code']+ s['RO'] + s['RW'],
320 s['RW'] + s['ZI'],
321 s['Code'],
322 s['RO'],
323 s['RW'],
324 s['ZI'],
325 0, 0))
326 elif self.armcc:
327 line_idx, line_start = 0, 0
328 for line in open(self.file_path, "r"):
329 line_idx += 1
330 if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Object Name") > 0:
331 line_start = line_idx + 1
332 if line_idx > line_start and line_start > 0:
333 content = line.split()
334 if len(content) == 7:
335 self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
336 (content[6],
337 "no library",
338 int(content[0]) + int(content[2]) + int(content[3]),
339 int(content[3]) + int(content[4]),
340 content[0],
341 content[2],
342 content[3],
343 content[4],
344 content[1],
345 content[5]))
346 else:
347 break
348 line_idx, line_start = 0, 0
349 for line in open(self.file_path, "r"):
350 line_idx += 1
351 if line.find("Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name") > 0:
352 line_start = line_idx + 1
353 if line_idx > line_start and line_start > 0:
354 content = line.split()
355 if len(content) == 7:
356 obj_name = content[6]
357 library_file = ""
358 for line in open(self.file_path, "r"):
359 if line.find(obj_name) > 0:
360 ch_r = line[line.find(obj_name) + len(obj_name)]
361 ch_l = line[line.find(obj_name) - 1]
362 if ch_l == '(' and ch_r == ')':
363 library_file = line.split()[6][:line.split()[6].find('(')]
364 if len(library_file) == 0:
365 library_file = "no library"
366 self.__cur.execute("insert into Object values (?,?,?,?,?,?,?,?,?,?)",
367 (content[6],
368 library_file,
369 int(content[0]) + int(content[2]) + int(content[3]),
370 int(content[3]) + int(content[4]),
371 content[0],
372 content[2],
373 content[3],
374 content[4],
375 content[1],
376 content[5]))
377 else:
378 break
379
380 def __get_ram_and_flash_start_addr(self):
381 start = False
382 for line in open(self.file_path, "r"):
383 if line.find('Memory Configuration') >= 0:
384 start = True
385 if line.find('Linker script and memory map') == 0:
386 break
387 if start:
388 if line.find('FLASH') >= 0:
389 flash_start_addr = int(line.split()[1].strip(), 16)
390 flash_end_addr = int(line.split()[1].strip(), 16) + int(line.split()[2].strip(), 16)
391 if line.find('RAM') >= 0:
392 ram_start_addr = int(line.split()[1].strip(), 16)
393 ram_end_addr = int(line.split()[1].strip(), 16) + int(line.split()[2].strip(), 16)
394 return flash_start_addr, flash_end_addr, ram_start_addr, ram_end_addr
395
396 def __get_info_from_gnuarm_map(self):
397 def get_key_content():
398 start, end, real_start = False, False, False
399 content = ""
400 for line in open(self.file_path, "r"):
401 if line.find('Linker script and memory map') >= 0:
402 start = True
403 if line.find('OUTPUT(') == 0:
404 end = True
405 if start and not real_start:
406 if(line[0] == '.'):
407 real_start = True
408 if real_start and not end:
409 content += line
410 return content.split('\n')[:-2]
411
412 def output_to_gnuarm_info():
413 type_founded = False
414 info = [cur_sym_name, cur_sym_size, cur_sym_addr, "type", obj, lib, cur_sec_name]
415 for type in ["text", "data", "bss", "rodata"]:
416 info[3] = type
417 if cur_sym_name.find('.' + type) == 0:
418 if cur_sym_name.find('.' + type + '.') == 0:
419 info[0] = cur_sym_name[len(type)+2:]
420 self.__gnuarm_info.append(info)
421 type_founded = True
422 break
423 self.__gnuarm_info.append(info)
424 type_founded = True
425 if not type_founded:
426 if int(cur_sym_addr,16) >= flash_start_addr and int(cur_sym_addr,16) <= flash_end_addr:
427 info[3] = "unknown_flash"
428 self.__gnuarm_info.append(info)
429 if int(cur_sym_addr,16) >= ram_start_addr and int(cur_sym_addr,16) <= ram_end_addr:
430 info[3] = "unknown_ram"
431 self.__gnuarm_info.append(info)
432
433 def get_obj_and_lib(line):
434 lib = line[line.rfind('/') + 1:line.rfind('.o') + 2]
435 obj = lib[lib.find("(") + 1:lib.rfind('.o')] + '.o'
436 if lib.find('(') > 0:
437 lib = lib[:lib.find("(")]
438 else:
439 lib = "no library"
440 return lib, obj
441
442 def get_part_list(idx):
443 ret = []
444 while len(lines[idx]) > 0 and lines[idx].split()[0].find('0x0000') >= 0:
445 if len(lines[idx].split()) == 2:
446 cur_addr = lines[idx].split()[0].strip()
447 cur_name = lines[idx].split()[1].strip()
448 if cur_sym_name.find(cur_name) >= 0:
449 break
450 else:
451 delta = -1
452 cur_idx = idx + 1
453 next_addr = ''
454 while len(lines[cur_idx]) >= 0:
455 if lines[cur_idx].find('0x0000') >= 0:
456 for s in lines[cur_idx].split():
457 if s.find('0x0000') >= 0:
458 next_addr = s.strip()
459 delta = int(next_addr, 16) - int(cur_addr, 16)
460 if delta >= 0:
461 break
462 cur_idx += 1
463 ret.append(['0x' + cur_addr[-8:], cur_sym_name+'.'+cur_name, delta])
464 else:
465 break
466 idx += 1
467 return ret
468
469 lines = get_key_content()
470 i_max = len(lines)
471 part_list = []
472 cur_sym_name, cur_sym_addr, cur_sym_size, lib, obj = "", "", "", "", ""
473 flash_start_addr , flash_end_addr , ram_start_addr , ram_end_addr = self.__get_ram_and_flash_start_addr()
474 for i in range(i_max):
475 lib, obj = "no_library", "no_object"
476 if len(lines[i]) > 0 and lines[i][0] == '.':
477 cur_sec_name = lines[i].split()[0][1:]
478 if len(lines[i].split()) > 1:
479 cur_sec_addr = lines[i].split()[1]
480 cur_sec_size = lines[i].split()[2]
481 else:
482 cur_sec_addr = lines[i+1].split()[0]
483 cur_sec_size = lines[i+1].split()[1]
484 if cur_sec_addr.find('0x00') < 0:
485 continue
486 if int(cur_sec_addr,16) >= flash_start_addr and int(cur_sec_addr,16) <= flash_end_addr:
487 self.__sec_dict[cur_sec_name] = {'name': cur_sec_name,
488 'addr': '0x' + cur_sec_addr[-8:],
489 'fill': 0,
490 'size': int(cur_sec_size, 16),
491 'type': 'flash'}
492 if int(cur_sec_addr,16) >= ram_start_addr and int(cur_sec_addr,16) <= ram_end_addr:
493 self.__sec_dict[cur_sec_name] = {'name': cur_sec_name,
494 'addr': '0x' + cur_sec_addr[-8:],
495 'fill': 0,
496 'size': int(cur_sec_size, 16),
497 'type': 'ram'}
498
499 if len(lines[i]) > 0 and lines[i][0] == ' ' and lines[i][1] != ' ':
500 cur_sym_name = lines[i].split()[0].strip()
501 if len(lines[i].split()) > 2:
502 cur_sym_addr = lines[i].split()[1].strip()
503 cur_sym_size = lines[i].split()[2].strip()
504 if cur_sym_addr.find('0x00') == 0 and cur_sym_size.find('0x') == 0:
505 cur_sym_addr = '0x' + cur_sym_addr[-8:]
506 cur_sym_size = int(cur_sym_size, 16)
507 if lines[i].find('/') > 0:
508 lib, obj = get_obj_and_lib(lines[i])
509 part_list = get_part_list(i+1)
510 else:
511 continue
512
513 elif len(lines[i+1]) > 0 and lines[i+1].split()[0].find('0x00') >= 0:
514 cur_sym_addr = lines[i+1].split()[0].strip()
515 cur_sym_size = lines[i+1].split()[1].strip()
516 if cur_sym_addr.find('0x00') == 0 and cur_sym_size.find('0x') == 0:
517 cur_sym_addr = '0x' + cur_sym_addr[-8:]
518 cur_sym_size = int(cur_sym_size, 16)
519 if lines[i+1].find('/') > 0:
520 lib, obj = get_obj_and_lib(lines[i+1])
521 part_list = get_part_list(i+2)
522 else:
523 continue
524 else:
525 continue
526
527 if cur_sym_name.find("*fill*") >= 0:
528 self.__sec_dict[cur_sec_name]['fill'] += cur_sym_size
529 continue
530
531 if len(part_list) > 0:
532 for s in part_list:
533 cur_sym_addr = s[0]
534 cur_sym_name = s[1]
535 cur_sym_size = s[2]
536 output_to_gnuarm_info()
537 else:
538 output_to_gnuarm_info()
539
540 def __output_to_excel(self):
541 def transform_db_2_excel(table_name):
542 worksheet = workbook.add_worksheet(name=table_name)
543 cursor = self.__cur.execute('select * from {}'.format(table_name))
544 names = list(map(lambda x: x[0], cursor.description))
545 len_list = []
546 for s in names:
547 len_list.append(max(15, len(s)))
548 for i in range(len(names)):
549 worksheet.write(0, i, names[i], title_m)
550
551 mysel = self.__cur.execute('select * from {}'.format(table_name))
552 for i, row in enumerate(mysel):
553 for j, value in enumerate(row):
554 len_list[j] = max(len_list[j], len(str(value)))
555 worksheet.write(i + 1, j, value, page_m)
556 worksheet.set_column(j, j, width=len_list[j])
557
558 table_list = ["Summary", "Section", "Library", "Object", "Function", "Data"]
559 if self.gnuarm:
560 table_list.append("Unknown")
561 workbook = Workbook('data.xlsx')
562 title_m = workbook.add_format({'bold': True,
563 'align': 'left',
564 'font_size': 12})
565 page_m = workbook.add_format({'align': 'left',
566 'font_size': 10})
567 for table in table_list:
568 transform_db_2_excel(table)
569 workbook.close()
570
571 def __collect_set(self, index):
572 name_list = []
573 detail_list = []
574 for s in self.__gnuarm_info:
575 if s[index] not in name_list:
576 name_list.append(s[index])
577 if s[3] == "text":
578 detail_list.append({'name': s[index], 'Code':s[1],'ZI':0, 'RO':0,'RW':0})
579 if s[3] == "rodata":
580 detail_list.append({'name': s[index], 'Code':0,'ZI':0, 'RO':s[1],'RW':0})
581 if s[3] == "data":
582 detail_list.append({'name': s[index], 'Code':0,'ZI':0, 'RO':0,'RW':s[1]})
583 if s[3] == "bss":
584 detail_list.append({'name': s[index], 'Code':0,'ZI':s[1], 'RO':0,'RW':0})
585 else:
586 for t in detail_list:
587 if t['name'] == s[index]:
588 if s[3] == "text":
589 t['Code'] += s[1]
590 if s[3] == "rodata":
591 t['RO'] += s[1]
592 if s[3] == "data":
593 t['RW'] += s[1]
594 if s[3] == "bss":
595 t['ZI'] += s[1]
596 return detail_list
597
598 def update(self):
599 """
600 Create the database and collect information from map file, then store
601 it into the database, and write to excel file for further usage.
602 """
603 self.__new()
604 if self.gnuarm:
605 self.__get_info_from_gnuarm_map()
606 self.__collect_summary()
607 self.__collect_section()
608 self.__collect_library()
609 self.__collect_obj()
610 self.__con.commit()
611 self.__output_to_excel()
612 self.__con.close()