书中分享了很多理论,方法和案例,
基于本书,本人在此基础上再分享一些更贴近实战的案例分享。
本人公众号 嵌入式Lee(嵌入式软硬件技术:RTOS,GUI,FS,协议栈,ARM,总线,嵌入式C,开发环境 and blablaba....多年经验分享,非硬货不发,带你扒开每一个技术背后的根本原理。)
一. 前言
前面分享了使用Tracealyzer进行RTOS任务栈分析,这一篇继续分享另外一个很重要的实践案例即堆的使用分析。
二. 事件
启动时xTraceKernelPortEnable创建事件记录PSF_EVENT_HEAP_CREATE
if (pxKernelPortData->xSystemHeapHandle == 0)
{
xTraceHeapCreate("System Heap", 0, 0, configTOTAL_HEAP_SIZE, &pxKernelPortData->xSystemHeapHandle);
}
FreeRTOSConfig.h需要配置堆大小我这里使用了编译器符号,&_heap_size,按照实际设置即可。
#define configTOTAL_HEAP_SIZE &_heap_size
xTraceHeapCreate实现如下,会发送事件PSF_EVENT_HEAP_CREATE
traceResult xTraceHeapCreate(const char *szName, TraceUnsignedBaseType_t uxCurrent, TraceUnsignedBaseType_t uxHighWaterMark, TraceUnsignedBaseType_t uxMax, TraceHeapHandle_t *pxHeapHandle)
{
TraceUnsignedBaseType_t uxStates[3];
uxStates[TRC_HEAP_STATE_INDEX_CURRENT] = uxCurrent;
uxStates[TRC_HEAP_STATE_INDEX_HIGHWATERMARK] = uxHighWaterMark;
uxStates[TRC_HEAP_STATE_INDEX_MAX] = uxMax;
return xTraceObjectRegisterInternal(PSF_EVENT_HEAP_CREATE, 0, szName, 3, uxStates, TRC_ENTRY_OPTION_HEAP, (TraceObjectHandle_t*)pxHeapHandle);
}
事件记录如下,即一个指针加3个WORD的uxStates信息
if (xTraceEventBegin(uiEventCode, sizeof(void*) + uxStateCount * sizeof(TraceUnsignedBaseType_t), &xEventHandle) == TRC_SUCCESS)
{
xTraceEventAddPointer(xEventHandle, pvObject);
for (i = 0; i < uxStateCount; i++)
{
xTraceEventAddUnsignedBaseType(xEventHandle, uxStates[i]);
}
xTraceEventEnd(xEventHandle);
}
宏TRC_CFG_INCLUDE_MEMMANG_EVENTS要配置为1
TRC_CFG_SCHEDULING_ONLY配置为0
在pvPortMalloc和pvPortFree接口中,对应的事件记录处理
trcKernelPort.h中
#undef traceMALLOC
#define traceMALLOC( pvAddress, uiSize ) \
if (xTraceIsRecorderEnabled()) \
{ \
xTraceHeapAlloc(xTraceKernelPortGetSystemHeapHandle(), pvAddress, uiSize); \
}
#undef traceFREE
#define traceFREE( pvAddress, uiSize ) \
if (xTraceIsRecorderEnabled()) \
{ \
xTraceHeapFree(xTraceKernelPortGetSystemHeapHandle(), pvAddress, uiSize); \
}
#endif
最终调用xTraceHeapAlloc和xTraceHeapFree实现
存储的事件为
PSF_EVENT_MALLOC 或PSF_EVENT_MALLOC_FAILED,参数是一个地址一个大小
if (xTraceEventBegin(pvAddress != 0 ? PSF_EVENT_MALLOC : PSF_EVENT_MALLOC_FAILED, sizeof(void*) + sizeof(TraceUnsignedBaseType_t), &xEventHandle) == TRC_SUCCESS)
{
xTraceEventAddPointer(xEventHandle, pvAddress);
xTraceEventAddUnsignedBaseType(xEventHandle, uxSize);
xTraceEventEnd(xEventHandle);
}
PSF_EVENT_FREE 或PSF_EVENT_FREE_FAILED,参数是一个地址一个大小
if (xTraceEventBegin(pvAddress != 0 ? PSF_EVENT_FREE : PSF_EVENT_FREE_FAILED, sizeof(void*) + sizeof(TraceUnsignedBaseType_t), &xEventHandle) == TRC_SUCCESS)
{
xTraceEventAddPointer(xEventHandle, pvAddress);
xTraceEventAddUnsignedBaseType(xEventHandle, uxSize);
xTraceEventEnd(xEventHandle);
}
事件信息存储在
全局变量pxKernelPortData->xSystemHeapHandle中
对应的初始化
traceResult xTraceHeapCreate(const char *szName, TraceUnsignedBaseType_t uxCurrent, TraceUnsignedBaseType_t uxHighWaterMark, TraceUnsignedBaseType_t uxMax, TraceHeapHandle_t *pxHeapHandle)
{
TraceUnsignedBaseType_t uxStates[3];
uxStates[TRC_HEAP_STATE_INDEX_CURRENT] = uxCurrent;
uxStates[TRC_HEAP_STATE_INDEX_HIGHWATERMARK] = uxHighWaterMark;
uxStates[TRC_HEAP_STATE_INDEX_MAX] = uxMax;
return xTraceObjectRegisterInternal(PSF_EVENT_HEAP_CREATE, 0, szName, 3, uxStates, TRC_ENTRY_OPTION_HEAP, (TraceObjectHandle_t*)pxHeapHandle);
}
xTraceHeapCreate调用xTraceObjectRegisterInternal,从&pxEntryTable->axEntries[xIndex]中创建分配空间出来
*pxObjectHandle = (TraceObjectHandle_t)xEntryHandle;
所以pxKernelPortData->xSystemHeapHandle即指向上述pxEntryTable->axEntries分配出来的xEntryHandle,
指向的内容是
如下3个索引的WORD,即当前值,heap的最大值,heap的最大使用值。
#define TRC_HEAP_STATE_INDEX_CURRENT 0
#define TRC_HEAP_STATE_INDEX_HIGHWATERMARK 1
#define TRC_HEAP_STATE_INDEX_MAX 2
以下就是获取出CURRENT值
if (pvAddress != 0)
{
/* This should never fail */
TRC_ASSERT_ALWAYS_EVALUATE(xTraceEntryGetState(xHeapHandle, TRC_HEAP_STATE_INDEX_CURRENT, &uxCurrent) == TRC_SUCCESS);
/* This should never fail */
TRC_ASSERT_ALWAYS_EVALUATE(xTraceEntryGetState(xHeapHandle, TRC_HEAP_STATE_INDEX_HIGHWATERMARK, &uxHighWaterMark) == TRC_SUCCESS);
uxCurrent += uxSize;
if (uxCurrent > uxHighWaterMark)
{
uxHighWaterMark = uxCurrent;
/* This should never fail */
TRC_ASSERT_ALWAYS_EVALUATE(xTraceEntrySetState(xHeapHandle, TRC_HEAP_STATE_INDEX_HIGHWATERMARK, uxHighWaterMark) == TRC_SUCCESS);
}
/* This should never fail */
TRC_ASSERT_ALWAYS_EVALUATE(xTraceEntrySetState(xHeapHandle, TRC_HEAP_STATE_INDEX_CURRENT, uxCurrent) == TRC_SUCCESS);
}
抓包事件如下,2个参数
#define PSF_EVENT_MALLOC 0x38
#define PSF_EVENT_FREE 0x39
#define PSF_EVENT_MALLOC_FAILED 0xE9
#define PSF_EVENT_FREE_FAILED 0xEA
那么找如下字段即可
0x38 0x20
0x39 0x20
0xE9 0x20
0xEA 0x20
必然要如下38 20表示MALLOC事件,0x009C是事件计数,
0x00007EAC是时间戳,0x081096E0是堆地址,0x00000070是申请的大小112字节。
三. 实测
菜单栏views->Memory Heap Utilization打开窗口
显示堆的申请和释放过程,实际就是一个计数器,申请就递增,释放就减少,如果这个数一
直递增说明申请的多释放的少,最终导致堆不够。
Event log视图也可以看到事件
Trace视图也可以看到事件
Trace视图双击malloc或者free,弹出窗口,显示malloc和free的历史,可以看到谁申请了什么时候释放了,这样可以追踪申请和释放是否正常
四. 总结
以上分享使用Tracealyzer进行堆分析的案例,实际就是一个计数器事件,malloc则计数值增加free则递减,可以看到什么时候malloc什么时候free,如果有未free则可以通过图表结合前后事件分析出来。