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.c...@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
--
Crash-utility mailing list -- devel@lists.crash-utility.osci.io
To unsubscribe send an email to devel-le...@lists.crash-utility.osci.io
https://${domain_name}/admin/lists/devel.lists.crash-utility.osci.io/
Contribution Guidelines: https://github.com/crash-utility/crash/wiki

Reply via email to