物联网操作系统-调试与优化

调试

FreeRTOS 提供了很多调试手段:
⚫ 打印
⚫ 断言: configASSERT
⚫ Trace
⚫ Hook 函数(回调函数)
 

打印


printf: FreeRTOS 工程里使用了 microlib,里面实现了 printf 函数。
我们只需实现一下函数即可使用printf:
 

int fputc( int ch, FILE *f );

断言


一般的 C 库里面,断言就是一个函数:
 

void assert(scalar expression);

它的作用是:确认 expression 必须为真,如果 expression 为假的话就中止程序。
在FreeRTOS里,使用configASSERT(),比如:
 

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

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

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

configASSERT(x)中,如果x为假,表示发生了很严重的错误,必须停止系统的运行。
它用在很多场合,比如:
⚫ 队列操作
 

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize !=
(UBaseType_t)0U)));
configASSERT( !((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLe
ngth != 1 )));

⚫ 中断级别的判断
 

void vPortValidateInterruptPriority( void )
{
uint32_t ulCurrentInterrupt;
uint8_t ucCurrentPriority;
/* Obtain the number of the currently executing interrupt. */
ulCurrentInterrupt = vPortGetIPSR();
/* Is the interrupt number a user defined interrupt? */
if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
{
/* Look up the interrupt's priority. */
ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];
configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
}

Trace


FreeRTOS 中定义了很多 trace 开头的宏,这些宏被放在系统个关键位置。
它们一般都是空的宏,这不会影响代码:不影响编程处理的程序大小、不影响运行时间。
我们要调试某些功能时,可以修改宏:修改某些标记变量、打印信息等待。

Malloc Hook 函数


编程时,一般的逻辑错误都容易解决。难以处理的是内存越界、栈溢出等。
内存越界经常发生在堆的使用过程总:堆,就是使用malloc得到的内存。
并没有很好的方法检测内存越界,但是可以提供一些回调函数:
⚫ 使用 pvPortMalloc 失败时,如果在 FreeRTOSConfig.h 里配置
configUSE_MALLOC_FAILED_HOOK 为 1,会调用:
 

void vApplicationMallocFailedHook( void );

栈溢出 Hook 函数


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

void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName );

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

⚫ 方法 2:
◼ 创建任务时,它的栈被填入固定的值,比如: 0xa5
◼ 检测栈里最后 16 字节的数据,如果不是 0xa5 的话表示栈即将、或者已经被
用完了
◼ 没有方法 1 快速,但是也足够快
◼ 能捕获几乎所有的栈溢出
◼ 为什么是几乎所有?可能有些函数使用栈时,非常凑巧地把栈设置为 0xa5:
几乎不可能

优化


在 Windows 中,当系统卡顿时我们可以查看任务管理器找到最消耗 CPU 资源的程序。
在FreeRTOS中,我们也可以查看任务使用CPU的情况、使用栈的情况,然后针对性地进
行优化。
这就是查看"任务的统计"信息。


栈使用情况


在创建任务时分配了栈,可以填入固定的数值比如 0xa5,以后可以使用以下函数查看"
栈的高水位",也就是还有多少空余的栈空间:
 

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

原理是:从栈底往栈顶逐个字节地判断,它们的值持续是 0xa5 就表示它是空闲的。
函数说明:

任务运行时间统计


对于同优先级的任务,它们按照时间片轮流运行:你执行一个 Tick,我执行一个 Tick。
是否可以在Tick中断函数中,统计当前任务的累计运行时间?
不行!很不精确,因为有更高优先级的任务就绪时,当前任务还没运行一个完整的Tick
就被抢占了。
我们需要比Tick更快的时钟,比如Tick周期时1ms,我们可以使用另一个定时器,让它
发生中断的周期时0.1ms甚至更短。
使用这个定时器来衡量一个任务的运行时间,原理如下图所示:

⚫ 切换到 Task1 时,使用更快的定时器记录当前时间 T1
⚫ Task1 被切换出去时,使用更快的定时器记录当前时间 T4
⚫ (T4-T1)就是它运行的时间,累加起来
⚫ 关键点:在 vTaskSwitchContext 函数中,使用更快的定时器统计运行时间

涉及的代码


⚫ 配置
 

#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1

⚫ 实现宏 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),它用来
初始化更快的定时器
⚫ 实现这两个宏之一,它们用来返回当前时钟值(更快的定时器)
◼ portGET_RUN_TIME_COUNTER_VALUE():直接返回时钟值
◼ portALT_GET_RUN_TIME_COUNTER_VALUE(Time):设置 Time 变量等于
时钟值
代码执行流程:
⚫ 初始化更快的定时器:启动调度器时

在任务切换时统计运行时间

⚫ 获得统计信息,可以使用下列函数
◼ uxTaskGetSystemState:对于每个任务它的统计信息都放在一个 TaskStatus_t
结构体里

◼ vTaskList:得到的信息是可读的字符串,比如
◼ vTaskGetRunTimeStats: 得到的信息是可读的字符串
 

函数说明


⚫ uxTaskGetSystemState:获得任务的统计信息
 

UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
uint32_t * const pulTotalRunTime );

⚫ vTaskList :获得任务的统计信息,形式为可读的字符串。 注意,
pcWriteBuffer 必须足够大。

void vTaskList( signed char *pcWriteBuffer );

可读信息格式如下:

⚫ vTaskGetRunTimeStats:获得任务的运行信息,形式为可读的字符串。注
意, pcWriteBuffer 必须足够大。
 

void vTaskGetRunTimeStats( signed char *pcWriteBuffer );

可读信息格式如下:

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值