Skip to content

调试

一般来说有四种方式

  1. 微库打印
  2. 断言
  3. Trace
  4. Hook函数(回调函数)

打印

使用微库函数的时候可以使用printf进行打印

断言

一般在C库里面断言就是一个函数

c
void assert(scalar expression);

作用就是确定expression是真的话就终止程序

在FreeRTOS里面使用的是configASSERT(x) 默认是空的, 一般FreeRTOS里面会调用很多次这个, 来判断参数是否有效

在FreeRTOS里,使用configASSERT(),比如:

c
#define configASSERT(x)  if (!x) while(1);

我们可以让它提供更多信息,比如:

c
#define configASSERT(x)  \
	if (!x) \
	{
		printf("%s %s %d\r\n", __FILE__, __FUNCTION__, __LINE__); \
        while(1); \
 	}

Trace

FreeRTOS里面有很多个以这个开头的宏, 会被放在一些关键位置

它们一般都是空的宏,这不会影响代码:不影响编程处理的程序大小、不影响运行时间。

我们要调试某些功能时,可以修改宏:修改某些标记变量、打印信息等待。

trace宏描述
traceTASK_INCREMENT_TICK(xTickCount)当tick计数自增之前此宏函数被调用。参数xTickCount当前的Tick值,它还没有增加。
traceTASK_SWITCHED_OUT()vTaskSwitchContext中,把当前任务切换出去之前调用此宏函数。
traceTASK_SWITCHED_IN()vTaskSwitchContext中,新的任务已经被切换进来了,就调用此函数。
traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue)当正在执行的当前任务因为试图去读取一个空的队列、信号或者互斥量而进入阻塞状态时,此函数会被立即调用。参数pxQueue保存的是试图读取的目标队列、信号或者互斥量的句柄,传递给此宏函数。
traceBLOCKING_ON_QUEUE_SEND(pxQueue)当正在执行的当前任务因为试图往一个已经写满的队列或者信号或者互斥量而进入了阻塞状态时,此函数会被立即调用。参数pxQueue保存的是试图写入的目标队列、信号或者互斥量的句柄,传递给此宏函数。
traceQUEUE_SEND(pxQueue)当一个队列或者信号发送成功时,此宏函数会在内核函数xQueueSend(),xQueueSendToFront(),xQueueSendToBack(),以及所有的信号give函数中被调用,参数pxQueue是要发送的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_SEND_FAILED(pxQueue)当一个队列或者信号发送失败时,此宏函数会在内核函数xQueueSend(),xQueueSendToFront(),xQueueSendToBack(),以及所有的信号give函数中被调用,参数pxQueue是要发送的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_RECEIVE(pxQueue)当读取一个队列或者接收信号成功时,此宏函数会在内核函数xQueueReceive()以及所有的信号take函数中被调用,参数pxQueue是要接收的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_RECEIVE_FAILED(pxQueue)当读取一个队列或者接收信号失败时,此宏函数会在内核函数xQueueReceive()以及所有的信号take函数中被调用,参数pxQueue是要接收的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_SEND_FROM_ISR(pxQueue)当在中断中发送一个队列成功时,此函数会在xQueueSendFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue)当在中断中发送一个队列失败时,此函数会在xQueueSendFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_RECEIVE_FROM_ISR(pxQueue)当在中断中读取一个队列成功时,此函数会在xQueueReceiveFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue)当在中断中读取一个队列失败时,此函数会在xQueueReceiveFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceTASK_DELAY_UNTIL()当一个任务因为调用了vTaskDelayUntil()进入了阻塞状态的前一刻此宏函数会在vTaskDelayUntil()中被立即调用。
traceTASK_DELAY()当一个任务因为调用了vTaskDelay()进入了阻塞状态的前一刻此宏函数会在vTaskDelay中被立即调用。

Malloc Hook函数

编程的时候越界以及栈溢出的问题很难解决, 一般堆使用的过程中发生越界很难被察觉, 堆就是malloc函数获得的内存

  • 使用pvPortMalloc失败时,如果在FreeRTOSConfig.h里配置configUSE_MALLOC_FAILED_HOOK为1,会调用:

    c
    void vApplicationMallocFailedHook( void );

栈溢出的Hook函数

在切换任务(vTaskSwitchContext)时调用taskCHECK_FOR_STACK_OVERFLOW来检测栈是否溢出,如果溢出会调用:

c
void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName );

怎么判断栈溢出?有两种方法:

  • 方法1:

    • 当前任务被切换出去之前,它的整个运行现场都被保存在栈里,这时很可能就是它对栈的使用到达了峰值。
    • 这方法很高效,但是并不精确
    • 比如:任务在运行过程中调用了函数A大量地使用了栈,调用完函数A后才被调度。

方法2:

  • 创建任务时,它的栈被填入固定的值,比如:0xa5

  • 检测栈里最后16字节的数据,如果不是0xa5的话表示栈即将、或者已经被用完了

  • 没有方法1快速,但是也足够快

  • 能捕获几乎所有的栈溢出

  • 为什么是几乎所有?可能有些函数使用栈时,非常凑巧地把栈设置为0xa5:几乎不可能

方法的切换使用的宏configCHECK_FOR_STACK_OVERLOW, 使用1的时候为第一种方法, 配置为大于1的时候使用的是第二种方法