qinyunti 发表于 2024-6-23 16:09

《嵌入式软件的时间分析》从书中到实践-Tracealyzer目标代码启动命令实现

<div class='showpostmsg'><p>书中分享了很多理论,方法和案例,</p>

<p>基于本书,本人在此基础上再分享一些更贴近实战的案例分享。</p>

<p>本人公众号 嵌入式Lee(嵌入式软硬件技术:RTOS,GUI,FS,协议栈,ARM,总线,嵌入式C,开发环境 and blablaba....多年经验分享,非硬货不发,带你扒开每一个技术背后的根本原理。)</p>

<p>&nbsp;</p>

<div></div>

<div></div>

<p>&nbsp;</p>

<h1>一.&nbsp;<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">&nbsp;</td>
                        <td valign="top" width="58">&nbsp;</td>
                </tr>
        </tbody>
</table>

<p>CmdCode目前只有一个即1,启动控制</p>

<p>Param1为1表示启动为0表示停止</p>

<p>Param2~5未用</p>

<p>checksumLS|checksumLS校验,计算方法是0xFFFF-前面所有字节的算数累加和。</p>

<p>&nbsp;</p>

<div style="text-align: center;"></div>

<p>&nbsp;</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-&gt;uiRecorderEnabled == 1)
    {
      return;
    }</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>然后设置trace时间戳频率,默认没有初始化的timestampFrequency&nbsp;为0,然后设置为TRC_HWTC_FREQ_HZ,TRC_HWTC_FREQ_HZ由用户在trcHardwarePort.h中实现</p>

<section>
<pre>
<code> xTraceTimestampGetFrequency(&amp;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">&nbsp;</div>
</section>

<p>设置时间周期也是一样的</p>

<section>
<pre>
<code>xTraceTimestampGetPeriod(&amp;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">&nbsp;</div>
</section>

<p>&nbsp;</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-&gt;uiRecorderEnabled = 1;</p>

<h2><strong>2.2停止通过prvSetRecorderDisabled实现</strong></h2>

<p>如果已经停止则直接退出</p>

<section>
<pre>
<code> if (pxTraceRecorderData-&gt;uiRecorderEnabled == 0)
    {
      return;
    }</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>设置停止标志</p>

<pre>
<code> pxTraceRecorderData-&gt;uiRecorderEnabled = 0;</code></pre>

<p>回调</p>

<pre>
<code> xTraceStreamPortOnTraceEnd();</code></pre>

<p>&nbsp;</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">&nbsp;</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">&nbsp;</div>
</section>

<p>&nbsp;</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">&nbsp;</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">&nbsp;</div>
</section>

<p>&nbsp;</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-&gt;uiPSF = TRACE_PSF_ENDIANESS_IDENTIFIER;
    pxHeader-&gt;uiVersion = TRACE_FORMAT_VERSION;
    pxHeader-&gt;uiPlatform = TRACE_KERNEL_VERSION;
    for (i = 0; i &lt; TRC_PLATFORM_CFG_LENGTH; i++)
    {
      pxHeader-&gt;platformCfg = platform_cfg;
      if (platform_cfg == 0)
      {
            break;
      }
    }
    pxHeader-&gt;uiPlatformCfgPatch = TRC_PLATFORM_CFG_PATCH;
    pxHeader-&gt;uiPlatformCfgMinor = TRC_PLATFORM_CFG_MINOR;
    pxHeader-&gt;uiPlatformCfgMajor = TRC_PLATFORM_CFG_MAJOR;
    pxHeader-&gt;uiNumCores = TRC_CFG_CORE_COUNT;
    pxHeader-&gt;isrTailchainingThreshold = TRC_CFG_ISR_TAILCHAINING_THRESHOLD;
    /* Lowest bit used for TRC_IRQ_PRIORITY_ORDER */
    pxHeader-&gt;uiOptions = ((TRC_IRQ_PRIORITY_ORDER) &lt;&lt; 0);
    /* 3rd bit used for TRC_CFG_TEST_MODE */
    pxHeader-&gt;uiOptions |= ((TRC_CFG_TEST_MODE) &lt;&lt; 2);
    return TRC_SUCCESS;
}</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</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;                      /**&lt; Timer type (direction) */
    TraceUnsignedBaseType_t frequency;/**&lt; Timer Frequency */
    uint32_t period;                  /**&lt; Timer Period */
    uint32_t wraparounds;               /**&lt; Nr of timer wraparounds */
    uint32_t osTickHz;                  /**&lt; RTOS tick frequency */
    uint32_t latestTimestamp;         /**&lt; Latest timestamp */
    uint32_t osTickCount;               /**&lt; RTOS tick count */
} TraceTimestamp_t;</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</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-&gt;frequency = 0;
    pxTraceTimestamp-&gt;period = 0;

    pxTraceTimestamp-&gt;osTickHz = TRC_TICK_RATE_HZ;
    pxTraceTimestamp-&gt;osTickCount = 0;
    pxTraceTimestamp-&gt;wraparounds = 0;
    pxTraceTimestamp-&gt;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-&gt;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-&gt;latestTimestamp = pxTraceTimestamp-&gt;period - 1;
#endif

    xTraceSetComponentInitialized(TRC_RECORDER_COMPONENT_TIMESTAMP);

    return TRC_SUCCESS;
}</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>prvSetRecorderEnabled时会设置时间戳频率和周期</p>

<section>
<pre>
<code>xTraceTimestampGetFrequency(&amp;timestampFrequency);
    /* If not overridden using xTraceTimestampSetFrequency(...), use default value */
    if (timestampFrequency == 0)
    {
      xTraceTimestampSetFrequency((TraceUnsignedBaseType_t)(TRC_HWTC_FREQ_HZ));
    }



    xTraceTimestampGetPeriod(&amp;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">&nbsp;</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">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>&nbsp; &nbsp; &nbsp;&nbsp;xTraceEventAdd32(xEventHandle,&nbsp;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>&nbsp;</p>

<p>EEWORLDIMGTK3</p>

<p>然后是TRC_ENTRY_TABLE_SLOTS个,任务信息</p>

<section>
<pre>
<code>for (i = 0; i &lt; (TRC_ENTRY_TABLE_SLOTS); i++)
    {
      xTraceEntryGetAtIndex(i, &amp;xEntryHandle);
      xTraceEntryGetAddress(xEntryHandle, &amp;pvEntryAddress);
      /* We only send used entry slots */
      if (pvEntryAddress != 0)
      {
            /* Send entry */
            if (xTraceEventBeginRawOfflineBlocking(sizeof(TraceEntry_t), &amp;xEventHandle) == TRC_SUCCESS)
            {
                xTraceEventAddData(xEventHandle, (void*)xEntryHandle, sizeof(TraceEntry_t));
                xTraceEventEndOfflineBlocking(xEventHandle);
            }
      }
    }</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>每一个任务的信息如下,包括任务的地址,名字,状态等共52字节,最多TRC_ENTRY_TABLE_SLOTS个,这里是有多少任务就发多少,获取到地址为0即结束。</p>

<pre>
<code>typedef struct TraceEntry
{
    void* pvAddress;                                                /**&lt; */
    TraceUnsignedBaseType_t xStates;   /**&lt; */
    uint32_t uiOptions;                                             /**&lt; */
    char szSymbol;                /**&lt; */
} TraceEntry_t;</code></pre>

<p>&nbsp;</p>

<p>如下记录了4个</p>

<p>&nbsp;</p>

<div style="text-align: center;"></div>

<p>&nbsp;</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), &amp;xEventHandle) == TRC_SUCCESS)
    {
      xTraceEventAdd32(xEventHandle, (uint32_t)pvCurrentTask);
      xTraceEventEndOffline(xEventHandle);
    }</code></pre>

<div contenteditable="false" tabindex="-1">&nbsp;</div>
</section>

<p>&nbsp;</p>

<p>&nbsp;</p>

<h1>四.&nbsp;<strong>总结</strong></h1>

<p>本文分享了,启动停止的实现,以及启动时发送到上位机的信息。</p>

<p>后面在继续分享下,发送部分的处理细节。</p>

<p>&nbsp;</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]
查看完整版本: 《嵌入式软件的时间分析》从书中到实践-Tracealyzer目标代码启动命令实现