使用 ftrace

ftrace 是用來瞭解 Linux 核心內部發生什麼事的偵錯工具。以下各節將詳細說明基本 ftrace 功能、ftrace 與 atrace 的使用方式 (可擷取核心事件),以及動態 ftrace。

如要進一步瞭解 systrace 無法提供的進階 ftrace 功能,請參閱 <kernel tree>/Documentation/trace/ftrace.txt 中的 ftrace 說明文件。

使用 atrace 擷取核心事件

atrace (frameworks/native/cmds/atrace) 會使用 ftrace 擷取核心事件。反過來說,systrace.py (或 Catapult 較新版本中的 run_systrace.py) 會使用 ADB 在裝置上執行 atrace。atrace 會執行以下操作:

  • 透過設定屬性 (debug.atrace.tags.enableflags) 來設定使用者模式追蹤。
  • 寫入適當的 ftrace sysfs 節點,啟用所需的 ftrace 功能。不過,由於 ftrace 支援更多功能,您可以自行設定某些 sysfs 節點,然後使用 atrace。

除了啟動時追蹤作業外,請使用 atrace 將屬性設為適當的值。這個屬性是位元遮罩,除了查看適當的標頭 (可能會因 Android 版本而異),沒有其他方法可以判斷正確的值。

啟用 ftrace 事件

ftrace sysfs 節點位於 /sys/kernel/tracing,追蹤記錄事件則分為 /sys/kernel/tracing/events 中的各個類別。

如要針對個別類別啟用事件,請使用:

echo 1 > /sys/kernel/tracing/events/irq/enable

如要針對個別事件啟用事件,請使用:

echo 1 > /sys/kernel/tracing/events/sched/sched_wakeup/enable

如果您已透過寫入 sysfs 節點啟用額外事件,atrace 就不會將這些事件重設。針對 Qualcomm 裝置啟動,常見的模式是啟用 kgsl (GPU) 和 mdss (顯示管道) 追蹤點,然後使用 atrace 或 systrace

adb shell "echo 1 > /sys/kernel/tracing/events/mdss/enable"
adb shell "echo 1 > /sys/kernel/tracing/events/kgsl/enable"
./systrace.py sched freq idle am wm gfx view binder_driver irq workq ss sync -t 10 -b 96000 -o full_trace.html

您也可以在沒有 atrace 或 systrace 的情況下使用 ftrace,這在您需要僅限核心的追蹤記錄 (或您花時間手動編寫使用者模式追蹤記錄屬性) 時非常實用。如要只執行 ftrace:

  1. 將緩衝區大小設為足以追蹤的值:
    echo 96000 > /sys/kernel/tracing/buffer_size_kb
    
  2. 啟用追蹤功能:
    echo 1 > /sys/kernel/tracing/tracing_on
    
  3. 執行測試,然後停用追蹤功能:
    echo 0 > /sys/kernel/tracing/tracing_on
    
  4. 傾印追蹤記錄:
    cat /sys/kernel/tracing/trace > /data/local/tmp/trace_output
    

trace_output 會以文字格式提供追蹤記錄。如要使用 Catapult 進行視覺化,請從 GitHub 取得 Catapult 存放區,然後執行 trace2html:

catapult/tracing/bin/trace2html ~/path/to/trace_file

根據預設,這會在相同目錄中寫入 trace_file.html

關聯事件

同時查看 Catapult 視覺化效果和 ftrace 記錄通常很有幫助。舉例來說,Catapult 不會將部分 ftrace 事件 (尤其是供應商專屬事件) 視覺化。不過,Catapult 的時間戳記會與追蹤記錄中的首個事件或 atrace 傾印的特定時間戳記相關,而原始 ftrace 時間戳記則會根據 Linux 核心中的特定絕對時鐘來源。

如要從 Catapult 事件中找出特定 ftrace 事件,請按照下列步驟操作:

  1. 開啟原始 ftrace 記錄。根據預設,近期版本的 systrace 會壓縮追蹤記錄:
    • 如果您使用 --no-compress 擷取 systrace,則會在 HTML 檔案中,在以「BEGIN TRACE」開頭的部分中找到該檔案。
    • 如果沒有,請從 Catapult 樹狀結構 (tracing/bin/html2trace) 執行 html2trace,以便解壓縮追蹤記錄。
  2. 在 Catapult 圖表中找出相對時間戳記。
  3. 找出追蹤記錄開頭包含 tracing_mark_sync 的行。內容應如下所示:
    <5134>-5134  (-----) [003] ...1    68.104349: tracing_mark_write: trace_event_clock_sync: parent_ts=68.104286
    

    如果這行不存在 (或您使用 ftrace 時未使用 atrace),則時間會以 ftrace 記錄中的第一個事件為基準。
    1. 將相對時間戳記 (以毫秒為單位) 加到 parent_ts 中的值 (以秒為單位)。
    2. 搜尋新的時間戳記。

這些步驟應可讓您抵達活動現場 (或至少非常接近活動現場)。

使用動態 ftrace

如果 systrace 和標準 ftrace 不足以解決問題,您可以使用最後一項工具:動態 ftrace。動態 ftrace 涉及在開機後重寫核心程式碼,因此基於安全性考量,無法在正式版核心中使用。不過,2015 年和 2016 年發生的每個棘手效能錯誤,最終都歸因於使用動態 ftrace。這對於偵錯不可中斷的休眠特別有用,因為每次觸及觸發不可中斷休眠的函式時,您都可以在核心中取得堆疊追蹤。您也可以在關閉中斷和預取的情況下對各個部分進行偵錯,這對於證明問題非常有用。

如要開啟動態 ftrace,請編輯核心的 defconfig:

  1. 移除 CONFIG_STRICT_MEMORY_RWX (如果存在的話)。如果您使用的是 3.18 以上版本和 arm64,則不會出現此問題。
  2. 新增以下項目:CONFIG_DYNAMIC_FTRACE=y、CONFIG_FUNCTION_TRACER=y、CONFIG_IRQSOFF_TRACER=y、CONFIG_FUNCTION_PROFILER=y 和 CONFIG_PREEMPT_TRACER=y
  3. 重新建構並啟動新的核心。
  4. 執行以下指令,檢查是否有可用的追蹤程式:
    cat /sys/kernel/tracing/available_tracers
    
  5. 確認指令傳回 functionirqsoffpreemptoffpreemptirqsoff
  6. 執行下列指令,確保動態 ftrace 運作正常:
    cat /sys/kernel/tracing/available_filter_functions | grep <a function you care about>
    

完成這些步驟後,您將可使用動態 ftrace、函式分析器、irqsoff 分析器和 preemptoff 分析器。由於 ftrace 功能強大但複雜,我們強烈建議在使用前先閱讀相關文件。irqsoff 和 preemptoff 主要用於確認驅動程式可能會讓中斷或預取關閉太久。

函式分析器是解決效能問題的最佳選項,通常用於找出函式呼叫的位置。


如果函式分析器提供的資料不夠具體,您可以將 ftrace 追蹤點與函式分析器結合。您可以使用與平常相同的方式啟用 ftrace 事件,這些事件會與追蹤記錄交錯。如果您要偵錯的特定函式偶爾會發生長時間的不可中斷休眠,這項功能就非常實用:將 ftrace 篩選器設為所需函式、啟用追蹤點,然後進行追蹤。您可以使用 trace2html 剖析產生的追蹤記錄,找出所需事件,然後在原始追蹤記錄中取得附近的堆疊追蹤記錄。

使用 lockstat

有時,ftrace 就足夠了,您只需要對看似是核心鎖定競爭的項目進行偵錯。還有一個值得嘗試的核心選項:CONFIG_LOCK_STAT。這是最後的手段,因為這會使核心的大小膨脹到大多數裝置無法處理的程度,因此在 Android 裝置上運作極為困難。

不過,lockstat 會使用偵錯鎖定基礎結構,這對許多其他應用程式都很有用。所有負責裝置啟動的工作人員都應設法讓這個選項在所有裝置上運作,因為有時您會想:「如果我能開啟 LOCK_STAT,就能在五分鐘內確認或駁回這個問題,而不是花上五天。」


如果您可以使用設定選項啟動核心,鎖定追蹤功能就會類似 ftrace:

  1. 啟用追蹤功能:
    echo 1 > /proc/sys/kernel/lock_stat
    
  2. 執行測試。
  3. 停用追蹤:
    echo 0 > /proc/sys/kernel/lock_stat
    
  4. 傾印追蹤記錄:
    cat /proc/lock_stat > /data/local/tmp/lock_stat
    

如需解讀結果輸出內容的相關說明,請參閱 <kernel>/Documentation/locking/lockstat.txt 的 lockstat 說明文件。

使用供應商追蹤點

請先使用上游追蹤點,但有時您可能需要使用供應商追蹤點:

  { "gfx",        "Graphics",         ATRACE_TAG_GRAPHICS, {
        { OPT,      "events/mdss/enable" },
        { OPT,      "events/sde/enable" },
        { OPT,      "events/mali_systrace/enable" },
    } },

追蹤點可透過 HAL 服務擴充,讓您新增裝置專屬的追蹤點/類別。追蹤點已與 perfetto、atrace/systrace 和裝置端系統追蹤應用程式整合。

實作追蹤點/類別的 API 如下:

  • listCategories() 會產生 (vec<TracingCategory> categories)。
  • enableCategories(vec<string> categories) 會產生 (Status status)。
  • disableAllCategories() 會產生 (Status status)。
如需更多資訊,請參閱 AOSP 中的 HAL 定義和預設實作方式: