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

《嵌入式软件的时间分析》从书中到实践-Tracealyzer事件记录格式与丢事件监测

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

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

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

<p>&nbsp;</p>

<div></div>

<h1>一.&nbsp;<strong>前言</strong></h1>

<p>本文继续在前面的基础上分析Tracealyzer发送到上位机的事件信息是如何记录的。</p>

<p>直接从代码结合抓包数据进行分析。</p>

<h1><strong>二.事件记录格式</strong></h1>

<p>trcKernelPort.h中定义了很多事件类型,以PSF_EVENT_开头</p>

<p>&nbsp;</p>

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

<p>&nbsp;</p>

<p>每次记录事件,都是按照如下形式进行</p>

<p>先xTraceEventBegin</p>

<p>然后TraceEventAddxxx</p>

<p>然后xTraceEventEnd</p>

<p>其中xTraceEventBegin调用xTraceEventBeginOffline即宏TRC_EVENT_BEGIN_OFFLINE实现如下</p>

<section>
<pre>
<code>#define TRC_EVENT_BEGIN_OFFLINE(uiEventCode, uiPayloadSize, pxEventHandle) \
    ( \
      (xTraceEventBeginRawOffline(sizeof(TraceBaseEvent_t) + (uiPayloadSize), pxEventHandle)) == TRC_SUCCESS ? \
      ( \
            pxTraceEventDataTable-&gt;coreEventData.eventCounter++, \
            SET_BASE_EVENT_DATA((TraceBaseEvent_t*)(((TraceEventData_t*)*(pxEventHandle))-&gt;pvBlob), \
                uiEventCode, \
                (((TraceEventData_t*)*(pxEventHandle))-&gt;size - sizeof(TraceBaseEvent_t)) / sizeof(uint32_t), \
                pxTraceEventDataTable-&gt;coreEventData.eventCounter), \
            ((TraceEventData_t*)*(pxEventHandle))-&gt;offset += sizeof(TraceBaseEvent_t), \
            TRC_SUCCESS \
      ) : TRC_FAIL \
    )</code></pre>

<pre data-lang="properties">
<span style="white-space: normal">其中eventCounter++即递增事件计数,SET_BASE_EVENT_DATA即设置每个事件的基本信息,</span></pre>
</section>

<p>事件格式,即如下包括事件ID,总的事件计数,和时间戳</p>

<p>&nbsp;</p>

<pre>
<code>/**
* @internal Trace Base Event Structure
*/
typedef struct {
    uint16_t EventID;       /**&lt; */
    uint16_t EventCount;    /**&lt; */
    uint32_t TS;            /**&lt; */
} TraceBaseEvent_t;</code></pre>

<p>其中&nbsp;SET_BASE_EVENT_DATA实现如下</p>

<section>
<pre>
<code>#define SET_BASE_EVENT_DATA(pxEvent, eventId, paramCount, eventCount) \
    ( \
      (pxEvent)-&gt;EventID = TRC_EVENT_SET_PARAM_COUNT(eventId, paramCount), \
      (pxEvent)-&gt;EventCount = TRC_EVENT_SET_EVENT_COUNT(eventCount), \
      xTraceTimestampGet(&amp;(pxEvent)-&gt;TS) \
    )</code></pre>

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

<p>&nbsp;</p>

<p>事件ID,TRC_EVENT_SET_PARAM_COUNT如下高4位表示参数个数,低12位表示事件类型,即PSF_EVENT_开始的宏。</p>

<pre>
<code>#define TRC_EVENT_SET_PARAM_COUNT(id, n) (((uint16_t)(id)) | ((((uint16_t)(n)) &amp; 0xF) &lt;&lt; 12))</code></pre>

<p>&nbsp;</p>

<p>所以可以看到事件记录格式如下</p>

<p>EEWORLDIMGTK1</p>

<p>&nbsp;</p>

<p>添加了上述信息后然后调用TraceEventAddxxx添加参数,</p>

<p>最后调用xTraceEventEnd将事件写入到发送缓存中。</p>

<p>以任务切换事件为例,如果切换到执行某个任务,则回调traceTASK_SWITCHED_IN</p>

<p>即xTraceTaskSwitch</p>

<section>
<pre>
<code>traceResult xTraceTaskSwitch(void *pvTask, TraceUnsignedBaseType_t uxPriority)
{
    traceResult xResult = TRC_FAIL;
    TraceEventHandle_t xEventHandle = 0;
    void* pvCurrent = 0;

    TRACE_ALLOC_CRITICAL_SECTION();
   
    (void)pvTask;
    (void)uxPriority;
    if (xTraceIsRecorderEnabled() == 0)
    {
      return xResult;
    }
    TRACE_ENTER_CRITICAL_SECTION();
    xTraceStateSet(TRC_STATE_IN_TASKSWITCH);
    xTraceTaskGetCurrent(&amp;pvCurrent);
    if (pvCurrent != pvTask)
    {
      xTraceTaskSetCurrent(pvTask);
      
      if (xTraceEventBegin(PSF_EVENT_TASK_ACTIVATE, sizeof(void*) + sizeof(uint32_t), &amp;xEventHandle) == TRC_SUCCESS)
      {
            xTraceEventAddPointer(xEventHandle, pvTask);
            xTraceEventAdd32(xEventHandle, (uint32_t)uxPriority);
            xTraceEventEnd(xEventHandle);
            xResult = TRC_SUCCESS;
      }
    }
    xTraceStateSet(TRC_STATE_IN_APPLICATION);
    TRACE_EXIT_CRITICAL_SECTION();
    return xResult;
}</code></pre>

<p>&nbsp;</p>
</section>

<p>其中核心的事件记录处理如下</p>

<section>
<pre>
<code>   if (xTraceEventBegin(PSF_EVENT_TASK_ACTIVATE, sizeof(void*) + sizeof(uint32_t), &amp;xEventHandle) == TRC_SUCCESS)
      {
            xTraceEventAddPointer(xEventHandle, pvTask);
            xTraceEventAdd32(xEventHandle, (uint32_t)uxPriority);
            xTraceEventEnd(xEventHandle);
            xResult = TRC_SUCCESS;
      }</code></pre>

<p>&nbsp;</p>
</section>

<p>事件类型是PSF_EVENT_TASK_ACTIVATE=0x37</p>

<p>参数为两个</p>

<p>一个(void*)类型的任务地址,即pvTask</p>

<p>一个uint32_t类型的任务优先级,即uxPriority</p>

<p>所以传入xTraceEventBegin的参数大小是sizeof(void*) + sizeof(uint32_t)即两个WORD。</p>

<p>xTraceEventAddPointer添加指针参数,实现如下</p>

<section>
<pre>
<code>#define TRC_EVENT_ADD_POINTER(xEventHandle, value) \
    TRC_COMMA_EXPR_TO_STATEMENT_EXPR_3( \
      ((void**)((TraceEventData_t*)(xEventHandle))-&gt;pvBlob)[((TraceEventData_t*)(xEventHandle))-&gt;offset / sizeof(void*)] = (value), \
      ((TraceEventData_t*)(xEventHandle))-&gt;offset += sizeof(void*), \
      TRC_SUCCESS \
    )</code></pre>

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

<p>xTraceEventAdd32添加ige无符号32位值,实现如下</p>

<section>
<pre>
<code>#define TRC_EVENT_ADD_32(xEventHandle, value) \
    TRC_COMMA_EXPR_TO_STATEMENT_EXPR_3( \
      ((uint32_t*)((TraceEventData_t*)(xEventHandle))-&gt;pvBlob)[((TraceEventData_t*)(xEventHandle))-&gt;offset / sizeof(uint32_t)] = (value), \
      ((TraceEventData_t*)(xEventHandle))-&gt;offset += sizeof(uint32_t), \
      TRC_SUCCESS \
    )</code></pre>

<p>&nbsp;</p>
</section>

<p>xTraceEventAdd32添加ige无符号32位值,实现如下</p>

<section>
<pre>
<code>#define TRC_EVENT_ADD_32(xEventHandle, value) \
    TRC_COMMA_EXPR_TO_STATEMENT_EXPR_3( \
      ((uint32_t*)((TraceEventData_t*)(xEventHandle))-&gt;pvBlob)[((TraceEventData_t*)(xEventHandle))-&gt;offset / sizeof(uint32_t)] = (value), \
      ((TraceEventData_t*)(xEventHandle))-&gt;offset += sizeof(uint32_t), \
      TRC_SUCCESS \
    )</code></pre>

<p>&nbsp;</p>
</section>

<p>上述基本事件信息和参数都存储在pvBlob开始的区域,</p>

<p>最后xTraceEventEnd-&gt;xTraceEventEndOffline-&gt;xTraceStreamPortCommit将事件存入发送缓存区。</p>

<p>最后清除pvBlob记录,以便下次继续进行事件记录。</p>

<section>
<pre>
<code>#define RESET_EVENT_DATA(p) \
    ( \
      (p)-&gt;pvBlob = 0, \
      (p)-&gt;size = 0, \
      (p)-&gt;offset = 0 \
    )</code></pre>

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

<p>&nbsp;</p>

<p>EEWORLDIMGTK2</p>

<p>对应抓取的包如下</p>

<p>0x2037表示是PSF_EVENT_TASK_ACTIVATE事件,0x00BE是事件计数值,0x00012809是时间戳,,0x081103A8是任务地址,0x00000001是任务优先级。</p>

<p>后面0x20EB是另外一个事件,事件参数有2个,事件ID是0xEB可以看到是PSF_EVENT_UNUSED_STACK,事件计数器0x00BF是在前一个事件基础上递增,时间戳是0x00013911,参数1是0x081103A8,参数2是0x00000FA0.</p>

<p>可以看到xTraceStackMonitorReport中,PSF_EVENT_UNUSED_STACK事件除了基本事件信息后面就是任务地址和栈最低MARK值,这里是0x00000FA0,说明该任务最少剩余该大小栈未使用。</p>

<section>
<pre>
<code> if (xTraceEventBegin(PSF_EVENT_UNUSED_STACK, sizeof(void*) + sizeof(uint32_t), &amp;xEventHandle) == TRC_SUCCESS)
      {
            xTraceEventAddPointer(xEventHandle, pxStackMonitorEntry-&gt;pvTask);
            xTraceEventAdd32(xEventHandle, (uint32_t)pxStackMonitorEntry-&gt;uxPreviousLowWaterMark);
            xTraceEventEnd(xEventHandle);
      }
</code></pre>

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

<p>&nbsp;</p>

<p>EEWORLDIMGTK3</p>

<h1><strong>三.丢事件监测</strong></h1>

<p>EEWORLDIMGTK4</p>

<p>如上图live Stream可能会显示丢事件数,其实现原理如下</p>

<section>
<pre>
<code>#define TRC_EVENT_BEGIN_OFFLINE(uiEventCode, uiPayloadSize, pxEventHandle) \
    ( \
      (xTraceEventBeginRawOffline(sizeof(TraceBaseEvent_t) + (uiPayloadSize), pxEventHandle)) == TRC_SUCCESS ? \
      ( \
            pxTraceEventDataTable-&gt;coreEventData.eventCounter++, \
            SET_BASE_EVENT_DATA((TraceBaseEvent_t*)(((TraceEventData_t*)*(pxEventHandle))-&gt;pvBlob), \
                uiEventCode, \
                (((TraceEventData_t*)*(pxEventHandle))-&gt;size - sizeof(TraceBaseEvent_t)) / sizeof(uint32_t), \
                pxTraceEventDataTable-&gt;coreEventData.eventCounter), \
            ((TraceEventData_t*)*(pxEventHandle))-&gt;offset += sizeof(TraceBaseEvent_t), \
            TRC_SUCCESS \
      ) : TRC_FAIL \
    )</code></pre>

<p>&nbsp;</p>
</section>

<p>每记录一个事件时,TRC_EVENT_BEGIN_OFFLINE宏中,事件计数递增eventCounter++,然后</p>

<p>SET_BASE_EVENT_DATA记录到事件信息中</p>

<section>
<pre>
<code>typedef struct {
    uint16_t EventID;       /**&lt; */
    uint16_t EventCount;    /**&lt; */
    uint32_t TS;            /**&lt; */
} TraceBaseEvent_t;</code></pre>

<p>&nbsp;</p>
</section>

<p>EventCount会递增,上位机可以通过该值是否连续递增来确认是否丢事件,以及丢了多少个事件。</p>

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

<p>以上分析了Tracealyzer的发送到上位机的事件记录是如何组织的,基于此就可以手动解析事件信息方便调试分析。甚至可以自己开发类似的GUI或者其他测试分析工具。</p>

<p>&nbsp;</p>

<p>&nbsp;</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事件记录格式与丢事件监测