Skip to content

Commit 12a01f6

Browse files
committed
Add summary for test building
When users are building tests with `mbed compile --tests` whey will by default get memory map breakdown report. This can be suppresed in the future with command line switch. For now it is visible each time users build test cases. List is sorted by project name created with `build_project` API. Changes: * `build_project` now returns tuple (this breaks build_api.build_project API!) * Memmap script got a aggregation function to print summary from small 'JSON' partial reports. * Report is generated by `test_api.build_tests` function only! Example: ``` +----------------------------------------------------------------------+--------+-----------+-------------+------------+-------+-------+-----------+ | name | target | toolchain | total_flash | static_ram | stack | heap | total_ram | +----------------------------------------------------------------------+--------+-----------+-------------+------------+-------+-------+-----------+ | features-feature_ipv4-tests-mbedmicro-net-nist_internet_time_service | K64F | GCC_ARM | 132136 | 62288 | 32768 | 65536 | 160592 | | features-feature_ipv4-tests-mbedmicro-net-tcp_client_echo | K64F | GCC_ARM | 125613 | 62448 | 32768 | 65540 | 160756 | | features-feature_ipv4-tests-mbedmicro-net-tcp_client_hello_world | K64F | GCC_ARM | 125949 | 62448 | 32768 | 65540 | 160756 | | features-feature_ipv4-tests-mbedmicro-net-udp_echo_client | K64F | GCC_ARM | 123613 | 62276 | 32768 | 65536 | 160580 | | features-storage-tests-cfstore-add_del | K64F | GCC_ARM | 96080 | 13052 | 32768 | 65540 | 111360 | | features-storage-tests-cfstore-close | K64F | GCC_ARM | 95520 | 12004 | 32768 | 65540 | 110312 | | features-storage-tests-cfstore-create | K64F | GCC_ARM | 99144 | 13036 | 32768 | 65540 | 111344 | | features-storage-tests-cfstore-example1 | K64F | GCC_ARM | 98592 | 12368 | 32768 | 65536 | 110672 | | features-storage-tests-cfstore-example2 | K64F | GCC_ARM | 95232 | 12012 | 32768 | 65540 | 110320 | | features-storage-tests-cfstore-example3 | K64F | GCC_ARM | 95264 | 11856 | 32768 | 65536 | 110160 | | features-storage-tests-cfstore-example4 | K64F | GCC_ARM | 92632 | 12012 | 32768 | 65540 | 110320 | | features-storage-tests-cfstore-example5 | K64F | GCC_ARM | 92344 | 11856 | 32768 | 65536 | 110160 | | features-storage-tests-cfstore-find | K64F | GCC_ARM | 96344 | 13028 | 32768 | 65540 | 111336 | | features-storage-tests-cfstore-find2 | K64F | GCC_ARM | 93192 | 12004 | 32768 | 65540 | 110312 | | features-storage-tests-cfstore-flash | K64F | GCC_ARM | 97784 | 12532 | 32768 | 65540 | 110840 | | features-storage-tests-cfstore-flush | K64F | GCC_ARM | 96464 | 12012 | 32768 | 65540 | 110320 | | features-storage-tests-cfstore-flush2 | K64F | GCC_ARM | 95056 | 12004 | 32768 | 65540 | 110312 | | features-storage-tests-cfstore-init | K64F | GCC_ARM | 93120 | 12012 | 32768 | 65540 | 110320 | | features-storage-tests-cfstore-misc | K64F | GCC_ARM | 96808 | 12516 | 32768 | 65540 | 110824 | | features-storage-tests-cfstore-open | K64F | GCC_ARM | 98632 | 12540 | 32768 | 65540 | 110848 | | features-storage-tests-cfstore-read | K64F | GCC_ARM | 94112 | 12540 | 32768 | 65540 | 110848 | | features-storage-tests-cfstore-write | K64F | GCC_ARM | 94488 | 12004 | 32768 | 65540 | 110312 | | features-storage-tests-flash_journal-basicapi | K64F | GCC_ARM | 104712 | 21236 | 32768 | 65540 | 119544 | | frameworks-utest-tests-unit_tests-basic_test | K64F | GCC_ARM | 71534 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-case_async_validate | K64F | GCC_ARM | 74598 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-case_control_async | K64F | GCC_ARM | 74630 | 11476 | 32768 | 65540 | 109784 | | frameworks-utest-tests-unit_tests-case_control_repeat | K64F | GCC_ARM | 72790 | 11452 | 32768 | 65540 | 109760 | | frameworks-utest-tests-unit_tests-case_selection | K64F | GCC_ARM | 72302 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-case_setup_failure | K64F | GCC_ARM | 72630 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-case_teardown_failure | K64F | GCC_ARM | 72790 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-control_type | K64F | GCC_ARM | 82462 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-minimal_async_scheduler | K64F | GCC_ARM | 72182 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-minimal_scheduler | K64F | GCC_ARM | 71998 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-test_assertion_failure_test_setup | K64F | GCC_ARM | 71710 | 11460 | 32768 | 65540 | 109768 | | frameworks-utest-tests-unit_tests-test_setup_case_selection_failure | K64F | GCC_ARM | 71702 | 11468 | 32768 | 65540 | 109776 | | frameworks-utest-tests-unit_tests-test_setup_failure | K64F | GCC_ARM | 71710 | 11468 | 32768 | 65540 | 109776 | | tests-integration-basic | K64F | GCC_ARM | 67566 | 10780 | 32768 | 65540 | 109088 | | tests-integration-threaded_blinky | K64F | GCC_ARM | 68326 | 10780 | 32768 | 65540 | 109088 | | tests-mbed_drivers-c_strings | K64F | GCC_ARM | 74438 | 11468 | 32768 | 65540 | 109776 | | tests-mbed_drivers-callback | K64F | GCC_ARM | 88310 | 11972 | 32768 | 65540 | 110280 | | tests-mbed_drivers-dev_null | K64F | GCC_ARM | 90213 | 10784 | 32768 | 65540 | 109092 | | tests-mbed_drivers-echo | K64F | GCC_ARM | 71918 | 11468 | 32768 | 65540 | 109776 | | tests-mbed_drivers-generic_tests | K64F | GCC_ARM | 77624 | 11468 | 32768 | 65540 | 109776 | | tests-mbed_drivers-rtc | K64F | GCC_ARM | 85854 | 11308 | 32768 | 65540 | 109616 | | tests-mbed_drivers-stl_features | K64F | GCC_ARM | 80726 | 11476 | 32768 | 65540 | 109784 | | tests-mbed_drivers-ticker | K64F | GCC_ARM | 70974 | 11308 | 32768 | 65540 | 109616 | | tests-mbed_drivers-ticker_2 | K64F | GCC_ARM | 70790 | 11308 | 32768 | 65540 | 109616 | | tests-mbed_drivers-ticker_3 | K64F | GCC_ARM | 71038 | 11308 | 32768 | 65540 | 109616 | | tests-mbed_drivers-timeout | K64F | GCC_ARM | 70886 | 11308 | 32768 | 65540 | 109616 | | tests-mbed_drivers-wait_us | K64F | GCC_ARM | 70414 | 11308 | 32768 | 65540 | 109616 | | tests-mbedmicro-mbed-attributes | K64F | GCC_ARM | 71534 | 11460 | 32768 | 65540 | 109768 | | tests-mbedmicro-mbed-call_before_main | K64F | GCC_ARM | 73112 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-mbed-cpp | K64F | GCC_ARM | 73400 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-mbed-div | K64F | GCC_ARM | 73176 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-basic | K64F | GCC_ARM | 68390 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-isr | K64F | GCC_ARM | 74480 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-mail | K64F | GCC_ARM | 74992 | 11300 | 32768 | 65540 | 109608 | | tests-mbedmicro-rtos-mbed-mutex | K64F | GCC_ARM | 74048 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-queue | K64F | GCC_ARM | 74912 | 11300 | 32768 | 65540 | 109608 | | tests-mbedmicro-rtos-mbed-semaphore | K64F | GCC_ARM | 74296 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-signals | K64F | GCC_ARM | 74328 | 10780 | 32768 | 65540 | 109088 | | tests-mbedmicro-rtos-mbed-threads | K64F | GCC_ARM | 75214 | 11460 | 32768 | 65540 | 109768 | | tests-mbedmicro-rtos-mbed-timer | K64F | GCC_ARM | 68430 | 10780 | 32768 | 65540 | 109088 | | tests-storage_abstraction-basicapi | K64F | GCC_ARM | 107808 | 28908 | 32768 | 65540 | 127216 | +----------------------------------------------------------------------+--------+-----------+-------------+------------+-------+-------+-----------+ ``` Refactored after code review Refactored parse() function Polishing Moved memory usage reporting function call to test.py to group all reporters in one place Bug-fix: on ARM toolchain we were not fetching statistics from last element of memap result list
1 parent 458b46c commit 12a01f6

File tree

5 files changed

+186
-109
lines changed

5 files changed

+186
-109
lines changed

tools/build_api.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def build_project(src_path, build_path, target, toolchain_name,
176176

177177
if report != None:
178178
start = time()
179-
179+
180180
# If project_id is specified, use that over the default name
181181
id_name = project_id.upper() if project_id else name.upper()
182182
description = project_description if project_description else name
@@ -232,6 +232,7 @@ def build_project(src_path, build_path, target, toolchain_name,
232232
cur_result["elapsed_time"] = end - start
233233
cur_result["output"] = toolchain.get_output()
234234
cur_result["result"] = "OK"
235+
cur_result["memory_usage"] = toolchain.map_outputs
235236

236237
add_result_to_report(report, cur_result)
237238

@@ -294,7 +295,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
294295

295296
if report != None:
296297
start = time()
297-
298+
298299
# If project_id is specified, use that over the default name
299300
id_name = project_id.upper() if project_id else name.upper()
300301
description = name
@@ -377,7 +378,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
377378
toolchain.copy_files(resources.libraries, build_path, resources=resources)
378379
if resources.linker_script:
379380
toolchain.copy_files(resources.linker_script, build_path, resources=resources)
380-
381+
381382
if resource.hex_files:
382383
toolchain.copy_files(resources.hex_files, build_path, resources=resources)
383384

@@ -399,12 +400,12 @@ def build_library(src_paths, build_path, target, toolchain_name,
399400
except Exception, e:
400401
if report != None:
401402
end = time()
402-
403+
403404
if isinstance(e, ToolException):
404405
cur_result["result"] = "FAIL"
405406
elif isinstance(e, NotSupportedException):
406407
cur_result["result"] = "NOT_SUPPORTED"
407-
408+
408409
cur_result["elapsed_time"] = end - start
409410

410411
toolchain_output = toolchain.get_output()
@@ -428,7 +429,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
428429
if not lib.is_supported(target, toolchain_name):
429430
print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
430431
return False
431-
432+
432433
# We need to combine macros from parameter list with macros from library definition
433434
MACROS = lib.macros if lib.macros else []
434435
if macros:
@@ -441,7 +442,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
441442
dependencies_paths = lib.dependencies
442443
inc_dirs = lib.inc_dirs
443444
inc_dirs_ext = lib.inc_dirs_ext
444-
445+
445446
""" src_path: the path of the source directory
446447
build_path: the path of the build directory
447448
target: ['LPC1768', 'LPC11U24', 'LPC2368']
@@ -522,7 +523,7 @@ def build_lib(lib_id, target, toolchain_name, options=None, verbose=False, clean
522523
# Copy Headers
523524
for resource in resources:
524525
toolchain.copy_files(resource.headers, build_path, resources=resource)
525-
526+
526527
dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
527528

528529
# Compile Sources
@@ -716,7 +717,7 @@ def mcu_toolchain_matrix(verbose_html=False, platform_filter=None):
716717
perm_counter += 1
717718
else:
718719
text = "-"
719-
720+
720721
row.append(text)
721722
pt.add_row(row)
722723

@@ -942,6 +943,49 @@ def print_build_results(result_list, build_name):
942943
result += "\n"
943944
return result
944945

946+
def print_build_memory_usage_results(report):
947+
""" Generate result table with memory usage values for build results
948+
Agregates (puts together) reports obtained from self.get_memory_summary()
949+
@param report Report generated during build procedure. See
950+
"""
951+
from prettytable import PrettyTable
952+
columns_text = ['name', 'target', 'toolchain']
953+
columns_int = ['static_ram', 'stack', 'heap', 'total_ram', 'total_flash']
954+
table = PrettyTable(columns_text + columns_int)
955+
956+
for col in columns_text:
957+
table.align[col] = 'l'
958+
959+
for col in columns_int:
960+
table.align[col] = 'r'
961+
962+
for target in report:
963+
for toolchain in report[target]:
964+
for name in report[target][toolchain]:
965+
for dlist in report[target][toolchain][name]:
966+
for dlistelem in dlist:
967+
# Get 'memory_usage' record and build table with statistics
968+
record = dlist[dlistelem]
969+
if 'memory_usage' in record and record['memory_usage']:
970+
# Note that summary should be in the last record of
971+
# 'memory_usage' section. This is why we are grabbing
972+
# last "[-1]" record.
973+
row = [
974+
record['description'],
975+
record['target_name'],
976+
record['toolchain_name'],
977+
record['memory_usage'][-1]['summary']['static_ram'],
978+
record['memory_usage'][-1]['summary']['stack'],
979+
record['memory_usage'][-1]['summary']['heap'],
980+
record['memory_usage'][-1]['summary']['total_ram'],
981+
record['memory_usage'][-1]['summary']['total_flash'],
982+
]
983+
table.add_row(row)
984+
985+
result = "Memory map breakdown for built projects (values in Bytes):\n"
986+
result += table.get_string(sortby='name')
987+
return result
988+
945989
def write_build_report(build_report, template_filename, filename):
946990
build_report_failing = []
947991
build_report_passing = []
@@ -963,14 +1007,14 @@ def write_build_report(build_report, template_filename, filename):
9631007
def scan_for_source_paths(path, exclude_paths=None):
9641008
ignorepatterns = []
9651009
paths = []
966-
1010+
9671011
def is_ignored(file_path):
9681012
for pattern in ignorepatterns:
9691013
if fnmatch.fnmatch(file_path, pattern):
9701014
return True
9711015
return False
972-
973-
1016+
1017+
9741018
""" os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
9751019
When topdown is True, the caller can modify the dirnames list in-place
9761020
(perhaps using del or slice assignment), and walk() will only recurse into

tools/memap.py

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def __init__(self):
2626

2727
self.misc_flash_sections = ('.interrupts', '.flash_config')
2828

29-
self.other_sections = ('.interrupts_ram', '.init', '.ARM.extab', \
30-
'.ARM.exidx', '.ARM.attributes', '.eh_frame', \
31-
'.init_array', '.fini_array', '.jcr', '.stab', \
29+
self.other_sections = ('.interrupts_ram', '.init', '.ARM.extab',
30+
'.ARM.exidx', '.ARM.attributes', '.eh_frame',
31+
'.init_array', '.fini_array', '.jcr', '.stab',
3232
'.stabstr', '.ARM.exidx', '.ARM')
3333

3434
# sections to print info (generic for all toolchains)
@@ -43,6 +43,9 @@ def __init__(self):
4343
# list of all object files and mappting to module names
4444
self.object_to_module = dict()
4545

46+
# Memory usage summary structure
47+
self.mem_summary = dict()
48+
4649
def module_add(self, module_name, size, section):
4750
"""
4851
Adds a module / section to the list
@@ -67,7 +70,7 @@ def check_new_section_gcc(self, line):
6770
return i # should name of the section (assuming it's a known one)
6871

6972
if line.startswith('.'):
70-
return 'unknown' # all others are clasified are unknown
73+
return 'unknown' # all others are classified are unknown
7174
else:
7275
return False # everything else, means no change in section
7376

@@ -363,11 +366,12 @@ def generate_output(self, export_format, file_output=None):
363366

364367
# Create table
365368
columns = ['Module']
366-
for i in list(self.print_sections):
367-
columns.append(i)
369+
columns.extend(self.print_sections)
368370

369371
table = PrettyTable(columns)
370372
table.align["Module"] = "l"
373+
for col in self.print_sections:
374+
table.align[col] = 'r'
371375

372376
for i in list(self.print_sections):
373377
table.align[i] = 'r'
@@ -388,8 +392,12 @@ def generate_output(self, export_format, file_output=None):
388392
for k in self.print_sections:
389393
row.append(self.modules[i][k])
390394

391-
json_obj.append({"module":i, "size":{\
392-
k:self.modules[i][k] for k in self.print_sections}})
395+
json_obj.append({
396+
"module":i,
397+
"size":{
398+
k:self.modules[i][k] for k in self.print_sections
399+
}
400+
})
393401

394402
table.add_row(row)
395403

@@ -399,16 +407,19 @@ def generate_output(self, export_format, file_output=None):
399407

400408
table.add_row(subtotal_row)
401409

410+
summary = {
411+
'summary':{
412+
'static_ram':(subtotal['.data']+subtotal['.bss']),
413+
'heap':(subtotal['.heap']),
414+
'stack':(subtotal['.stack']),
415+
'total_ram':(subtotal['.data']+subtotal['.bss']+subtotal['.heap']+subtotal['.stack']),
416+
'total_flash':(subtotal['.text']+subtotal['.data']+misc_flash_mem),
417+
}
418+
}
419+
402420
if export_format == 'json':
403-
json_obj.append({\
404-
'summary':{\
405-
'total_static_ram':(subtotal['.data']+subtotal['.bss']),\
406-
'allocated_heap':(subtotal['.heap']),\
407-
'allocated_stack':(subtotal['.stack']),\
408-
'total_ram':(subtotal['.data']+subtotal['.bss']+subtotal['.heap']+subtotal['.stack']),\
409-
'total_flash':(subtotal['.text']+subtotal['.data']+misc_flash_mem),}})
410-
411-
file_desc.write(json.dumps(json_obj, indent=4))
421+
json_to_file = json_obj + [summary]
422+
file_desc.write(json.dumps(json_to_file, indent=4))
412423
file_desc.write('\n')
413424

414425
elif export_format == 'csv-ci': # CSV format for the CI system
@@ -467,33 +478,38 @@ def generate_output(self, export_format, file_output=None):
467478
if file_desc is not sys.stdout:
468479
file_desc.close()
469480

481+
self.mem_summary = json_obj + [summary]
482+
470483
return True
471484

485+
def get_memory_summary(self):
486+
"""! Object is available only after self.generate_output('json') is called
487+
@return Return memory summary object
488+
"""
489+
return self.mem_summary
490+
472491
def parse(self, mapfile, toolchain):
473492
"""
474493
Parse and decode map file depending on the toolchain
475494
"""
476495

496+
result = True
477497
try:
478-
file_input = open(mapfile, 'rt')
498+
with open(mapfile, 'rt') as file_input:
499+
if toolchain == "ARM" or toolchain == "ARM_STD" or toolchain == "ARM_MICRO":
500+
self.search_objects(os.path.abspath(mapfile), "ARM")
501+
self.parse_map_file_armcc(file_input)
502+
elif toolchain == "GCC_ARM":
503+
self.parse_map_file_gcc(file_input)
504+
elif toolchain == "IAR":
505+
self.search_objects(os.path.abspath(mapfile), toolchain)
506+
self.parse_map_file_iar(file_input)
507+
else:
508+
result = False
479509
except IOError as error:
480510
print "I/O error({0}): {1}".format(error.errno, error.strerror)
481-
return False
482-
483-
if toolchain == "ARM" or toolchain == "ARM_STD" or toolchain == "ARM_MICRO":
484-
self.search_objects(os.path.abspath(mapfile), "ARM")
485-
self.parse_map_file_armcc(file_input)
486-
elif toolchain == "GCC_ARM":
487-
self.parse_map_file_gcc(file_input)
488-
elif toolchain == "IAR":
489-
self.search_objects(os.path.abspath(mapfile), toolchain)
490-
self.parse_map_file_iar(file_input)
491-
else:
492-
return False
493-
494-
file_input.close()
495-
496-
return True
511+
result = False
512+
return result
497513

498514
def main():
499515

0 commit comments

Comments
 (0)