記憶體分配效能分析¶
所有記憶體分配的低開銷(適用於生產環境)統計,按檔案和行號跟蹤。
用法:kconfig 選項:- CONFIG_MEM_ALLOC_PROFILING
CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
CONFIG_MEM_ALLOC_PROFILING_DEBUG 為由於缺少註釋而未被統計的分配新增警告
- 啟動引數
sysctl.vm.mem_profiling={0|1|never}[,compressed]
當設定為“never”時,記憶體分配效能分析開銷將降至最低,並且無法在執行時啟用(sysctl 變為只讀)。當 CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y 時,預設值為“1”。當 CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=n 時,預設值為“never”。“compressed”可選引數將嘗試以緊湊格式儲存頁面標籤引用,避免頁面擴充套件。這可以提高效能和記憶體消耗,但是可能會因系統配置而失敗。如果壓縮失敗,將發出警告並且記憶體分配效能分析將被停用。
- sysctl
/proc/sys/vm/mem_profiling
- 執行時資訊
/proc/allocinfo
示例輸出
root@moria-kvm:~# sort -g /proc/allocinfo|tail|numfmt --to=iec
2.8M 22648 fs/kernfs/dir.c:615 func:__kernfs_new_node
3.8M 953 mm/memory.c:4214 func:alloc_anon_folio
4.0M 1010 drivers/staging/ctagmod/ctagmod.c:20 [ctagmod] func:ctagmod_start
4.1M 4 net/netfilter/nf_conntrack_core.c:2567 func:nf_ct_alloc_hashtable
6.0M 1532 mm/filemap.c:1919 func:__filemap_get_folio
8.8M 2785 kernel/fork.c:307 func:alloc_thread_stack_node
13M 234 block/blk-mq.c:3421 func:blk_mq_alloc_rqs
14M 3520 mm/mm_init.c:2530 func:alloc_large_system_hash
15M 3656 mm/readahead.c:247 func:page_cache_ra_unbounded
55M 4887 mm/slub.c:2259 func:alloc_slab_page
122M 31168 mm/page_ext.c:270 func:alloc_page_ext
操作原理¶
記憶體分配效能分析建立在程式碼標記的基礎之上,程式碼標記是一個用於宣告靜態結構體(通常以某種方式描述檔案和行號,因此稱為程式碼標記),然後在執行時查詢和操作它們的庫,例如,迭代它們以在 debugfs/procfs 中列印它們。
為了新增分配呼叫的統計,我們將其替換為宏呼叫 alloc_hooks(),該宏呼叫 - 宣告一個程式碼標籤 - 在 task_struct 中儲存一個指向它的指標 - 呼叫真正的分配函式 - 最後,將 task_struct alloc 標籤指標恢復為其先前的值。
這允許巢狀 alloc_hooks() 呼叫,最近的一個生效。這對於 mm/ 程式碼內部的分配非常重要,這些分配不屬於外部分配上下文,應該單獨計數:例如,slab 物件擴充套件向量,或者 slab 從頁面分配器分配頁面時。
因此,正確的用法需要確定應標記分配呼叫堆疊中的哪個函式。有許多輔助函式本質上包裝了例如 kmalloc() 並做更多的工作,然後在多個地方被呼叫;我們通常希望在這些輔助函式的呼叫者中進行統計,而不是在輔助函式本身中。
要修復給定的輔助函式,例如 foo(),請執行以下操作: - 將其分配呼叫切換到 _noprof() 版本,例如 kmalloc_noprof()
將其重新命名為 foo_noprof()
像這樣定義一個 foo() 的宏版本
#define foo(...) alloc_hooks(foo_noprof(__VA_ARGS__))
也可以在您自己的資料結構中儲存指向 alloc 標籤的指標。
當您實現一個“代表”其他程式碼進行分配的通用資料結構時,請這樣做 - 例如,rhashtable 程式碼。這樣,我們就不會在 /proc/allocinfo 中看到 rhashtable.c 的一大行,而是可以按 rhashtable 型別將其分解。
為此: - 像任何其他分配函式一樣,掛鉤您資料結構的 init 函式。
在您的 init 函式中,使用方便的宏 alloc_tag_record() 在您的資料結構中記錄 alloc 標籤。
然後,對您的分配使用以下形式:alloc_hooks_tag(ht->your_saved_tag, kmalloc_noprof(...))