記憶體分配效能分析

所有記憶體分配的低開銷(適用於生產環境)統計,按檔案和行號跟蹤。

用法: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(...))