This is an automated email from Gerrit. "Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7761
-- gerrit commit b6ef0ccc0e83f5814e442ba008fb6fb2e2f52da5 Author: Erhan Kurubas <erhan.kuru...@espressif.com> Date: Tue Jul 4 05:55:41 2023 +0200 target/espressif: add support to collect gcov data from target Code coverage data is initally stored on the target itself. This patch allows to dump data from target to host during runtime. More details; https://docs.espressif.com/projects/esp-idf/en/latest/esp32/ api-guides/app_trace.html#gcov-source-code-coverage Checkpatch-ignore: MACRO_ARG_REUSE Signed-off-by: Erhan Kurubas <erhan.kuru...@espressif.com> Change-Id: I88ddd7b4021feacf9281d7e89f459a96c20b3322 diff --git a/doc/openocd.texi b/doc/openocd.texi index 832047f0ea..f0848df15b 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11415,6 +11415,14 @@ Stops Espressif multi-core SystremView tracing started with above command. Requests ongoing Espressif multi-core SystremView tracing status. @end deffn +@deffn {Command} {esp gcov} [dump] +Dumps collected coverage (gcov) data from target. +@url{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#gcov-source-code-coverage} +@itemize @bullet +@item @code{dump} - Used for @uref{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#hard-coded-dump, hard-coded dump mode}. +@end itemize +@end deffn + @anchor{softwaredebugmessagesandtracing} @section Software Debug Messages and Tracing @cindex Linux-ARM DCC support diff --git a/src/target/espressif/esp32_apptrace.c b/src/target/espressif/esp32_apptrace.c index 884224116e..8d477a426a 100644 --- a/src/target/espressif/esp32_apptrace.c +++ b/src/target/espressif/esp32_apptrace.c @@ -28,6 +28,7 @@ #include <target/target_type.h> #include <target/smp.h> #include <server/server.h> +#include <target/xtensa/xtensa_algorithm.h> #include "esp_xtensa.h" #include "esp_xtensa_smp.h" #include "esp_xtensa_apptrace.h" @@ -48,6 +49,17 @@ #define ESP32_APPTRACE_TGT_STATE_TMO 5000 #define ESP_APPTRACE_BLOCKS_POOL_SZ 10 +#define ESP_APPTRACE_FILE_CMD_FOPEN 0x0 +#define ESP_APPTRACE_FILE_CMD_FCLOSE 0x1 +#define ESP_APPTRACE_FILE_CMD_FWRITE 0x2 +#define ESP_APPTRACE_FILE_CMD_FREAD 0x3 +#define ESP_APPTRACE_FILE_CMD_FSEEK 0x4 +#define ESP_APPTRACE_FILE_CMD_FTELL 0x5 +#define ESP_APPTRACE_FILE_CMD_FTELL 0x5 +#define ESP_APPTRACE_FILE_CMD_STOP 0x6 /* indicates that there is no files to transfer */ + +#define ESP_GCOV_FILES_MAX_NUM 512 + struct esp32_apptrace_dest_file_data { int fout; }; @@ -75,6 +87,16 @@ struct esp32_apptrace_block { uint32_t data_len; }; +struct esp32_gcov_cmd_data { + FILE * files[ESP_GCOV_FILES_MAX_NUM]; + uint32_t files_num; + bool wait4halt; +}; + +static int esp_gcov_process_data(struct esp32_apptrace_cmd_ctx *ctx, + unsigned int core_id, + uint8_t *data, + uint32_t data_len); static int esp32_apptrace_data_processor(void *priv); static int esp32_apptrace_get_data_info(struct esp32_apptrace_cmd_ctx *ctx, struct esp32_apptrace_target_state *target_state, @@ -439,6 +461,7 @@ int esp32_apptrace_cmd_ctx_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct c struct xtensa *xtensa = target->arch_info; if (xtensa->common_magic == XTENSA_COMMON_MAGIC) { cmd_ctx->hw = target_to_esp_xtensa(target)->apptrace.hw; + cmd_ctx->algo_hw = target_to_esp_xtensa(target)->esp.algo_hw; } else { /* TODO: riscv is not supported yet */ command_print(cmd, "Unsupported target arch 0x%X", xtensa->common_magic); return ERROR_FAIL; @@ -1252,7 +1275,7 @@ static int esp32_sysview_start(struct esp32_apptrace_cmd_ctx *ctx) { uint8_t cmds[] = { SEGGER_SYSVIEW_COMMAND_ID_START }; uint32_t fired_target_num = 0; - struct esp32_apptrace_target_state target_state[ESP32_APPTRACE_MAX_CORES_NUM] = {0}; + struct esp32_apptrace_target_state target_state[ESP32_APPTRACE_MAX_CORES_NUM] = {{0}}; struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; /* get current block id */ @@ -1603,6 +1626,631 @@ COMMAND_HANDLER(esp32_cmd_sysview_mcore) return esp32_cmd_apptrace_generic(CMD, ESP_APPTRACE_CMD_MODE_SYSVIEW_MCORE, CMD_ARGV, CMD_ARGC); } +static int esp_gcov_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, + struct command_invocation *cmd, + const char **argv, + int argc) +{ + int res = esp32_apptrace_cmd_ctx_init(cmd_ctx, cmd, ESP_APPTRACE_CMD_MODE_SYNC); + if (res) + return res; + cmd_ctx->process_data = esp_gcov_process_data; + + struct esp32_gcov_cmd_data *cmd_data = calloc(1, sizeof(*cmd_data)); + if (!cmd_data) { + LOG_ERROR("Failed to alloc cmd data!"); + esp32_apptrace_cmd_ctx_cleanup(cmd_ctx); + return ERROR_FAIL; + } + cmd_ctx->stop_tmo = 3.0; + cmd_ctx->cmd_priv = cmd_data; + + if (argc > 0) + cmd_data->wait4halt = strtoul(argv[0], NULL, 10); + + cmd_ctx->trace_format.hdr_sz = ESP32_APPTRACE_USER_BLOCK_HDR_SZ; + cmd_ctx->trace_format.core_id_get = esp32_apptrace_core_id_get; + cmd_ctx->trace_format.usr_block_len_get = esp32_apptrace_usr_block_len_get; + return ERROR_OK; +} + +static int esp_gcov_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx) +{ + struct esp32_gcov_cmd_data *cmd_data = cmd_ctx->cmd_priv; + int res = ERROR_OK; + + for (unsigned int i = 0; i < ESP_GCOV_FILES_MAX_NUM; i++) { + if (cmd_data->files[i] && fclose(cmd_data->files[i])) { + LOG_ERROR("Failed to close file 0x%p (%d)!", cmd_data->files[i], errno); + res = ERROR_FAIL; + } + } + free(cmd_data); + cmd_ctx->cmd_priv = NULL; + esp32_apptrace_cmd_ctx_cleanup(cmd_ctx); + return res; +} + +#ifdef _WIN32 +#define DIR_SEPARATORS ("\\/") +#define IS_DIR_SEPARATOR(c) ((c) == '\\' || (c) == '/') +#else +#define DIR_SEPARATORS ("/") +#define IS_DIR_SEPARATOR(c) ((c) == '/') +#endif + +static const char *esp_gcov_filename_alloc(const char *orig_fname) +{ + const char *gcov_prefix; + size_t prefix_length; + int strip = 0, orig_fname_len = strlen(orig_fname); + + if (orig_fname_len == 0) + return NULL; + + LOG_DEBUG("Convert gcov file path '%s'", orig_fname); + /* Check if the level of dirs to strip off specified. */ + char *tmp = getenv("OPENOCD_GCOV_PREFIX_STRIP"); + if (tmp) { + strip = atoi(tmp); + /* Do not consider negative values. */ + if (strip < 0) + strip = 0; + } + + /* Get file name relocation prefix. Non-absolute values are ignored. */ + gcov_prefix = getenv("OPENOCD_GCOV_PREFIX"); + prefix_length = gcov_prefix ? strlen(gcov_prefix) : 0; + + /* Remove an unnecessary trailing '/' */ + if (prefix_length && IS_DIR_SEPARATOR(gcov_prefix[prefix_length - 1])) + prefix_length--; + + /* If no prefix was specified and a prefix stip, then we assume relative. */ + if (!prefix_length && strip) { + gcov_prefix = "."; + prefix_length = 1; + } + + /* Allocate and initialize the filename scratch space. */ + char *filename = (char *)malloc(prefix_length + orig_fname_len + 1); + if (prefix_length) + memcpy(filename, gcov_prefix, prefix_length); + const char *striped_fname = orig_fname; + while (strip--) { + /* assume that file path starts with '/', it is generated by GCC + * under Windows we skip over drive letter, + * path can contain mixed Windows and Unix and */ + /* can look like + * `c:\esp\esp-idf\examples\system\gcov\build/esp-idf/main/CMakeFiles/__idf_main.dir/gcov_example.c.gcda` */ + tmp = strpbrk(striped_fname + 1, DIR_SEPARATORS); + if (!tmp) + break; + striped_fname = tmp; + } + if (strip > 0) + LOG_WARNING("Failed to srip %d dir names in gcov file path '%s'!", strip, orig_fname); + strcpy(&filename[prefix_length], striped_fname); + + return filename; +} + +static int esp_gcov_fopen(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + *resp_len = 0; + if (cmd_data->files_num == ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Max gcov files num exceeded!"); + return ERROR_FAIL; + } + + if (data_len == 0) { + LOG_ERROR("Missed FOPEN args!"); + return ERROR_FAIL; + } + int len = strlen((char *)data); + if (len == 0) { + LOG_ERROR("Missed FOPEN path arg!"); + return ERROR_FAIL; + } + if (data_len - len - 1 == 0) { + LOG_ERROR("Missed FOPEN mode arg!"); + return ERROR_FAIL; + } + + uint32_t fd = cmd_data->files_num; + char *mode = (char *)data + len + 1; + const char *fname = esp_gcov_filename_alloc((const char *)data); + if (!fname) { + LOG_ERROR("Failed to alloc memory for file name!"); + return ERROR_FAIL; + } + LOG_INFO("Open file 0x%x '%s'", fd + 1, fname); + cmd_data->files[fd] = fopen(fname, mode); + if (!cmd_data->files[fd]) { + /* do not report error on reading non-existent file */ + if (errno != ENOENT || !strchr(mode, 'r')) + LOG_ERROR("Failed to open file '%s', mode '%s' (%d)!", fname, mode, errno); + fd = 0; + } else { + fd++; /* 1-based, 0 indicates error */ + } + *resp_len = sizeof(fd); + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + if (fd != 0) + fclose(cmd_data->files[fd - 1]); + free((void *)fname); + return ERROR_FAIL; + } + target_buffer_set_u32(target, *resp, fd); + + if (fd != 0) + cmd_data->files_num++; + + free((void *)fname); + return ERROR_OK; +} + +static int esp_gcov_fclose(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + *resp_len = 0; + if (data_len < sizeof(uint32_t)) { + LOG_ERROR("Missed FCLOSE args!"); + return ERROR_FAIL; + } + uint32_t fd = target_buffer_get_u32(target, data); + fd--; + if (fd >= ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Invalid file desc received 0x%x!", fd); + return ERROR_FAIL; + } + if (!cmd_data->files[fd]) { + LOG_ERROR("FCLOSE for not open file!"); + return ERROR_FAIL; + } + + int32_t fret = fclose(cmd_data->files[fd]); + if (fret) + LOG_ERROR("Failed to close file %d (%d)!", fd, errno); + else + cmd_data->files[fd] = NULL; + + *resp_len = sizeof(fret); + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + return ERROR_FAIL; + } + target_buffer_set_u32(target, *resp, fret); + + return ERROR_OK; +} + +static int esp_gcov_fwrite(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + *resp_len = 0; + if (data_len < sizeof(uint32_t)) { + LOG_ERROR("Missed FWRITE args!"); + return ERROR_FAIL; + } + uint32_t fd = target_buffer_get_u32(target, data); + fd--; + if (fd >= ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Invalid file desc received 0x%x!", fd); + return ERROR_FAIL; + } + if (!cmd_data->files[fd]) { + LOG_ERROR("FWRITE for not open file!"); + return ERROR_FAIL; + } + + uint32_t fret = fwrite(data + sizeof(fd), data_len - sizeof(fd), 1, cmd_data->files[fd]); + if (fret != 1) + LOG_ERROR("Failed to write %ld byte (%d)!", (long)(data_len - sizeof(fd)), errno); + + *resp_len = sizeof(fret); + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + return ERROR_FAIL; + } + target_buffer_set_u32(target, *resp, fret); + + return ERROR_OK; +} + +static int esp_gcov_fread(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + uint32_t fret; + + *resp_len = 0; + if (data_len < 2 * sizeof(uint32_t)) { + LOG_ERROR("Missed FREAD args!"); + return ERROR_FAIL; + } + uint32_t fd = target_buffer_get_u32(target, data); + fd--; + if (fd >= ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Invalid file desc received 0x%x!", fd); + return ERROR_FAIL; + } + if (!cmd_data->files[fd]) { + LOG_ERROR("FREAD for not open file!"); + return ERROR_FAIL; + } + uint32_t len = target_buffer_get_u32(target, data + sizeof(fd)); + *resp_len = sizeof(fret) + len; + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + return ERROR_FAIL; + } + fret = fread(*resp + sizeof(fret), 1, len, cmd_data->files[fd]); + if (fret == 0) + LOG_ERROR("Failed to read %d byte (%d)!", len, errno); + *resp_len = sizeof(fret) + fret; + target_buffer_set_u32(target, *resp, fret); + + return ERROR_OK; +} + +static int esp_gcov_fseek(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + *resp_len = 0; + if (data_len < (sizeof(uint32_t) + 2 * sizeof(int32_t))) { + LOG_ERROR("Missed FSEEK args!"); + return ERROR_FAIL; + } + uint32_t fd = target_buffer_get_u32(target, data); + fd--; + if (fd >= ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Invalid file desc received 0x%x!", fd); + return ERROR_FAIL; + } + if (!cmd_data->files[fd]) { + LOG_ERROR("FSEEK for not open file!"); + return ERROR_FAIL; + } + + int32_t off; + memcpy(&off, data + sizeof(fd), sizeof(off)); + int32_t whence; + memcpy(&whence, data + sizeof(fd) + sizeof(off), sizeof(whence)); + + int32_t fret = fseek(cmd_data->files[fd], off, whence); + *resp_len = sizeof(fret); + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + return ERROR_FAIL; + } + target_buffer_set_u32(target, *resp, fret); + + return ERROR_OK; +} + +static int esp_gcov_ftell(struct target *target, + struct esp32_gcov_cmd_data *cmd_data, + uint8_t *data, + uint32_t data_len, + uint8_t **resp, + uint32_t *resp_len) +{ + *resp_len = 0; + if (data_len < sizeof(uint32_t)) { + LOG_ERROR("Missed FTELL args!"); + return ERROR_FAIL; + } + uint32_t fd = target_buffer_get_u32(target, data); + fd--; + if (fd >= ESP_GCOV_FILES_MAX_NUM) { + LOG_ERROR("Invalid file desc received 0x%x!", fd); + return ERROR_FAIL; + } + if (!cmd_data->files[fd]) { + LOG_ERROR("FTELL for not open file!"); + return ERROR_FAIL; + } + + int32_t fret = ftell(cmd_data->files[fd]); + *resp_len = sizeof(fret); + *resp = malloc(*resp_len); + if (!*resp) { + LOG_ERROR("Failed to alloc mem for resp!"); + return ERROR_FAIL; + } + target_buffer_set_u32(target, *resp, fret); + + return ERROR_OK; +} + +/*TODO: support for multi-block data transfers */ +static int esp_gcov_process_data(struct esp32_apptrace_cmd_ctx *ctx, + unsigned int core_id, + uint8_t *data, + uint32_t data_len) +{ + int ret = ERROR_OK; + struct esp32_gcov_cmd_data *cmd_data = ctx->cmd_priv; + uint8_t *resp; + uint32_t resp_len = 0; + + LOG_DEBUG("Got block %d bytes [%x %x]", data_len, data[0], data[1]); + + if (data_len < 1) { + LOG_ERROR("Too small data length %d!", data_len); + return ERROR_FAIL; + } + + switch (*data) { + case ESP_APPTRACE_FILE_CMD_FOPEN: + ret = esp_gcov_fopen(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_FCLOSE: + ret = esp_gcov_fclose(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_FWRITE: + ret = esp_gcov_fwrite(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_FREAD: + ret = esp_gcov_fread(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_FSEEK: + ret = esp_gcov_fseek(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_FTELL: + ret = esp_gcov_ftell(ctx->cpus[core_id], cmd_data, data + 1, data_len - 1, &resp, &resp_len); + break; + case ESP_APPTRACE_FILE_CMD_STOP: + ctx->running = 0; + break; + default: + LOG_ERROR("Invalid FCMD 0x%x!", *data); + ret = ERROR_FAIL; + } + if (ret != ERROR_OK) + return ret; + + if (resp_len) { + /* write response */ + int res = esp_apptrace_usr_block_write(ctx->hw, ctx->cpus[core_id], + ctx->last_blk_id, + resp, + resp_len); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write data to (%s)!", target_name(ctx->cpus[core_id])); + free(resp); + return res; + } + free(resp); + for (unsigned int i = 0; i < ctx->cores_num; i++) { + if (i == core_id) + continue; + res = ctx->hw->ctrl_reg_write(ctx->cpus[i], + ctx->last_blk_id, + 0 /*all read*/, + true /*host connected*/, + true /*host data*/); + if (res != ERROR_OK) { + LOG_ERROR("Failed to ack data on (%s)!", target_name(ctx->cpus[i])); + return res; + } + LOG_DEBUG("Ack block %d target (%s)!", ctx->last_blk_id, target_name(ctx->cpus[i])); + } + } else { + for (unsigned int i = 0; i < ctx->cores_num; i++) { + int res = ctx->hw->ctrl_reg_write(ctx->cpus[i], + ctx->last_blk_id, + 0 /*all read*/, + true /*host connected*/, + false /*host data*/); + if (res != ERROR_OK) { + LOG_ERROR("Failed to ack data on (%s)!", target_name(ctx->cpus[i])); + return res; + } + LOG_DEBUG("Ack block %d target (%s)!", ctx->last_blk_id, target_name(ctx->cpus[i])); + } + } + + return ERROR_OK; +} + +int esp_gcov_poll(struct target *target, void *priv) +{ + int res = ERROR_OK; + struct esp32_apptrace_cmd_ctx *cmd_ctx = (struct esp32_apptrace_cmd_ctx *)priv; + + while (!openocd_is_shutdown_pending() && target->state != TARGET_HALTED && cmd_ctx->running) { + res = esp32_apptrace_poll(cmd_ctx); + if (res != ERROR_OK) { + LOG_ERROR("Failed to poll target for gcov data (%d)!", res); + break; + } + /* let registered timer callbacks to run */ + target_call_timer_callbacks(); + } + return res; +} + +static struct esp_dbg_stubs *get_stubs_from_target(struct target **target) +{ + struct esp_dbg_stubs *dbg_stubs = NULL; + const char *arch = target_get_gdb_arch(*target); + + if (arch) { + if (strncmp(arch, "xtensa", 5) != 0) { + LOG_ERROR("Unsupported target arch '%s'!", arch); + return NULL; + } /* Riscv is not supported yet in the upstream */ + } else { + LOG_ERROR("Unknown target arch!"); + return NULL; + } + + struct xtensa *xtensa = (*target)->arch_info; + if (xtensa->common_magic != XTENSA_COMMON_MAGIC) { + LOG_ERROR("Unsupported target arch '%s'!", arch); + return NULL; + } /* TODO: riscv is not supported yet */ + + if ((*target)->smp) { + struct target_list *head; + struct target *curr; + foreach_smp_target(head, (*target)->smp_targets) { + curr = head->target; + dbg_stubs = &(target_to_esp_xtensa(curr)->esp.dbg_stubs); + if (target_was_examined(curr) && dbg_stubs->base && + dbg_stubs->entries_count > 0) { + *target = curr; + break; + } + } + } else { + dbg_stubs = &(target_to_esp_xtensa(*target)->esp.dbg_stubs); + } + return dbg_stubs; +} + +COMMAND_HANDLER(esp32_cmd_gcov) +{ + static struct esp32_apptrace_cmd_ctx s_at_cmd_ctx; + int res = ERROR_OK; + struct target *target = get_current_target(CMD_CTX); + enum target_state old_state; + struct algorithm_run_data run; + uint32_t func_addr; + bool dump = false; + uint32_t stub_capabilites; + bool gcov_idf_has_thread = false; + + if (CMD_ARGC > 0) { + if (strcmp(CMD_ARGV[0], "dump") == 0) { + dump = true; + } else { + command_print(CMD, "Invalid action!"); + return ERROR_FAIL; + } + } + + /* init cmd context */ + res = esp_gcov_cmd_init(&s_at_cmd_ctx, CMD, CMD_ARGV, CMD_ARGC); + if (res != ERROR_OK) { + command_print(CMD, "Failed to init cmd ctx (%d)!", res); + return res; + } + /* command can be invoked on unexamined core, if so find examined one */ + if (target->smp && !target_was_examined(target)) { + struct target_list *head; + struct target *curr = target; + LOG_WARNING("Current target '%s' was not examined!", target_name(target)); + foreach_smp_target(head, target->smp_targets) { + curr = head->target; + if (target_was_examined(curr)) { + LOG_WARNING("Run command on target '%s'", target_name(target)); + break; + } + } + if (curr == target) { + command_print(CMD, "There is no examined core to run command!"); + return ERROR_FAIL; + } + target = curr; + } + old_state = target->state; + if (dump) { + /* connect */ + res = esp32_apptrace_connect_targets(&s_at_cmd_ctx, true, true); + if (res != ERROR_OK) { + command_print(CMD, "Failed to connect to targets (%d)!", res); + esp_gcov_cmd_cleanup(&s_at_cmd_ctx); + return res; + } + esp_gcov_poll(target, &s_at_cmd_ctx); + } else { + struct target *run_target = target; + /* connect and halt target, debug stubs info will be read if this is the first time target is halted */ + res = esp32_apptrace_connect_targets(&s_at_cmd_ctx, true, false); + if (res != ERROR_OK) { + command_print(CMD, "Failed to connect to targets (%d)!", res); + esp_gcov_cmd_cleanup(&s_at_cmd_ctx); + return res; + } + struct esp_dbg_stubs *dbg_stubs = get_stubs_from_target(&run_target); + if (!dbg_stubs || dbg_stubs->entries_count < 1 || dbg_stubs->desc.data_alloc == 0) { + command_print(CMD, "No dbg stubs found!"); + esp_gcov_cmd_cleanup(&s_at_cmd_ctx); + return ERROR_FAIL; + } + func_addr = dbg_stubs->entries[ESP_DBG_STUB_ENTRY_GCOV]; + LOG_DEBUG("GCOV_FUNC = 0x%x", func_addr); + if (func_addr == 0) { + command_print(CMD, "GCOV stub not found!"); + esp_gcov_cmd_cleanup(&s_at_cmd_ctx); + return ERROR_FAIL; + } + stub_capabilites = dbg_stubs->entries[ESP_DBG_STUB_CAPABILITIES]; + gcov_idf_has_thread = stub_capabilites & ESP_DBG_STUB_CAP_GCOV_THREAD; + LOG_DEBUG("STUB_CAP = 0x%x", stub_capabilites); + memset(&run, 0, sizeof(run)); + run.hw = s_at_cmd_ctx.algo_hw; + run.stack_size = 1024; + if (!gcov_idf_has_thread) { + run.usr_func_arg = &s_at_cmd_ctx; + run.usr_func = esp_gcov_poll; + } + run.on_board.min_stack_addr = dbg_stubs->desc.min_stack_addr; + run.on_board.min_stack_size = ESP_DBG_STUBS_STACK_MIN_SIZE; + run.on_board.code_buf_addr = dbg_stubs->desc.tramp_addr; + run.on_board.code_buf_size = ESP_DBG_STUBS_CODE_BUF_SIZE; + /* this function works for SMP and non-SMP targets + * set num_args to 1 in order to read return code coming with "a2" reg */ + esp_xtensa_smp_run_onboard_func(run_target, &run, func_addr, 1); + LOG_DEBUG("FUNC RET = 0x%" PRIx32, run.ret_code); + if (run.ret_code == ERROR_OK && gcov_idf_has_thread) { + res = target_resume(target, 1, 0, 1, 0); + if (res != ERROR_OK) { + LOG_ERROR("Failed to resume target (%d)!", res); + return res; + } + esp_gcov_poll(target, &s_at_cmd_ctx); + } + } + /* disconnect */ + res = esp32_apptrace_connect_targets(&s_at_cmd_ctx, false, old_state == TARGET_RUNNING); + if (res != ERROR_OK) + LOG_ERROR("Failed to disconnect targets (%d)!", res); + res = esp_gcov_cmd_cleanup(&s_at_cmd_ctx); + if (res != ERROR_OK) + LOG_ERROR("Failed to cleanup cmd ctx (%d)!", res); + return res; +} + const struct command_registration esp32_apptrace_command_handlers[] = { { .name = "apptrace", @@ -1631,5 +2279,12 @@ const struct command_registration esp32_apptrace_command_handlers[] = { .usage = "(start file://<outfile> [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]) | (stop) | (status)", }, + { + .name = "gcov", + .handler = esp32_cmd_gcov, + .mode = COMMAND_EXEC, + .help = "GCOV: Dumps gcov info collected on target.", + .usage = "[dump]", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/espressif/esp32_apptrace.h b/src/target/espressif/esp32_apptrace.h index 3873342226..43fd42a5a6 100644 --- a/src/target/espressif/esp32_apptrace.h +++ b/src/target/espressif/esp32_apptrace.h @@ -81,6 +81,7 @@ struct esp32_apptrace_cmd_ctx { /* TODO: use cores num from target */ unsigned int cores_num; const struct esp32_apptrace_hw *hw; + const struct algorithm_hw *algo_hw; enum target_state target_state; uint32_t last_blk_id; struct list_head free_trace_blocks; --