Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
rt-thread 系统实战系列(一) 之 在系统调试利器
发布于 2021-10-09 10:10:54 浏览:3122
订阅该版
[tocm] ## 前言 得力于 msh 我们可以在 rt-thread 运行的时候执行一些内置命令,查看系统运行状态。 但是对于一个嵌入式开发工程师,有这些是远远不够的。更多时候,我们想知道一些更细节的东西。 比如,线程控制块结构体在内存中的数据,或者某个外设几个寄存器的值,或者某个链表,从一个节点找到下一个节点,进而找到链表上所有链表节点。 ### 实现几个小工具 目前已经实现的有: ``` od_mem - show memory value in hex od_thread - dump thread od_sem - dump semaphore od_event - dump event od_mutex - dump mutex od_mailbox - dump mail box od_msgqueue - dump message queue od_memheap - dump memory heap od_memheap_item - dump memory heap item od_mempool - dump memory pool od_timer - dump timer ``` 其中,第一个 `od_mem` 有帮助信息,其它的暂时没有,具体使用请看下面详解。 #### od_mem ``` msh >od_mem Usage: od_mem [-gl]
start address of memory[hex]
end address of memory[hex] [-g
] group size[4] [-l
] line size[32] ``` 使用很简单,给定起始地址(开始地址必须小于结束地址),gl 两个参数使用默认值也可以。 ``` msh >od_mem 0xE000E010 0xE000E01F 0xE000E010 07000000 3F900200 A22F0100 3E490040 ....?.......>I.@ ``` > 注:***请勿尝试读取非法内存地址,这将引起 Hard Fault!!!*** #### od_sem 下面以 `od_sem` 为例,说明后面几个命令怎么和 `od_mem` 配合使用,来在线调试内核。 单独执行 `od_sem` 命令,列出当前所有对象名,对象首地址,以及信号量的值。 ``` msh >od_sem sem addr value -------- | ---------- | ----- shrx 0x2000A4D8 0 GuiSem 0x20009E84 0 sdram 0x20001FC4 1 heap 0x20004128 1 ``` 后面带上信号量对象名,可以有如下更详细的输出信息。 ``` msh >od_sem shrx 0x2000A4D8 73687278 00000000 81000000 909E0020 2C0F0020 ECA40020 ECA40020 00000000 shrx........... ,.. ... ... .... name: shrx type: 0x81 flag: 0x00 prev object: 0x20000F2C next object: 0x20009E90 prev suspend: 0x2000A4EC next suspend: 0x2000A4EC value: 0x0000 ``` 首先是 `rt_semaphore` 结构体在内存中的十六进制格式显示。然后是所有 `rt_semaphore` 对象元素的值,包括对象名,类型,初始化 `flags`,对象链表,挂起线程链表等等。 其中 "prev suspend" "next suspend" 是等待当前信号量对象的所有线程列表。'shrx' 首地址是 '0x2000A4D8', '0x2000A4EC' 恰恰指向了 'shrx' 说明没有任何线程等待这个信号量。 > 注:'0x2000A4D8' 和 '0x2000A4EC' 的值差见下文。 进一步,通过使用 `od_mem` ,查看 0x20000F2C 0x20009E90 内存附近有什么。 ``` msh >od_mem 0x20000F20 0x20000F4C 0x20000F20 A45D0020 80000000 01000000 E4A40020 34410020 20000000 02000000 24470020 .]. ........... 4A. .......$G. 0x20000F40 8C1D0020 24000000 03000000 4C ... $.......L msh >od_mem 0x20009E84 0x20009EB0 0x20009E84 47756953 656D0000 01010000 D01F0020 E4A40020 989E0020 989E0020 00000000 GuiSem......... ... ... ... .... 0x20009EA4 A11EA01E E4400020 F89E0020 6C .....@. ... l ``` > 注:'0x20000F2C' 和 '0x20000F20' 的值差见下文,下同。 可以看出来,0x20009E84(0x20009E90 - 0xC) 这个位置是一个叫 "GuiSem" 的对象,而 0x20000F20 这个位置没有明显的名称,大胆猜测,这里是一个链表头。 继续使用 `od_sem` 查看下去, ``` msh >od_sem heap 0x20004128 68656170 00000000 81010000 2C0F0020 D01F0020 3C410020 3C410020 01000000 heap........,.. ...
od_thread thread addr pri init pri flags stack addr stack size sp tick timer -------- | ---------- | --- | -------- | -------- | ---------- | ---------- | ---------- | ---- | ---------- tshell 0x2000A700 30 30 0x00 0x2000A798 0x00000800 0x2000AE34 3 0x2000A74C ledtick 0x2000A328 30 30 0x00 0x2000A3C0 0x00000100 0x2000A43C 2 0x2000A374 tidle0 0x20003664 31 31 0x00 0x200036E4 0x00000800 0x20003E8C 5 0x200036B0 timer 0x20004248 30 30 0x00 0x200042C8 0x00000400 0x2000466C 10 0x20004294 main 0x20005D98 16 16 0x00 0x20005E30 0x00004000 0x20009CF4 17 0x20005DE4 msh >od_thread timer 0x20004248 74696D65 72000000 80000000 A45D0020 70360020 5C420020 5C420020 6C460020 timer........]. p6. \B. \B. lF. 0x20004268 9FAA0108 00000000 C8420020 00040000 00000000 021E1E00 00000040 00000000 .........B. ...............@.... 0x20004288 00000000 0A000000 0A000000 74696D65 72000000 89000000 F05D0020 BC360020 ............timer........]. .6. 0x200042A8 A8420020 A8420020 69A20108 48420020 00000000 00000000 00000000 00000000 .B. .B. i...HB. ................ name: timer type: 0x80 flag: 0x00 prev object: 0x20003670 next object: 0x20005DA4 prev thread: 0x2000425C next thread: 0x2000425C sp: 0x2000466C entry: 0x0801AA9F parameter: 0x00000000 stack addr: 0x200042C8 stack size: 0x00000400 error: 0 stat: 2 curr prior: 30 init prior: 30 value: 0 value: 0 init tick: 10 value: 10 ``` 根据每一个线程的 "stack addr" "stack size",再借助 "od_mem" 命令,可以查看每一个线程的线程栈使用情况。不需要用仿真器打断点必须停下来看了。 ### 查看外设寄存器 通过查看手册可以得知,`SysTick` 的值是 0xE000E010, ``` msh >od_mem 0xE000E010 0xE000E01F 0xE000E010 07000000 3F900200 A22F0100 3E490040 ....?.......>I.@ ``` `SysTick_Type` 定义为 ``` typedef struct { __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ } SysTick_Type; ``` 由此可知,SysTick->CTRL = 0x00000007,SysTick->LOAD = 0x0002903F,SysTick->,SysTick->LOAD = 0x0002903F = 0x00012FA2 其它外设寄存器依此类推。 ### 全局变量 不能通过全局变量名称查看变量的值,除非你知道那个变量在内存中的地址。 ### `rt-thread` 中的几种对象指针偏移计算方法 由 `struct rt_object` 定义可知 ``` struct rt_object { char name[RT_NAME_MAX]; /**< name of kernel object */ rt_uint8_t type; /**< type of kernel object */ rt_uint8_t flag; /**< flag of kernel object */ #ifdef RT_USING_MODULE void *module_id; /**< id of application module */ #endif rt_list_t list; /**< list node of kernel object */ }; typedef struct rt_object *rt_object_t; /**< Type for kernel objects. */ ``` `list` 元素和 `rt_object` 结构体首地址有个偏移,这个偏移大小是 RT_NAME_MAX + 4 [+ 4]。 所以,上文 0x20000F2C - 0xC = 0x20000F20,0x20009E90 - 0xC = 0x20009E84。 又 `struct rt_ipc_object` 结构体定义 ``` struct rt_ipc_object { struct rt_object parent; /**< inherit from rt_object */ rt_list_t suspend_thread; /**< threads pended on this resource */ }; ``` 信号量等同步消息机制对象的 'suspend_thread' 和 对象首地址偏移大小是 sizeof(struct rt_object)。 所以,0x2000A4D8 + 0x14 = 0x2000A4EC。 同理,可以计算出 `struct rt_thread` 中的 `list` 和 `tlist` 两个元素相对于首地址偏移大小。 ## 结尾 这次有这个想法,一个是想添加一种调试的方式,另一个是想使用这种方式,减少暂停系统运行的前提下可以查看内核资源,可以查看某些外设配置。虽然不够直观,但也算是一种在无法停止系统运行的状况下,窥探系统的无奈之举。 > **特别声明:**获取内核对象过程中没有使用任何关中断保护。不能确保在打印某个对象信息的过程中,该对象被意外修改或者删除了。但是,考虑此工具仅用于 debug ,为了将内存占用和对系统影响降到最低,**不做关中断,不对异变内存做缓存处理**。 鉴于此,使用过程中可能存在数据异常,需要重复几次,排除异常数据。 目前不能十分确定,在遍历链表的过程中,是否会引起 CPU 异常等严重后果。 ## PS 1. 新增头文件,引出 c 调用接口,摆脱 finsh 可以在代码中的任意位置使用这些功能。 2. 添加 -h 和 -a 两个命令行参数。前一个显示帮助信息,后一个用于打印出内核对象链表上所有节点对象的关键信息。 欢迎下载使用,留下您宝贵的意见和建议。 > [源码仓库](https://gitee.com/thewon/od_cmds)
9
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
出出啊
恃人不如自恃,人之为己者不如己之自为也
文章
43
回答
1517
被采纳
342
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部