Perf 環形緩衝區¶
1. 簡介¶
環形緩衝區是一種基本的資料傳輸機制。 perf 使用環形緩衝區將事件資料從核心傳輸到使用者空間,另一種環形緩衝區,即所謂的輔助 (AUX) 環形緩衝區,在 Intel PT、Arm CoreSight 等硬體跟蹤中也發揮著重要作用。
環形緩衝區的實現至關重要,但也是一項非常具有挑戰性的工作。一方面,核心和使用者空間中的 perf 工具使用環形緩衝區來交換資料並將資料儲存到資料檔案中,因此環形緩衝區需要以高吞吐量傳輸資料;另一方面,環形緩衝區管理應避免顯著的過載,從而分散分析結果。
本文件深入探討了 perf 環形緩衝區的細節,分為兩個部分:首先,它解釋了 perf 環形緩衝區的實現,然後第二部分討論了 AUX 環形緩衝區機制。
2. 環形緩衝區實現¶
2.1 基本演算法¶
也就是說,典型的環形緩衝區由一個頭指標和一個尾指標管理;頭指標由寫入者操作,尾指標分別由讀取者更新。
+---------------------------+
| | |***|***|***| | |
+---------------------------+
`-> Tail `-> Head
* : the data is filled by the writer.
Figure 1. Ring buffer
Perf 使用相同的方式來管理其環形緩衝區。在實現中,有兩個關鍵資料結構儲存在一組連續的頁面中:控制結構,然後是環形緩衝區本身。 包含控制結構的頁面被稱為“使用者頁面”。儲存在連續的虛擬地址中簡化了環形緩衝區地址的定位,它位於使用者頁面之後的頁面中。
控制結構命名為 perf_event_mmap_page,它包含一個頭指標 data_head 和一個尾指標 data_tail。當核心開始將記錄填充到環形緩衝區中時,它會更新頭指標以保留記憶體,以便以後可以安全地將事件儲存到緩衝區中。 另一方面,當用戶頁面是可寫對映時,perf 工具在從環形緩衝區消耗資料後有權更新尾指標。 另一種情況是使用者頁面的只讀對映,將在 2.3.3 將樣本寫入緩衝區 節中討論。
user page ring buffer
+---------+---------+ +---------------------------------------+
|data_head|data_tail|...| | |***|***|***|***|***| | | |
+---------+---------+ +---------------------------------------+
` `----------------^ ^
`----------------------------------------------|
* : the data is filled by the writer.
Figure 2. Perf ring buffer
當使用 perf record 工具時,我們可以使用選項 -m 或 --mmap-pages= 指定環形緩衝區大小,給定的大小將被向上舍入為 2 的冪,即頁面大小的倍數。 雖然核心一次性分配所有記憶體頁,但會延遲將頁面對映到 VMA 區域,直到 perf 工具從使用者空間訪問緩衝區。 換句話說,在 perf 工具中第一次從使用者空間訪問緩衝區的頁面時,會發生頁面錯誤的data abort異常,核心會利用這個機會將頁面對映到程序 VMA 中(參見 perf_mmap_fault()),因此 perf 工具在從異常返回後可以繼續訪問該頁面。
2.2 用於不同跟蹤模式的環形緩衝區¶
perf 以不同的模式分析程式:預設模式、按執行緒模式、按 CPU 模式和系統範圍模式。 本節描述這些模式以及環形緩衝區如何滿足它們的要求。 最後,我們將回顧由這些模式引起的競爭條件。
2.2.1 預設模式¶
通常,我們執行 perf record 命令,後跟一個分析程式名稱,如下面的命令
perf record test_program
此命令沒有指定 CPU 和執行緒模式的任何選項,perf 工具在 perf 事件上應用預設模式。 它將系統中所有 CPU 和分析程式的 PID 對映到 perf 事件上,並在事件上啟用繼承模式,以便子任務繼承事件。 因此,perf 事件被歸因於
evsel::cpus::map[] = { 0 .. _SC_NPROCESSORS_ONLN-1 }
evsel::threads::map[] = { pid }
evsel::attr::inherit = 1
這些歸因最終將反映在環形緩衝區的部署上。 如下圖所示,perf 工具為每個 CPU 分配單獨的環形緩衝區,但它只為分析程式啟用事件,而不是為系統中的所有執行緒啟用事件。 T1 執行緒代表“test_program”的執行緒上下文,而 T2 和 T3 是系統中的無關執行緒。 perf 樣本專門為 T1 執行緒收集,並存儲在與 T1 執行緒正在執行的 CPU 關聯的環形緩衝區中。
T1 T2 T1
+----+ +-----------+ +----+
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
+----+--------------+-----------+----------+----+-------->
| |
v v
+-----------------------------------------------------+
| Ring buffer 0 |
+-----------------------------------------------------+
T1
+-----+
CPU1 |xxxxx|
-----+-----+--------------------------------------------->
|
v
+-----------------------------------------------------+
| Ring buffer 1 |
+-----------------------------------------------------+
T1 T3
+----+ +-------+
CPU2 |xxxx| |xxxxxxx|
--------------------------+----+--------+-------+-------->
|
v
+-----------------------------------------------------+
| Ring buffer 2 |
+-----------------------------------------------------+
T1
+--------------+
CPU3 |xxxxxxxxxxxxxx|
-----------+--------------+------------------------------>
|
v
+-----------------------------------------------------+
| Ring buffer 3 |
+-----------------------------------------------------+
T1: Thread 1; T2: Thread 2; T3: Thread 3
x: Thread is in running state
Figure 3. Ring buffer for default mode
2.2.2 按執行緒模式¶
透過在 perf 命令中指定選項 --per-thread,例如
perf record --per-thread test_program
perf 事件不對映到任何 CPU,並且僅繫結到分析的程序,因此,perf 事件的歸因是
evsel::cpus::map[0] = { -1 }
evsel::threads::map[] = { pid }
evsel::attr::inherit = 0
在這種模式下,為分析的執行緒分配單個環形緩衝區; 如果該執行緒被排程到 CPU 上,則在該 CPU 上啟用事件; 並且如果該執行緒從 CPU 排程出去,則在該 CPU 上停用事件。 當執行緒從一個 CPU 遷移到另一個 CPU 時,事件將在先前的 CPU 上被停用,並在下一個 CPU 上相應地啟用。
T1 T2 T1
+----+ +-----------+ +----+
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
+----+--------------+-----------+----------+----+-------->
| |
| T1 |
| +-----+ |
CPU1 | |xxxxx| |
--|--+-----+----------------------------------|---------->
| | |
| | T1 T3 |
| | +----+ +---+ |
CPU2 | | |xxxx| |xxx| |
--|-----|-----------------+----+--------+---+-|---------->
| | | |
| | T1 | |
| | +--------------+ | |
CPU3 | | |xxxxxxxxxxxxxx| | |
--|-----|--+--------------+-|-----------------|---------->
| | | | |
v v v v v
+-----------------------------------------------------+
| Ring buffer |
+-----------------------------------------------------+
T1: Thread 1
x: Thread is in running state
Figure 4. Ring buffer for per-thread mode
當 perf 在按執行緒模式下執行時,為分析的執行緒 T1 分配一個環形緩衝區。 環形緩衝區專用於執行緒 T1,如果執行緒 T1 正在執行,則 perf 事件將被記錄到環形緩衝區中; 當執行緒休眠時,所有關聯的事件將被停用,因此不會將任何跟蹤資料記錄到環形緩衝區中。
2.2.3 按 CPU 模式¶
選項 -C 用於收集 CPU 列表上的樣本,例如,下面的 perf 命令接收選項 -C 0,2
perf record -C 0,2 test_program
它將 perf 事件對映到 CPU 0 和 2,並且該事件不與任何 PID 關聯。 因此,perf 事件歸因被設定為
evsel::cpus::map[0] = { 0, 2 }
evsel::threads::map[] = { -1 }
evsel::attr::inherit = 0
這導致 perf record 會話將取樣 CPU0 和 CPU2 上的所有執行緒,並在 test_program 退出之前終止。 即使 CPU1 和 CPU3 上有任務正在執行,由於它們沒有環形緩衝區,因此將忽略這兩個 CPU 上的任何活動。 一個用例是將按執行緒模式和按 CPU 模式的選項組合在一起,例如,一起指定選項 –C 0,2 和 ––per–thread,只有在分析的執行緒被排程到任何列出的 CPU 上時,才會記錄樣本。
T1 T2 T1
+----+ +-----------+ +----+
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
+----+--------------+-----------+----------+----+-------->
| | |
v v v
+-----------------------------------------------------+
| Ring buffer 0 |
+-----------------------------------------------------+
T1
+-----+
CPU1 |xxxxx|
-----+-----+--------------------------------------------->
T1 T3
+----+ +-------+
CPU2 |xxxx| |xxxxxxx|
--------------------------+----+--------+-------+-------->
| |
v v
+-----------------------------------------------------+
| Ring buffer 1 |
+-----------------------------------------------------+
T1
+--------------+
CPU3 |xxxxxxxxxxxxxx|
-----------+--------------+------------------------------>
T1: Thread 1; T2: Thread 2; T3: Thread 3
x: Thread is in running state
Figure 5. Ring buffer for per-CPU mode
2.2.4 系統範圍模式¶
透過使用選項 –a 或 ––all–cpus,perf 收集所有 CPU 上所有任務的樣本,我們稱之為系統範圍模式,該命令是
perf record -a test_program
與按 CPU 模式類似,perf 事件不繫結到任何 PID,並且對映到系統中的所有 CPU
evsel::cpus::map[] = { 0 .. _SC_NPROCESSORS_ONLN-1 }
evsel::threads::map[] = { -1 }
evsel::attr::inherit = 0
在系統範圍模式下,每個 CPU 都有自己的環形緩衝區,所有執行緒在執行狀態下都被監視,並且樣本被記錄到屬於事件發生的 CPU 的環形緩衝區中。
T1 T2 T1
+----+ +-----------+ +----+
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
+----+--------------+-----------+----------+----+-------->
| | |
v v v
+-----------------------------------------------------+
| Ring buffer 0 |
+-----------------------------------------------------+
T1
+-----+
CPU1 |xxxxx|
-----+-----+--------------------------------------------->
|
v
+-----------------------------------------------------+
| Ring buffer 1 |
+-----------------------------------------------------+
T1 T3
+----+ +-------+
CPU2 |xxxx| |xxxxxxx|
--------------------------+----+--------+-------+-------->
| |
v v
+-----------------------------------------------------+
| Ring buffer 2 |
+-----------------------------------------------------+
T1
+--------------+
CPU3 |xxxxxxxxxxxxxx|
-----------+--------------+------------------------------>
|
v
+-----------------------------------------------------+
| Ring buffer 3 |
+-----------------------------------------------------+
T1: Thread 1; T2: Thread 2; T3: Thread 3
x: Thread is in running state
Figure 6. Ring buffer for system wide mode
2.3 訪問緩衝區¶
在瞭解了環形緩衝區在各種模式下如何分配的基礎上,本節解釋瞭如何訪問環形緩衝區。
2.3.1 生產者-消費者模型¶
在 Linux 核心中,PMU 事件可以生成樣本,這些樣本儲存到環形緩衝區中; 使用者空間中的 perf 命令透過從環形緩衝區中讀取資料來消耗樣本,並最終將資料儲存到檔案中以進行後期分析。 這是使用環形緩衝區的典型生產者-消費者模型。
perf 程序輪詢 PMU 事件,並在沒有傳入事件時休眠。 為了防止核心和使用者空間之間頻繁的交換,核心事件核心層引入了一個水位線,該水位線儲存在 perf_buffer::watermark 中。 當一個樣本被記錄到環形緩衝區中,並且如果使用的緩衝區超過水位線,核心會喚醒 perf 程序以從環形緩衝區中讀取樣本。
Perf
/ | Read samples
Polling / `--------------| Ring buffer
v v ;---------------------v
+----------------+ +---------+---------+ +-------------------+
|Event wait queue| |data_head|data_tail| |***|***| | |***|
+----------------+ +---------+---------+ +-------------------+
^ ^ `------------------------^
| Wake up tasks | Store samples
+-----------------------------+
| Kernel event core layer |
+-----------------------------+
* : the data is filled by the writer.
Figure 7. Writing and reading the ring buffer
當核心事件核心層通知使用者空間時,由於多個事件可能共享同一個環形緩衝區來記錄樣本,因此核心層會迭代與環形緩衝區關聯的每個事件,並喚醒正在等待事件的任務。 這是透過核心函式 ring_buffer_wakeup() 完成的。
perf 程序被喚醒後,它開始逐個檢查環形緩衝區,如果它發現任何包含樣本的環形緩衝區,它將讀取樣本以進行統計或儲存到資料檔案中。 鑑於 perf 程序能夠在任何 CPU 上執行,這導致環形緩衝區可能同時從多個 CPU 訪問,從而導致競爭條件。 競爭條件處理在 2.3.5 記憶體同步 節中描述。
2.3.2 環形緩衝區的屬性¶
Linux 核心支援環形緩衝區的兩個寫入方向:向前和向後。 前向寫入從環形緩衝區的開頭儲存樣本,後向寫入從環形緩衝區的末尾以相反的方向儲存資料。 perf 工具確定寫入方向。
此外,該工具可以將緩衝區以讀寫模式或只讀模式對映到使用者空間。
讀寫模式下的環形緩衝區以屬性 PROT_READ | PROT_WRITE 對映。 憑藉寫入許可權,perf 工具更新 data_tail 以指示資料起始位置。 結合作為當前資料結束位置的頭指標 data_head,perf 工具可以輕鬆知道從哪裡讀取資料。
或者,在只讀模式下,只有核心會更新 data_head,而由於對映屬性 PROT_READ,使用者空間無法訪問 data_tail。
因此,下面的矩陣說明了方向和對映特徵的各種組合。 perf 工具採用這些組合中的兩種來支援緩衝區型別:非覆蓋緩衝區和可覆蓋緩衝區。
對映模式 |
向前 |
向後 |
|---|---|---|
讀寫 |
非覆蓋環形緩衝區 |
未使用 |
只讀 |
未使用 |
可覆蓋環形緩衝區 |
非覆蓋環形緩衝區使用具有前向寫入的讀寫對映。 它從環形緩衝區的開頭開始儲存資料,並在溢位時環繞,這與普通環形緩衝區中的讀寫模式一起使用。 當消費者跟不上生產者時,它會丟失一些資料,核心會記錄它丟失了多少記錄,並在下次在環形緩衝區中找到空間時生成 PERF_RECORD_LOST 記錄。
可覆蓋環形緩衝區使用具有隻讀模式的後向寫入。 它從環形緩衝區的末尾儲存資料,並且 data_head 保留當前資料的位置,perf 始終知道它從哪裡開始讀取,直到環形緩衝區的末尾,因此它不需要 data_tail。 在這種模式下,它不會生成 PERF_RECORD_LOST 記錄。
2.3.3 將樣本寫入緩衝區¶
當獲取一個樣本並將其儲存到環形緩衝區中時,核心會根據樣本型別準備樣本欄位; 然後,它準備用於寫入環形緩衝區的資訊,該資訊儲存在結構 perf_output_handle 中。 最後,核心將樣本輸出到環形緩衝區中,並更新使用者頁面中的頭指標,以便 perf 工具可以看到最新值。
結構 perf_output_handle 用作跟蹤與緩衝區相關資訊的臨時上下文。 它的優點是它允許不同的事件併發寫入緩衝區。 例如,軟體事件和硬體 PMU 事件都被啟用以進行分析,perf_output_handle 的兩個例項分別用作軟體事件和硬體事件的單獨上下文。 這允許每個事件保留自己的記憶體空間來填充記錄資料。
2.3.4 從緩衝區讀取樣本¶
在使用者空間中,perf 工具利用 perf_event_mmap_page 結構來處理緩衝區的頭和尾。 它還使用 perf_mmap 結構來跟蹤環形緩衝區的上下文,此上下文包括有關緩衝區的起始和結束地址的資訊。 此外,即使發生溢位,也可以利用掩碼值來計算迴圈緩衝區指標。
與核心類似,使用者空間中的 perf 工具首先從環形緩衝區中讀取記錄的資料,然後更新緩衝區的尾指標 perf_event_mmap_page::data_tail。
2.3.5 記憶體同步¶
具有寬鬆記憶體模型的現代 CPU 無法保證記憶體排序,這意味著可能會無序訪問環形緩衝區和 perf_event_mmap_page 結構。 為了確保訪問 perf 環形緩衝區的特定順序,記憶體屏障用於確保資料依賴性。 記憶體同步的基本原理如下
Kernel User space
if (LOAD ->data_tail) { LOAD ->data_head
(A) smp_rmb() (C)
STORE $data LOAD $data
smp_wmb() (B) smp_mb() (D)
STORE ->data_head STORE ->data_tail
}
tools/include/linux/ring_buffer.h 中的註釋對為什麼以及如何使用記憶體屏障進行了很好的描述,這裡我們將只提供另一種解釋
(A) 是一種控制依賴,因此 CPU 確保檢查指標 perf_event_mmap_page::data_tail 和將樣本填充到環形緩衝區之間的順序;
(D) 與 (A) 配對。 (D) 將讀取環形緩衝區資料與寫入指標 data_tail 分開,perf 工具首先消耗樣本,然後告訴核心已釋放資料塊。 由於讀取操作後跟寫入操作,因此 (D) 是一個完整的記憶體屏障。
(B) 是兩個寫入操作中間的寫入屏障,它確保記錄樣本必須先於更新頭指標。
(C) 與 (B) 配對。 (C) 是一個讀取記憶體屏障,以確保在讀取樣本之前獲取頭指標。
為了實現上述演算法,引入了核心中的 perf_output_put_handle() 函式以及使用者空間中的兩個輔助函式 ring_buffer_read_head() 和 ring_buffer_write_tail(),它們依賴於上述記憶體屏障來確保資料依賴性。
一些架構支援具有 load-acquire 和 store-release 操作的單向滲透屏障,這些屏障更寬鬆,效能損失更小,因此 (C) 和 (D) 可以分別最佳化為使用屏障 smp_load_acquire() 和 smp_store_release()。
如果某個架構在其記憶體模型中不支援 load-acquire 和 store-release,它將回退到舊的記憶體屏障操作方式。 在這種情況下,smp_load_acquire() 封裝了 READ_ONCE() + smp_mb(),由於 smp_mb() 的成本很高,因此 ring_buffer_read_head() 不呼叫 smp_load_acquire(),而是使用屏障 READ_ONCE() + smp_rmb()。
3. AUX 環形緩衝區的機制¶
在本章中,我們將解釋 AUX 環形緩衝區的實現。 在第一部分中,它將討論 AUX 環形緩衝區和常規環形緩衝區之間的連線,然後第二部分將檢查 AUX 環形緩衝區如何與常規環形緩衝區協同工作,以及 AUX 環形緩衝區為取樣機制引入的附加功能。
3.1 AUX 和常規環形緩衝區之間的關係¶
通常,AUX 環形緩衝區是常規環形緩衝區的輔助。 常規環形緩衝區主要用於儲存事件樣本,並且每個事件格式都符合 union perf_event 中的定義; AUX 環形緩衝區用於記錄硬體跟蹤資料,並且跟蹤資料格式與硬體 IP 相關。
AUX 環形緩衝區的通用用法和優點是它由硬體直接寫入,而不是由核心寫入。 例如,寫入常規環形緩衝區的常規分析樣本會導致中斷。 跟蹤執行需要大量的樣本,並且使用中斷對於常規環形緩衝區機制來說是壓倒性的。 擁有一個 AUX 緩衝區允許一個與核心更分離的記憶體區域,並由硬體跟蹤直接寫入。
AUX 環形緩衝區重用與常規環形緩衝區相同的演算法進行緩衝區管理。 控制結構 perf_event_mmap_page 擴充套件了新欄位 aux_head 和 aux_tail,分別用於 AUX 環形緩衝區的頭指標和尾指標。
在初始化階段,除了 mmap()-ed 常規環形緩衝區之外,perf 工具在 auxtrace_mmap__mmap() 函式中呼叫第二個系統呼叫,用於 mmap 具有非零檔案偏移量的 AUX 緩衝區; 核心中的 rb_alloc_aux() 相應地分配頁面,這些頁面將延遲對映到 VMA 中,在處理頁面錯誤時,這與常規環形緩衝區的惰性機制相同。
AUX 事件和 AUX 跟蹤資料是兩個不同的東西。 讓我們看一個例子
perf record -a -e cycles -e cs_etm// -- sleep 2
上面的命令啟用了兩個事件:一個是來自 PMU 的事件 cycles,另一個是來自 Arm CoreSight 的 AUX 事件 cs_etm,兩者都儲存到常規環形緩衝區中,而 CoreSight 的 AUX 跟蹤資料儲存在 AUX 環形緩衝區中。
因此,我們可以看到常規環形緩衝區和 AUX 環形緩衝區是成對分配的。 預設模式下的 perf 為每個 CPU 分配常規環形緩衝區和 AUX 環形緩衝區,這與系統範圍模式相同,但是,預設模式僅為分析的程式記錄樣本,而後一種模式分析系統中的所有程式。 對於按執行緒模式,perf 工具為整個會話僅分配一個常規環形緩衝區和一個 AUX 環形緩衝區。 對於按 CPU 模式,perf 為選項 -C 指定的選定 CPU 分配兩種環形緩衝區。
下圖演示了系統範圍模式下的緩衝區佈局; 如果一個 CPU 上有任何活動,AUX 事件樣本和硬體跟蹤資料將被記錄到 CPU 的專用緩衝區中。
T1 T2 T1
+----+ +-----------+ +----+
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
+----+--------------+-----------+----------+----+-------->
| | |
v v v
+-----------------------------------------------------+
| Ring buffer 0 |
+-----------------------------------------------------+
| | |
v v v
+-----------------------------------------------------+
| AUX Ring buffer 0 |
+-----------------------------------------------------+
T1
+-----+
CPU1 |xxxxx|
-----+-----+--------------------------------------------->
|
v
+-----------------------------------------------------+
| Ring buffer 1 |
+-----------------------------------------------------+
|
v
+-----------------------------------------------------+
| AUX Ring buffer 1 |
+-----------------------------------------------------+
T1 T3
+----+ +-------+
CPU2 |xxxx| |xxxxxxx|
--------------------------+----+--------+-------+-------->
| |
v v
+-----------------------------------------------------+
| Ring buffer 2 |
+-----------------------------------------------------+
| |
v v
+-----------------------------------------------------+
| AUX Ring buffer 2 |
+-----------------------------------------------------+
T1
+--------------+
CPU3 |xxxxxxxxxxxxxx|
-----------+--------------+------------------------------>
|
v
+-----------------------------------------------------+
| Ring buffer 3 |
+-----------------------------------------------------+
|
v
+-----------------------------------------------------+
| AUX Ring buffer 3 |
+-----------------------------------------------------+
T1: Thread 1; T2: Thread 2; T3: Thread 3
x: Thread is in running state
Figure 8. AUX ring buffer for system wide mode
3.2 AUX 事件¶
類似於 perf_output_begin() 和 perf_output_end() 為常規環形緩衝區工作,perf_aux_output_begin() 和 perf_aux_output_end() 用於 AUX 環形緩衝區,以處理硬體跟蹤資料。
一旦硬體跟蹤資料儲存到 AUX 環形緩衝區中,PMU 驅動程式將透過呼叫 pmu::stop() 回撥來停止硬體跟蹤。 與常規環形緩衝區類似,AUX 環形緩衝區需要應用如 2.3.5 記憶體同步 節中討論的記憶體同步機制。 由於 AUX 環形緩衝區由 PMU 驅動程式管理,因此屏障 (B)(一個寫入屏障,用於確保在更新頭指標之前,跟蹤資料在外部可見)被要求在 PMU 驅動程式中實現。
然後 pmu::stop() 可以安全地呼叫 perf_aux_output_end() 函式來完成兩件事
它將一個事件
PERF_RECORD_AUX填充到常規環形緩衝區中,此事件傳遞了關於硬體跟蹤資料塊已儲存到 AUX 環形緩衝區中的起始地址和資料大小的資訊;由於硬體跟蹤驅動程式已將新的跟蹤資料儲存到 AUX 環形緩衝區中,因此引數 size 指示硬體跟蹤消耗了多少位元組,因此
perf_aux_output_end()更新頭指標perf_buffer::aux_head以反映最新的緩衝區使用情況。
最後,PMU 驅動程式將重新啟動硬體跟蹤。 在此臨時暫停期間,它將丟失硬體跟蹤資料,這將導致解碼階段出現不連續性。
事件 PERF_RECORD_AUX 表示在核心中處理的 AUX 事件,但它缺乏在 perf 檔案中儲存 AUX 跟蹤資料的資訊。 當 perf 工具將跟蹤資料從 AUX 環形緩衝區複製到 perf 資料檔案時,它會合成一個 PERF_RECORD_AUXTRACE 事件,該事件不是核心 ABI,它由 perf 工具定義,用於描述 AUX 環形緩衝區中的哪個部分的資料被儲存。 之後,perf 工具根據 PERF_RECORD_AUXTRACE 事件從 perf 檔案中讀取 AUX 跟蹤資料,PERF_RECORD_AUX 事件用於透過與時間順序相關聯來解碼資料塊。
3.3 快照模式¶
Perf 支援 AUX 環形緩衝區的快照模式,在這種模式下,使用者只記錄用戶感興趣的特定時間點的 AUX 跟蹤資料。 例如,下面給出瞭如何使用 Arm CoreSight 以 1 秒間隔拍攝快照的示例
perf record -e cs_etm//u -S -a program &
PERFPID=$!
while true; do
kill -USR2 $PERFPID
sleep 1
done
快照模式的主要流程是
在拍攝快照之前,AUX 環形緩衝區以自由執行模式執行。 在自由執行模式下,perf 不記錄任何 AUX 事件和跟蹤資料;
一旦 perf 工具接收到 USR2 訊號,它將觸發回撥函式
auxtrace_record::snapshot_start()以停用硬體跟蹤。 然後,核心驅動程式使用硬體跟蹤資料填充 AUX 環形緩衝區,並將事件PERF_RECORD_AUX儲存在常規環形緩衝區中;然後 perf 工具拍攝快照,
record__read_auxtrace_snapshot()從 AUX 環形緩衝區中讀取硬體跟蹤資料並將其儲存到 perf 資料檔案中;快照完成後,
auxtrace_record::snapshot_finish()重新啟動 AUX 跟蹤的 PMU 事件。
perf 只訪問快照模式下的頭指標 perf_event_mmap_page::aux_head,而不觸及尾指標 aux_tail,這是因為 AUX 環形緩衝區可以在自由執行模式下溢位,在這種情況下,尾指標無用。 或者,引入回撥 auxtrace_record::find_snapshot() 來決定 AUX 環形緩衝區是否已環繞,最後它修復了用於計算跟蹤資料大小的 AUX 緩衝區的頭部。
我們知道,緩衝區的部署可以是按執行緒模式、按 CPU 模式或系統範圍模式,並且快照可以應用於任何這些模式。 下面是一個使用系統範圍模式拍攝快照的示例。
Snapshot is taken
|
v
+------------------------+
| AUX Ring buffer 0 | <- aux_head
+------------------------+
v
+--------------------------------+
| AUX Ring buffer 1 | <- aux_head
+--------------------------------+
v
+--------------------------------------------+
| AUX Ring buffer 2 | <- aux_head
+--------------------------------------------+
v
+---------------------------------------+
| AUX Ring buffer 3 | <- aux_head
+---------------------------------------+
Figure 9. Snapshot with system wide mode