[Crash-utility] [PATCH v2 2/2] kmem: introduce -t flag to get slab debug trace

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux