gdbstub component

This commit is contained in:
Dmitry 2021-04-15 10:52:09 +03:00
parent 74f9ce437f
commit 7bb91f912c
22 changed files with 681 additions and 161 deletions

View File

@ -1319,6 +1319,9 @@ esp_err_t uart_flush_input(uart_port_t uart_num)
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags)
{
esp_err_t r;
#ifdef CONFIG_ESP_GDBSTUB_ENABLED
UART_CHECK((uart_num != CONFIG_ESP_CONSOLE_UART_NUM), "UART used by GDB-stubs! Please disable GDB in menuconfig.", ESP_FAIL);
#endif // CONFIG_ESP_GDBSTUB_ENABLED
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
UART_CHECK((rx_buffer_size > SOC_UART_FIFO_LEN), "uart rx buffer length error", ESP_FAIL);
UART_CHECK((tx_buffer_size > SOC_UART_FIFO_LEN) || (tx_buffer_size == 0), "uart tx buffer length error", ESP_FAIL);

View File

@ -1,16 +1,15 @@
idf_build_get_property(target IDF_TARGET)
idf_component_register(SRCS "src/gdbstub.c" "src/packet.c"
set(esp_gdbstub_srcs "src/gdbstub.c"
"src/packet.c"
"esp_common/gdbstub_common.c"
"${target}/gdbstub_${target}.c"
"xtensa/gdbstub-entry.S"
"xtensa/gdbstub_xtensa.c")
idf_component_register(SRCS "${esp_gdbstub_srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include"
PRIV_INCLUDE_DIRS "private_include" "${target}" "xtensa"
LDFRAGMENTS "linker.lf"
REQUIRES "freertos"
PRIV_REQUIRES "soc" "esp_rom")
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
target_include_directories(${COMPONENT_LIB} PUBLIC "xtensa" "${target}")
target_sources(${COMPONENT_LIB} PRIVATE "xtensa/gdbstub_xtensa.c" "${target}/gdbstub_${target}.c")
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
target_include_directories(${COMPONENT_LIB} PUBLIC "riscv" "${target}")
target_sources(${COMPONENT_LIB} PRIVATE "riscv/gdbstub_riscv.c" "${target}/gdbstub_${target}.c")
endif()
PRIV_REQUIRES "soc" "xtensa" "esp_rom")

View File

@ -16,41 +16,3 @@
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#define UART_NUM CONFIG_ESP_CONSOLE_UART_NUM
void esp_gdbstub_target_init(void)
{
}
int esp_gdbstub_getchar(void)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_RXFIFO_CNT) == 0) {
;
}
return REG_READ(UART_FIFO_REG(UART_NUM));
}
void esp_gdbstub_putchar(int c)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_TXFIFO_CNT) >= 126) {
;
}
REG_WRITE(UART_FIFO_REG(UART_NUM), c);
}
void esp_gdbstub_flush()
{
//not needed for uart
}
int esp_gdbstub_readmem(intptr_t addr)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see esp_cpu_configure_region_protection */
return -1;
}
uint32_t val_aligned = *(uint32_t *)(addr & (~3));
uint32_t shift = (addr & 3) * 8;
return (val_aligned >> shift) & 0xff;
}

View File

@ -16,41 +16,3 @@
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#define UART_NUM CONFIG_ESP_CONSOLE_UART_NUM
void esp_gdbstub_target_init()
{
}
int esp_gdbstub_getchar()
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_RXFIFO_CNT) == 0) {
;
}
return REG_READ(UART_FIFO_AHB_REG(UART_NUM));
}
void esp_gdbstub_putchar(int c)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_TXFIFO_CNT) >= 126) {
;
}
REG_WRITE(UART_FIFO_AHB_REG(UART_NUM), c);
}
void esp_gdbstub_flush()
{
//not needed for uart
}
int esp_gdbstub_readmem(intptr_t addr)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see esp_cpu_configure_region_protection */
return -1;
}
uint32_t val_aligned = *(uint32_t *)(addr & (~3));
uint32_t shift = (addr & 3) * 8;
return (val_aligned >> shift) & 0xff;
}

View File

@ -17,40 +17,3 @@
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#define UART_NUM CONFIG_ESP_CONSOLE_UART_NUM
void esp_gdbstub_target_init()
{
}
int esp_gdbstub_getchar()
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_RXFIFO_CNT) == 0) {
;
}
return REG_READ(UART_FIFO_AHB_REG(UART_NUM));
}
void esp_gdbstub_putchar(int c)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_TXFIFO_CNT) >= 126) {
;
}
REG_WRITE(UART_FIFO_AHB_REG(UART_NUM), c);
}
void esp_gdbstub_flush()
{
//not needed for uart
}
int esp_gdbstub_readmem(intptr_t addr)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see cpu_configure_region_protection */
return -1;
}
uint32_t val_aligned = *(uint32_t *)(addr & (~3));
uint32_t shift = (addr & 3) * 8;
return (val_aligned >> shift) & 0xff;
}

View File

@ -0,0 +1,129 @@
// Copyright 2015-2019 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.
#include "soc/uart_periph.h"
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#include "hal/uart_ll.h"
#define UART_NUM CONFIG_ESP_CONSOLE_UART_NUM
static uart_dev_t *gdb_uart = NULL;
void esp_gdbstub_target_init(void)
{
switch (UART_NUM) {
case 0:
gdb_uart = &UART0;
break;
case 1:
gdb_uart = &UART1;
break;
case 2:
gdb_uart = &UART2;
break;
default:
gdb_uart = &UART0;
break;
}
}
int esp_gdbstub_getchar(void)
{
if (gdb_uart == NULL) {
esp_gdbstub_target_init();
}
unsigned char data;
while (uart_ll_get_rxfifo_len(gdb_uart) == 0) {
}
uart_ll_read_rxfifo(gdb_uart, &data, 1);
return data;
}
void esp_gdbstub_putchar(int c)
{
if (gdb_uart == NULL) {
esp_gdbstub_target_init();
}
while (uart_ll_get_txfifo_len(gdb_uart) <= 126) {
}
uart_ll_write_txfifo(gdb_uart, (uint8_t *)&c, 1);
}
void esp_gdbstub_flush()
{
//not needed for uart
}
int esp_gdbstub_getfifo()
{
if (gdb_uart == NULL) {
esp_gdbstub_target_init();
}
int doDebug = 0;
int fifolen = uart_ll_get_rxfifo_len(gdb_uart);
while (fifolen != 0) {
unsigned char data;
uart_ll_read_rxfifo(gdb_uart, &data, 1);
if (data == 0x3) {
doDebug = 1; //Check if any of the chars is Ctrl+C. Throw away rest.
}
if (data == '+') {
doDebug = 1; //Check if any of the chars is '+'. Throw away rest.
}
fifolen--;
}
uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
return doDebug;
}
int esp_gdbstub_readmem(intptr_t addr)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see cpu_configure_region_protection */
return -1;
}
uint32_t val_aligned = *(uint32_t *)(addr & (~3));
uint32_t shift = (addr & 3) * 8;
return (val_aligned >> shift) & 0xff;
}
int esp_gdbstub_writemem(unsigned int addr, unsigned char data)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see cpu_configure_region_protection */
return -1;
}
int *i = (int *)(addr & (~3));
if ((addr & 3) == 0) {
*i = (*i & 0xffffff00) | (data << 0);
}
if ((addr & 3) == 1) {
*i = (*i & 0xffff00ff) | (data << 8);
}
if ((addr & 3) == 2) {
*i = (*i & 0xff00ffff) | (data << 16);
}
if ((addr & 3) == 3) {
*i = (*i & 0x00ffffff) | (data << 24);
}
asm volatile("ISYNC\nISYNC\n");
return 0;
}

View File

@ -14,13 +14,11 @@
#pragma once
#include "esp_gdbstub_arch.h"
#ifdef __cplusplus
extern "C" {
#endif
void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame) __attribute__((noreturn));
void esp_gdbstub_init(void);
#ifdef __cplusplus
}

View File

@ -18,7 +18,7 @@
#include <stddef.h>
#include <stdbool.h>
#include "esp_gdbstub.h"
#include "esp_gdbstub_arch.h"
#include "sdkconfig.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
@ -30,6 +30,7 @@
#define GDBSTUB_ST_ENDPACKET -1
#define GDBSTUB_ST_ERR -2
#define GDBSTUB_ST_OK -3
#define GDBSTUB_ST_CONT -4
/* Special task index values */
#define GDBSTUB_CUR_TASK_INDEX_UNKNOWN -1
@ -125,6 +126,20 @@ int esp_gdbstub_readmem(intptr_t addr);
*/
void esp_gdbstub_flush(void);
/**
* Write a byte to target memory
* @param addr address
* @param data data byte
* @return 0 in case of success, -1 in case of error
*/
int esp_gdbstub_writemem(unsigned int addr, unsigned char data);
/**
* Read a data from fifo and detect start symbol
* @return 1 if break symbol was detected, or 0 if not
*/
int esp_gdbstub_getfifo(void);
/**** GDB packet related functions ****/
/** Begin a packet */
@ -143,7 +158,7 @@ void esp_gdbstub_send_hex(int val, int bits);
void esp_gdbstub_send_end(void);
/** Send a packet with a string as content */
void esp_gdbstub_send_str_packet(const char* str);
void esp_gdbstub_send_str_packet(const char *str);
/** Get a hex value from the gdb packet */
uint32_t esp_gdbstub_gethex(const unsigned char **ptr, int bits);

View File

@ -17,6 +17,15 @@
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#include "soc/uart_reg.h"
#include "soc/periph_defs.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "hal/wdt_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//#include "esp_task_wdt.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static inline int gdb_tid_to_task_index(int tid);
@ -25,14 +34,17 @@ static void init_task_info(void);
static void find_paniced_task_index(void);
static void set_active_task(size_t index);
static int handle_task_commands(unsigned char *cmd, int len);
static void esp_gdbstub_send_str_as_hex(const char *str);
#endif
static void send_reason(void);
static esp_gdbstub_scratch_t s_scratch;
static esp_gdbstub_gdb_regfile_t *gdb_local_regfile = &s_scratch.regfile;
/**
* @breef panic handler
*/
void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame)
{
#ifndef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
@ -80,7 +92,9 @@ void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame)
}
}
/**
* Set interrupt reason to GDB
*/
static void send_reason(void)
{
esp_gdbstub_send_start();
@ -89,13 +103,159 @@ static void send_reason(void)
esp_gdbstub_send_end();
}
/**
* Swap bytes in word
*/
static uint32_t gdbstub_hton(uint32_t i)
{
return __builtin_bswap32(i);
}
static wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
static wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
static wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
static bool wdt0_context_enabled = false;
static bool wdt1_context_enabled = false;
static bool rtc_wdt_ctx_enabled = false;
/**
* Disable all enabled WDTs
*/
static inline void disable_all_wdts(void)
{
wdt0_context_enabled = wdt_hal_is_enabled(&wdt0_context);
wdt1_context_enabled = wdt_hal_is_enabled(&wdt1_context);
rtc_wdt_ctx_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx);
//Task WDT is the Main Watchdog Timer of Timer Group 0
if (true == wdt0_context_enabled) {
wdt_hal_write_protect_disable(&wdt0_context);
wdt_hal_disable(&wdt0_context);
wdt_hal_feed(&wdt0_context);
wdt_hal_write_protect_enable(&wdt0_context);
}
//Interupt WDT is the Main Watchdog Timer of Timer Group 1
if (true == wdt1_context_enabled) {
wdt_hal_write_protect_disable(&wdt1_context);
wdt_hal_disable(&wdt1_context);
wdt_hal_feed(&wdt1_context);
wdt_hal_write_protect_enable(&wdt1_context);
}
if (true == rtc_wdt_ctx_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_feed(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
}
}
/**
* Enable all enabled WDTs
*/
static inline void enable_all_wdts(void)
{
//Task WDT is the Main Watchdog Timer of Timer Group 0
if (false == wdt0_context_enabled) {
wdt_hal_write_protect_disable(&wdt0_context);
wdt_hal_enable(&wdt0_context);
wdt_hal_write_protect_enable(&wdt0_context);
}
// Interupt WDT is the Main Watchdog Timer of Timer Group 1
if (false == wdt1_context_enabled) {
wdt_hal_write_protect_disable(&wdt1_context);
wdt_hal_enable(&wdt1_context);
wdt_hal_write_protect_enable(&wdt1_context);
}
if (false == rtc_wdt_ctx_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_enable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
}
}
/**
* @breef Handle UART interrupt
*
* Handle UART interrupt for gdbstub. The function disable WDT.
* If Ctrl+C combination detected (0x03), then application will start to process incoming GDB messages.
* The gdbstub will stay in this interrupt until continue command will be received ($c#63).
*
* @param curr_regs - actual registers frame
*
*/
void gdbstub_handle_uart_int(XtExcFrame *regs_frame)
{
// Disable all enabled WDT on enter
disable_all_wdts();
int doDebug = esp_gdbstub_getfifo();
s_scratch.signal = esp_gdbstub_get_signal(regs_frame);
if (doDebug) {
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
init_task_info();
#endif// CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
esp_gdbstub_frame_to_regfile(regs_frame, gdb_local_regfile);
send_reason();
while (true) {
unsigned char *cmd;
size_t size;
int res = esp_gdbstub_read_command(&cmd, &size);
if (res == '-') {
send_reason();
continue;
}
if (res > 0) {
/* character received instead of a command */
continue;
}
if (res == -2) {
esp_gdbstub_send_str_packet("E01");
continue;
}
res = esp_gdbstub_handle_command(cmd, size);
if (res == -2) {
esp_gdbstub_send_str_packet(NULL);
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (res == GDBSTUB_ST_CONT) {
break;
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
}
}
}
intr_handle_t intr_handle_;
extern void _xt_gdbstub_int(void * );
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/** @breef Init gdbstub
* Init uart interrupt for gdbstub
* */
void esp_gdbstub_init(void)
{
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, _xt_gdbstub_int, NULL, &intr_handle_);
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/** Send string as a het to uart */
static void esp_gdbstub_send_str_as_hex(const char *str)
{
while (*str) {
esp_gdbstub_send_hex(*str, 8);
str++;
}
}
#endif
/** Send all registers to gdb */
static void handle_g_command(const unsigned char* cmd, int len)
static void handle_g_command(const unsigned char *cmd, int len)
{
uint32_t *p = (uint32_t *) &s_scratch.regfile;
esp_gdbstub_send_start();
@ -106,7 +266,7 @@ static void handle_g_command(const unsigned char* cmd, int len)
}
/** Receive register values from gdb */
static void handle_G_command(const unsigned char* cmd, int len)
static void handle_G_command(const unsigned char *cmd, int len)
{
uint32_t *p = (uint32_t *) &s_scratch.regfile;
for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
@ -116,7 +276,7 @@ static void handle_G_command(const unsigned char* cmd, int len)
}
/** Read memory to gdb */
static void handle_m_command(const unsigned char* cmd, int len)
static void handle_m_command(const unsigned char *cmd, int len)
{
intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
cmd++;
@ -135,12 +295,32 @@ static void handle_m_command(const unsigned char* cmd, int len)
esp_gdbstub_send_end();
}
/** Write memory from gdb */
static void handle_M_command(const unsigned char *cmd, int len)
{
intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
cmd++; // skip '.'
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
cmd++; // skip ':'
if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
esp_gdbstub_send_str_packet("E01");
return;
}
for (int k = 0; k < size; k++) {
esp_gdbstub_writemem(addr, esp_gdbstub_gethex(&cmd, 8));
addr++;
}
esp_gdbstub_send_start();
esp_gdbstub_send_str_packet("OK");
esp_gdbstub_send_end();
}
/** Handle a command received from gdb */
int esp_gdbstub_handle_command(unsigned char *cmd, int len)
{
unsigned char *data = cmd + 1;
if (cmd[0] == 'g')
{
if (cmd[0] == 'g') {
handle_g_command(data, len - 1);
} else if (cmd[0] == 'G') {
/* receive content for all registers from gdb */
@ -148,6 +328,9 @@ int esp_gdbstub_handle_command(unsigned char *cmd, int len)
} else if (cmd[0] == 'm') {
/* read memory to gdb */
handle_m_command(data, len - 1);
} else if (cmd[0] == 'M') {
/* write to memory from GDB */
handle_M_command(data, len - 1);
} else if (cmd[0] == '?') {
/* Reply with stop reason */
send_reason();
@ -155,6 +338,8 @@ int esp_gdbstub_handle_command(unsigned char *cmd, int len)
} else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
return handle_task_commands(cmd, len);
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
} else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
return GDBSTUB_ST_CONT;
} else {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
@ -209,10 +394,22 @@ static bool get_task_handle(size_t index, TaskHandle_t *handle)
return true;
}
static eTaskState get_task_state(size_t index)
{
return eSuspended;
// return s_scratch.tasks[index].eCurrentState;
}
static int get_task_cpu_id(size_t index)
{
return 0;
// return s_scratch.tasks[index].xCoreID;
}
/** Get the index of the task running on the current CPU, and save the result */
static void find_paniced_task_index(void)
{
TaskHandle_t cur_handle = xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
TaskHandle_t cur_handle = (TaskHandle_t)xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
TaskHandle_t handle;
for (int i = 0; i < s_scratch.task_count; i++) {
if (get_task_handle(i, &handle) && cur_handle == handle) {
@ -244,7 +441,7 @@ static void set_active_task(size_t index)
}
/** H command sets the "current task" for the purpose of further commands */
static void handle_H_command(const unsigned char* cmd, int len)
static void handle_H_command(const unsigned char *cmd, int len)
{
const char *ret = "OK";
if (cmd[1] == 'g') {
@ -270,7 +467,7 @@ static void handle_H_command(const unsigned char* cmd, int len)
}
/** qC returns the current thread ID */
static void handle_qC_command(const unsigned char* cmd, int len)
static void handle_qC_command(const unsigned char *cmd, int len)
{
esp_gdbstub_send_start();
esp_gdbstub_send_str("QC");
@ -282,7 +479,7 @@ static void handle_qC_command(const unsigned char* cmd, int len)
* Since GDB isn't going to ask about the tasks which haven't been listed by q*ThreadInfo,
* and the state of tasks can not change (no stepping allowed), simply return "OK" here.
*/
static void handle_T_command(const unsigned char* cmd, int len)
static void handle_T_command(const unsigned char *cmd, int len)
{
esp_gdbstub_send_str_packet("OK");
}
@ -299,14 +496,14 @@ static void send_single_thread_info(int task_index)
/** qfThreadInfo requests the start of the thread list, qsThreadInfo (below) is repeated to
* get the subsequent threads.
*/
static void handle_qfThreadInfo_command(const unsigned char* cmd, int len)
static void handle_qfThreadInfo_command(const unsigned char *cmd, int len)
{
assert(s_scratch.task_count > 0); /* There should be at least one task */
send_single_thread_info(0);
s_scratch.thread_info_index = 1;
}
static void handle_qsThreadInfo_command(const unsigned char* cmd, int len)
static void handle_qsThreadInfo_command(const unsigned char *cmd, int len)
{
int task_index = s_scratch.thread_info_index;
if (task_index == s_scratch.task_count) {
@ -319,7 +516,7 @@ static void handle_qsThreadInfo_command(const unsigned char* cmd, int len)
}
/** qThreadExtraInfo requests the thread name */
static void handle_qThreadExtraInfo_command(const unsigned char* cmd, int len)
static void handle_qThreadExtraInfo_command(const unsigned char *cmd, int len)
{
cmd += sizeof("qThreadExtraInfo,") - 1;
int task_index = gdb_tid_to_task_index(esp_gdbstub_gethex(&cmd, -1));
@ -329,19 +526,41 @@ static void handle_qThreadExtraInfo_command(const unsigned char* cmd, int len)
return;
}
esp_gdbstub_send_start();
const char* task_name = pcTaskGetTaskName(handle);
while (*task_name) {
esp_gdbstub_send_hex(*task_name, 8);
task_name++;
esp_gdbstub_send_str_as_hex("Name: ");
esp_gdbstub_send_str_as_hex((const char *)pcTaskGetTaskName(handle));
esp_gdbstub_send_hex(' ', 8);
eTaskState state = get_task_state(task_index);
switch (state) {
case eRunning:
esp_gdbstub_send_str_as_hex("State: Running ");
esp_gdbstub_send_str_as_hex("@CPU");
esp_gdbstub_send_hex(get_task_cpu_id(task_index) + '0', 8);
break;
case eReady:
esp_gdbstub_send_str_as_hex("State: Ready");
break;
case eBlocked:
esp_gdbstub_send_str_as_hex("State: Blocked");
break;
case eSuspended:
esp_gdbstub_send_str_as_hex("State: Suspended");
break;
case eDeleted:
esp_gdbstub_send_str_as_hex("State: Deleted");
break;
default:
esp_gdbstub_send_str_as_hex("State: Invalid");
break;
}
/** TODO: add "Running" or "Suspended" and "CPU0" or "CPU1" */
esp_gdbstub_send_end();
}
bool command_name_matches(const char* pattern, const unsigned char* ucmd, int len)
bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len)
{
const char* cmd = (const char*) ucmd;
const char* end = cmd + len;
const char *cmd = (const char *) ucmd;
const char *end = cmd + len;
for (; *pattern && cmd < end; ++cmd, ++pattern) {
if (*pattern == '?') {
continue;
@ -375,6 +594,8 @@ static int handle_task_commands(unsigned char *cmd, int len)
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
} else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
return GDBSTUB_ST_CONT;
} else {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
@ -383,3 +604,4 @@ static int handle_task_commands(unsigned char *cmd, int len)
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS

View File

@ -53,7 +53,7 @@ void esp_gdbstub_send_str(const char *c)
// 'bits'/4 dictates the number of hex chars sent.
void esp_gdbstub_send_hex(int val, int bits)
{
const char* hex_chars = "0123456789abcdef";
const char *hex_chars = "0123456789abcdef";
for (int i = bits; i > 0; i -= 4) {
esp_gdbstub_send_char(hex_chars[(val >> (i - 4)) & 0xf]);
}
@ -68,7 +68,7 @@ void esp_gdbstub_send_end(void)
}
// Send a packet with a string as content
void esp_gdbstub_send_str_packet(const char* str)
void esp_gdbstub_send_str_packet(const char *str)
{
esp_gdbstub_send_start();
if (str != NULL) {
@ -164,7 +164,7 @@ int esp_gdbstub_read_command(unsigned char **out_cmd, size_t *out_size)
// A # has been received. Get and check the received chsum.
sentchs[0] = esp_gdbstub_getchar();
sentchs[1] = esp_gdbstub_getchar();
const unsigned char* c_ptr = &sentchs[0];
const unsigned char *c_ptr = &sentchs[0];
unsigned char rchsum = esp_gdbstub_gethex(&c_ptr, 8);
if (rchsum != chsum) {
esp_gdbstub_putchar('-');

View File

@ -0,0 +1,44 @@
#include "freertos/xtensa_rtos.h"
// ------------------------------------------------
.section .iram1,"ax"
.global gdbstub_handle_uart_int
.global _xt_gdbstub_int
.align 4
_xt_gdbstub_int:
/* Allocate exception frame and save minimal context. */
mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
rsr a0, EPC_1 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -16 /* for debug backtrace */
#endif
s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
/* Save exc cause and vaddr into exception frame */
rsr a0, EXCCAUSE
s32i a0, sp, XT_STK_EXCCAUSE
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
s32i a0, sp, XT_STK_A0
mov a6,sp
call0 gdbstub_handle_uart_int
ret

View File

@ -13,7 +13,6 @@
// limitations under the License.
#include <string.h>
#include "esp_gdbstub.h"
#include "esp_gdbstub_common.h"
#include "soc/cpu.h"
#include "soc/soc_memory_layout.h"

View File

@ -26,11 +26,19 @@ menu "ESP System Settings"
Just resets the processor without outputting anything
config ESP_SYSTEM_PANIC_GDBSTUB
bool "Invoke GDBStub"
bool "GDBStub on panic"
select ESP_GDBSTUB_ENABLED
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
config ESP_SYSTEM_GDBSTUB_RUNTIME
bool "GDBStub at runtime"
select FREERTOS_UNICORE
select ESP_GDBSTUB_ENABLED
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it and to do a debug on runtime.
This feature will switch system to single core mode.
endchoice
config ESP_SYSTEM_SINGLE_CORE_MODE

View File

@ -59,6 +59,9 @@ For easy interaction with IDF Monitor, use the keyboard shortcuts given in the t
* - * Ctrl+X (or X)
- Exit the program
-
* - Ctrl+]
- Interrupt running application
- Pauses IDF monitor and run GDB_ project debugger to debug the application at runtime.
Any keys pressed, other than ``Ctrl-]`` and ``Ctrl-T``, will be sent through the serial port.
@ -186,17 +189,20 @@ Launching GDB with GDBStub
By default, if esp-idf crashes, the panic handler prints relevant registers and the stack dump (similar to the ones above) over the serial port. Then it resets the board.
Furthermore, the application can be configured to run GDBStub in the background and handle the Ctrl+C event from the monitor.
Optionally, the panic handler can be configured to run GDBStub, the tool which can communicate with GDB_ project debugger. GDBStub allows to read memory, examine call stack frames and variables, etc. It is not as versatile as JTAG debugging, but this method does not require any special hardware.
To enable GDBStub, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``Invoke GDBStub``.
To enable GDBStub on panic, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``GDBStub on panic`` or set :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``GDBStub on runtime``.
In this case, if the panic handler is triggered, as soon as IDF Monitor sees that GDBStub has loaded, it automatically pauses serial monitoring and runs GDB with necessary arguments. After GDB exits, the board is reset via the RTS serial line. If this line is not connected, please reset the board manually by pressing its Reset button.
In this case, if the panic handler or Ctrl+C command is triggered, as soon as IDF Monitor sees that GDBStub has loaded, it automatically pauses serial monitoring and runs GDB with necessary arguments. After GDB exits, the board is reset via the RTS serial line. If this line is not connected, please reset the board manually by pressing its Reset button.
In the background, IDF Monitor runs the following command::
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -ex "set serial baud BAUD" -ex "target remote PORT" -ex interrupt build/PROJECT.elf :idf_target:`Hello NAME chip`
Output Filtering
~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gdbstub)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := gdbstub
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,127 @@
# GDBstub example
This example shows how to use gdbstub and it's functionality at runtime to debug an application with GDB.
With the gdbstub component it is possible to run GDB from IDF Monitor by pressing Ctrl+C and debug
the application using GDB. It is also possible to read/modify memory values, interrupt and continue the application.
Upon exit from GDB, the application will continue to work in IDF Monitor as before.
## How to use example
### Hardware Required
he example can run on any commonly available ESP32 development board.
There are two possible ways to execute gdbstub with GDB: from IDF Monitor and as standalone application.
gdbstub support ESP32, ESP32-S2 and ESP32-S3 chips.
### Configure the project
By default, the example is already pre-configured, but the user can change configuration options with the following command:
```
idf.py menuconfig
```
Current example is pre-configured. The user can scroll through the system parameters and see the settings.
Most important one is:
-> Component Config -> ESP System Settings -> Panic handler behaviour -> GDBStub on runtime
This selection switches gdbstub to runtime mode.
Depending on the project, following settings could be used:
-> Component Config -> GDB Stub -> ...
The user can enable or disable task list handling and define a maximum amount of tasks.
Note that gdbstub can now only be used when FreeRTOS is run on the first core only.
This setting is located here:
-> Component Config -> FreeRTOS -> Run FreeRTOS only on first core.
### Build and Flash
Build the project and flash it to the board, then run IDF Monitor to view the serial output:
```
idf.py -p PORT flash monitor
```
Replace PORT with the name of the serial port to use, for example COM4 for Windows or /dev/ttyUSB0 for Linux.
To exit the serial monitor, type ``Ctrl-]``.
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
In addition, it is also possible to run GDB and connect to the Esp32 directly, without IDF Monitor.
```
xtensa-esp32-elf-gdb ./build/gdbstub.elf -ex "set serial baud 115200" -ex "target remote \\.\COM10"
```
This will execute GDB and GDB will connect to your Esp32 by serial port COM10 with baudrate 115200.
## Example Output
The example demonstrates how to switch to GDB, watch values, change values, continue to run, and exit from GDB to the application.
To switch to GDB, the user presses Ctrl+C. This will stop the application and run the GDB.
In GDB, the user can print values "print call_count" and "print update_log_level" and then
change them "set call_count 100" and "set update_log_level = ESP_LOG_WARN".
The user can continue running the application in GDB by entering "continue" and then interrupt the application by pressing Ctrl+C.
The user can check again that the application has worked by checking variable "print call_count".
The user can exit from GDB to continue seeing the trace from IDF Monitor by pressing "quit" and then "y".
The user will see in IDF Monitor that call_count and logging level have changed.
A typical console output for such a scenario is shown below:
```
I (300) cpu_start: Starting scheduler on PRO CPU.
Hello GDB example!
I (307) gdbstub_example: INFO mode enabled. Call - 0. To enter GDB please press "Ctrl+C"
W (317) gdbstub_example: WARN mode enabled. Call - 0. To enter GDB please press "Ctrl+C"
I (1317) gdbstub_example: INFO mode enabled. Call - 1. To enter GDB please press "Ctrl+C"
W (1317) gdbstub_example: WARN mode enabled. Call - 1. To enter GDB please press "Ctrl+C"
To exit from the idf.py please use "Ctrl+]"
$T02#b6GNU gdb (crosstool-NG esp-2020r3) 8.1.0.20180627-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-host_w64-mingw32 --target=xtensa-esp32-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from c:\esp-idf\examples\system\gdbstub\build\gdbstub.elf...done.
Remote debugging using \\.\COM15
0x400dff0a in esp_pm_impl_waiti () at C:/esp-idf/components/esp_pm/pm_impl.c:533
533 asm("waiti 0");
(gdb) print call_count
$1 = 2
(gdb) set call_count = 100
(gdb) print call_count
$2 = 100
(gdb) print update_log_level
$3 = ESP_LOG_DEBUG
(gdb) set update_log_level = ESP_LOG_WARN
(gdb) print update_log_level
$4 = ESP_LOG_WARN
(gdb) c
Continuing.
Thread 1 received signal SIGINT, Interrupt.
0x400dff0a in esp_pm_impl_waiti () at C:/esp-idf/components/esp_pm/pm_impl.c:533
533 asm("waiti 0");
(gdb) print call_count
$6 = 108
(gdb) quit
A debugging session is active.
Inferior 1 [Remote target] will be killed.
Quit anyway? (y or n) y
W (13977) gdbstub_example: WARN mode enabled. Call - 108. To enter GDB please press "Ctrl+C"
W (14977) gdbstub_example: WARN mode enabled. Call - 109. To enter GDB please press "Ctrl+C"
W (15977) gdbstub_example: WARN mode enabled. Call - 110. To enter GDB please press "Ctrl+C"
W (16977) gdbstub_example: WARN mode enabled. Call - 111. To enter GDB please press "Ctrl+C"
```
To reproduce this scenario run the application by: idf.py -P PORT flash monitor
Then:
1. Interrupt the application by pressing Ctrl+C
2. In GDB, print the application values by typing in GDB command line "print call_count" or "print update_log_level"
3. Modify the application values by typing in GDB command line "set call_count = 100" or "set update_log_level = ESP_LOG_WARN"
4. Continue the application by typing in GDB command line "continue"
5. Interrupt application by pressing Ctrl+C
6. Check the value by typing in GDB command line "print call_count" or "print update_log_level"
7. Exit GDB by typing "quit" and then "y"
To exit from monitor please use Ctrl+]

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "gdbstub_main.c"
INCLUDE_DIRS "")

View File

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

View File

@ -0,0 +1,39 @@
/* Hello World Example
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 "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
int call_count = 0;
static const char *TAG = "gdbstub_example";
esp_log_level_t log_level = ESP_LOG_DEBUG;
esp_log_level_t update_log_level = ESP_LOG_DEBUG;
void app_main(void)
{
printf("Hello GDB example!\n");
call_count = 0;
for (;;) {
ESP_LOGD(TAG, "DEBUG mode enabled. Call - %i. To enter GDB please press \"Ctrl+C\"", call_count);
ESP_LOGI(TAG, "INFO mode enabled. Call - %i. To enter GDB please press \"Ctrl+C\"", call_count);
ESP_LOGW(TAG, "WARN mode enabled. Call - %i. To enter GDB please press \"Ctrl+C\"", call_count++);
if (update_log_level != log_level) {
log_level = update_log_level;
esp_log_level_set(TAG, log_level);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}

View File

@ -0,0 +1,22 @@
#
# GDB Stub
#
CONFIG_ESP_GDBSTUB_ENABLED=y
CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
CONFIG_ESP_GDBSTUB_MAX_TASKS=32
# end of GDB Stub
#
# FreeRTOS
#
CONFIG_FREERTOS_UNICORE=y
#
# ESP System Settings
#
# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set
# CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT is not set
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME=y
CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE=y
# end of ESP System Settings