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(a)transsion.com>
---
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