RTOS
-
系统死机排查思路
反汇编现场寄存器 kasan watchpoint: 针对固定位置 栈溢出检测: cache:检查地址对齐、长度对齐(少/多刷,少/多无效)。 现象一般是watchpoint抓不到,软件复位无效,如USB驱动与cpu会同时操作内存。 硬件: 排除个体差异,存在电压、电流等不稳定,或者芯片不良等异常或内存、flash异常。 -
freertos内存管理
freertos一共有5种内存管理算法,heap3直接使用的是c库的管理算法,只是对c库封装了一层接口。本章节主要分析heap1、heap2、heap4; heap1 heap2 heap4 heap5 heap5与heap4相比,支持了允许内存堆跨越多个不连续的内存段,这种情况针对一颗soc内部有多块硬件存储设备而言就比较方便,如一个soc内置了1MB SRAM和8MB HPRAM,就可以用这种方式。 参考文献:https://blog.csdn.net/zhzht19861011/article/details/51606068 -
freertos栈溢出检测原理
portSTACK_GROWTH > 0:栈向上生长 portSTACK_GROWTH < 0: 栈向下生长,本章使用栈向下生长 打开 configRECORD_STACK_HIGH_ADDRESS 1,将PCB中将打开pxEndOfStack,结构如下。 adress:0+ulStackDepth ------------------- pxEndOfStack(栈底) |XXXXXXXXXXXXXXXXXX| adress:0+x --------------------pxTopOfStack(栈顶) / StkCur(当前栈) | | | | | | adress:0 ------------------- pxStack / pxTaskStatus->pxStackBase / StkBot 栈溢出检测原理 configCHECK_FOR_STACK_OVERFLOW 1 #if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) /* Only the current stack state is to be checked. */ #define taskCHECK_FOR_STACK_OVERFLOW() \ { \ /* Is the currently saved stack pointer within the stack limit? */ \ if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ { \ vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ } \ } #endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ 因为栈是向下生长的,所有判断pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack即表示溢出。 configCHECK_FOR_STACK_OVERFLOW > 1 打开tskSET_NEW_STACKS_TO_KNOWN_VALUE =1 #if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 #else #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 #endif 将栈填充为 0xa5 prvInitialiseNewTask #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) { /* Fill the stack with a known value to assist debugging. */ ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); } #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ 通过比较栈顶的值是否不等于0xa5a5a5a5来判断栈溢出,前面讲栈都填充为0xa5。 #if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) #define taskCHECK_FOR_STACK_OVERFLOW() \ { \ const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ \ if( ( pulStack[ 0 ] != ulCheckValue ) || \ ( pulStack[ 1 ] != ulCheckValue ) || \ ( pulStack[ 2 ] != ulCheckValue ) || \ ( pulStack[ 3 ] != ulCheckValue ) ) \ { \ vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ } \ } #endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ /*-----------------------------------------------------------*/ 所以两种检测方法,第一种是通过比较pxTopOfStack的是否小于起始地址,第二种是看填充的数据是否被修改。 -
内存相关问题排查思路
最近在项目上遇到一个棘手问题,有一块内存在运行过程中概率性的被异常修改,由于牵扯到多方的应用程序,受限于调试手段排查了不少时间,现简单总结下遇到此类问题的排查思路。 添加额外的magic 核心原理就是在malloc的时候,额外多申请一些内存,通常情况下是在头部和尾部多申请一块空间,填充固定的值如0x55,待调用free函数的时候就检测该头部和尾部的数据是否还是原来填充的值如0x55,如果不是那说明该内存已经被修改了,打印释放的backtrace,即可初步定为是那段内存被踩。 记录最近申请和释放的内存 核心原理使用一个大数组记录最近申请和释放的内存,在调用malloc和free的时候都可以backtrace谁调用的,当出现问题时将记录的数据dump出来,检查哪些内存申请了没有释放或者二次释放。 asan机制 每个字节的内存使用1个bit来进行权限管理,在申请内存时将bit设置为可读写,在释放内存时将对应的bit设置为不可读写。当在进行内存读写操作时检查权限,这种方法对应use after free有很好的效果,缺点就是需要工具链支持kernel-address编译参数,在读写内存时进行插桩代码检查权限,这样也损耗性能,同时需要额外分配1/8的内存资源来管理权限。 https://github.com/ErichStyger/mcuoneclipse/blob/master/Examples/MCUXpresso/tinyK22/tinyK22_FreeRTOS_ASAN/source/McuASAN.c