mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 04:43:33 +00:00
gdbstub component
This commit is contained in:
@@ -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
|
||||
|
||||
|
@@ -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('-');
|
||||
|
Reference in New Issue
Block a user