乐于分享
好东西不私藏

OpenOCD高级调试技巧-修改源码在TARGET为RISCV时支持FreeRTOS调试

OpenOCD高级调试技巧-修改源码在TARGET为RISCV时支持FreeRTOS调试

一. 前言

前文《GDB高级调试技巧-FreeRTOS内核信息显示》以手动的方式进行FreeRTOS的内核信息的打印。实际上OpenOCD是支持RTOS调试的,target支持-rtos选项,支持FreeRTOSRTOS,但是不是所有的Target都支持,比如对于FreeRTOS就只支持cortex_mhla_targettarget,不支持RISCV。我们就来修改OpenOCD的源码,使其在targetRISCV时支持FreeRTOS。其中OpenOCD开发环境搭建参考《STEP BY STEP设计一个RISC-V仿真器之一:OpenOCD开发环境搭建

二. OpenOCDRTOS相关的配置与原理

2.1 配置

1) OpenOCD的配置

OpenOCD支持RTOS, 默认是未使能的,需要在target配置-rtos参数,即-rtos rtos_type

rtos_type 可以为auto, none, eCos, ThreadX, FreeRTOS, linux, ChibiOS, embKernel, mqx, uCOS-III, nuttx, RIOT, Zephyr, rtkernel

还有一个特殊的hwthread 这个实际不是RTOS. SMP系统中(硬件线程)OpenOCD 将其视为 RTOS GDB就可以将CPU cores (“hardware threads”) 作为一个线程对待,可以使用info threads 查看状态,thread命令可用于切换corestep stepi命令可针对某个core

配置实例

$_TARGETNAME configure -rtos FreeRTOS或者$_TARGETNAME configure -rtos auto

其中_TARGETNAME是前面创建的target名字,推荐使用auto自动检测即可。

2) 目标代码中的适配

OpenOCD识别对应的RTOS的原理是检测特定系统的特定符号。

这些符号实际就是对应系统的内核全局变量,可以获取内核的相关信息。

所以实际也可以手动去实现这些功能的,即我们前文分享的《GDB高级调试技巧-FreeRTOS内核信息显示》。

以下是不同系统对应的符号

eCos 
Cyg_Thread::thread_list, Cyg_Scheduler_Base::current_thread.
ThreadX 
_tx_thread_current_ptr, _tx_thread_created_ptr, _tx_thread_created_count.
FreeRTOS 
pxCurrentTCB, 
pxReadyTasksLists, 
xDelayedTaskList1, 
xDelayedTaskList2, 
pxDelayedTaskList, pxOverflowDelayedTaskList, xPendingReadyList, uxCurrentNumberOfTasks, uxTopUsedPriority, xSchedulerRunning.
linux 
init_task
ChibiOS 
rlist, 
ch_debug, 
chSysInit.
embKernel 
Rtos::sCurrentTask, 
Rtos::sListReady, 
Rtos::sListSleep, 
Rtos::sListSuspended, 
Rtos::sMaxPriorities, 
Rtos::sCurrentTaskCount.
mqx 
_mqx_kernel_data, 
MQX_init_struct.
uC/OS-III
OSRunning, 
OSTCBCurPtr, 
OSTaskDbgListPtr, 
OSTaskQty.
nuttx 
g_readytorun, 
g_tasklisttable.
RIOT 
sched_threads, 
sched_num_threads, 
sched_active_pid, 
max_threads, _
tcb_name_offset.
Zephyr 
_kernel, 
_kernel_openocd_offsets, _kernel_openocd_size_t_size
rtkernel 
Multiple struct offsets.

上述很多符号对应的RTOS是默认有的,但是有些可能需要一些配置或者人工添加

lZephyr 需要使用DEBUG_THREAD_INFO选项编译

lFreeRTOS需要编译链接contrib/rtos-helpers/FreeRTOS-openocd.c

luC/OS-III需要编译链接contrib/rtos-helpers/uCOS-III-openocd.c

3)FreeRTOS的实例

OpenOCD的源码复制

contrib\rtos-helpers\FreeRTOS-openocd.c到自己的工程编译进去。

其中#define USED __attribute__((used))USED是避免编译时优化。

const int USED uxTopUsedPriority = configMAX_PRIORITIES – 1;

如果使用了–gc-sections则还需要增加以下链接参数,避免链接时优化

-Wl,–undefined=uxTopUsedPriority

配置脚本中增加

target create $_TARGETNAME0 riscv -chain-position $_CHIPNAME.cpu -coreid 0

$_TARGETNAME0 configure -rtos FreeRTOS

或者

$_TARGETNAME0 configure -rtos auto

连接上GDB即可。 连接GDBOpenOCD成功检测到RTOS则会打印相关信息,例如

Info : Auto-detected RTOS: FreeRTOS

4)GDB相关的多线程命令

参考https://sourceware.org/gdb/current/onlinedocs/gdb.html/Threads.html

后面第三章实测部分会演示实例。

5)问题

我们这里平台是riscv, 所以会看到OpenOCD打印

Error: Could not find target in FreeRTOS compatibility list

搜索打印信息位于如下函数位置

staticintfreertos_create(struct target *target){    for (unsigned int i = 0; i < ARRAY_SIZE(freertos_params_list); i++)        if (strcmp(freertos_params_list[i].target_name, target_type_name(target)) == 0) {            target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];            return ERROR_OK;        }    LOG_ERROR("Could not find target in FreeRTOS compatibility list");    return ERROR_FAIL;}

我们看到freertos_params_list下只有cortex_mhla_target的支持,即riscv平台不支持FreeRTOS

我们就来修改OpenOCD源码增加这个支持。

2.2 关键代码

1) 配置命令执行过程

相关代码位于src\rtos\,比如FreeRTOS就是src\rtos\freertos.c

src\target\target.c中执行target配置命令

static COMMAND_HELPER(target_configure, struct target *target, unsigned int index, bool is_configure)

n = nvp_name2value(nvp_config_opts, CMD_ARGV[index]);

搜寻nvp_config_opts, 匹配选项-rtos, 找到n

{ .name = “-rtos”, .value = TCFG_RTOS },

switch (n->value) {对应

     case TCFG_RTOS:            if (is_configure) {                if (index == CMD_ARGC) {                    command_print(CMD"missing argument to %s"CMD_ARGV[index - 1]);                    return ERROR_COMMAND_ARGUMENT_INVALID;                }                retval = rtos_create(CMD, target, CMD_ARGV[index]);                if (retval != ERROR_OK)                    return retval;                index++;            } else {                if (index != CMD_ARGC)                    return ERROR_COMMAND_SYNTAX_ERROR;                if (target->rtos)                    command_print(CMD"%s", target->rtos->type->name);            }            /* loop for more */            break;

调用

retval = rtos_create(CMD, target, CMD_ARGV[index]);

其中CMD_ARGV[index]为配置-rtos选项的参数比如auto,FreeRTOS

rtos_create中根据指定的os名字,创建对应的对象

对于auto暂时只先os_alloc,分配target->rtos,赋值为rtos_types[0]

对于其他的则os_alloc_create即先os_alloc,分配target->rtos,赋值为rtos_types[x],对于freertos就是&freertos_rtos,然后调用对应create接口

target->rtos->type->create对于freertos就是

调用&freertos_rtoscreate成员即

.create = freertos_create,

过程如下

2) freertos_create

遍历freertos_params_list,查询targetname是否在freertos_params_listtarget_name中,在就记录freertos_params_list

target->rtos->rtos_specific_params = (void *)&freertos_params_list[i]

staticintfreertos_create(struct target *target){    for (unsigned int i = 0; i < ARRAY_SIZE(freertos_params_list); i++)        if (strcmp(freertos_params_list[i].target_name, target_type_name(target)) == 0) {            target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];            return ERROR_OK;        }    LOG_ERROR("Could not find target in FreeRTOS compatibility list");    return ERROR_FAIL;}

我们看到目前的实现freertos_params_list中只有target_namecortex_mhla_target

所以我们需要增加一个riscv的实现,

增加一个

{    "riscv",                /* target_name */    4,                      /* thread_count_width; */    4,                      /* pointer_width; */    12,                     /* list_next_offset; */    20,                     /* list_width; */    4,                      /* list_elem_next_offset; */    12,                     /* list_elem_content_offset */    0,                      /* thread_stack_offset; */    52,                     /* thread_name_offset; */    0,                      /* stacking_info */    0,    0,    &rtos_standard_cortex_riscv_fpu_stacking,    },

其中只有参数struct rtos_register_stacking和平台相关,其他参数对于FreeRTOS都是一样的。

3) os检测原理

通过struct rtos_type的成员.detect_rtos,对于freertos

freertos_detect_rtos来实现。

rtos_thread_packet->rtos_qsymbol中调用os->type->detect_rtos(target)

staticboolfreertos_detect_rtos(struct target *target){    if ((target->rtos->symbols) &&            (target->rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address != 0)) {        /* looks like FreeRTOS */        return true;    }    return false;}

即来判断

target->rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address不为0则表示为FreeRTOS

freertos_symbol_list数组中的

{ “pxDelayedTaskList”, false },

freertos_symbol_list记录了需要的符号。

target->rtos->symbols是通过以下函数赋值的

.get_symbol_list_to_lookup = freertos_get_symbol_list_to_lookup,

rtos_qsymbol->next_symbol中调用os->type->get_symbol_list_to_lookup(&os->symbols);

staticintfreertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]){    unsigned int i;    *symbol_list = calloc(            ARRAY_SIZE(freertos_symbol_list), sizeof(struct symbol_table_elem));    for (i = 0; i < ARRAY_SIZE(freertos_symbol_list); i++) {        (*symbol_list)[i].symbol_name = freertos_symbol_list[i].name;        (*symbol_list)[i].optional = freertos_symbol_list[i].optional;    }    return 0;}

所以这里只要存在pxReadyTasksLists这个符号,即这个全局变量即可,即GDB中能够p pxReadyTasksLists看到这个变量,只要检测到有这个变量则认为存在FreeRTOS

4) 适配不同的target

freertos_rtos中的实现都是通用的

const struct rtos_type freertos_rtos = {    .name = "FreeRTOS",    .detect_rtos = freertos_detect_rtos,    .create = freertos_create,    .update_threads = freertos_update_threads,    .get_thread_reg_list = freertos_get_thread_reg_list,    .get_symbol_list_to_lookup = freertos_get_symbol_list_to_lookup,};

适配target需要freertos_params_list中增加一个target的参数,所以需要重点了解这些参数的含义,然后按照对应的target添加即可。

参数结构体如下

struct freertos_params {    const char *target_name;    const unsigned char thread_count_width;    const unsigned char pointer_width;    const unsigned char list_next_offset;           /* offsetof(List_t, xListEnd.pxNext) */    const unsigned char list_width;                 /* sizeof(List_t) */    const unsigned char list_elem_next_offset;      /* offsetof(ListItem_t, pxNext) */    const unsigned char list_elem_content_offset;   /* offsetof(ListItem_t, pvOwner) */    const unsigned char thread_stack_offset;        /* offsetof(TCB_t, pxTopOfStack) */    const unsigned char thread_name_offset;         /* offsetof(TCB_t, pcTaskName) */    const struct rtos_register_stacking *stacking_info_cm3;    const struct rtos_register_stacking *stacking_info_cm4f;    const struct rtos_register_stacking *stacking_info_cm4f_fpu;};

1.target_nametarget的名字,-rtos选项后的参数

2.thread_count_width

3.pointer_width指针的宽度32位系统 4字节

4.list_next_offset

xListEnd.pxNextList_t中的偏移

这里需要注意configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES是否使能,未使能是3WORD12字节。

5.list_widthList_t结构体体的宽度

这里需要注意configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES是否使能,未使能是5WORD20字节。

struct xMINI_LIST_ITEM{    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */    configLIST_VOLATILE TickType_t xItemValue;    struct xLIST_ITEM * configLIST_VOLATILE pxNext;    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;};typedef struct xMINI_LIST_ITEM MiniListItem_t;typedef struct xLIST{    listFIRST_LIST_INTEGRITY_CHECK_VALUE                /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */    volatile UBaseType_t uxNumberOfItems;    ListItem_t * configLIST_VOLATILE pxIndex;           /*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */    MiniListItem_t xListEnd;                            /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */    listSECOND_LIST_INTEGRITY_CHECK_VALUE               /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */} List_t;

6.list_elem_next_offset

pxNextListItem_t中的偏移

这里需要注意configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES是否使能,未使能是1WORD4字节。

struct xLIST;struct xLIST_ITEM{    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */    configLIST_VOLATILE TickType_t xItemValue;          /*< The value being listed.  In most cases this is used to sort the list in descending order. */    struct xLIST_ITEM * configLIST_VOLATILE pxNext;     /*< Pointer to the next ListItem_t in the list. */    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */    void * pvOwner;                                     /*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */    struct xLIST * configLIST_VOLATILE pxContainer;     /*< Pointer to the list in which this list item is placed (if any). */    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */};typedef struct xLIST_ITEM ListItem_t;                   /* For some reason lint wants this as two separate definitions. */

7.list_elem_content_offset

pvOwnerListItem_t中的偏移

这里需要注意configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES是否使能,未使能是3WORD12字节。

8.thread_stack_offset

pxTopOfStackTCB_t中的偏移

0

typedef struct tskTaskControlBlock {        /* The old naming convention is used to prevent breaking kernel aware debuggers. */    volatile StackType_t    *pxTopOfStack;  /*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */

9.thread_name_offset

pcTaskNameTCB_t中的偏移,13WORD52字节

未使能portUSING_MPU_WRAPPERS

configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES

typedef struct tskTaskControlBlock {        /* The old naming convention is used to prevent breaking kernel aware debuggers. */    volatile StackType_t    *pxTopOfStack;  /*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */#if ( portUSING_MPU_WRAPPERS == 1 )    xMPU_SETTINGS   xMPUSettings;       /*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */#endif    ListItem_t          xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */    ListItem_t          xEventListItem;     /*< Used to reference a task from an event list. */    UBaseType_t         uxPriority;         /*< The priority of the task.  0 is the lowest priority. */    StackType_t         *pxStack;           /*< Points to the start of the stack. */    char                pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

10.Target相关的寄存器

   const struct rtos_register_stacking *stacking_info_cm3;    const struct rtos_register_stacking *stacking_info_cm4f;    const struct rtos_register_stacking *stacking_info_cm4f_fpu;

这里需要增加一个riscv的参数

增加一个

   const struct rtos_register_stacking *stacking_info_riscv_fpu;

这里感觉不应该不同targetrtos_register_stacking都放出来,应该是一个target对应一个rtos_register_stacking即可。

5) freertos_get_thread_reg_list

该函数比较关键,用于获取寄存器列表,对于不同的任务控制块,有不同的寄存器列表值(即上下文环境)

先进行了参数检查

param = (const struct freertos_params *) rtos->rtos_specific_params;

freertos_create时设置的

target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];

然后读取栈指针

    /* Read the stack pointer */    uint32_t pointer_casts_are_bad;    retval = target_read_u32(rtos->target,            thread_id + param->thread_stack_offset,            &pointer_casts_are_bad);    if (retval != ERROR_OK) {        LOG_ERROR("Error reading stack frame from FreeRTOS thread");        return retval;    }    stack_ptr = pointer_casts_are_bad;    LOG_DEBUG("FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,                                        thread_id + param->thread_stack_offset,                                        stack_ptr);

偏移thread_stack_offset是前面参数指定的

其实就是从栈帧处读内容,所以关键是要知道栈帧的位置,这个其实是记录在TCB中的第一个成员的,而TCB是可以通过上一篇文章的各种链表获取的。所以GDB是知道TCB的地址,知道栈帧的基地址的。

freertos_get_thread_reg_list函数是通过thread_id传递这个基地址的。

.get_thread_reg_list = freertos_get_thread_reg_list,

搜索get_thread_reg_list看在哪里调用

位于rtos_get_gdb_reg_list

看到来自于target->rtos->current_threadid

我们继续看target->rtos->current_threadid怎么来的

搜索current_threadid

看到来自于GDBTCP的包

rtos_thread_packet

即从H包中解析出来的,即GDB告诉OpenOCD的,正如我们前面分析的

继续来看freertos_get_thread_reg_list的实现后面是target相关的处理

先判断是不是armv7m看是cm3还是cm4,是cm4然后判断是不是使能fpu

所以我们在此再加一层,判断是不是riscv

通过以下接口判断是不是riscv

staticinlineboolis_riscv(conststruct riscv_info *riscv_info){    return riscv_info->common_magic == RISCV_COMMON_MAGIC;}

详细代码如下

    struct riscv_info *riscv = riscv_info(rtos->target);if (!is_riscv(riscv)) {// ....原来的逻辑    } else {        return rtos_generic_stack_read(rtos->target, param->stacking_info_riscv_fpu, stack_ptr, reg_list, num_regs);    }

6) FreeRTOS的栈帧GDB寄存器列表获取

现在来到最关键的一环,获取寄存器列表。因为核心是要获取任务的信息,即任务的上下文信息,即保存在栈中的CONTEX,即一系列的寄存器。

CM3为例先来看其他平台怎么写的,然后再依葫芦画瓢添加riscv的。

栈帧参考

https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/GCC/ARM_CM3/port.c

对应如下

高地址 偏移xPSR 0x3CPC   0x38LR   0x34R12  0x30R3   0x2CR2   0x28R1   0x24R0   0x20R11  0x1CR10  0x18R9   0x14R8   0x10R7   0x0CR6   0x08R5   0x04R4   0x00低地址   偏移从低地址开始算 即递减栈 栈是往下生长,指针指向低地址

GDB需要获取的寄存器列表参见

src\target\armv7m.h中的枚举

/* offsets into armv7m core register cache */enum {    /* for convenience, the first set of indices match     * the Cortex-M DCRSR.REGSEL selectors     */    ARMV7M_R0 = ARMV7M_REGSEL_R0,    ARMV7M_R1 = ARMV7M_REGSEL_R1,    ARMV7M_R2 = ARMV7M_REGSEL_R2,    ARMV7M_R3 = ARMV7M_REGSEL_R3,    ARMV7M_R4 = ARMV7M_REGSEL_R4,    ARMV7M_R5 = ARMV7M_REGSEL_R5,    ARMV7M_R6 = ARMV7M_REGSEL_R6,    ARMV7M_R7 = ARMV7M_REGSEL_R7,    ARMV7M_R8 = ARMV7M_REGSEL_R8,    ARMV7M_R9 = ARMV7M_REGSEL_R9,    ARMV7M_R10 = ARMV7M_REGSEL_R10,    ARMV7M_R11 = ARMV7M_REGSEL_R11,    ARMV7M_R12 = ARMV7M_REGSEL_R12,    ARMV7M_R13 = ARMV7M_REGSEL_R13,    ARMV7M_R14 = ARMV7M_REGSEL_R14,    ARMV7M_PC = ARMV7M_REGSEL_PC,    ARMV7M_XPSR = ARMV7M_REGSEL_XPSR,
对应
static const struct stack_register_offset rtos_standard_cortex_m3_stack_offsets[ARMV7M_NUM_CORE_REGS] = {   { ARMV7M_R0,   0x2032 },      /* r0   */   { ARMV7M_R1,   0x2432 },      /* r1   */   { ARMV7M_R2,   0x2832 },      /* r2   */   { ARMV7M_R3,   0x2c32 },      /* r3   */   { ARMV7M_R4,   0x0032 },      /* r4   */   { ARMV7M_R5,   0x0432 },      /* r5   */   { ARMV7M_R6,   0x0832 },      /* r6   */   { ARMV7M_R7,   0x0c32 },      /* r7   */   { ARMV7M_R8,   0x1032 },      /* r8   */   { ARMV7M_R9,   0x1432 },      /* r9   */   { ARMV7M_R10,  0x1832 },      /* r10  */   { ARMV7M_R11,  0x1c32 },      /* r11  */   { ARMV7M_R12,  0x3032 },      /* r12  */   { ARMV7M_R13,  -2,   32 },      /* sp   */   { ARMV7M_R14,  0x3432 },      /* lr   */   { ARMV7M_PC,   0x3832 },      /* pc   */   { ARMV7M_XPSR, 0x3c32 },      /* xPSR */};

所以参数

const struct rtos_register_stacking rtos_standard_cortex_m3_stacking = {    .stack_registers_size = 0x40,    .stack_growth_direction = -1,    .num_output_registers = ARMV7M_NUM_CORE_REGS,    .calculate_process_stack = rtos_standard_cortex_m3_stack_align,    .register_offsets = rtos_standard_cortex_m3_stack_offsets};

那么我们就来增阿吉riscv的参数,src\rtos\rtos_standard_stackings.c中增加

static const struct stack_register_offset rtos_standard_cortex_riscv_fpu_stack_offsets[] = {}

const struct rtos_register_stacking rtos_standard_cortex_riscv_fpu_stacking = {    .stack_registers_size = 0xF8,  /* 栈帧的大小 62个WORD */    .stack_growth_direction = -1,    .num_output_registers = 33/* rtos_standard_cortex_riscv_fpu_stack_offsets中寄存器个数 即从栈帧stack_registers_size范围内选寄存器到num_output_registers个寄存器 */    .calculate_process_stack = 0,    .register_offsets = rtos_standard_cortex_riscv_fpu_stack_offsets};

参考pxPortInitialiseStack的实现来确认栈帧的寄存器列表

/*

* mstatus

* x31

* x30

* x29

* x28

* x27

* x26

* x25

* x24

* x23

* x22

* x21

* x20

* x19

* x18

* x17

* x16

* x15

* x14

* x13

* x12

* x11

* pvParameters

* x9

* x8

* x7

* x6

* x5

* portTASK_RETURN_ADDRESS

* [chip specific registers go here]

* pxCode

*/

30+32 62个寄存器

其中chip specific registers32

栈帧为

//以下是FreeRTOS栈帧信息  高地址    { 0,  0xF432 },       /* mstatus   */    { 1,  0xF032 },       /* x31 t6 */    { 2,  0xEC32 },       /* x30 t5 */    { 3,  0xE832 },       /* x29 t4 */    { 4,  0xE432 },       /* x28 t3 */    { 5,  0xE032 },       /* x27 s11 */    { 6,  0xDC32 },       /* x26 s10*/    { 7,  0xD832 },       /* x25 s9 */    { 8,  0xD432 },       /* x24 s8 */    { 9,  0xD032 },       /* x23 s7 */    { 100xCC32 },       /* x22 s6 */    { 110xC832 },       /* x21 s5 */    { 120xC432 },       /* x20 s4 */    { 130xC032 },       /* x19 s3 */    { 140xBC32 },       /* x18 s2 */    { 150xB832 },       /* x17 a7  */    { 160xB432 },       /* x16 a6  */    { 170xB032 },       /* x15 a5  */    { 180xAC32 },       /* x14 a4  */    { 190xA832 },       /* x13 a3  */    { 200xA432 },       /* x12 a2  */    { 210xA032 },       /* x11 a1  */    { 220x9C32 },       /* x10 a0  pvParameters */    { 230x9832 },       /* x9 s1  */    { 240x9432 },       /* x8 s0/fp */    { 250x9032 },       /* x7 t2 */    { 260x8C32 },       /* x6 t1 */    { 270x8832 },       /* x5 t0 */    { 280x8432 },       /* x1 ra portTASK_RETURN_ADDRESS */    /* 前面29个WORD */    /* 后面32+1个WORD */    { 290x8032 },       /* ft11 */     { 300x7C 32 },        /* ft10 */    { 310x7832 },       /* ft9 */    { 320x7432 },       /* ft8 */    { 330x7032 },       /* fs11 */    { 340x6C32 },       /* fs10 */    { 350x6832 },       /* fs9 */    { 360x6432 },       /* fs8 */    { 370x6032 },       /* fs7 */    { 380x5C32 },       /* fs6 */    { 390x5832 },       /* fs5 */    { 400x5432 },       /* fs4 */    { 410x5032 },       /* fs3 */    { 420x4C32 },       /* fs2 */    { 430x4832 },       /* fa7 */    { 440x4432 },       /* fa6 */    { 450x4032 },       /* fa5 */    { 460x3C32 },       /* fa4 */    { 470x3832 },       /* fa3 */    { 480x3432 },       /* fa2 */    { 490x3032 },       /* fa1 */    { 500x2C32 },       /* fa0 */    { 510x2832 },       /* fs1 */    { 520x2432 },       /* fs0 */    { 530x2032 },       /* ft7 */    { 540x1C32 },       /* ft6 */    { 550x1832 },       /* ft5 */    { 560x1432 },       /* ft4 */    { 570x1032 },       /* ft3 */    { 580x0C32 },       /* ft2 */    { 590x0832 },       /* ft1 */    { 600x0432 },       /* ft0 */    { 610x0032 },       /* x1 ra pxCode*/    //低地址   偏移从低地址开始算 即递减栈 栈是往下生长,指针指向低地址

对应要发给GDB的寄存器列表

static const struct stack_register_offset rtos_standard_cortex_riscv_fpu_stack_offsets[] = {    { 0,  -132 },         /* x0 */    { 1,  0x8432 },       /* x1 ra */    { 2,  -232 },         /* x2 sp */    { 3,  -132 },         /* x3 gp */    { 4,  -132 },         /* x4 tp */    { 5,  0x8832 },       /* x5 t0 */    { 6,  0x8C32 },       /* x6 t1 */    { 7,  0x9032 },       /* x7 t2 */    { 8,  0x9432 },       /* x8 s0/fp */    { 9,  0x9832 },       /* x9 s1 */    { 100x9C32 },       /* x10 a0 */    { 110xA032 },       /* x11 a1 */    { 120xA432 },       /* x12 a2 */    { 130xA832 },       /* x13 a3 */    { 140xAC32 },       /* x14 a4 */    { 150xB032 },       /* x15 a5  */    { 160xB432 },       /* x16 a6  */    { 170xB832 },       /* x17 a7  */    { 180xBC32 },       /* x18 s2  */    { 190xC032 },       /* x19 s3  */    { 200xC432 },       /* x20 s4  */    { 210xC832 },       /* x21 s5  */    { 220xCC32 },       /* x22 s6  */    { 230xD032 },       /* x23 s7  */    { 240xD432 },       /* x24 s8 */    { 250xD832 },       /* x25 s9 */    { 260xDC32 },       /* x26 s10 */    { 270xE032 },       /* x27 s11 */    { 280xE432 },       /* x28 t3 */    { 290xE832 },       /* x29 t4 */    { 300xEC32 },       /* x30 t5 */    { 310xF032 },       /* x31 t6 */    { 320x0032 },       /* pc pxCode */}

2.3 调试

动态打开调试信息

telnet

log_level 3打印更多信息,然后操作,然后log_level 2

减少打印信息,这样可以看到操作更多的打印信息。

例如几个关键路径加打印确认是否执行

如果提示错误Error: No symbols for FreeRTOS

说明symbols没有赋值

  if (!rtos->symbols) {        LOG_ERROR("No symbols for FreeRTOS");        return -3;    }

赋值是在freertos_get_symbol_list_to_lookup中所以可以在此加打印看是否执行

其他路径的调试类似。比如要看栈帧基地址,获取的寄存器列表内容等,都可以类似的添加打印。

三. 实测

针对常见的命令进行测试

3.1自动检测到RTOS

Openocd配置脚本中配置

$_TARGETNAME0 configure -rtos auto

GDB连接上时

target extended-remote 172.16.11.110:3333

自动检测到FreeRTOSOpenOCD打印

Info : Auto-detected RTOS: FreeRTOS

3.2自动通知创建新线程

GDB运行到指定断点,中间有创建任务,会打印如下New Thread信息

(gdb) b app.c:90Breakpoint 1 at 0x20004f70file acore/app.c, line 90.(gdb) cContinuing.[New Thread 672723960][New Thread 672725640][New Thread 672732152][New Thread 672736416][New Thread 672727888][Switching to Thread 672723960]Thread 2 "app_entry" hit Breakpoint 1, app_entry (arg=<optimized out>) at acore/app.c:90/home/lhj//examples/usb_demo/acore/app.c:90:2095:beg:0x20004f70

3.3查看线程

语法

info threads [-gid] [-stopped] [-running] [thread-id-list]

thread-id-list的形式可以是类似以下形式

2-3 4.5 6.7-9 7.*

info threads(gdb) info threads  Id   Target Id                                                      Frame * 3    Thread 672714336 "app_entry" (Name: app_entry, State: Running) app_entry (arg=<optimized out>) at acore/app.c:91  4    Thread 672716016 "IDLE" (Name: IDLE)                           prvIdleTask (pvParameters=0x0)    at /home/lhj//os/freertos_10_2_1/freertos/tasks.c:3068  5    Thread 672718264 "Tmr Svc" (Name: Tmr Svc)                     syscall_yield ()    at /home/lhj/os/freertos_10_2_1/portable/riscv/syscallASM.S:9  6    Thread 672722528 "uart_tx_task" (Name: uart_tx_task)           wrapper_task_func (arg=0x2818de40)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42  7    Thread 672726792 "shell_task" (Name: shell_task)               wrapper_task_func (arg=0x2818eee8)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42(gdb) 

Id是线程的ID, 型号表示当前运行线程

-gid可以查看全局ID

(gdb) info threads -gid  Id   GId  Target Id                                                      Frame * 3    3    Thread 672714336 "app_entry" (Name: app_entry, State: Running) app_entry (arg=<optimized out>) at acore/app.c:91  4    4    Thread 672716016 "IDLE" (Name: IDLE)                           prvIdleTask (pvParameters=0x0)    at /home/lhj/os/freertos_10_2_1/freertos/tasks.c:3068  5    5    Thread 672718264 "Tmr Svc" (Name: Tmr Svc)                     syscall_yield ()    at /home/lhj/os/freertos_10_2_1/portable/riscv/syscallASM.S:9  6    6    Thread 672722528 "uart_tx_task" (Name: uart_tx_task)           wrapper_task_func (arg=0x2818de40)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42  7    7    Thread 672726792 "shell_task" (Name: shell_task)               wrapper_task_func (arg=0x2818eee8)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42(gdb) 

-stopped查看stopped的线程

-running查看运行的线程

符号$_thread表示线程ID,进程内唯一,不同进程可能一样

符号 $_gthread表示全局线程ID,全局唯一,info threads -gid查看

符号$_inferior_thread_count表示live线程的个数

3.4切换线程

thread thread-id

3切换到4

(gdb) info threads -gid  Id   GId  Target Id                                                      Frame * 3    3    Thread 672714336 "app_entry" (Name: app_entry, State: Running) app_entry (arg=<optimized out>) at acore/app.c:91  4    4    Thread 672716016 "IDLE" (Name: IDLE)                           prvIdleTask (pvParameters=0x0)    at /home/lhj/os/freertos_10_2_1/freertos/tasks.c:3068  5    5    Thread 672718264 "Tmr Svc" (Name: Tmr Svc)                     syscall_yield ()    at /home/lhj/os/freertos_10_2_1/portable/riscv/syscallASM.S:9  6    6    Thread 672722528 "uart_tx_task" (Name: uart_tx_task)           wrapper_task_func (arg=0x2818de40)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42  7    7    Thread 672726792 "shell_task" (Name: shell_task)               wrapper_task_func (arg=0x2818eee8)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42(gdb) thread 4[Switching to thread 4 (Thread 672716016)]#0  prvIdleTask (pvParameters=0x0) at /home/lhj//os/freertos_10_2_1/freertos/tasks.c:3068/home/lhj//os/freertos_10_2_1/freertos/tasks.c:3068:128142:beg:0x2000b45e(gdb) info threads  Id   Target Id                                                      Frame   3    Thread 672714336 "app_entry" (Name: app_entry, State: Running) app_entry (arg=<optimized out>) at acore/app.c:91* 4    Thread 672716016 "IDLE" (Name: IDLE)                           prvIdleTask (pvParameters=0x0)    at /home/lhj/os/freertos_10_2_1/freertos/tasks.c:3068  5    Thread 672718264 "Tmr Svc" (Name: Tmr Svc)                     syscall_yield ()    at /home/lhj/os/freertos_10_2_1/portable/riscv/syscallASM.S:9  6    Thread 672722528 "uart_tx_task" (Name: uart_tx_task)           wrapper_task_func (arg=0x2818de40)    at /home/lhj/os_shim/src/freertos_10_2_1/os_task.c:42  7    Thread 672726792 "shell_task" (Name: shell_task)               wrapper_task_func (arg=0x2818eee8)    at /home/lhj/os/os_shim/src/freertos_10_2_1/os_task.c:42(gdb) 

3.5 对多个线程执行命令

thread apply [thread-id-list | all] args

3.6 线程startexit消息

set print thread-events on使能

set print thread-events off关闭

show print thread-events查看使能状态

3.7 查找线程

thread find[regexp]

例如

(gdb) thread find 3Thread 3 has target id 'Thread 672714336'(gdb) thread find IDLEThread 4 has target name 'IDLE'Thread 4 has extra info 'Name: IDLE'(gdb) 

四. 问题

Remote ‘g’ packet reply is too long (expected 132 bytes, got 248 bytes): 

发送给GDB的寄存器链表寄存器的个数不一样,需要按照前文介绍进行确认。

五. 总结

    以上修改OpenOCD的源码,是的RISCV对应的target支持FreeRTOS的调试,这样可以方便的切换线程,查看线程状态等,方便调试,这里是基于OpenOCD的原生支持,当然前文介绍的手动添加脚本去查看信息也是可以的,殊途同归。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » OpenOCD高级调试技巧-修改源码在TARGET为RISCV时支持FreeRTOS调试

评论 抢沙发

7 + 6 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮