基于 Yocto 的 Linux 發(fā)行版上測試 Percepio 的 Tracealyzer 中的 Linux 支持功能的經(jīng)驗。在此過程中,我重點介紹了此類可視化跟蹤診斷工具如何幫助開發(fā)人員評估其嵌入式系統(tǒng)的性能,從分析驅(qū)動程序和中斷處理程序到檢查用戶空間應用程序和編譯器選項。
首先讓我們看看如何評估 Linux 驅(qū)動程序?qū)崿F(xiàn)。值得注意的是,Tracealyzer for Linux 利用了 LTTng,這是一個開源跟蹤器和分析器,允許開發(fā)人員通過解析 LTTng 的輸出并生成可視化和詳細統(tǒng)計數(shù)據(jù)來評估內(nèi)核的性能。
圖 1. 自定義 Linux 驅(qū)動程序。
正在評估驅(qū)動程序的設備有三個主要接口。I2C 接口控制設備,SPI 接口用于將數(shù)據(jù)流回 Linux 設備,GPIO 是一條中斷線,用于指示何時有數(shù)據(jù)可供使用。當 GPIO 線被置位時,驅(qū)動程序會發(fā)送一個 I2C 命令來指示設備開始流式傳輸,這將通過 SPI 接口完成。驅(qū)動程序?qū)⒅甘厩度胧?Linux 系統(tǒng)中的 DMA 控制器 (DMAC) 管理 SPI 總線和系統(tǒng) RAM 之間的數(shù)據(jù)傳輸,以確保 CPU 能夠管理其他任務。最后,Linux 設備上的應用程序代碼用于從 RAM 檢索流式傳輸?shù)臄?shù)據(jù)并將其存儲在非易失性存儲器中。
Tracealyzer 用于驗證兩個重要指標。首先,從 GPIO 被置為有效到發(fā)出 I2C 命令的時間量保持在最短。其次,Linux 內(nèi)核為驅(qū)動程序提供了足夠的執(zhí)行周期,使其能夠定期管理 DMAC 遇到的任何問題。目標是保證在流式傳輸過程中丟失最少的數(shù)據(jù),Tracealyzer 用于幫助確保這一保證。
設備開發(fā)套件
Tracealyzer 使用 LTTng 生成的文件,因此通過在設備的基于 Yocto 項目的板級支持包 (BSP) 中創(chuàng)建自定義層,將嵌入式 Linux 平臺配置為支持 LTTng。生成的 Linux 映像加載到 SD 卡上并在開發(fā)套件上啟動。加載設備驅(qū)動程序并將生成的跟蹤數(shù)據(jù)存儲在卡上以供離線分析。然后在主機上啟動 Tracealyzer 以查看和分析收集到的跟蹤。
對于調(diào)查內(nèi)核空間,“跟蹤視圖”、“參與者實例”、“CPU 負載圖”和“上下文切換強度”視圖最為合適。跟蹤視圖(圖 2)顯示內(nèi)核中使用的交換器(空閑任務)在整個捕獲過程中占用了最多的執(zhí)行資源,這是預料之中的。當驅(qū)動程序正在運行并將數(shù)據(jù)從設備傳輸?shù)介_發(fā)板時,特定內(nèi)核線程將獲得平臺上更大的執(zhí)行資源塊來執(zhí)行傳輸。
圖 2. 垂直跟蹤視圖使用垂直時間線顯示事件流。時間從頂部開始向下增長。每列代表系統(tǒng)中的一個執(zhí)行上下文(通常是一個任務或中斷處理程序),列中的矩形顯示特定任務的運行時間。水平標簽(左側(cè))標記記錄的軟件事件。圖表完全響應,放大后可顯示更多細節(jié)。Tracealyzer 中的大多數(shù)其他視圖都鏈接回跟蹤視圖,因此單擊數(shù)據(jù)點即可顯示時間線中相應事件發(fā)生的位置。
下一個重要視圖是參與者實例。選擇下拉菜單中的“執(zhí)行時間”將生成一個圖表,該圖表指示任何特定“參與者”(定義為任何執(zhí)行元素,例如線程或進程)所占用的時間量。選擇一個內(nèi)核線程會顯示其執(zhí)行時間在 350、450 和 550 微秒處出現(xiàn)幾個峰值。要了解這些峰值是否真的值得關注,我們需要了解系統(tǒng)的時序要求或?qū)⑵渑c系統(tǒng)在“正?!睏l件下的運行情況評估進行比較。
圖 3. Actor 實例窗口。
從跟蹤中選擇另一個內(nèi)核線程會顯示其中一個內(nèi)核線程的執(zhí)行時間出現(xiàn)相對較大的峰值?!癈PU 負載”視圖描述了不同參與者的 CPU 利用率。我們可以看到,突出顯示的內(nèi)核線程的執(zhí)行時間峰值不會導致 CPU 利用率異常高(最大 CPU 利用率略高于 1%)。因此,該峰值無需擔心。
圖 4. 使用 CPU 的不同參與者。
最后一個視圖是“上下文切換強度”視圖,它顯示內(nèi)核驅(qū)動程序是否性能良好且不會導致內(nèi)核崩潰。
圖 5?!吧舷挛那袚Q強度”視圖。
與其他線程相比,這并未顯示任何特定內(nèi)核線程的任何重大上下文切換。如果驅(qū)動程序中存在性能問題,則內(nèi)核線程可能會發(fā)生重大上下文切換。這可能是由于內(nèi)核調(diào)度程序?qū)?zhí)行時間分配給內(nèi)核線程,然后在一段時間后移至另一個線程,但如果該線程需要執(zhí)行資源,則立即切換回該線程。同樣,確定大約 20 次上下文切換是否可以接受取決于系統(tǒng)要求或系統(tǒng)正常運行時執(zhí)行的測量。
這些視圖還提供了一種快速概覽跟蹤并定位“熱點”或感興趣的異常以供進一步研究的方法。這是 Tracealyzer 的主要優(yōu)點之一,否則在包含數(shù)千甚至數(shù)百萬個事件的長跟蹤中很難找到它們。
發(fā)現(xiàn) Linux 中斷處理程序中的性能問題
大多數(shù) Linux 設備驅(qū)動程序都使用中斷。關于中斷,首先要記住的是,限制分配給中斷處理程序的操作數(shù)量很重要,因為內(nèi)核處于敏感狀態(tài),整個處理器也是如此。舉個例子,當中斷處理程序運行時,所有中斷都被屏蔽——也就是說,沒有其他中斷可以觸發(fā);如果中斷處理程序執(zhí)行時間很長,其他中斷可能會被錯過。因此,在處理中斷時,必須堅持最低限度,例如寄存器操作和在處理器內(nèi)存中移動數(shù)據(jù)。管理數(shù)據(jù)傳輸?shù)绕渌僮鲬薪o tasklet 或其他內(nèi)核機制。在驅(qū)動程序示例中,設備規(guī)范指出中斷設置為每 80 毫秒觸發(fā)一次,所以這定義了中斷處理程序執(zhí)行所需的最大時間。
知道何時使用 printk
Tracealyzer 可用于確保中斷處理程序執(zhí)行的操作盡可能少。例如,它幾乎消除了使用 printk 語句、比較內(nèi)核日志中的時間戳以及瀏覽無休止的 LTTng 跟蹤來評估性能的所有需要。相反,它提供了中斷處理程序的清晰而詳細的視圖。
在數(shù)據(jù)采集設備中,GPIO 信號通知驅(qū)動程序已準備好從設備收集數(shù)據(jù),驅(qū)動程序中的 printk 調(diào)用只是在內(nèi)核日志中記錄已收到中斷,返回值 IRQ_HANDLED 通知內(nèi)核已處理中斷。
但是,在加載內(nèi)核模塊并連接設備之前,LTTng 會在目標設備中啟動,并被指示只捕獲中斷處理程序。將 LTTng 跟蹤傳輸?shù)街鳈C并啟動 Tracealyzer 后,參與者實例圖僅顯示中斷處理程序和相關信息,以便更集中注意力。只需一眼,我們就可以知道中斷處理程序的觸發(fā)頻率(在此示例中大約為預期的 80 微秒)。但是,通過“選擇詳細信息”窗口深入挖掘并打開“執(zhí)行時間”選項可以發(fā)現(xiàn),中斷處理程序平均需要 3.3 毫秒來執(zhí)行實例。
圖 6. printk 調(diào)用的平均處理時間為 3.3 毫秒。
當刪除 printk 調(diào)用時,顯示參與者實例圖并且視圖仍然設置為“執(zhí)行時間”,Tracealyzer 顯示處理時間顯著下降,從使用 printk 時的 3.3 毫秒減少到不使用 printk 時的 14 微秒。
這種差異清楚地說明了 printk 調(diào)用涉及大量處理,在打印到內(nèi)核日志時必須考慮各種不同情況。因此,為了實現(xiàn)最佳性能,顯而易見的結(jié)論是盡量避免在中斷處理程序中使用 printk 或其任何派生函數(shù)。
Tracealyzer 還顯示執(zhí)行時間往往變化無常。雖然微秒的差異確實很小,但了解為什么會出現(xiàn)這種差異仍然很重要。
雖然捕獲的中間部分顯示中斷處理程序每 80 毫秒觸發(fā)一次,正如預期的那樣,但記錄的調(diào)用次數(shù)在開始和結(jié)束時要高得多。在捕獲結(jié)束時,事情變得非常不穩(wěn)定,在一個實例中,執(zhí)行在 325 毫秒后被調(diào)用。
這可能是因為中斷處理程序中的設備沒有被指示停止觸發(fā)其中斷。由于中斷始終存在,Linux 調(diào)度程序會不斷將執(zhí)行資源交還給中斷處理程序;這種不良現(xiàn)象通常稱為“抖動”,但 printk 語句掩蓋了這個錯誤。
有趣的是,即使沒有指示設備停止調(diào)用中斷,中斷處理程序的周期性也會在一段時間后再次穩(wěn)定下來。仔細查看設備規(guī)范會發(fā)現(xiàn),設備上存在一種故障安全機制,如果在 I2C 總線上沒有確認中斷,該機制會在一段時間后自動取消斷言中斷。由于在本例中,使用 printk 執(zhí)行中斷處理程序之間的時間約為 80 毫秒,因此執(zhí)行時間會進入取消斷言階段,從而掩蓋了代碼沒有充分取消斷言中斷的事實。
如果不使用 Tracealyzer,這個錯誤實際上會被隱藏起來,直到發(fā)布前不久刪除了多余的 printk 調(diào)用時才會被發(fā)現(xiàn)或顯現(xiàn)出來。在那個后期階段,必要的驅(qū)動程序修改將導致嚴重的延遲和成本。
評估 Linux 系統(tǒng)性能
Tracealyzer 還可用于調(diào)整 Linux 系統(tǒng)以最大限度提高性能;我們將使用 Linux 系統(tǒng)(例如 Nvidia 的 Jetson Nano)和用戶空間應用程序(例如 iperf)來說明這一點。
圖 7 顯示了本實驗的基本設置。
圖 7:使用 Nvidia 的 Jetson Nano 評估 Linux 系統(tǒng)性能。
調(diào)整 Jetson Nano 上 iperf 服務器的 CPU 親和性可以揭示該參數(shù)如何影響客戶端和服務器之間的總體吞吐量。術(shù)語“CPU 親和性”表示執(zhí)行上下文固定的特定 CPU 核心。通常,親和性是根據(jù)應用程序設置的。如果中斷和相應處理程序的 CPU 親和性與數(shù)據(jù)包接收過程的 CPU 親和性相匹配,則應該最大限度地減少數(shù)據(jù)包丟失,因為不會浪費時間在核心之間移動數(shù)據(jù) - 至少這是想法。
此分析和可能的優(yōu)化首先需要確定 Jetson Nano 上以太網(wǎng)接口 (eth0) 的親和性。這將指示哪個處理器核心處理來自 eth0 接口的中斷。通過不允許 Linux 選擇處理器核心,而是將 iperf 服務器執(zhí)行固定到特定核心,Mohammed 旨在評估對吞吐量的影響。由于 CPU0 負責處理來自 eth0 接口的中斷,因此他將 iperf 服務器固定到 CPU3。
從主機運行 iperf 測試,然后停止并銷毀 LTTng 會話,以避免產(chǎn)生大量無關事件的痕跡。
當 iperf 固定到 CPU3 時,這在捕獲中產(chǎn)生了一些有趣的結(jié)果。首先,Tracealyzer 顯示有四個 iperf 進程實例正在運行,盡管 Linux 只列出了一個實例。但與 Linux 報告的 PID 相對應的 iperf 實例只執(zhí)行了兩次:一次在 iperf 測量開始時,一次在測量結(jié)束時。
盡管 iperf 被固定到 CPU3,但 iperf 的其他實例仍在不同的 CPU 核心上執(zhí)行。這實際上并不罕見,因為應用程序可以實現(xiàn)自己的邏輯來選擇合適的 CPU 進行執(zhí)行??磥?iperf 也實現(xiàn)了這樣的邏輯。
圖 8. Tracealyzer 跟蹤 iperf 以評估嵌入式多核 Linux 系統(tǒng)性能
該跟蹤還顯示了 eth0 中斷處理程序的一系列執(zhí)行實例,這些實例與 iperf 實驗大約同時執(zhí)行,顯示了 eth0 中斷處理程序執(zhí)行時間與 iperf 實例執(zhí)行時間之間的相關性。
Tracealyzer 顯示,從 eth0 中斷處理程序完成到 iperf 實例開始執(zhí)行需要 55 微秒。當系統(tǒng)在所有 CPU 上負載 20 個進程時,新的 iperf 測量顯示吞吐量保持不變。
打開 Tracealyzer 并在人工強調(diào) CPU 核心的情況下進行捕獲,顯示中斷處理程序和 iperf 實例的執(zhí)行順序,其中 eth0 中斷處理程序執(zhí)行完成和 iperf 執(zhí)行開始之間的時間約為 40 微秒。
圖 9. 顯示重負載下的 CPU 的視圖。
雖然數(shù)字本身并不重要(盡管有趣的是,在負載下時間實際上更少),但系統(tǒng)負載和非負載時的時間數(shù)量級相同 - 40 微秒對 55 微秒。這是 Linux 內(nèi)核的一個很棒的特性,即使用戶空間應用程序似乎占用了系統(tǒng)的所有四個核心,它仍然可以確保其他用戶空間應用程序不會缺乏 CPU 資源,并且核心間通信不會受到影響。
分析正常和緊張條件下不同執(zhí)行元素之間的相互作用,可以發(fā)現(xiàn) Linux 內(nèi)核的一個巧妙特性,即盡最大努力為所有進程提供公平的 CPU 資源份額。