Introduce -t flag for kmem command to get slab debug trace. Here is the user help manual: 1. Dump slab debug trace when used "-st" with an allocated slab object address: crash> kmem -st ffff000007e79d00 CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME ffff000001c0ed00 3392 93 104 13 32k task_struct SLAB MEMORY NODE TOTAL ALLOCATED FREE fffffc00001f9e00 ffff000007e78000 0 8 6 2 FREE / [ALLOCATED] [ffff000007e79d00] object ffff000007e79d00 allocated in alloc_task_struct_node+36 when=4294915270 cpu=2 pid=415 __slab_alloc+60 kmem_cache_alloc_node+528 alloc_task_struct_node+36 dup_task_struct+56 copy_process+724 kernel_clone+276 __do_sys_clone+152 __se_sys_clone+60 __arm64_sys_clone+88 __invoke_syscall+36 invoke_syscall+284 el0_svc_common+248 do_el0_svc+56 el0_svc+248 el0t_64_sync_handler+92 el0t_64_sync+344 object ffff000007e79d00 freed in free_task_struct+32 when=4294911569 cpu=1 pid=0 kmem_cache_free+780 free_task_struct+32 free_task+164 __put_task_struct+328 put_task_struct+44 delayed_put_task_struct+64 rcu_do_batch+972 rcu_core+592 rcu_core_si+24 __softirqentry_text_start+388 do_softirq_own_stack+12 invoke_softirq+216 __irq_exit_rcu+164 irq_exit+20 handle_domain_irq+120 gic_handle_irq+312 2. Dump slab debug trace for each allocated object belongs to this slab when used "-st" with an slab page address: crash> kmem -st fffffc00001f9e00 CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME ffff000001c0ed00 3392 93 104 13 32k task_struct SLAB MEMORY NODE TOTAL ALLOCATED FREE fffffc00001f9e00 ffff000007e78000 0 8 6 2 FREE / [ALLOCATED] [ffff000007e78000] object ffff000007e78000 allocated in alloc_task_struct_node+36 when=4294911106 cpu=3 pid=1 __slab_alloc+60 kmem_cache_alloc_node+528 alloc_task_struct_node+36 dup_task_struct+56 copy_process+724 kernel_clone+276 __do_sys_clone+152 __se_sys_clone+60 __arm64_sys_clone+88 __invoke_syscall+36 invoke_syscall+284 el0_svc_common+248 do_el0_svc+56 el0_svc+248 el0t_64_sync_handler+92 el0t_64_sync+344 object ffff000007e78000 freed in free_task_struct+32 when=4294911104 cpu=1 pid=0 kmem_cache_free+780 free_task_struct+32 free_task+164 __put_task_struct+328 put_task_struct+44 delayed_put_task_struct+64 rcu_do_batch+972 rcu_core+592 rcu_core_si+24 __softirqentry_text_start+388 do_softirq_own_stack+12 invoke_softirq+216 __irq_exit_rcu+164 irq_exit+20 handle_domain_irq+120 gic_handle_irq+312 3. Dump slab debug trace for each allocated object belongs to slab cache when used "-S -t" with a slab cache address. crash> kmem -S -t ffff000001c0ed00 CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME ffff000001c0ed00 3392 93 104 13 32k task_struct CPU 0 KMEM_CACHE_CPU: ffff00003fd6b7a0 CPU 0 SLAB: (empty) CPU 0 PARTIAL: (empty) CPU 1 KMEM_CACHE_CPU: ffff00003fd8a7a0 CPU 1 SLAB: (empty) CPU 1 PARTIAL: (empty) CPU 2 KMEM_CACHE_CPU: ffff00003fda97a0 CPU 2 SLAB: (empty) CPU 2 PARTIAL: (empty) CPU 3 KMEM_CACHE_CPU: ffff00003fdc87a0 CPU 3 SLAB: (empty) CPU 3 PARTIAL: (empty) KMEM_CACHE_NODE NODE SLABS PARTIAL PER-CPU ffff000001eeb200 0 13 5 0 NODE 0 PARTIAL: SLAB MEMORY NODE TOTAL ALLOCATED FREE fffffc00000e5e00 ffff000003978000 0 8 5 3 fffffc00000e5e00 ffff000003978000 0 8 5 3 FREE / [ALLOCATED] [ffff000003978000] object ffff000003978000 allocated in alloc_task_struct_node+36 when=4294914449 cpu=1 pid=1 __slab_alloc+60 kmem_cache_alloc_node+528 alloc_task_struct_node+36 dup_task_struct+56 copy_process+724 kernel_clone+276 __do_sys_clone+152 __se_sys_clone+60 __arm64_sys_clone+88 __invoke_syscall+36 invoke_syscall+284 el0_svc_common+248 do_el0_svc+56 el0_svc+248 el0t_64_sync_handler+92 el0t_64_sync+344 With this patch, the slab allocation/free times can be sorted by a script, which will be helpful to inspect slab memory leak. Signed-off-by: qiwu.chen <qiwu.chen@xxxxxxxxxxxxx> --- defs.h | 7 ++++ help.c | 4 ++- memory.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/defs.h b/defs.h index 3d729c8..a46c702 100644 --- a/defs.h +++ b/defs.h @@ -2283,6 +2283,12 @@ struct offset_table { /* stash of commonly-used offsets */ long page_owner_handle; long page_owner_free_handle; long mem_section_page_ext; + long track_addr; + long track_addrs; + long track_pid; + long track_cpu; + long track_when; + long track_handle; }; struct size_table { /* stash of commonly-used sizes */ @@ -2462,6 +2468,7 @@ struct size_table { /* stash of commonly-used sizes */ long page_ext; long page_owner; long stack_record; + long track; }; struct array_table { diff --git a/help.c b/help.c index f8ec62f..81c70af 100644 --- a/help.c +++ b/help.c @@ -6816,7 +6816,7 @@ char *help_kmem[] = { "kmem", "kernel memory", "[-f|-F|-c|-C|-i|-v|-V|-n|-z|-o|-h] [-p|-t | -m member[,member]]\n" -" [[-s|-S|-S=cpu[s]|-r] [slab] [-I slab[,slab]]] [-g [flags]] [[-P] address]]", +" [[-s|-S|-S=cpu[s]|-r|-t] [slab] [-I slab[,slab]]] [-g [flags]] [[-P] address]]", " This command displays information about the use of kernel memory.\n", " -f displays the contents of the system free memory headers.", " also verifies that the page count equals nr_free_pages.", @@ -6894,6 +6894,8 @@ char *help_kmem[] = { " address when used with -s or -S, searches the kmalloc() slab subsystem", " for the slab containing of this virtual address, showing whether", " it is in use or free.", +" when added extra -t, displays the slab debug trace for the allocated", +" object belongs to this slab", " address when used with -f, the address can be either a page pointer,", " a physical address, or a kernel virtual address; the free_area", " header containing the page (if any) is displayed.", diff --git a/memory.c b/memory.c index 6c69b6a..3c4766b 100644 --- a/memory.c +++ b/memory.c @@ -865,6 +865,15 @@ vm_init(void) "kmem_cache_node", "partial"); MEMBER_OFFSET_INIT(kmem_cache_node_full, "kmem_cache_node", "full"); + STRUCT_SIZE_INIT(track, "track"); + MEMBER_OFFSET_INIT(track_addr, "track", "addr"); + if (MEMBER_EXISTS("track", "addrs")) + MEMBER_OFFSET_INIT(track_addrs, "track", "addrs"); + if (MEMBER_EXISTS("track", "handle")) + MEMBER_OFFSET_INIT(track_handle, "track", "handle"); + MEMBER_OFFSET_INIT(track_when, "track", "when"); + MEMBER_OFFSET_INIT(track_cpu, "track", "cpu"); + MEMBER_OFFSET_INIT(track_pid, "track", "pid"); } else { MEMBER_OFFSET_INIT(kmem_cache_s_c_nextp, "kmem_cache_s", "c_nextp"); @@ -5047,6 +5056,7 @@ get_task_mem_usage(ulong task, struct task_mem_usage *tm) #define SLAB_GATHER_FAILURE (ADDRESS_SPECIFIED << 26) #define GET_SLAB_ROOT_CACHES (ADDRESS_SPECIFIED << 27) #define GET_PAGE_OWNER (ADDRESS_SPECIFIED << 28) +#define GET_SLAB_DEBUG_TRACE (ADDRESS_SPECIFIED << 29) #define GET_ALL \ (GET_SHARED_PAGES|GET_TOTALRAM_PAGES|GET_BUFFERS_PAGES|GET_SLAB_PAGES) @@ -5309,6 +5319,8 @@ cmd_kmem(void) meminfo.reqname = p1; meminfo.cache = value[i]; meminfo.flags |= CACHE_SET; + if (tflag) + meminfo.flags |= GET_SLAB_DEBUG_TRACE; if ((i+1) == spec_addr) { /* done? */ if (meminfo.calls++) fprintf(fp, "\n"); @@ -5318,6 +5330,8 @@ cmd_kmem(void) } else { meminfo.spec_addr = value[i]; meminfo.flags = ADDRESS_SPECIFIED; + if (tflag) + meminfo.flags |= GET_SLAB_DEBUG_TRACE; if (Sflag && (vt->flags & KMALLOC_SLUB)) meminfo.flags |= VERBOSE; if (meminfo.calls++) @@ -20015,6 +20029,85 @@ do_kmem_cache_slub(struct meminfo *si) FREEBUF(per_cpu); } +/* + * Return offset of the end of info block which is inuse + free pointer if + * not overlapping with object. + */ +static inline uint get_info_end(struct meminfo *si) +{ + uint inuse = UINT(si->cache_buf + OFFSET(kmem_cache_inuse)); + uint offset = UINT(si->cache_buf + OFFSET(kmem_cache_offset)); + + if (offset >= inuse) + return inuse + sizeof(void *); + else + return inuse; +} + +#define TRACK_ADDRS_COUNT 16 +void print_track(struct meminfo *si, char *track, ulong object, enum track_item alloc) +{ + ulong track_addr, addr, addrs, when, entries, nr_entries; + uint i, cpu, pid, handle; + ulonglong jiffies; + char buf[BUFSIZE]; + + track_addr = object + get_info_end(si) + alloc * STRUCT_SIZE("track"); + if (!readmem(track_addr, KVADDR, track, SIZE(track), "track", FAULT_ON_ERROR)) + return; + + addr = ULONG(track + OFFSET(track_addr)); + if (addr) { + when = ULONG(track + OFFSET(track_when)); + cpu = UINT(track + OFFSET(track_cpu)); + pid = UINT(track + OFFSET(track_pid)); + get_uptime(NULL, &jiffies); + fprintf(fp, "object %lx %s in %s when=%lu cpu=%u pid=%d\n", + object, alloc ? "freed" : "allocated", + value_to_symstr(addr, buf, 0), + when, cpu, pid); + if (VALID_MEMBER(track_addrs)) { + addrs = track_addr + OFFSET(track_addrs); + stack_trace_print(addrs, TRACK_ADDRS_COUNT); + } else if (VALID_MEMBER(track_handle)) { + handle = UINT(track + OFFSET(track_handle)); + nr_entries = stack_depot_fetch(handle, &entries); + stack_trace_print(entries, nr_entries); + } else { + fprintf(fp, "stack trace missing\n"); + handle = track_addr + OFFSET(track_handle); + nr_entries = stack_depot_fetch(handle, &entries); + stack_trace_print(entries, nr_entries); + } + } +} + +#define SLAB_STORE_USER (0x00010000UL) +static ulong get_slab_store_user_flag(void) +{ + ulong slab_store_user_flag; + + if (enumerator_value("_SLAB_STORE_USER", &slab_store_user_flag)) + return (1 << slab_store_user_flag); + else + return SLAB_STORE_USER; +} + +static void slab_debug_trace_show(struct meminfo *si, ulong object) +{ + ulong flags; + char *track; + + flags = ULONG(si->cache_buf + OFFSET(kmem_cache_flags)); + if (!(flags & get_slab_store_user_flag())) + return; + + track = (char *)GETBUF(SIZE(track)); + print_track(si, track, object, TRACK_ALLOC); + print_track(si, track, object, TRACK_FREE); + FREEBUF(track); +} + #define DUMP_SLAB_INFO_SLUB() \ { \ char b1[BUFSIZE], b2[BUFSIZE]; \ @@ -20070,7 +20163,8 @@ do_slab_slub(struct meminfo *si, int verbose) if (!verbose) { DUMP_SLAB_INFO_SLUB(); - return TRUE; + if (!(si->flags & GET_SLAB_DEBUG_TRACE)) + return TRUE; } cpu_freelist = 0; @@ -20173,6 +20267,8 @@ do_slab_slub(struct meminfo *si, int verbose) if (is_free && (cpu_slab >= 0)) fprintf(fp, "(cpu %d cache)", cpu_slab); fprintf(fp, "\n"); + if (!is_free && (si->flags & GET_SLAB_DEBUG_TRACE)) + slab_debug_trace_show(si, p + red_left_pad); } return TRUE; @@ -20283,11 +20379,10 @@ do_node_lists_slub(struct meminfo *si, ulong node_ptr, int node) } -#define SLAB_STORE_USER (0x00010000UL) flags = ULONG(si->cache_buf + OFFSET(kmem_cache_flags)); if (INVALID_MEMBER(kmem_cache_node_full) || - !(flags & SLAB_STORE_USER)) { + !(flags & get_slab_store_user_flag())) { fprintf(fp, "NODE %d FULL:\n (not tracked)\n", node); return; } -- 2.25.1 -- Crash-utility mailing list -- devel@xxxxxxxxxxxxxxxxxxxxxxxxxxx To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxxxxxxxxxxxx https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/ Contribution Guidelines: https://github.com/crash-utility/crash/wiki