diff --git a/scripts/pylib/twister/twisterlib/coverage.py b/scripts/pylib/twister/twisterlib/coverage.py index 067dcfbcca7c4b..c215ae5bd83868 100644 --- a/scripts/pylib/twister/twisterlib/coverage.py +++ b/scripts/pylib/twister/twisterlib/coverage.py @@ -71,6 +71,7 @@ def retrieve_gcov_data(input_file): @staticmethod def create_gcda_files(extracted_coverage_info): + gcda_created = True logger.debug("Generating gcda files") for filename, hexdump_val in extracted_coverage_info.items(): # if kobject_hash is given for coverage gcovr fails @@ -83,19 +84,33 @@ def create_gcda_files(extracted_coverage_info): pass continue - with open(filename, 'wb') as fp: - fp.write(bytes.fromhex(hexdump_val)) + try: + with open(filename, 'wb') as fp: + fp.write(bytes.fromhex(hexdump_val)) + except ValueError: + logger.exception("Unable to convert hex data for file: {}".format(filename)) + gcda_created = False + except FileNotFoundError: + logger.exception("Unable to create gcda file: {}".format(filename)) + gcda_created = False + return gcda_created def generate(self, outdir): + coverage_completed = True for filename in glob.glob("%s/**/handler.log" % outdir, recursive=True): gcov_data = self.__class__.retrieve_gcov_data(filename) capture_complete = gcov_data['complete'] extracted_coverage_info = gcov_data['data'] if capture_complete: - self.__class__.create_gcda_files(extracted_coverage_info) - logger.debug("Gcov data captured: {}".format(filename)) + gcda_created = self.__class__.create_gcda_files(extracted_coverage_info) + if gcda_created: + logger.debug("Gcov data captured: {}".format(filename)) + else: + logger.error("Gcov data invalid for: {}".format(filename)) + coverage_completed = False else: logger.error("Gcov data capture incomplete: {}".format(filename)) + coverage_completed = False with open(os.path.join(outdir, "coverage.log"), "a") as coveragelog: ret = self._generate(outdir, coveragelog) @@ -111,6 +126,10 @@ def generate(self, outdir): } for r in self.output_formats.split(','): logger.info(report_log[r]) + else: + coverage_completed = False + logger.debug("All coverage data processed: {}".format(coverage_completed)) + return coverage_completed class Lcov(CoverageTool): @@ -280,4 +299,5 @@ def run_coverage(testplan, options): coverage_tool.add_ignore_file('generated') coverage_tool.add_ignore_directory('tests') coverage_tool.add_ignore_directory('samples') - coverage_tool.generate(options.outdir) + coverage_completed = coverage_tool.generate(options.outdir) + return coverage_completed diff --git a/scripts/pylib/twister/twisterlib/handlers.py b/scripts/pylib/twister/twisterlib/handlers.py index e4a0d9f5a1a208..2ffeb4bfc19520 100755 --- a/scripts/pylib/twister/twisterlib/handlers.py +++ b/scripts/pylib/twister/twisterlib/handlers.py @@ -358,6 +358,13 @@ def __init__(self, instance, type_str): """ super().__init__(instance, type_str) + def get_test_timeout(self): + timeout = super().get_test_timeout() + if self.options.coverage: + # wait more for gcov data to be dumped on console + timeout += 120 + return timeout + def monitor_serial(self, ser, halt_event, harness): log_out_fp = open(self.log, "wb") diff --git a/scripts/pylib/twister/twisterlib/twister_main.py b/scripts/pylib/twister/twisterlib/twister_main.py index 336c95f3609287..8f4b4f64893d98 100644 --- a/scripts/pylib/twister/twisterlib/twister_main.py +++ b/scripts/pylib/twister/twisterlib/twister_main.py @@ -207,9 +207,10 @@ def main(options): report.summary(runner.results, options.disable_unrecognized_section_test, duration) + coverage_completed = True if options.coverage: if not options.build_only: - run_coverage(tplan, options) + coverage_completed = run_coverage(tplan, options) else: logger.info("Skipping coverage report generation due to --build-only.") @@ -235,6 +236,7 @@ def main(options): runner.results.failed or runner.results.error or (tplan.warnings and options.warnings_as_errors) + or (options.coverage and not coverage_completed) ): return 1 diff --git a/subsys/testsuite/Kconfig b/subsys/testsuite/Kconfig index e2e7fba764eb40..eff533a9da2d60 100644 --- a/subsys/testsuite/Kconfig +++ b/subsys/testsuite/Kconfig @@ -78,7 +78,8 @@ config COVERAGE_GCOV_HEAP_SIZE default 16384 help This option configures the heap size allocated for gcov coverage - data to be dumped over serial. + data to be dumped over serial. If the value is 0, no buffer will be used, + data will be dumped directly over serial. config COVERAGE_DUMP bool "Dump coverage data on exit" @@ -86,6 +87,15 @@ config COVERAGE_DUMP help Dump collected coverage information to console on exit. +config FORCE_COVERAGE + bool "Force coverage" + select HAS_COVERAGE_SUPPORT + help + Regardless of platform support, it will enable coverage data production. + If the platform does not support coverage by default, setting this config + does not guarantee that coverage data will be gathered. + Application may not fit memory or crash at runtime. + config TEST_USERSPACE bool "Indicate that this test exercises user mode" help diff --git a/subsys/testsuite/coverage/coverage.c b/subsys/testsuite/coverage/coverage.c index e787135ffdcdd7..dc120c1460577b 100644 --- a/subsys/testsuite/coverage/coverage.c +++ b/subsys/testsuite/coverage/coverage.c @@ -36,25 +36,54 @@ void __gcov_exit(void) } /** - * buff_write_u64 - Store 64 bit data on a buffer and return the size + * print_u8 - Print 8 bit of gcov data + */ +static inline void print_u8(uint8_t v) +{ + printk("%02x", v); +} + +/** + * print_u32 - Print 32 bit of gcov data + */ +static inline void print_u32(uint32_t v) +{ + uint8_t *ptr = (uint8_t *)&v; + + print_u8(*ptr); + print_u8(*(ptr+1)); + print_u8(*(ptr+2)); + print_u8(*(ptr+3)); +} + +/** + * write_u64 - Store 64 bit data on a buffer and return the size */ -static inline void buff_write_u64(void *buffer, size_t *off, uint64_t v) +static inline void write_u64(void *buffer, size_t *off, uint64_t v) { - memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v)); + if (buffer != NULL) { + memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v)); + } else { + print_u32(*((uint32_t *)&v)); + print_u32(*(((uint32_t *)&v) + 1)); + } *off = *off + sizeof(uint64_t); } /** - * buff_write_u32 - Store 32 bit data on a buffer and return the size + * write_u32 - Store 32 bit data on a buffer and return the size */ -static inline void buff_write_u32(void *buffer, size_t *off, uint32_t v) +static inline void write_u32(void *buffer, size_t *off, uint32_t v) { - memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v)); + if (buffer != NULL) { + memcpy((uint8_t *)buffer + *off, (uint8_t *)&v, sizeof(v)); + } else { + print_u32(v); + } *off = *off + sizeof(uint32_t); } - size_t calculate_buff_size(struct gcov_info *info) { uint32_t iter; @@ -102,14 +131,13 @@ size_t calculate_buff_size(struct gcov_info *info) return size; } - /** - * populate_buffer - convert from gcov data set (info) to + * gcov_to_gcda - convert from gcov data set (info) to * .gcda file format. * This buffer will now have info similar to a regular gcda * format. */ -size_t populate_buffer(uint8_t *buffer, struct gcov_info *info) +size_t gcov_to_gcda(uint8_t *buffer, struct gcov_info *info) { struct gcov_fn_info *functions; struct gcov_ctr_info *counters_per_func; @@ -119,22 +147,22 @@ size_t populate_buffer(uint8_t *buffer, struct gcov_info *info) size_t buffer_write_position = 0; /* File header. */ - buff_write_u32(buffer, - &buffer_write_position, - GCOV_DATA_MAGIC); + write_u32(buffer, + &buffer_write_position, + GCOV_DATA_MAGIC); - buff_write_u32(buffer, - &buffer_write_position, - info->version); + write_u32(buffer, + &buffer_write_position, + info->version); - buff_write_u32(buffer, - &buffer_write_position, - info->stamp); + write_u32(buffer, + &buffer_write_position, + info->stamp); #ifdef GCOV_12_FORMAT - buff_write_u32(buffer, - &buffer_write_position, - info->checksum); + write_u32(buffer, + &buffer_write_position, + info->checksum); #endif for (iter_functions = 0U; @@ -144,25 +172,25 @@ size_t populate_buffer(uint8_t *buffer, struct gcov_info *info) functions = info->functions[iter_functions]; - buff_write_u32(buffer, - &buffer_write_position, - GCOV_TAG_FUNCTION); + write_u32(buffer, + &buffer_write_position, + GCOV_TAG_FUNCTION); - buff_write_u32(buffer, - &buffer_write_position, - GCOV_TAG_FUNCTION_LENGTH); + write_u32(buffer, + &buffer_write_position, + GCOV_TAG_FUNCTION_LENGTH); - buff_write_u32(buffer, - &buffer_write_position, - functions->ident); + write_u32(buffer, + &buffer_write_position, + functions->ident); - buff_write_u32(buffer, - &buffer_write_position, - functions->lineno_checksum); + write_u32(buffer, + &buffer_write_position, + functions->lineno_checksum); - buff_write_u32(buffer, - &buffer_write_position, - functions->cfg_checksum); + write_u32(buffer, + &buffer_write_position, + functions->cfg_checksum); counters_per_func = functions->ctrs; @@ -174,29 +202,28 @@ size_t populate_buffer(uint8_t *buffer, struct gcov_info *info) continue; } - buff_write_u32(buffer, - &buffer_write_position, - GCOV_TAG_FOR_COUNTER(iter_counts)); + write_u32(buffer, + &buffer_write_position, + GCOV_TAG_FOR_COUNTER(iter_counts)); #ifdef GCOV_12_FORMAT /* GCOV 12 counts the length by bytes */ - buff_write_u32(buffer, - &buffer_write_position, - counters_per_func->num * 2U * 4); + write_u32(buffer, + &buffer_write_position, + counters_per_func->num * 2U * 4); #else - buff_write_u32(buffer, - &buffer_write_position, - counters_per_func->num * 2U); + write_u32(buffer, + &buffer_write_position, + counters_per_func->num * 2U); #endif for (iter_counter_values = 0U; iter_counter_values < counters_per_func->num; iter_counter_values++) { - buff_write_u64(buffer, - &buffer_write_position, - counters_per_func->\ - values[iter_counter_values]); + write_u64(buffer, + &buffer_write_position, + counters_per_func->values[iter_counter_values]); } counters_per_func++; @@ -205,20 +232,21 @@ size_t populate_buffer(uint8_t *buffer, struct gcov_info *info) return buffer_write_position; } -void dump_on_console(const char *filename, char *ptr, size_t len) +void dump_on_console_start(const char *filename) { - uint32_t iter; - printk("\n%c", FILE_START_INDICATOR); while (*filename != '\0') { printk("%c", *filename++); } printk("%c", GCOV_DUMP_SEPARATOR); +} - /* Data dump */ - - for (iter = 0U; iter < len; iter++) { - printk(" %02x", (uint8_t)*ptr++); +void dump_on_console_data(char *ptr, size_t len) +{ + if (ptr != NULL) { + for (size_t iter = 0U; iter < len; iter++) { + print_u8((uint8_t)*ptr++); + } } } @@ -237,21 +265,22 @@ void gcov_coverage_dump(void) printk("\nGCOV_COVERAGE_DUMP_START"); while (gcov_list) { + dump_on_console_start(gcov_list->filename); size = calculate_buff_size(gcov_list); buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT); - if (!buffer) { + if (CONFIG_COVERAGE_GCOV_HEAP_SIZE > 0 && !buffer) { printk("No Mem available to continue dump\n"); goto coverage_dump_end; } - written_size = populate_buffer(buffer, gcov_list); + written_size = gcov_to_gcda(buffer, gcov_list); if (written_size != size) { printk("Write Error on buff\n"); goto coverage_dump_end; } - dump_on_console(gcov_list->filename, buffer, size); + dump_on_console_data(buffer, size); k_heap_free(&gcov_heap, buffer); gcov_list = gcov_list->next;