100 lines · plain
1.. SPDX-License-Identifier: GPL-2.02 3===========================4MEMORY ALLOCATION PROFILING5===========================6 7Low overhead (suitable for production) accounting of all memory allocations,8tracked by file and line number.9 10Usage:11kconfig options:12- CONFIG_MEM_ALLOC_PROFILING13 14- CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT15 16- CONFIG_MEM_ALLOC_PROFILING_DEBUG17 adds warnings for allocations that weren't accounted because of a18 missing annotation19 20Boot parameter:21 sysctl.vm.mem_profiling=0|1|never22 23 When set to "never", memory allocation profiling overhead is minimized and it24 cannot be enabled at runtime (sysctl becomes read-only).25 When CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y, default value is "1".26 When CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=n, default value is "never".27 28sysctl:29 /proc/sys/vm/mem_profiling30 31Runtime info:32 /proc/allocinfo33 34Example output::35 36 root@moria-kvm:~# sort -g /proc/allocinfo|tail|numfmt --to=iec37 2.8M 22648 fs/kernfs/dir.c:615 func:__kernfs_new_node38 3.8M 953 mm/memory.c:4214 func:alloc_anon_folio39 4.0M 1010 drivers/staging/ctagmod/ctagmod.c:20 [ctagmod] func:ctagmod_start40 4.1M 4 net/netfilter/nf_conntrack_core.c:2567 func:nf_ct_alloc_hashtable41 6.0M 1532 mm/filemap.c:1919 func:__filemap_get_folio42 8.8M 2785 kernel/fork.c:307 func:alloc_thread_stack_node43 13M 234 block/blk-mq.c:3421 func:blk_mq_alloc_rqs44 14M 3520 mm/mm_init.c:2530 func:alloc_large_system_hash45 15M 3656 mm/readahead.c:247 func:page_cache_ra_unbounded46 55M 4887 mm/slub.c:2259 func:alloc_slab_page47 122M 31168 mm/page_ext.c:270 func:alloc_page_ext48 49Theory of operation50===================51 52Memory allocation profiling builds off of code tagging, which is a library for53declaring static structs (that typically describe a file and line number in54some way, hence code tagging) and then finding and operating on them at runtime,55- i.e. iterating over them to print them in debugfs/procfs.56 57To add accounting for an allocation call, we replace it with a macro58invocation, alloc_hooks(), that59- declares a code tag60- stashes a pointer to it in task_struct61- calls the real allocation function62- and finally, restores the task_struct alloc tag pointer to its previous value.63 64This allows for alloc_hooks() calls to be nested, with the most recent one65taking effect. This is important for allocations internal to the mm/ code that66do not properly belong to the outer allocation context and should be counted67separately: for example, slab object extension vectors, or when the slab68allocates pages from the page allocator.69 70Thus, proper usage requires determining which function in an allocation call71stack should be tagged. There are many helper functions that essentially wrap72e.g. kmalloc() and do a little more work, then are called in multiple places;73we'll generally want the accounting to happen in the callers of these helpers,74not in the helpers themselves.75 76To fix up a given helper, for example foo(), do the following:77- switch its allocation call to the _noprof() version, e.g. kmalloc_noprof()78 79- rename it to foo_noprof()80 81- define a macro version of foo() like so:82 83 #define foo(...) alloc_hooks(foo_noprof(__VA_ARGS__))84 85It's also possible to stash a pointer to an alloc tag in your own data structures.86 87Do this when you're implementing a generic data structure that does allocations88"on behalf of" some other code - for example, the rhashtable code. This way,89instead of seeing a large line in /proc/allocinfo for rhashtable.c, we can90break it out by rhashtable type.91 92To do so:93- Hook your data structure's init function, like any other allocation function.94 95- Within your init function, use the convenience macro alloc_tag_record() to96 record alloc tag in your data structure.97 98- Then, use the following form for your allocations:99 alloc_hooks_tag(ht->your_saved_tag, kmalloc_noprof(...))100