《嵌入式软件的时间分析》从书中到实践-Tracealyzer目标代码启动命令实现
<div class='showpostmsg'><p>书中分享了很多理论,方法和案例,</p><p>基于本书,本人在此基础上再分享一些更贴近实战的案例分享。</p>
<p>本人公众号 嵌入式Lee(嵌入式软硬件技术:RTOS,GUI,FS,协议栈,ARM,总线,嵌入式C,开发环境 and blablaba....多年经验分享,非硬货不发,带你扒开每一个技术背后的根本原理。)</p>
<p> </p>
<div></div>
<div></div>
<p> </p>
<h1>一. <strong>前言</strong></h1>
<p>前一篇分享了目标程序的框架和数据流,这一篇来分享下,启动命令和其实现,即启动时发送到上位机的信息。</p>
<h1><strong>二.启停命令和实现</strong></h1>
<p>xTraceEnable(TRC_START);直接启动而</p>
<p>xTraceEnable(TRC_START_FROM_HOST)是等待主机发起启动命令启动,不阻塞。</p>
<p>xTraceEnable(TRC_START_AWAIT_HOST)则是等待主机发起启动命令启动,阻塞。</p>
<p>xTraceEnable(TRC_START_AWAIT_HOST)时通过xTraceStreamPortReadData接收上位机发送过来的命令,阻塞等待启动命令。</p>
<p>xTraceTzCtrl时通过xTraceStreamPortReadData接收上位机发送过来的启动或者停止命令,启动或者停止。</p>
<p><strong>以上接收处理</strong><strong>xTraceStreamPortReadData</strong><strong>必须一次性读到完整的命令,其实这里的处理不健壮,理论上应该是读任意字节,然后进行拼接处理是否是完整帧。</strong></p>
<p>prvIsValidCommand可以看到命令格式为</p>
<table cellspacing="0">
<tbody>
<tr>
<td valign="top" width="51">
<p>cmdCode</p>
</td>
<td valign="top" width="51">
<p>param1</p>
</td>
<td valign="top" width="51">
<p>Param2</p>
</td>
<td valign="top" width="51">
<p>Param3</p>
</td>
<td valign="top" width="51">
<p>Param4</p>
</td>
<td valign="top" width="51">
<p>Param5</p>
</td>
<td valign="top" width="58">
<p>checksumLSB</p>
</td>
<td valign="top" width="58">
<p>checksumMSB</p>
</td>
</tr>
<tr>
<td valign="top" width="51">
<p>CMD_SET_ACTIVE=1</p>
</td>
<td valign="top" width="51">
<p>1:启动</p>
<p>0:停止</p>
</td>
<td valign="top" width="51">
<p>0</p>
</td>
<td valign="top" width="51">
<p>0</p>
</td>
<td valign="top" width="51">
<p>0</p>
</td>
<td valign="top" width="51">
<p>0</p>
</td>
<td valign="top" width="58"> </td>
<td valign="top" width="58"> </td>
</tr>
</tbody>
</table>
<p>CmdCode目前只有一个即1,启动控制</p>
<p>Param1为1表示启动为0表示停止</p>
<p>Param2~5未用</p>
<p>checksumLS|checksumLS校验,计算方法是0xFFFF-前面所有字节的算数累加和。</p>
<p> </p>
<div style="text-align: center;"></div>
<p> </p>
<p>启动命令为</p>
<p>01 01 00 00 00 00 FD FF</p>
<p>校验0xFFFD+0x01+0x01=0xFFFF</p>
<p>停止命令为</p>
<p>01 00 00 00 00 00FE FF</p>
<p>校验0xFFFE+0x01=0xFFFF</p>
<h2><strong>2.1启动通过prvSetRecorderEnabled实现</strong></h2>
<p>该函数先看是否已经启动,已经启动则直接退出</p>
<section>
<pre>
<code> if (pxTraceRecorderData->uiRecorderEnabled == 1)
{
return;
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>然后设置trace时间戳频率,默认没有初始化的timestampFrequency 为0,然后设置为TRC_HWTC_FREQ_HZ,TRC_HWTC_FREQ_HZ由用户在trcHardwarePort.h中实现</p>
<section>
<pre>
<code> xTraceTimestampGetFrequency(&timestampFrequency);
/* If not overridden using xTraceTimestampSetFrequency(...), use default value */
if (timestampFrequency == 0)
{
xTraceTimestampSetFrequency((TraceUnsignedBaseType_t)(TRC_HWTC_FREQ_HZ));
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p>设置时间周期也是一样的</p>
<section>
<pre>
<code>xTraceTimestampGetPeriod(&timestampPeriod);
/* If not overridden using xTraceTimestampSetPeriod(...), use default value */
if (timestampPeriod == 0)
{
xTraceTimestampSetPeriod((TraceUnsignedBaseType_t)(TRC_HWTC_PERIOD));
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>然后清除事件缓存</p>
<p>xTraceInternalEventBufferClear();</p>
<p>回调</p>
<p>xTraceStreamPortOnTraceBegin</p>
<p>然后存储头信息,时间戳信息,任务信息,事件启动信息</p>
<p>prvTraceStoreHeader();</p>
<p>prvTraceStoreTimestampInfo();</p>
<p>prvTraceStoreEntryTable();</p>
<p>prvTraceStoreStartEvent();</p>
<p>这个下一节重点分析</p>
<p>最后设置启动标志</p>
<p>pxTraceRecorderData->uiRecorderEnabled = 1;</p>
<h2><strong>2.2停止通过prvSetRecorderDisabled实现</strong></h2>
<p>如果已经停止则直接退出</p>
<section>
<pre>
<code> if (pxTraceRecorderData->uiRecorderEnabled == 0)
{
return;
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>设置停止标志</p>
<pre>
<code> pxTraceRecorderData->uiRecorderEnabled = 0;</code></pre>
<p>回调</p>
<pre>
<code> xTraceStreamPortOnTraceEnd();</code></pre>
<p> </p>
<h2><strong>2.3事件记录时判断是否使能</strong></h2>
<p>通过xTraceIsRecorderEnabled获取是否启动</p>
<p>xTraceTzCtrl中必须使能才更新栈事件</p>
<section>
<pre>
<code> if (xTraceIsRecorderEnabled())
{
xTraceDiagnosticsCheckStatus();
xTraceStackMonitorReport();
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p>所有事件接口的入口也会根据此判断,未启动则不会记录事件</p>
<section>
<pre>
<code>#define xTraceEventBegin(uiEventCode, uiTotalPayloadSize, pxEventHandle) \
(xTraceIsRecorderEnabled() ? xTraceEventBeginOffline(uiEventCode, uiTotalPayloadSize, pxEventHandle) : TRC_FAIL)
#define xTraceEventEnd(xEventHandle) \
(xTraceIsRecorderEnabled() == 0 ? TRC_FAIL : xTraceEventEndOffline(xEventHandle))
</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>以下事件记录时也是要先判断</p>
<section>
<pre>
<code>xTraceTaskSwitch
#define traceMALLOC( pvAddress, uiSize ) \
if (xTraceIsRecorderEnabled()) \
{ \
xTraceHeapAlloc(xTraceKernelPortGetSystemHeapHandle(), pvAddress, uiSize); \
}
#define traceFREE( pvAddress, uiSize ) \
if (xTraceIsRecorderEnabled()) \
{ \
xTraceHeapFree(xTraceKernelPortGetSystemHeapHandle(), pvAddress, uiSize); \
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p>所以如果未启动就不会记录任何事件。</p>
<h1><strong>三.启动时记录的事件</strong></h1>
<p>前面看到启动时记录了如下信息,逐一分析</p>
<h2><strong>3.1</strong><strong>prvTraceStoreHeader();</strong></h2>
<p>头信息如下</p>
<section>
<pre>
<code>typedef struct TraceHeader
{
uint32_t uiPSF;
uint16_t uiVersion;
uint16_t uiPlatform;
uint32_t uiOptions;
uint32_t uiNumCores;
uint32_t isrTailchainingThreshold;
char platformCfg;
uint16_t uiPlatformCfgPatch;
uint8_t uiPlatformCfgMinor;
uint8_t uiPlatformCfgMajor;
} TraceHeader_t;</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>其初始化如下</p>
<section>
<pre>
<code>traceResult xTraceHeaderInitialize(TraceHeaderBuffer_t *pxBuffer)
{
uint32_t i;
char* platform_cfg = TRC_PLATFORM_CFG;
TRC_ASSERT_EQUAL_SIZE(TraceHeaderBuffer_t, TraceHeader_t);
if (pxBuffer == 0)
{
return TRC_FAIL;
}
pxHeader = (TraceHeader_t*)pxBuffer;
pxHeader->uiPSF = TRACE_PSF_ENDIANESS_IDENTIFIER;
pxHeader->uiVersion = TRACE_FORMAT_VERSION;
pxHeader->uiPlatform = TRACE_KERNEL_VERSION;
for (i = 0; i < TRC_PLATFORM_CFG_LENGTH; i++)
{
pxHeader->platformCfg = platform_cfg;
if (platform_cfg == 0)
{
break;
}
}
pxHeader->uiPlatformCfgPatch = TRC_PLATFORM_CFG_PATCH;
pxHeader->uiPlatformCfgMinor = TRC_PLATFORM_CFG_MINOR;
pxHeader->uiPlatformCfgMajor = TRC_PLATFORM_CFG_MAJOR;
pxHeader->uiNumCores = TRC_CFG_CORE_COUNT;
pxHeader->isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
/* Lowest bit used for TRC_IRQ_PRIORITY_ORDER */
pxHeader->uiOptions = ((TRC_IRQ_PRIORITY_ORDER) << 0);
/* 3rd bit used for TRC_CFG_TEST_MODE */
pxHeader->uiOptions |= ((TRC_CFG_TEST_MODE) << 2);
return TRC_SUCCESS;
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>对应值如下</p>
<p>EEWORLDIMGTK1</p>
<h2><strong>3.2</strong><strong>prvTraceStoreTimestampInfo();</strong></h2>
<p>时间戳信息</p>
<p>对应数据结构如下</p>
<section>
<pre>
<code>typedef struct TraceTimestamp
{
uint32_t type; /**< Timer type (direction) */
TraceUnsignedBaseType_t frequency;/**< Timer Frequency */
uint32_t period; /**< Timer Period */
uint32_t wraparounds; /**< Nr of timer wraparounds */
uint32_t osTickHz; /**< RTOS tick frequency */
uint32_t latestTimestamp; /**< Latest timestamp */
uint32_t osTickCount; /**< RTOS tick count */
} TraceTimestamp_t;</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p>其初始化如下</p>
<section>
<pre>
<code>traceResult xTraceTimestampInitialize(TraceTimestampBuffer_t *pxBuffer)
{
TRC_ASSERT_EQUAL_SIZE(TraceTimestampBuffer_t, TraceTimestamp_t);
/* This should never fail */
TRC_ASSERT(pxBuffer != 0);
pxTraceTimestamp = (TraceTimestamp_t*)pxBuffer;
/* These will be set when tracing is enabled */
pxTraceTimestamp->frequency = 0;
pxTraceTimestamp->period = 0;
pxTraceTimestamp->osTickHz = TRC_TICK_RATE_HZ;
pxTraceTimestamp->osTickCount = 0;
pxTraceTimestamp->wraparounds = 0;
pxTraceTimestamp->type = TRC_HWTC_TYPE;
#if (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_INCR || TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_INCR || TRC_HWTC_TYPE == TRC_OS_TIMER_INCR)
pxTraceTimestamp->latestTimestamp = 0;
#elif (TRC_HWTC_TYPE == TRC_FREE_RUNNING_32BIT_DECR || TRC_HWTC_TYPE == TRC_CUSTOM_TIMER_DECR || TRC_HWTC_TYPE == TRC_OS_TIMER_DECR)
pxTraceTimestamp->latestTimestamp = pxTraceTimestamp->period - 1;
#endif
xTraceSetComponentInitialized(TRC_RECORDER_COMPONENT_TIMESTAMP);
return TRC_SUCCESS;
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>prvSetRecorderEnabled时会设置时间戳频率和周期</p>
<section>
<pre>
<code>xTraceTimestampGetFrequency(&timestampFrequency);
/* If not overridden using xTraceTimestampSetFrequency(...), use default value */
if (timestampFrequency == 0)
{
xTraceTimestampSetFrequency((TraceUnsignedBaseType_t)(TRC_HWTC_FREQ_HZ));
}
xTraceTimestampGetPeriod(&timestampPeriod);
/* If not overridden using xTraceTimestampSetPeriod(...), use default value */
if (timestampPeriod == 0)
{
xTraceTimestampSetPeriod((TraceUnsignedBaseType_t)(TRC_HWTC_PERIOD));
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p>比如抓取实例如下</p>
<p>EEWORLDIMGTK2</p>
<h2><strong>3.3</strong><strong>prvTraceStoreEntryTable();</strong></h2>
<section>
<pre>
<code> xTraceEventAdd32(xEventHandle, uiEntryCount);
xTraceEventAdd32(xEventHandle, TRC_ENTRY_TABLE_SLOT_SYMBOL_SIZE);
xTraceEventAdd32(xEventHandle, TRC_ENTRY_TABLE_STATE_COUNT);</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p> xTraceEventAdd32(xEventHandle, TRC_ENTRY_TABLE_STATE_COUNT);</p>
<p>先添加任务数这里是0x0A</p>
<p>然后是SLOT,这里是0x20,即宏#define TRC_CFG_ENTRY_SYMBOL_MAX_LENGTH 32向上圆整4字节对齐。</p>
<p>然后是STATE_COUNT是3</p>
<p> </p>
<p>EEWORLDIMGTK3</p>
<p>然后是TRC_ENTRY_TABLE_SLOTS个,任务信息</p>
<section>
<pre>
<code>for (i = 0; i < (TRC_ENTRY_TABLE_SLOTS); i++)
{
xTraceEntryGetAtIndex(i, &xEntryHandle);
xTraceEntryGetAddress(xEntryHandle, &pvEntryAddress);
/* We only send used entry slots */
if (pvEntryAddress != 0)
{
/* Send entry */
if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceEntry_t), &xEventHandle) == TRC_SUCCESS)
{
xTraceEventAddData(xEventHandle, (void*)xEntryHandle, sizeof(TraceEntry_t));
xTraceEventEndOfflineBlocking(xEventHandle);
}
}
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p>每一个任务的信息如下,包括任务的地址,名字,状态等共52字节,最多TRC_ENTRY_TABLE_SLOTS个,这里是有多少任务就发多少,获取到地址为0即结束。</p>
<pre>
<code>typedef struct TraceEntry
{
void* pvAddress; /**< */
TraceUnsignedBaseType_t xStates; /**< */
uint32_t uiOptions; /**< */
char szSymbol; /**< */
} TraceEntry_t;</code></pre>
<p> </p>
<p>如下记录了4个</p>
<p> </p>
<div style="text-align: center;"></div>
<p> </p>
<h2><strong>3.4</strong><strong>prvTraceStoreStartEvent();</strong></h2>
<p>prvTraceStoreStartEvent();</p>
<p>记录调用prvSetRecorderEnabled的任务的地址</p>
<section>
<pre>
<code>if (xTraceEventBeginOffline(PSF_EVENT_TRACE_START, sizeof(uint32_t), &xEventHandle) == TRC_SUCCESS)
{
xTraceEventAdd32(xEventHandle, (uint32_t)pvCurrentTask);
xTraceEventEndOffline(xEventHandle);
}</code></pre>
<div contenteditable="false" tabindex="-1"> </div>
</section>
<p> </p>
<p> </p>
<h1>四. <strong>总结</strong></h1>
<p>本文分享了,启动停止的实现,以及启动时发送到上位机的信息。</p>
<p>后面在继续分享下,发送部分的处理细节。</p>
<p> </p>
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页:
[1]