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