threadx的trace源碼分析--Apple的學習筆記 一,前言 二,帶着問題看源碼 三,traceX底層源碼分析 四,小結

一,前言

之前做了基於Segger RTT的上下位機,來測試os的task調度。我向來對整體系統(上下位機的通信很感興趣)知道了RTT的原理,所以就進行了資源利用,後來底層我改成了自己寫的精簡FIFO,也很好用。那麼threadx的traceX模塊設計是否也能爲我所用呢!

二,帶着問題看源碼

1. threadx中的traceX模塊的上下位機設計原理和RTT一樣嗎?
答:不一樣。我看了Azure RTOS TraceX的使用,它描述是轉儲(將內存數據轉爲s19等上位機工具支持的格式),而且是事後查看。於是我去看了單片機端的code,就找到了TX_TRACE_IN_LINE_INSERT。也就是FIFO進入的函數,沒有找到退出的函數,且用循環覆蓋來實現環形FIFO。所以我理解上位機是不支持實時查看的,等於單片機一直把log放入FIFO內存,等用戶暫停調試單片機的時候,可以將內存取出轉儲後,通過上位機來查看切換情況。

2.TX_TRACE_IN_LINE_INSERT入隊列中怎麼沒有開關中斷?
答:RTT主要用來實時打印及和上位機交互數據的(其實是用來代替串口的),所用不同優先級的task會去調用它的寫數據函數,就存在一個是否函數可重入的問題,由於多個task用一個資源,那麼就需要用到互斥,也就是要加開關全局中斷的。當然,全局開關中斷也不能加太多,否則會影響整體系統運行,但是不添加的話,可能打印的log準確性可能會有問題,這在使用的時候可以綜合考慮。
threadx的trace log功能僅僅是向內存寫入事件切換的時間戳及相關對象的4個附帶信息。那麼若僅在切換的時候寫入,或者說是os 切換各種事件的時候寫入,那麼我理解不在task或中斷運行時候寫入就不存在打斷問題,因爲所有打斷都是os切換導致的,若僅在os切換事件的時候寫入,就可以不用添加開關中斷。

3.循環隊列成員爲什麼都用指針
答:循環隊列之前瞭解的比較多。設計時候結構體用數組的比較多,單鏈表方式也有,裏面特別的元素成員就是size和溢出flag。這裏的設計直接用end和start地址,所以省略了size。關於溢出的判斷也變簡單了,就是start==end地址說明溢出了。我理解這是不同的用途導致FIFO設計時候使用的結構體也設計的不同,因爲此trace功能只有FIFO輸入沒有輸出,所有size成員用途不大。稍微功能有點點區別,就可以考慮更爲優化的設計了。設計真是千變萬化呢,這就是編碼吸引我的地方,可以自由創作,然後尋找最優方案。

三,traceX底層源碼分析

官網先看了help,也把traceX上位機軟件打開對照字段看了下。還是很容易理解它的設計的。主的一個結構體對象中,包括了obj和buffer成員。
爲什麼要obj和buffer2個成員呢?原因就是buffer的後面4個u32字節爲每個obj成員特有的,所以trace對象就包括了obj和buffer2個成員,buffer可以引用obj的數據。上位機用的就是buffer成員。
最簡單的看代碼的方法就是先看數據結構,因爲數據解構決定算法設計。

/* Define the an Trace Buffer Entry.  */
 typedef struct TX_TRACE_BUFFER_ENTRY_STRUCT
{

    ULONG                    tx_trace_buffer_entry_thread_pointer;
    ULONG                    tx_trace_buffer_entry_thread_priority;
    ULONG                    tx_trace_buffer_entry_event_id;
    ULONG                    tx_trace_buffer_entry_time_stamp;
#ifdef TX_MISRA_ENABLE
    ULONG                    tx_trace_buffer_entry_info_1;
    ULONG                    tx_trace_buffer_entry_info_2;
    ULONG                    tx_trace_buffer_entry_info_3;
    ULONG                    tx_trace_buffer_entry_info_4;
#else
    ULONG                    tx_trace_buffer_entry_information_field_1;
    ULONG                    tx_trace_buffer_entry_information_field_2;
    ULONG                    tx_trace_buffer_entry_information_field_3;
    ULONG                    tx_trace_buffer_entry_information_field_4;
#endif
} TX_TRACE_BUFFER_ENTRY;

代碼的結構體和上位機的截圖前8個完全對應,所以就是從單片機獲取的數據。對應的4個不同內容也有註釋,比如queue對象,就有queue prt,destination ptr,wait option和enqueued和上位機一致。

/* I1 = queue ptr, I2 = destination ptr, I3 = wait option, I4 = enqueued    */
#define TX_TRACE_QUEUE_RECEIVE                              68          
/* I1 = queue ptr, I2 = source ptr, I3 = wait option, I4 = enqueued         */
#define TX_TRACE_QUEUE_SEND                                 69          

再看看源碼中TX_TRACE_IN_LINE_INSERT宏函數,賦值8個字段,全部可以對應上。

trace_event_ptr -> tx_trace_buffer_entry_thread_pointer = (ULONG) trace_thread_ptr; \
trace_event_ptr -> tx_trace_buffer_entry_thread_priority = (ULONG) trace_priority; \
trace_event_ptr -> tx_trace_buffer_entry_event_id = (ULONG) (i); \
trace_event_ptr -> tx_trace_buffer_entry_time_stamp = (ULONG) TX_TRACE_TIME_SOURCE; \
TX_TRACE_INFO_FIELD_ASSIGNMENT((a),(b),(c),(d)) \

我之前說它是FIFO且覆蓋方式代碼如下,完成current的指針賦值8個字段後,就指向下一個空閒內容,且判斷下一個空閒內容是否爲end,若爲end就指向start。

trace_event_ptr++; \
if (trace_event_ptr >= _tx_trace_buffer_end_ptr) \
{ \
     trace_event_ptr =  _tx_trace_buffer_start_ptr; \
     _tx_trace_buffer_current_ptr =  trace_event_ptr;  \
     _tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer =  (ULONG) trace_event_ptr; \
     if (_tx_trace_full_notify_function) \
                (_tx_trace_full_notify_function)((VOID *) _tx_trace_header_ptr); \
} \
else \
{ \
     _tx_trace_buffer_current_ptr =  trace_event_ptr;  \
      _tx_trace_header_ptr -> tx_trace_header_buffer_current_pointer =  (ULONG) trace_event_ptr; \
} \

這裏可圈可點的內容如下,就是剛進入函數時候的if判斷是否使能。而且用的是mask對象的方式,我之前自己設計沒有考慮到此項,這樣就可以實現通過宏定義來配置使能某些想觀察的對象。而非控制所有,可以用來調整的顆粒度爲obj對象。這是本次閱讀源碼設計最大的收穫。

#define TX_TRACE_IN_LINE_INSERT(i,a,b,c,d,e) \
        { \
        TX_TRACE_BUFFER_ENTRY     *trace_event_ptr; \
        ULONG                      trace_system_state; \
        ULONG                      trace_priority; \
        TX_THREAD                 *trace_thread_ptr; \
            trace_event_ptr =  _tx_trace_buffer_current_ptr; \
            if ((trace_event_ptr) && (_tx_trace_event_enable_bits & ((ULONG) (e)))) \
            { \
               ...
            } \

四,小結

同樣的模板字段可以輸出不同的含義內容,這就是buffer複用的思想。這樣就可以使用同一的框架,這樣的設計思想我也可以借鑑。另外,對不同對象進行mask使能的方式也很有用。哈哈,收穫頗豐~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章