| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <string.h> |
| #include "coverage.h" |
| |
| K_HEAP_DEFINE(gcov_heap, CONFIG_COVERAGE_GCOV_HEAP_SIZE); |
| |
| static struct gcov_info *gcov_info_head; |
| |
| /** |
| * Is called by gcc-generated constructor code for each object file compiled |
| * with -fprofile-arcs. |
| */ |
| void __gcov_init(struct gcov_info *info) |
| { |
| info->next = gcov_info_head; |
| gcov_info_head = info; |
| } |
| |
| void __gcov_merge_add(gcov_type *counters, unsigned int n_counters) |
| { |
| /* Unused. */ |
| } |
| |
| void __gcov_exit(void) |
| { |
| /* Unused. */ |
| } |
| |
| /** |
| * 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 write_u64(void *buffer, size_t *off, uint64_t 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); |
| } |
| |
| /** |
| * write_u32 - Store 32 bit data on a buffer and return the size |
| */ |
| static inline void write_u32(void *buffer, size_t *off, uint32_t 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 gcov_calculate_buff_size(struct gcov_info *info) |
| { |
| uint32_t iter; |
| uint32_t iter_1; |
| uint32_t iter_2; |
| |
| /* Few fixed values at the start: magic number, |
| * version, stamp, and checksum. |
| */ |
| #ifdef GCOV_12_FORMAT |
| uint32_t size = sizeof(uint32_t) * 4; |
| #else |
| uint32_t size = sizeof(uint32_t) * 3; |
| #endif |
| |
| for (iter = 0U; iter < info->n_functions; iter++) { |
| /* space for TAG_FUNCTION and FUNCTION_LENGTH |
| * ident |
| * lineno_checksum |
| * cfg_checksum |
| */ |
| size += (sizeof(uint32_t) * 5); |
| |
| for (iter_1 = 0U; iter_1 < GCOV_COUNTERS; iter_1++) { |
| if (!info->merge[iter_1]) { |
| continue; |
| } |
| |
| /* for function counter and number of values */ |
| size += (sizeof(uint32_t) * 2); |
| |
| for (iter_2 = 0U; |
| iter_2 < info->functions[iter]->ctrs->num; |
| iter_2++) { |
| |
| /* Iter for values which is uint64_t */ |
| size += (sizeof(uint64_t)); |
| } |
| |
| } |
| |
| |
| } |
| |
| return size; |
| } |
| |
| /** |
| * gcov_populate_buffer - convert from gcov data set (info) to |
| * .gcda file format. |
| * This buffer will now have info similar to a regular gcda |
| * format. |
| */ |
| size_t gcov_populate_buffer(uint8_t *buffer, struct gcov_info *info) |
| { |
| struct gcov_fn_info *functions; |
| struct gcov_ctr_info *counters_per_func; |
| uint32_t iter_functions; |
| uint32_t iter_counts; |
| uint32_t iter_counter_values; |
| size_t buffer_write_position = 0; |
| |
| /* File header. */ |
| write_u32(buffer, |
| &buffer_write_position, |
| GCOV_DATA_MAGIC); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| info->version); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| info->stamp); |
| |
| #ifdef GCOV_12_FORMAT |
| write_u32(buffer, |
| &buffer_write_position, |
| info->checksum); |
| #endif |
| |
| for (iter_functions = 0U; |
| iter_functions < info->n_functions; |
| iter_functions++) { |
| |
| functions = info->functions[iter_functions]; |
| |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| GCOV_TAG_FUNCTION); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| GCOV_TAG_FUNCTION_LENGTH); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| functions->ident); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| functions->lineno_checksum); |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| functions->cfg_checksum); |
| |
| counters_per_func = functions->ctrs; |
| |
| for (iter_counts = 0U; |
| iter_counts < GCOV_COUNTERS; |
| iter_counts++) { |
| |
| if (!info->merge[iter_counts]) { |
| continue; |
| } |
| |
| write_u32(buffer, |
| &buffer_write_position, |
| GCOV_TAG_FOR_COUNTER(iter_counts)); |
| |
| #ifdef GCOV_12_FORMAT |
| /* GCOV 12 counts the length by bytes */ |
| write_u32(buffer, |
| &buffer_write_position, |
| counters_per_func->num * 2U * 4); |
| #else |
| 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++) { |
| |
| write_u64(buffer, |
| &buffer_write_position, |
| counters_per_func->values[iter_counter_values]); |
| } |
| |
| counters_per_func++; |
| } |
| } |
| return buffer_write_position; |
| } |
| |
| void gcov_reset_counts(struct gcov_info *info) |
| { |
| struct gcov_fn_info *functions; |
| struct gcov_ctr_info *counters_per_func; |
| uint32_t iter_functions; |
| uint32_t iter_counts; |
| uint32_t iter_counter_values; |
| |
| for (iter_functions = 0U; |
| iter_functions < info->n_functions; |
| iter_functions++) { |
| |
| functions = info->functions[iter_functions]; |
| counters_per_func = functions->ctrs; |
| |
| for (iter_counts = 0U; |
| iter_counts < GCOV_COUNTERS; |
| iter_counts++) { |
| for (iter_counter_values = 0U; |
| iter_counter_values < counters_per_func->num; |
| iter_counter_values++) { |
| counters_per_func->values[iter_counter_values] = 0; |
| } |
| } |
| } |
| } |
| |
| void gcov_reset_all_counts(void) |
| { |
| struct gcov_info *gcov_list = NULL; |
| |
| #ifdef CONFIG_MULTITHREADING |
| k_sched_lock(); |
| #endif |
| |
| gcov_list = gcov_get_list_head(); |
| |
| while (gcov_list) { |
| gcov_reset_counts(gcov_list); |
| gcov_list = gcov_list->next; |
| } |
| |
| #ifdef CONFIG_MULTITHREADING |
| k_sched_unlock(); |
| #endif |
| } |
| |
| void dump_on_console_start(const char *filename) |
| { |
| printk("\n%c", FILE_START_INDICATOR); |
| while (*filename != '\0') { |
| printk("%c", *filename++); |
| } |
| printk("%c", GCOV_DUMP_SEPARATOR); |
| } |
| |
| 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++); |
| } |
| } |
| } |
| |
| /** |
| * Retrieves gcov coverage data and sends it over the given interface. |
| */ |
| void gcov_coverage_dump(void) |
| { |
| uint8_t *buffer; |
| size_t size; |
| size_t written_size; |
| struct gcov_info *gcov_list_first = gcov_info_head; |
| struct gcov_info *gcov_list = gcov_info_head; |
| |
| if (!k_is_in_isr()) { |
| #ifdef CONFIG_MULTITHREADING |
| k_sched_lock(); |
| #endif |
| } |
| printk("\nGCOV_COVERAGE_DUMP_START"); |
| while (gcov_list) { |
| |
| dump_on_console_start(gcov_list->filename); |
| size = gcov_calculate_buff_size(gcov_list); |
| |
| buffer = k_heap_alloc(&gcov_heap, size, K_NO_WAIT); |
| if (CONFIG_COVERAGE_GCOV_HEAP_SIZE > 0 && !buffer) { |
| printk("No Mem available to continue dump\n"); |
| goto coverage_dump_end; |
| } |
| |
| written_size = gcov_populate_buffer(buffer, gcov_list); |
| if (written_size != size) { |
| printk("Write Error on buff\n"); |
| goto coverage_dump_end; |
| } |
| |
| dump_on_console_data(buffer, size); |
| |
| k_heap_free(&gcov_heap, buffer); |
| gcov_list = gcov_list->next; |
| if (gcov_list_first == gcov_list) { |
| goto coverage_dump_end; |
| } |
| } |
| coverage_dump_end: |
| printk("\nGCOV_COVERAGE_DUMP_END\n"); |
| if (!k_is_in_isr()) { |
| #ifdef CONFIG_MULTITHREADING |
| k_sched_unlock(); |
| #endif |
| } |
| return; |
| } |
| |
| struct gcov_info *gcov_get_list_head(void) |
| { |
| /* Locking someway before getting this is recommended. */ |
| return gcov_info_head; |
| } |
| |
| /* Initialize the gcov by calling the required constructors */ |
| void gcov_static_init(void) |
| { |
| extern uintptr_t __init_array_start, __init_array_end; |
| uintptr_t func_pointer_start = (uintptr_t) &__init_array_start; |
| uintptr_t func_pointer_end = (uintptr_t) &__init_array_end; |
| |
| while (func_pointer_start < func_pointer_end) { |
| void (**p)(void); |
| /* get function pointer */ |
| p = (void (**)(void)) func_pointer_start; |
| (*p)(); |
| func_pointer_start += sizeof(p); |
| } |
| } |