1. 数据断点DWT介绍
在ARM Cortex-M及A系列CPU中,有很多调试组件,使用它们可以执行各种调试功能,包括断点(breakpoint)、数据段点(Data WatchPoint 即 DWT)及各种跟踪(trace)等。
硬件调试模块允许内核在访问数据断点时停止。内核停止时,内核的内部状态和系统的外部状态都是可以查询的。完成查询后,内核和外设可以被复原,程序将继续执行。
数据断点在调试内存被非法改写的时候非常有用,以往我们遇到类似问题的时候,经常使用JLINk+GDB的这一黄金组合来watch住一个变量(或者一段内存),将这个变量设置成只读,或者不可访问的模式,当它被访问或者改写的时候,系统就会Halt停下,这时我们可以查看系统状态、内存值、BT命令查看调用者等等,帮助调试。
如:在gdb中可通过下面的几种方法来设置watchpoint:
(gdb) watch
在指定变量/内存地址(表达式)expr设置一个watchpoint。一但expr值有变化时,将停住程序。
(gdb) rwatch
当expr被读时,停住程序。
(gdb) awatch
当expr被读或被写时,停住程序。
(gdb) info watchpoints
查看watchpoint
(gdb)d n
删除watchpoint, 同删除breakpoint一样
举例:
想监控0地址处其实的1K字节是否被访问,若有read或者write访问,则程序停下
awatch (char[1024])*0x0
想监控0x1001dd08处的一段结构体的内部数据是否被改写,若发生改写则程序停下,只读无影响
watch ((k_mm_list_t *)0x1001dd08)->mbinfo->free_ptr
上述调试方法是硬件板子上JTAG或SWD口引出来后支持的调试手段,在IOT设备中,硬件往往很小,这类调试口经常是没有引出来的,在这种环境下,AliOS Things支持了一种在线数据断点的功能,即提供API接口,可以在软件中增加watch点,达到硬件GDB的DWT功能。
2. AliOS Things在线数据断点DWT的实现
2.1 AliOS Things下在线数据断点说明
使用限制:当前仅支持 ARMV7m的数据断点,即Cortex-M3/M4系列CPU
需要说明的是:
1. ARM9(armv5)架构的CPU由于ARM没有将DWT相关的寄存器透出,所以无法使用软件在线数据断点的功能,只能通过仿真器使用硬件DWT。
2. Cortex A系列DWT的使用介绍将在后续给出
3. ARMV7m支持的DWT的个数为4个
给出2个API:
2.2 API 列表
API | 说明 |
---|---|
debug_watch_on(void *addr) | 启动DWT,监控一个地址(4 字节的数据) |
debug_watch_off() | 关闭DWT |
调用示例1:watch一个全局变量
int g_dwt_test = 0;
void debug_watch_on(&g_dwt_test);
...
调用示例2:栈溢出检测
检测原理:任务的栈底base 保存的是固定的magic,正常情况下任务的栈大小足够大,任务的运行过程中任务栈不会下探到栈底stack_base的位置,如果发生这种情况,magic值被改写,就发生了栈溢出的情况。
所以我们在在任务被调度之前watch on栈底的magic,在任务被调度出去的之后watch off
如:
/*任务的栈底base 保存的是固定的magic*/
void * watch_addr = g_next_task->stack_base;
debug_watch off();
debug_watch_on(watch_addr);
cpu_task_switch();
...
2.3 watch命中后的使用
AliOS Things维测组件提供了强大的系统异常快照功能
(参考ATA:https://www.atatech.org/articles/134601)
我们将系统快照功能对接到DWT数据断点上,即发生watch命中后,自动输出当前系统的寄存器、任务状态、内存状态、OS(mutex/sem/bufqueue)状态以及此时的栈回溯backtrace ,可以直接显示当前谁改写了变量,导致了watch点的发生,大大提高了bug定位效率。
总结:watch命令可以在代码中的任意使用,无需连接硬件,结合AliOS Things维测特性,对内存改写、栈溢出等Bug定位非常有效