用GDB动态打印快速实现嵌入式系统的调试输出
print是最常用的调试手段之一。嵌入式系统通常使用串口来输出print消息,而且通常需要开发人员自行实现。这里介绍一种使用GDB动态打印命令dprintf来实现print消息的输出,这种方法非常便捷,不需要编写串口驱动就可以print消息到宿主机。
http://blog.csdn.net/zoomdy/article/details/50622698
mingdu.zheng at gmail dot com
dprintf使用示例
以FreeRTOS的一个示例程序作为示范。
#include <FreeRTOS.h>
#include <task.h>
void vPrintString(const char *str) {
(void) str;
}
void vTaskFunction(void *pvParameters) {
char *pcTaskName = (char *) pvParameters;
for (;;) {
vPrintString(pcTaskName);
vTaskDelay(250 / portTICK_RATE_MS);
}
}
static const char * const pcTextForTask1 = "Task 1 is running\r\n";
static const char * const pcTextForTask2 = "Task 2 is running\t\n";
int main(void) __attribute__((noreturn));
int main(void) {
xTaskCreate(vTaskFunction, "Task 1", 1000, (void* )pcTextForTask1, 1, NULL);
xTaskCreate(vTaskFunction, "Task 2", 1000, (void* )pcTextForTask2, 2, NULL);
vTaskStartScheduler();
for (;;);
}
这是《Using The FreeRTOS Real Time Kernel》这本书的Example4示例程序。程序功能很简单,创建两个相同的任务,每隔250ms打印一条消息。程序使用vPrintString函数来打印输出。按惯例,应当将vPrintString实现为对串口驱动的调用,通过串口来发送需要print的消息。但是这里的vPrintString函数实现是空的。通过GDB加载执行这个程序,在执行到main后,输入
dprintf vPrintString,"%s",str
然后继续执行程序,就可以在GDB窗口中看到目标程序打印的消息
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
dprintf用法
dprintf location,template,expression [,expression ...]
location:为需要打印消息的位置,可以是函数名、行号、文件名:行号等。
template:打印的格式,相当于printf函数的第一个参数。
expression:格式化消息的数据源。
举几个例子
dprintf main.c:4,"%s print %s\n",pxCurrentTCB->pcTaskName,str
dprintf xTaskIncrementTick,"xTickCount=%d\n",xTickCount
更详细的用法请参考Dynamic Printf。
dprintf原理
dprintf命令会设置一个特殊的断点,这可以通过info break查看
info break
Num Type Disp Enb Address What
1 dprintf keep y 0x08000a76 in xTaskIncrementTick at tasks.c:1942
printf "xTickCount=%d\n",xTickCount
2 dprintf keep y 0x08000f54 in vPrintString at main.c:4
printf "%s print %s\n",pxCurrentTCB->pcTaskName,str
dprintf设置了类型为dprintf的特殊断点,当程序执行到该断点时,GDB将按照template和expression打印消息,打印完成后继续执行程序。这个过程非常短暂(和仿真器的速度有关),人是感觉不出程序停顿的,所以看起来就跟是目标程序在打印消息一样。
dprintf局限性
dprintf的本质是断点,因此目标程序并不是持续执行的,目标程序因为断点而暂停执行,等dprintf打印消息后再继续,在实时系统中这或许是不可接受的。特别是当打印的频率比较高时,目标程序会被频繁地进入断点暂停执行。目标程序停顿的时间则跟仿真器的速度有关,速度越慢的仿真器,其停顿时间越长。因此dprintf仅可以作为权宜之计来使用,例如在实现串口驱动之前、临时需要打印某些消息时。