Merge branch 'feature/gdbstubs_rt' into 'master'

Extension of GDBStub component for handling GDB by serial port at runtime.

See merge request espressif/esp-idf!10312
This commit is contained in:
Ivan Grokhotkov 2021-05-18 17:14:51 +00:00
commit 38d902f544
28 changed files with 775 additions and 245 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

@ -9,8 +9,12 @@ idf_component_register(SRCS "src/gdbstub.c" "src/packet.c"
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")
target_sources(${COMPONENT_LIB} PRIVATE "xtensa/gdbstub_xtensa.c"
"xtensa/gdbstub-entry.S"
"esp_common/gdbstub_common.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")
target_sources(${COMPONENT_LIB} PRIVATE "riscv/gdbstub_riscv.c"
"${target}/gdbstub_${target}.c")
endif()

View File

@ -1,4 +1,4 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include esp32 xtensa
COMPONENT_SRCDIRS := src esp32 xtensa
COMPONENT_SRCDIRS := src esp32 xtensa esp_common
COMPONENT_ADD_LDFRAGMENTS += linker.lf

View File

@ -1,56 +0,0 @@
// 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"
#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

@ -1,56 +0,0 @@
// 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"
#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

@ -1,56 +0,0 @@
// Copyright 2015-2020 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 "sdkconfig.h"
#include "soc/uart_periph.h"
#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,133 @@
// 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;
#if SOC_UART_NUM > 1
case 1:
gdb_uart = &UART1;
break;
#endif
#if SOC_UART_NUM > 2
case 2:
gdb_uart = &UART2;
break;
#endif
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,12 @@
#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);
void esp_gdbstub_panic_handler(void *frame);
#ifdef __cplusplus
}

View File

@ -18,7 +18,8 @@
#include <stddef.h>
#include <stdbool.h>
#include "esp_gdbstub.h"
#include "gdbstub_target_config.h"
#include "esp_gdbstub_arch.h"
#include "sdkconfig.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
@ -30,6 +31,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 +127,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 +159,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

@ -58,3 +58,8 @@ int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
{
return 5; // SIGTRAP, see IDF-2490
}
void _xt_gdbstub_int(void * frame)
{
}

View File

@ -17,6 +17,14 @@
#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"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static inline int gdb_tid_to_task_index(int tid);
@ -25,16 +33,20 @@ 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;
void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame)
/**
* @brief panic handler
*/
void esp_gdbstub_panic_handler(void *in_frame)
{
esp_gdbstub_frame_t *frame = (esp_gdbstub_frame_t *)in_frame;
#ifndef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
esp_gdbstub_frame_to_regfile(frame, &s_scratch.regfile);
#else
@ -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(esp_gdbstub_frame_t *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
/** @brief 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;
@ -212,7 +397,7 @@ static bool get_task_handle(size_t index, TaskHandle_t *handle)
/** 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 +429,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 +455,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 +467,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 +484,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 +504,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 +514,20 @@ 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++;
}
/** TODO: add "Running" or "Suspended" and "CPU0" or "CPU1" */
esp_gdbstub_send_str_as_hex("Name: ");
esp_gdbstub_send_str_as_hex((const char *)pcTaskGetTaskName(handle));
esp_gdbstub_send_hex(' ', 8);
// Current version report only Suspended state
esp_gdbstub_send_str_as_hex("State: Suspended");
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 +561,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;

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,43 @@
#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

@ -322,7 +322,7 @@ void esp_panic_handler(panic_info_t *info)
wdt_hal_disable(&rtc_wdt_ctx);
wdt_hal_write_protect_enable(&rtc_wdt_ctx);
panic_print_str("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler((esp_gdbstub_frame_t *)info->frame);
esp_gdbstub_panic_handler((void *)info->frame);
#else
#if CONFIG_ESP_COREDUMP_ENABLE
static bool s_dumping_core;
@ -373,7 +373,7 @@ void esp_panic_handler(panic_info_t *info)
}
void __attribute__((noreturn,no_sanitize_undefined)) panic_abort(const char *details)
void __attribute__((noreturn, no_sanitize_undefined)) panic_abort(const char *details)
{
g_panic_abort = true;
s_panic_abort_details = (char *) details;

View File

@ -61,6 +61,10 @@ volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0};
static void main_task(void* args);
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
void esp_gdbstub_init(void);
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
extern void app_main(void);
void esp_startup_start_app_common(void)
@ -79,6 +83,10 @@ void esp_startup_start_app_common(void)
#endif
#endif
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
esp_gdbstub_init();
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, ESP_TASK_MAIN_CORE);

View File

@ -10,7 +10,6 @@ This tool can be launched from an IDF project by running ``idf.py monitor``.
For the legacy GNU Make system, run ``make monitor``.
Keyboard Shortcuts
==================
@ -59,7 +58,10 @@ For easy interaction with IDF Monitor, use the keyboard shortcuts given in the t
* - * Ctrl+X (or X)
- Exit the program
-
* - Ctrl+C
- Interrupt running application
- Pauses IDF monitor and run GDB_ project debugger to debug the application at runtime. This requires :ref:CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME option to be enabled.
Any keys pressed, other than ``Ctrl-]`` and ``Ctrl-T``, will be sent through the serial port.
@ -184,19 +186,23 @@ To decode each address, IDF Monitor runs the following command in the background
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,8 @@
#
# 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,4 @@
#
# "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

View File

@ -33,6 +33,7 @@ import json
import locale
import os
import os.path
import signal
import subprocess
import sys
from collections import Counter, OrderedDict
@ -723,7 +724,16 @@ def init_cli(verbose_output=None):
return CLI(help=cli_help, verbose_output=verbose_output, all_actions=all_actions)
def signal_handler(_signal, _frame):
# The Ctrl+C processed by other threads inside
pass
def main():
# Processing of Ctrl+C event for all threads made by main()
signal.signal(signal.SIGINT, signal_handler)
checks_output = check_environment()
cli = init_cli(verbose_output=checks_output)
# the argument `prog_name` must contain name of the file - not the absolute path to it!

View File

@ -140,6 +140,8 @@ class Monitor(object):
self._decode_panic = decode_panic
self._reading_panic = PANIC_IDLE
self._panic_buffer = b''
self.gdb_exit = False
self.start_cmd_sent = False
def invoke_processing_last_line(self):
# type: () -> None
@ -149,43 +151,70 @@ class Monitor(object):
# type: () -> None
self.console_reader.start()
self.serial_reader.start()
self.gdb_exit = False
self.start_cmd_sent = False
try:
while self.console_reader.alive and self.serial_reader.alive:
try:
item = self.cmd_queue.get_nowait()
except queue.Empty:
try:
item = self.event_queue.get(True, 0.03)
except queue.Empty:
continue
if self.gdb_exit is True:
self.gdb_exit = False
time.sleep(0.3)
try:
# Continue the program after exit from the GDB
self.serial.write(codecs.encode('+$c#63'))
self.start_cmd_sent = True
except serial.SerialException:
pass # this shouldn't happen, but sometimes port has closed in serial thread
except UnicodeEncodeError:
pass # this can happen if a non-ascii character was passed, ignoring
event_tag, data = item
if event_tag == TAG_CMD:
self.handle_commands(data, self.target)
elif event_tag == TAG_KEY:
try:
self.serial.write(codecs.encode(data))
item = self.cmd_queue.get_nowait()
except queue.Empty:
try:
item = self.event_queue.get(True, 0.03)
except queue.Empty:
continue
(event_tag, data) = item
if event_tag == TAG_CMD:
self.handle_commands(data, self.target)
elif event_tag == TAG_KEY:
try:
self.serial.write(codecs.encode(data))
except serial.SerialException:
pass # this shouldn't happen, but sometimes port has closed in serial thread
except UnicodeEncodeError:
pass # this can happen if a non-ascii character was passed, ignoring
elif event_tag == TAG_SERIAL:
self.handle_serial_input(data)
if self._invoke_processing_last_line_timer is not None:
self._invoke_processing_last_line_timer.cancel()
self._invoke_processing_last_line_timer = threading.Timer(0.1, self.invoke_processing_last_line)
self._invoke_processing_last_line_timer.start()
# If no futher data is received in the next short period
# of time then the _invoke_processing_last_line_timer
# generates an event which will result in the finishing of
# the last line. This is fix for handling lines sent
# without EOL.
elif event_tag == TAG_SERIAL_FLUSH:
self.handle_serial_input(data, finalize_line=True)
else:
raise RuntimeError('Bad event data %r' % ((event_tag,data),))
except KeyboardInterrupt:
try:
yellow_print('To exit from IDF monitor please use \"Ctrl+]\"')
self.serial.write(codecs.encode('\x03'))
except serial.SerialException:
pass # this shouldn't happen, but sometimes port has closed in serial thread
except UnicodeEncodeError:
pass # this can happen if a non-ascii character was passed, ignoring
elif event_tag == TAG_SERIAL:
self.handle_serial_input(data)
if self._invoke_processing_last_line_timer is not None:
self._invoke_processing_last_line_timer.cancel()
self._invoke_processing_last_line_timer = threading.Timer(0.1, self.invoke_processing_last_line)
self._invoke_processing_last_line_timer.start()
# If no further data is received in the next short period
# of time then the _invoke_processing_last_line_timer
# generates an event which will result in the finishing of
# the last line. This is fix for handling lines sent
# without EOL.
elif event_tag == TAG_SERIAL_FLUSH:
self.handle_serial_input(data, finalize_line=True)
else:
raise RuntimeError('Bad event data %r' % ((event_tag, data),))
except SerialStopException:
normal_print('Stopping condition has been received\n')
except KeyboardInterrupt:
pass
finally:
try:
self.console_reader.stop()
@ -200,6 +229,13 @@ class Monitor(object):
def handle_serial_input(self, data, finalize_line=False):
# type: (bytes, bool) -> None
# Remove "+" after Continue command
if self.start_cmd_sent is True:
self.start_cmd_sent = False
pos = data.find(b'+')
if pos != -1:
data = data[(pos + 1):]
sp = data.split(b'\n')
if self._last_line_part != b'':
# add unprocessed part from previous "data" to the first line
@ -260,6 +296,7 @@ class Monitor(object):
def __exit__(self, *args, **kwargs): # type: ignore
""" Use 'with self' to temporarily disable monitoring behaviour """
self.console_reader.start()
self.serial_reader.gdb_exit = self.gdb_exit # write gdb_exit flag
self.serial_reader.start()
def prompt_next_action(self, reason): # type: (str) -> None
@ -480,10 +517,24 @@ class Monitor(object):
cmd = ['%sgdb' % self.toolchain_prefix,
'-ex', 'set serial baud %d' % self.serial.baudrate,
'-ex', 'target remote %s' % self.serial.port,
'-ex', 'interrupt', # monitor has already parsed the first 'reason' command, need a second
self.elf_file]
process = subprocess.Popen(cmd, cwd='.')
process.wait()
# Here we handling GDB as a process
# Open GDB process
try:
process = subprocess.Popen(cmd, cwd='.')
except KeyboardInterrupt:
pass
# We ignore Ctrl+C interrupt form external process abd wait responce util GDB will be finished.
while True:
try:
process.wait()
break
except KeyboardInterrupt:
pass # We ignore the Ctrl+C
self.gdb_exit = True
except OSError as e:
red_print('%s: %s' % (' '.join(cmd), e))
except KeyboardInterrupt:
@ -499,7 +550,6 @@ class Monitor(object):
subprocess.call(['stty', 'sane'])
except Exception:
pass # don't care if there's no stty, we tried...
self.prompt_next_action('gdb exited')
def output_enable(self, enable): # type: (bool) -> None
self._output_enabled = enable
@ -733,6 +783,8 @@ def main(): # type: () -> None
yellow_print('--- Print filter: {} ---'.format(args.print_filter))
monitor.main_loop()
except KeyboardInterrupt:
pass
finally:
if ws:
ws.close()

View File

@ -38,6 +38,7 @@ class SerialReader(StoppableThread):
self.baud = serial_instance.baudrate
self.serial = serial_instance
self.event_queue = event_queue
self.gdb_exit = False
if not hasattr(self.serial, 'cancel_read'):
# enable timeout for checking alive flag,
# if cancel_read not available
@ -47,10 +48,25 @@ class SerialReader(StoppableThread):
# type: () -> None
if not self.serial.is_open:
self.serial.baudrate = self.baud
self.serial.rts = True # Force an RTS reset on open
# We can come to this thread at startup or from external application line GDB.
# If we come from GDB we would like to continue to run without reset.
if self.gdb_exit:
self.serial.rts = False
self.serial.dtr = True
else: # if we exit from GDB, we don't need to reset the target
# This sequence of DTR/RTS and open/close set the serial port to
# condition when GDB not make reset of the target by switching DTR/RTS.
self.serial.rts = True # IO0=LOW
self.serial.dtr = self.serial.dtr # usbser.sys workaround
self.serial.rts = False # IO0=HIGH
self.serial.dtr = False
# Current state not reset the target!
self.gdb_exit = False
self.serial.open()
self.serial.rts = False
self.serial.dtr = self.serial.dtr # usbser.sys workaround
time.sleep(0.005) # Add a delay to meet the requirements of minimal EN low time (2ms for ESP32-C3)
self.serial.rts = False # Set rts/dtr to the working state
self.serial.dtr = self.serial.dtr # usbser.sys workaround
try:
while self.alive:
try: