esp32: Adds gcov over JTAG feature

Implements function to dump GCOV data to host via JTAG.
The following functionality was added:
 - Host file I/O
 - GCOV runtime I/O stubs
 - GCOV example
This commit is contained in:
Alexey Gerenkov 2017-08-17 02:36:00 +03:00
parent 3c470e7c43
commit 891f0db31d
14 changed files with 862 additions and 52 deletions

View File

@ -254,6 +254,10 @@ static volatile uint8_t *s_trax_blocks[] = {
#define ESP_APPTRACE_USR_DATA_LEN_MAX (ESP_APPTRACE_TRAX_BLOCK_SIZE - sizeof(esp_tracedata_hdr_t))
#endif
#define ESP_APPTRACE_HW_TRAX 0
#define ESP_APPTRACE_HW_MAX 1
#define ESP_APPTRACE_HW(_i_) (&s_trace_hw[_i_])
/** Trace data header. Every user data chunk is prepended with this header.
* User allocates block with esp_apptrace_buffer_get and then fills it with data,
* in multithreading environment it can happen that tasks gets buffer and then gets interrupted,
@ -326,8 +330,33 @@ static esp_apptrace_buffer_t s_trace_buf;
static esp_apptrace_lock_t s_log_lock = {.irq_stat = 0, .portmux = portMUX_INITIALIZER_UNLOCKED};
#endif
typedef struct {
uint8_t *(*get_up_buffer)(uint32_t, esp_apptrace_tmo_t *);
esp_err_t (*put_up_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_err_t (*flush_up_buffer)(uint32_t, esp_apptrace_tmo_t *);
uint8_t *(*get_down_buffer)(uint32_t *, esp_apptrace_tmo_t *);
esp_err_t (*put_down_buffer)(uint8_t *, esp_apptrace_tmo_t *);
bool (*host_is_connected)(void);
} esp_apptrace_hw_t;
static uint32_t esp_apptrace_trax_down_buffer_write_nolock(uint8_t *data, uint32_t size);
static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, esp_apptrace_tmo_t *tmo);
static uint8_t *esp_apptrace_trax_get_buffer(uint32_t size, esp_apptrace_tmo_t *tmo);
static esp_err_t esp_apptrace_trax_put_buffer(uint8_t *ptr, esp_apptrace_tmo_t *tmo);
static bool esp_apptrace_trax_host_is_connected(void);
static uint8_t *esp_apptrace_trax_down_buffer_get(uint32_t *size, esp_apptrace_tmo_t *tmo);
static esp_err_t esp_apptrace_trax_down_buffer_put(uint8_t *ptr, esp_apptrace_tmo_t *tmo);
static esp_apptrace_hw_t s_trace_hw[ESP_APPTRACE_HW_MAX] = {
{
.get_up_buffer = esp_apptrace_trax_get_buffer,
.put_up_buffer = esp_apptrace_trax_put_buffer,
.flush_up_buffer = esp_apptrace_trax_flush,
.get_down_buffer = esp_apptrace_trax_down_buffer_get,
.put_down_buffer = esp_apptrace_trax_down_buffer_put,
.host_is_connected = esp_apptrace_trax_host_is_connected
}
};
static inline int esp_apptrace_log_lock()
{
@ -586,7 +615,7 @@ static uint8_t *esp_apptrace_trax_down_buffer_get(uint32_t *size, esp_apptrace_t
return ptr;
}
static inline esp_err_t esp_apptrace_trax_down_buffer_put(uint8_t *ptr, esp_apptrace_tmo_t *tmo)
static esp_err_t esp_apptrace_trax_down_buffer_put(uint8_t *ptr, esp_apptrace_tmo_t *tmo)
{
/* nothing todo */
return ESP_OK;
@ -597,8 +626,8 @@ static uint32_t esp_apptrace_trax_down_buffer_write_nolock(uint8_t *data, uint32
uint32_t total_sz = 0;
while (total_sz < size) {
// ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock WRS %d-%d-%d %d", s_trace_buf.rb_down.wr, s_trace_buf.rb_down.rd,
// s_trace_buf.rb_down.cur_size, size);
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock WRS %d-%d-%d %d", s_trace_buf.rb_down.wr, s_trace_buf.rb_down.rd,
s_trace_buf.rb_down.cur_size, size);
uint32_t wr_sz = esp_apptrace_rb_write_size_get(&s_trace_buf.rb_down);
if (wr_sz == 0) {
break;
@ -607,15 +636,15 @@ static uint32_t esp_apptrace_trax_down_buffer_write_nolock(uint8_t *data, uint32
if (wr_sz > size - total_sz) {
wr_sz = size - total_sz;
}
// ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d", wr_sz);
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %d", wr_sz);
uint8_t *ptr = esp_apptrace_rb_produce(&s_trace_buf.rb_down, wr_sz);
if (!ptr) {
assert(false && "Failed to produce bytes to down buffer!");
}
// ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d to 0x%x from 0x%x", wr_sz, ptr, data + total_sz + wr_sz);
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %d to 0x%x from 0x%x", wr_sz, ptr, data + total_sz + wr_sz);
memcpy(ptr, data + total_sz, wr_sz);
total_sz += wr_sz;
// ESP_APPTRACE_LOGE("esp_apptrace_trax_down_buffer_write_nolock wr %d/%d", wr_sz, total_sz);
ESP_APPTRACE_LOGD("esp_apptrace_trax_down_buffer_write_nolock wr %d/%d", wr_sz, total_sz);
}
return total_sz;
}
@ -795,6 +824,11 @@ static esp_err_t esp_apptrace_trax_flush(uint32_t min_sz, esp_apptrace_tmo_t *tm
return res;
}
static bool esp_apptrace_trax_host_is_connected(void)
{
return eri_read(ESP_APPTRACE_TRAX_CTRL_REG) & ESP_APPTRACE_TRAX_HOST_CONNECT ? true : false;
}
static esp_err_t esp_apptrace_trax_dest_init()
{
for (int i = 0; i < ESP_APPTRACE_TRAX_BLOCKS_NUM; i++) {
@ -867,14 +901,11 @@ esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, uint32_t *size,
{
int res = ESP_OK;
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
uint8_t *(*apptrace_get_down_buffer)(uint32_t *, esp_apptrace_tmo_t *);
esp_err_t (*apptrace_put_down_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_get_down_buffer = esp_apptrace_trax_down_buffer_get;
apptrace_put_down_buffer = esp_apptrace_trax_down_buffer_put;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -888,12 +919,14 @@ esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, uint32_t *size,
esp_apptrace_tmo_init(&tmo, user_tmo);
uint32_t act_sz = *size;
*size = 0;
uint8_t * ptr = apptrace_get_down_buffer(&act_sz, &tmo);
uint8_t * ptr = hw->get_down_buffer(&act_sz, &tmo);
if (ptr && act_sz > 0) {
ESP_APPTRACE_LOGD("Read %d bytes from host", act_sz);
memcpy(buf, ptr, act_sz);
res = apptrace_put_down_buffer(ptr, &tmo);
res = hw->put_down_buffer(ptr, &tmo);
*size = act_sz;
} else {
res = ESP_ERR_TIMEOUT;
}
return res;
@ -902,12 +935,11 @@ esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, uint32_t *size,
uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size, uint32_t user_tmo)
{
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
uint8_t *(*apptrace_get_down_buffer)(uint32_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_get_down_buffer = esp_apptrace_trax_down_buffer_get;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return NULL;
@ -919,18 +951,17 @@ uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size,
// ESP_APPTRACE_LOGE("esp_apptrace_down_buffer_get %d", *size);
esp_apptrace_tmo_init(&tmo, user_tmo);
return apptrace_get_down_buffer(size, &tmo);
return hw->get_down_buffer(size, &tmo);
}
esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo)
{
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
esp_err_t (*apptrace_put_down_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_put_down_buffer = esp_apptrace_trax_down_buffer_put;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -941,21 +972,18 @@ esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, u
}
esp_apptrace_tmo_init(&tmo, user_tmo);
return apptrace_put_down_buffer(ptr, &tmo);
return hw->put_down_buffer(ptr, &tmo);
}
esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_t size, uint32_t user_tmo)
{
uint8_t *ptr = NULL;
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *);
esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_get_buffer = esp_apptrace_trax_get_buffer;
apptrace_put_buffer = esp_apptrace_trax_put_buffer;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -966,7 +994,7 @@ esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_
}
esp_apptrace_tmo_init(&tmo, user_tmo);
ptr = apptrace_get_buffer(size, &tmo);
ptr = hw->get_up_buffer(size, &tmo);
if (ptr == NULL) {
return ESP_ERR_NO_MEM;
}
@ -976,7 +1004,7 @@ esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_
memcpy(ptr, data, size);
// now indicate that this buffer is ready to be sent off to host
return apptrace_put_buffer(ptr, &tmo);
return hw->put_up_buffer(ptr, &tmo);
}
int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const char *fmt, va_list ap)
@ -984,14 +1012,11 @@ int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const c
uint16_t nargs = 0;
uint8_t *pout, *p = (uint8_t *)fmt;
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *);
esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_get_buffer = esp_apptrace_trax_get_buffer;
apptrace_put_buffer = esp_apptrace_trax_put_buffer;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -1014,7 +1039,7 @@ int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const c
ESP_APPTRACE_LOGE("Failed to store all printf args!");
}
pout = apptrace_get_buffer(1 + sizeof(char *) + nargs * sizeof(uint32_t), &tmo);
pout = hw->get_up_buffer(1 + sizeof(char *) + nargs * sizeof(uint32_t), &tmo);
if (pout == NULL) {
ESP_APPTRACE_LOGE("Failed to get buffer!");
return -1;
@ -1031,7 +1056,7 @@ int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const c
ESP_APPTRACE_LOGD("arg %x", arg);
}
int ret = apptrace_put_buffer(p, &tmo);
int ret = hw->put_up_buffer(p, &tmo);
if (ret != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to put printf buf (%d)!", ret);
return -1;
@ -1048,12 +1073,11 @@ int esp_apptrace_vprintf(const char *fmt, va_list ap)
uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32_t user_tmo)
{
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
uint8_t *(*apptrace_get_buffer)(uint32_t, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_get_buffer = esp_apptrace_trax_get_buffer;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return NULL;
@ -1064,18 +1088,17 @@ uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32
}
esp_apptrace_tmo_init(&tmo, user_tmo);
return apptrace_get_buffer(size, &tmo);
return hw->get_up_buffer(size, &tmo);
}
esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo)
{
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
esp_err_t (*apptrace_put_buffer)(uint8_t *, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_put_buffer = esp_apptrace_trax_put_buffer;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -1086,18 +1109,17 @@ esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32
}
esp_apptrace_tmo_init(&tmo, user_tmo);
return apptrace_put_buffer(ptr, &tmo);
return hw->put_up_buffer(ptr, &tmo);
}
esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, uint32_t usr_tmo)
{
esp_apptrace_tmo_t tmo;
//TODO: use ptr to HW transport iface struct
esp_err_t (*apptrace_flush)(uint32_t, esp_apptrace_tmo_t *);
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
apptrace_flush = esp_apptrace_trax_flush;
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
@ -1108,7 +1130,7 @@ esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, u
}
esp_apptrace_tmo_init(&tmo, usr_tmo);
return apptrace_flush(min_sz, &tmo);
return hw->flush_up_buffer(min_sz, &tmo);
}
esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t usr_tmo)
@ -1134,4 +1156,23 @@ esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t usr_tmo)
return res;
}
bool esp_apptrace_host_is_connected(esp_apptrace_dest_t dest)
{
esp_apptrace_hw_t *hw = NULL;
if (dest == ESP_APPTRACE_DEST_TRAX) {
#if CONFIG_ESP32_APPTRACE_DEST_TRAX
hw = ESP_APPTRACE_HW(ESP_APPTRACE_HW_TRAX);
#else
ESP_APPTRACE_LOGE("Application tracing via TRAX is disabled in menuconfig!");
return ESP_ERR_NOT_SUPPORTED;
#endif
} else {
ESP_APPTRACE_LOGE("Trace destinations other then TRAX are not supported yet!");
return ESP_ERR_NOT_SUPPORTED;
}
return hw->host_is_connected();
}
#endif

View File

@ -4,12 +4,14 @@
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_ADD_INCLUDEDIRS = include
COMPONENT_ADD_LDFLAGS := -lapp_trace
COMPONENT_ADD_LDFLAGS = -lapp_trace
# do not produce gcov info for this module, it is used as transport for gcov
CFLAGS := $(subst --coverage,,$(CFLAGS))
ifdef CONFIG_SYSVIEW_ENABLE
#COMPONENT_EXTRA_INCLUDES := freertos
COMPONENT_ADD_INCLUDEDIRS += \
sys_view/Config \
@ -17,8 +19,11 @@ COMPONENT_ADD_INCLUDEDIRS += \
sys_view/Sample/OS
COMPONENT_SRCDIRS += \
gcov \
sys_view/SEGGER \
sys_view/Sample/OS \
sys_view/Sample/Config \
sys_view/esp32
else
COMPONENT_SRCDIRS += gcov
endif

View File

@ -0,0 +1,93 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This module implements runtime file I/O API for GCOV.
#include "esp_task_wdt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/cpu.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "esp_app_trace.h"
#if CONFIG_ESP32_APPTRACE_ENABLE
#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include "esp_log.h"
const static char *TAG = "esp_gcov_rtio";
static void (*s_gcov_exit)(void);
static uint8_t s_gcov_down_buf[256];
void esp_gcov_dump()
{
#if CONFIG_FREERTOS_UNICORE == 0
esp_cpu_stall(!xPortGetCoreID());
#endif
while (!esp_apptrace_host_is_connected(ESP_APPTRACE_DEST_TRAX)) {
// to avoid complains that task watchdog got triggered for other tasks
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_feed=1;
TIMERG0.wdt_wprotect=0;
// to avoid reboot on INT_WDT
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_feed=1;
TIMERG1.wdt_wprotect=0;
}
if (s_gcov_exit) {
s_gcov_exit();
}
int ret = esp_apptrace_fstop(ESP_APPTRACE_DEST_TRAX);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send files transfer stop cmd (%d)!\n", ret);
}
}
int gcov_rtio_atexit(void (*function)(void))
{
s_gcov_exit = function;
esp_apptrace_down_buffer_config(s_gcov_down_buf, sizeof(s_gcov_down_buf));
return 0;
}
void *gcov_rtio_fopen(const char *path, const char *mode)
{
return esp_apptrace_fopen(ESP_APPTRACE_DEST_TRAX, path, mode);
}
int gcov_rtio_fclose(void *stream)
{
return esp_apptrace_fclose(ESP_APPTRACE_DEST_TRAX, stream);
}
size_t gcov_rtio_fwrite(const void *ptr, size_t size, size_t nmemb, void *stream)
{
return esp_apptrace_fwrite(ESP_APPTRACE_DEST_TRAX, ptr, size, nmemb, stream);
}
int gcov_rtio_fseek(void *stream, long offset, int whence)
{
return esp_apptrace_fseek(ESP_APPTRACE_DEST_TRAX, stream, offset, whence);
}
long gcov_rtio_ftell(void *stream)
{
return esp_apptrace_ftell(ESP_APPTRACE_DEST_TRAX, stream);
}
#endif

View File

@ -0,0 +1,340 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Hot It Works
// ************
//
// This module implements host file I/O protocol on top of apptrace module.
// The protocol is enough simple. It sends command with arguments to the host and receives response from it.
// Responses contains return values of respective file I/O API. This value is returned to the caller.
// Commands has the following format:
// * Header. See esp_apptrace_fcmd_hdr_t.
// * Operation arguments. See file operation helper structures below.
#include <string.h>
#include "esp_app_trace.h"
#if CONFIG_ESP32_APPTRACE_ENABLE
#define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include "esp_log.h"
const static char *TAG = "esp_host_file_io";
#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_STOP 0x6 // indicates that there is no files to transfer
/** File operation header */
typedef struct {
uint8_t cmd; ///< Command ID
} esp_apptrace_fcmd_hdr_t;
/** Helper structure for fopen */
typedef struct {
const char *path;
uint16_t path_len;
const char *mode;
uint16_t mode_len;
} esp_apptrace_fopen_args_t;
/** Helper structure for fclose */
typedef struct {
void *file;
} esp_apptrace_fclose_args_t;
/** Helper structure for fwrite */
typedef struct {
void * buf;
size_t size;
void * file;
} esp_apptrace_fwrite_args_t;
/** Helper structure for fread */
typedef struct {
size_t size;
void * file;
} esp_apptrace_fread_args_t;
/** Helper structure for fseek */
typedef struct {
long offset;
int whence;
void * file;
} esp_apptrace_fseek_args_t;
/** Helper structure for ftell */
typedef struct {
void *file;
} esp_apptrace_ftell_args_t;
static esp_err_t esp_apptrace_file_cmd_send(esp_apptrace_dest_t dest, uint8_t cmd, void (*prep_args)(uint8_t *, void *), void *args, uint32_t args_len)
{
esp_err_t ret;
esp_apptrace_fcmd_hdr_t *hdr;
uint8_t *ptr = esp_apptrace_buffer_get(dest, sizeof(*hdr) + args_len, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
if (ptr == NULL) {
return ESP_ERR_NO_MEM;
}
hdr = (esp_apptrace_fcmd_hdr_t *)ptr;
hdr->cmd = cmd;
if (prep_args) {
prep_args(ptr + sizeof(hdr->cmd), args);
}
// now indicate that this buffer is ready to be sent off to host
ret = esp_apptrace_buffer_put(dest, ptr, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to put apptrace buffer (%d)!", ret);
return ret;
}
ret = esp_apptrace_flush(dest, ESP_APPTRACE_TMO_INFINITE);//TODO: finite tmo
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to flush apptrace buffer (%d)!", ret);
return ret;
}
return ESP_OK;
}
static esp_err_t esp_apptrace_file_rsp_recv(esp_apptrace_dest_t dest, uint8_t *buf, uint32_t buf_len)
{
uint32_t tot_rd = 0;
while (tot_rd < buf_len) {
uint32_t rd_size = buf_len - tot_rd;
esp_err_t ret = esp_apptrace_read(dest, buf, &rd_size, ESP_APPTRACE_TMO_INFINITE); //TODO: finite tmo
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return ret;
}
tot_rd += rd_size;
}
return ESP_OK;
}
static void esp_apptrace_fopen_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fopen_args_t *args = priv;
memcpy(buf, args->path, args->path_len);
memcpy(buf + args->path_len, args->mode, args->mode_len);
}
void *esp_apptrace_fopen(esp_apptrace_dest_t dest, const char *path, const char *mode)
{
esp_apptrace_fopen_args_t cmd_args;
cmd_args.path = path;
cmd_args.path_len = strlen(path) + 1;
cmd_args.mode = mode;
cmd_args.mode_len = strlen(mode) + 1;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FOPEN, esp_apptrace_fopen_args_prepare,
&cmd_args, cmd_args.path_len+cmd_args.mode_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return NULL;
}
// now read the answer
uint8_t resp[sizeof(void *)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return NULL;
}
return *((void **)resp);
}
static void esp_apptrace_fclose_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fclose_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_fclose(esp_apptrace_dest_t dest, void *stream)
{
esp_apptrace_fclose_args_t cmd_args;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FCLOSE, esp_apptrace_fclose_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return EOF;
}
// now read the answer
uint8_t resp[sizeof(int)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return EOF;
}
return *((int *)resp);
}
static void esp_apptrace_fwrite_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fwrite_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
memcpy(buf + sizeof(args->file), args->buf, args->size);
}
size_t esp_apptrace_fwrite(esp_apptrace_dest_t dest, const void *ptr, size_t size, size_t nmemb, void *stream)
{
esp_apptrace_fwrite_args_t cmd_args;
cmd_args.buf = (void *)ptr;
cmd_args.size = size * nmemb;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FWRITE, esp_apptrace_fwrite_args_prepare,
&cmd_args, sizeof(cmd_args.file)+cmd_args.size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return 0;
}
// now read the answer
uint8_t resp[sizeof(size_t)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return 0;
}
return *((size_t *)resp);
}
static void esp_apptrace_fread_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fread_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
memcpy(buf + sizeof(args->file), &args->size, sizeof(args->size));
}
size_t esp_apptrace_fread(esp_apptrace_dest_t dest, void *ptr, size_t size, size_t nmemb, void *stream)
{
esp_apptrace_fread_args_t cmd_args;
cmd_args.size = size * nmemb;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FREAD, esp_apptrace_fread_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return 0;
}
// now read the answer
uint8_t resp[sizeof(size_t)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return 0;
}
if (*((size_t *)resp) > 0) {
ret = esp_apptrace_file_rsp_recv(dest, ptr, *((size_t *)resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read file data (%d)!", ret);
return 0;
}
}
return *((size_t *)resp);
}
static void esp_apptrace_fseek_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_fseek_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_fseek(esp_apptrace_dest_t dest, void *stream, long offset, int whence)
{
esp_apptrace_fseek_args_t cmd_args;
cmd_args.file = stream;
cmd_args.offset = offset;
cmd_args.whence = whence;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FSEEK, esp_apptrace_fseek_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return -1;
}
// now read the answer
uint8_t resp[sizeof(int)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return -1;
}
return *((int *)resp);
}
static void esp_apptrace_ftell_args_prepare(uint8_t *buf, void *priv)
{
esp_apptrace_ftell_args_t *args = priv;
memcpy(buf, &args->file, sizeof(args->file));
}
int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream)
{
esp_apptrace_ftell_args_t cmd_args;
cmd_args.file = stream;
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_FTELL, esp_apptrace_ftell_args_prepare,
&cmd_args, sizeof(cmd_args));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send file cmd (%d)!", ret);
return -1;
}
// now read the answer
uint8_t resp[sizeof(int)];
ret = esp_apptrace_file_rsp_recv(dest, resp, sizeof(resp));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to read response (%d)!", ret);
return -1;
}
return *((int *)resp);
}
int esp_apptrace_fstop(esp_apptrace_dest_t dest)
{
esp_err_t ret = esp_apptrace_file_cmd_send(dest, ESP_APPTRACE_FILE_CMD_STOP, NULL, NULL, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send files transfer stop cmd (%d)!", ret);
}
return ret;
}
#endif

View File

@ -161,4 +161,105 @@ uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size,
*/
esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t tmo);
/**
* @brief Checks whether host is connected.
*
* @param dest Indicates HW interface to use.
*
* @return true if host is connected, otherwise false
*/
bool esp_apptrace_host_is_connected(esp_apptrace_dest_t dest);
/**
* @brief Opens file on host.
* This function has the same semantic as 'fopen' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param path Path to file.
* @param mode Mode string. See fopen for details.
*
* @return non zero file handle on success, otherwise 0
*/
void *esp_apptrace_fopen(esp_apptrace_dest_t dest, const char *path, const char *mode);
/**
* @brief Closes file on host.
* This function has the same semantic as 'fclose' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Zero on success, otherwise non-zero. See fclose for details.
*/
int esp_apptrace_fclose(esp_apptrace_dest_t dest, void *stream);
/**
* @brief Writes to file on host.
* This function has the same semantic as 'fwrite' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param ptr Address of data to write.
* @param size Size of an item.
* @param nmemb Number of items to write.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Number of written items. See fwrite for details.
*/
size_t esp_apptrace_fwrite(esp_apptrace_dest_t dest, const void *ptr, size_t size, size_t nmemb, void *stream);
/**
* @brief Read file on host.
* This function has the same semantic as 'fread' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param ptr Address to store read data.
* @param size Size of an item.
* @param nmemb Number of items to read.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Number of read items. See fread for details.
*/
size_t esp_apptrace_fread(esp_apptrace_dest_t dest, void *ptr, size_t size, size_t nmemb, void *stream);
/**
* @brief Set position indicator in file on host.
* This function has the same semantic as 'fseek' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param stream File handle returned by esp_apptrace_fopen.
* @param offset Offset. See fseek for details.
* @param whence Position in file. See fseek for details.
*
* @return Zero on success, otherwise non-zero. See fseek for details.
*/
int esp_apptrace_fseek(esp_apptrace_dest_t dest, void *stream, long offset, int whence);
/**
* @brief Get current position indicator for file on host.
* This function has the same semantic as 'ftell' except for the first argument.
*
* @param dest Indicates HW interface to use.
* @param stream File handle returned by esp_apptrace_fopen.
*
* @return Current position in file. See ftell for details.
*/
int esp_apptrace_ftell(esp_apptrace_dest_t dest, void *stream);
/**
* @brief Indicates to the host that all file operations are completed.
* This function should be called after all file operations are finished and
* indicate to the host that it can perform cleanup operations (close open files etc.).
*
* @param dest Indicates HW interface to use.
*
* @return ESP_OK on success, otherwise see esp_err_t
*/
int esp_apptrace_fstop(esp_apptrace_dest_t dest);
/**
* @brief Triggers gcov info dump.
* This function waits for the host to connect to target before dumping data.
*/
void esp_gcov_dump(void);
#endif

View File

@ -89,13 +89,13 @@ SECTIONS
*libesp32.a:core_dump.o(.literal .text .literal.* .text.*)
*libapp_trace.a:(.literal .text .literal.* .text.*)
*libxtensa-debug-module.a:eri.o(.literal .text .literal.* .text.*)
*libesp32.a:app_trace.o(.literal .text .literal.* .text.*)
*libphy.a:(.literal .text .literal.* .text.*)
*librtc.a:(.literal .text .literal.* .text.*)
*libsoc.a:(.literal .text .literal.* .text.*)
*libhal.a:(.literal .text .literal.* .text.*)
*libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*)
*libspi_flash.a:spi_flash_rom_patch.o(.literal .text .literal.* .text.*)
*libgcov.a:(.literal .text .literal.* .text.*)
_iram_text_end = ABSOLUTE(.);
} > iram0_0_seg
@ -117,6 +117,7 @@ SECTIONS
*libesp32.a:panic.o(.rodata .rodata.*)
*libphy.a:(.rodata .rodata.*)
*libapp_trace.a:(.rodata .rodata.*)
*libgcov.a:(.rodata .rodata.*)
*libheap.a:multi_heap.o(.rodata .rodata.*)
*libheap.a:multi_heap_poisoning.o(.rodata .rodata.*)
_data_end = ABSOLUTE(.);

View File

@ -0,0 +1,25 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := gcov_example
include $(IDF_PATH)/make/project.mk
GCOV := $(call dequote,$(CONFIG_TOOLPREFIX))gcov
REPORT_DIR := $(BUILD_DIR_BASE)/coverage_report
lcov-report:
echo "Generating coverage report in: $(REPORT_DIR)"
echo "Using gcov: $(GCOV)"
mkdir -p $(REPORT_DIR)/html
lcov --gcov-tool $(GCOV) -c -d $(BUILD_DIR_BASE) -o $(REPORT_DIR)/$(PROJECT_NAME).info
genhtml -o $(REPORT_DIR)/html $(REPORT_DIR)/$(PROJECT_NAME).info
cov-data-clean:
echo "Remove coverage data files..."
find $(BUILD_DIR_BASE) -name "*.gcda" -exec rm {} +
rm -rf $(REPORT_DIR)
.PHONY: lcov-report cov-data-clean

View File

@ -0,0 +1,112 @@
# Blink Example with Coverage Info
See the README.md file in the upper level 'examples' directory for more information about examples.
GCC has useful feature which allows to generate code coverage information. Generated data show how many times every program execution paths has been taken.
Basing on coverage data developers can detect untested pieces of code and also it gives valuable information about critical (frequently used) execution paths.
In general case when coverage option is enabled GCC generates additional code to accumulate necessary data and save them into files. File system is not always available
in ESP32 based projects or size of the file storage can be very limited to keep all the coverage data. To overcome those limitations IDF provides functionality
to transfer the data to the host and save them on host file system. The data transfer is done via JTAG.
This example shows how to generate coverage information for the program.
## How To Gather Coverage Info
Below are the steps which should be performed to obtain coverage info. Steps 1-3 are already done for this example project. They should be performed if user wants to fork new IDF-based project and needs to collect coverage info.
1. Enable application tracing module in menuconfig. Choose `Trace memory` in `Component config -> Application Level Tracing -> Data Destination`.
2. Enable coverage info generation for necessary source files. To do this add the following line to the 'component.mk' files of your project:
`CFLAGS += --coverage`
It will enable coverage info for all source files of your component. If you need to enable the option only for certain files you need to add the following line for every file of interest:
`gcov_example.o: CFLAGS += --coverage`
Replace `gcov_example.o` with path to your file.
3. Add call to `esp_gcov_dump` function in your program. This function will wait for command from the host and dump coverage data. The exact place where to put call to `esp_gcov_dump` depends on the program.
Usually it should be placed at the end of the program execution (at exit). See `gcov_example.c` for example.
4. Build, flash and run program.
5. Wait until `esp_gcov_dump` is called. To detect this a call to `printf` can be used (see `gcov_example.c`) or, for example, you can use a LED to indicate the readiness to dump data.
6. Connect OpenOCD to the target and start telnet session with it.
7. Run the following OpenOCD command:
`esp32 gcov`
Example of the command output:
```
> esp32 gcov
Total trace memory: 16384 bytes
Connect targets...
Target halted. PRO_CPU: PC=0x400D0CDC (active) APP_CPU: PC=0x00000000
esp32: target state: halted
Resume targets
Targets connected.
Open file '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example.gcda'
Open file '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Open file '/home/alexey/projects/esp/esp-idf/examples/system/gcov/build/main/gcov_example_func.gcda'
Disconnect targets...
Target halted. PRO_CPU: PC=0x400D17CA (active) APP_CPU: PC=0x400D0CDC
esp32: target state: halted
Resume targets
Targets disconnected.
>
```
As shown in the output above there can be errors reported. This is because GCOV code tries to open non-existing coverage data files for reading before writing to them. It is normal situation and actually is not an error.
GCOV will save coverage data for every source file in directories for corresponding object files, usually under root build directory `build`.
## How To Process Coverage Info
There are several ways to process collected data. Two of the most common are:
1. Using `gcov` tool supplied along with xtensa toolchain. See [GCOV documentation](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) for details.
2. Using `lcov` and `genhtml` tools. This way allows to generate pretty looking coverage reports in html. This example shows how to add build target to generate such kind of reports.
Add the following lines to you project's `Makefile` after the line including `project.mk`:
```
GCOV := $(call dequote,$(CONFIG_TOOLPREFIX))gcov
REPORT_DIR := $(BUILD_DIR_BASE)/coverage_report
lcov-report:
echo "Generating coverage report in: $(REPORT_DIR)"
echo "Using gcov: $(GCOV)"
mkdir -p $(REPORT_DIR)/html
lcov --gcov-tool $(GCOV) -c -d $(BUILD_DIR_BASE) -o $(REPORT_DIR)/$(PROJECT_NAME).info
genhtml -o $(REPORT_DIR)/html $(REPORT_DIR)/$(PROJECT_NAME).info
cov-data-clean:
echo "Remove coverage data files..."
find $(BUILD_DIR_BASE) -name "*.gcda" -exec rm {} +
rm -rf $(REPORT_DIR)
.PHONY: lcov-report cov-data-clean
```
Those lines add two build targets:
* `lcov-report` - generates html coverage report in `$(BUILD_DIR_BASE)/coverage_report/html` directory.
* `cov-data-clean` - removes all coverage data files and reports.
To generate report type the following command:
`make lcov-report`
The sample output of the command is below:
```
Generating coverage report in: /home/alexey/projects/esp/esp-idf/examples/system/gcov/build/coverage_report
Using gcov: ~/projects/esp/crosstool-NG/builds/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcov
Capturing coverage data from /home/alexey/projects/esp/esp-idf/examples/system/gcov/build
Found gcov version: 5.2.0
Scanning /home/alexey/projects/esp/esp-idf/examples/system/gcov/build for .gcda files ...
Found 2 data files in /home/alexey/projects/esp/esp-idf/examples/system/gcov/build
Processing main/gcov_example_func.gcda
Processing main/gcov_example.gcda
Finished .info-file creation
Reading data file /home/alexey/projects/esp/esp-idf/examples/system/gcov/build/coverage_report/gcov_example.info
Found 2 entries.
Found common filename prefix "/home/alexey/projects/esp/esp-idf/examples/system/gcov"
Writing .css and .png files.
Generating output.
Processing file main/gcov_example.c
Processing file main/gcov_example_func.c
Writing directory view page.
Overall coverage rate:
lines......: 90.0% (18 of 20 lines)
functions..: 100.0% (3 of 3 functions)
```

View File

@ -0,0 +1,14 @@
menu "Example Configuration"
config BLINK_GPIO
int "Blink GPIO number"
range 0 34
default 5
help
GPIO number (IOxx) to blink on and off.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
GPIOs 35-39 are input-only so cannot be used as outputs.
endmenu

View File

@ -0,0 +1,6 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
CFLAGS += --coverage

View File

@ -0,0 +1,55 @@
/* Blink Example with covergae info
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_app_trace.h"
#include "sdkconfig.h"
/* Can run 'make menuconfig' to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO
void blink_dummy_func(void);
void blink_task(void *pvParameter)
{
int dump_gcov_after = 2;
/* Configure the IOMUX register for pad BLINK_GPIO (some pads are
muxed to GPIO on reset already, but some default to other
functions and need to be switched to GPIO. Consult the
Technical Reference for a list of pads and their default
functions.)
*/
gpio_pad_select_gpio(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
while(dump_gcov_after-- > 0) {
/* Blink off (output low) */
gpio_set_level(BLINK_GPIO, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
/* Blink on (output high) */
gpio_set_level(BLINK_GPIO, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
blink_dummy_func();
}
// Dump gcov data
printf("Ready to dump GCOV data...\n");
esp_gcov_dump();
printf("GCOV data have been dumped.\n");
while(1);
}
void app_main()
{
xTaskCreate(&blink_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}

View File

@ -0,0 +1,9 @@
#include <stdio.h>
void blink_dummy_func(void)
{
static int i;
printf("Counter = %d\n", i++);
}

View File

@ -0,0 +1,7 @@
CONFIG_ESP32_APPTRACE_DEST_TRAX=y
# CONFIG_ESP32_APPTRACE_DEST_NONE is not set
CONFIG_ESP32_APPTRACE_ENABLE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO=-1
CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH=0
CONFIG_ESP32_APPTRACE_PENDING_DATA_SIZE_MAX=0

View File

@ -125,7 +125,7 @@ COMPONENT_DIRS += $(abspath $(SRCDIRS))
endif
# The project Makefile can define a list of components, but if it does not do this we just take all available components
# in the component dirs. A component is COMPONENT_DIRS directory, or immediate subdirectory,
# in the component dirs. A component is COMPONENT_DIRS directory, or immediate subdirectory,
# which contains a component.mk file.
#
# Use the "make list-components" target to debug this step.
@ -222,6 +222,7 @@ LDFLAGS ?= -nostdlib \
$(COMPONENT_LDFLAGS) \
-lgcc \
-lstdc++ \
-lgcov \
-Wl,--end-group \
-Wl,-EL