All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: mm-commits@vger.kernel.org, qun-wei.lin@mediatek.com,
	matthias.bgg@gmail.com, chinwen.chang@mediatek.com,
	angelogioacchino.delregno@collabora.com,
	Kuan-Ying.Lee@mediatek.com, akpm@linux-foundation.org
Subject: + scripts-gdb-slab-add-slab-support.patch added to mm-nonmm-unstable branch
Date: Tue, 08 Aug 2023 11:16:53 -0700	[thread overview]
Message-ID: <20230808181653.DE456C433C7@smtp.kernel.org> (raw)


The patch titled
     Subject: scripts/gdb/slab: add slab support
has been added to the -mm mm-nonmm-unstable branch.  Its filename is
     scripts-gdb-slab-add-slab-support.patch

This patch will shortly appear at
     https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/scripts-gdb-slab-add-slab-support.patch

This patch will later appear in the mm-nonmm-unstable branch at
    git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Before you just go and hit "reply", please:
   a) Consider who else should be cc'ed
   b) Prefer to cc a suitable mailing list as well
   c) Ideally: find the original patch on the mailing list and do a
      reply-to-all to that, adding suitable additional cc's

*** Remember to use Documentation/process/submit-checklist.rst when testing your code ***

The -mm tree is included into linux-next via the mm-everything
branch at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
and is updated there every 2-3 working days

------------------------------------------------------
From: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
Subject: scripts/gdb/slab: add slab support
Date: Tue, 8 Aug 2023 16:30:17 +0800

Add 'lx-slabinfo' and 'lx-slabtrace' support.

This GDB scripts print slabinfo and slabtrace for user
to analyze slab memory usage.

Example output like below:
(gdb) lx-slabinfo
     Pointer       |         name         | active_objs  |   num_objs   | objsize  | objperslab  | pagesperslab
------------------ | -------------------- | ------------ | ------------ | -------- | ----------- | -------------
0xffff0000c59df480 | p9_req_t             | 0            | 0            | 280      | 29          | 2
0xffff0000c59df280 | isp1760_qh           | 0            | 0            | 160      | 25          | 1
0xffff0000c59df080 | isp1760_qtd          | 0            | 0            | 184      | 22          | 1
0xffff0000c59dee80 | isp1760_urb_listite  | 0            | 0            | 136      | 30          | 1
0xffff0000c59dec80 | asd_sas_event        | 0            | 0            | 256      | 32          | 2
0xffff0000c59dea80 | sas_task             | 0            | 0            | 448      | 36          | 4
0xffff0000c59de880 | bio-120              | 18           | 21           | 384      | 21          | 2
0xffff0000c59de680 | io_kiocb             | 0            | 0            | 448      | 36          | 4
0xffff0000c59de480 | bfq_io_cq            | 0            | 0            | 1504     | 21          | 8
0xffff0000c59de280 | bfq_queue            | 0            | 0            | 720      | 22          | 4
0xffff0000c59de080 | mqueue_inode_cache   | 1            | 28           | 1152     | 28          | 8
0xffff0000c59dde80 | v9fs_inode_cache     | 0            | 0            | 832      | 39          | 8
...

(gdb) lx-slabtrace --cache_name kmalloc-1k
63 <tty_register_device_attr+508> waste=16632/264 age=46856/46871/46888 pid=1 cpus=6,
   0xffff800008720240 <__kmem_cache_alloc_node+236>:    mov     x22, x0
   0xffff80000862a4fc <kmalloc_trace+64>:       mov     x21, x0
   0xffff8000095d086c <tty_register_device_attr+508>:   mov     x19, x0
   0xffff8000095d0f98 <tty_register_driver+704>:        cmn     x0, #0x1, lsl #12
   0xffff80000c2677e8 <vty_init+620>:   Cannot access memory at address 0xffff80000c2677e8
   0xffff80000c265a10 <tty_init+276>:   Cannot access memory at address 0xffff80000c265a10
   0xffff80000c26d3c4 <chr_dev_init+204>:       Cannot access memory at address 0xffff80000c26d3c4
   0xffff8000080161d4 <do_one_initcall+176>:    mov     w21, w0
   0xffff80000c1c1b58 <kernel_init_freeable+956>:       Cannot access memory at address 0xffff80000c1c1b58
   0xffff80000acf1334 <kernel_init+36>: bl      0xffff8000081ac040 <async_synchronize_full>
   0xffff800008018d00 <ret_from_fork+16>:       mrs     x28, sp_el0

(gdb) lx-slabtrace --cache_name kmalloc-1k --free
428 <not-available> age=4294958600 pid=0 cpus=0,

Link: https://lkml.kernel.org/r/20230808083020.22254-8-Kuan-Ying.Lee@mediatek.com
Signed-off-by: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Cc: Chinwen Chang <chinwen.chang@mediatek.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Qun-Wei Lin <qun-wei.lin@mediatek.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 scripts/gdb/linux/constants.py.in |   13 +
 scripts/gdb/linux/slab.py         |  326 ++++++++++++++++++++++++++++
 scripts/gdb/vmlinux-gdb.py        |    1 
 3 files changed, 340 insertions(+)

--- a/scripts/gdb/linux/constants.py.in~scripts-gdb-slab-add-slab-support
+++ a/scripts/gdb/linux/constants.py.in
@@ -20,6 +20,7 @@
 #include <linux/of_fdt.h>
 #include <linux/page_ext.h>
 #include <linux/radix-tree.h>
+#include <linux/slab.h>
 #include <linux/threads.h>
 
 /* We need to stringify expanded macros so that they can be parsed */
@@ -95,6 +96,16 @@ if IS_BUILTIN(CONFIG_PAGE_OWNER):
     LX_GDBPARSED(PAGE_EXT_OWNER)
     LX_GDBPARSED(PAGE_EXT_OWNER_ALLOCATED)
 
+/* linux/slab.h */
+LX_GDBPARSED(SLAB_RED_ZONE)
+LX_GDBPARSED(SLAB_POISON)
+LX_GDBPARSED(SLAB_KMALLOC)
+LX_GDBPARSED(SLAB_HWCACHE_ALIGN)
+LX_GDBPARSED(SLAB_CACHE_DMA)
+LX_GDBPARSED(SLAB_CACHE_DMA32)
+LX_GDBPARSED(SLAB_STORE_USER)
+LX_GDBPARSED(SLAB_PANIC)
+
 /* Kernel Configs */
 LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS)
 LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
@@ -136,3 +147,5 @@ if IS_BUILTIN(CONFIG_NUMA):
 LX_CONFIG(CONFIG_DEBUG_VIRTUAL)
 LX_CONFIG(CONFIG_STACKDEPOT)
 LX_CONFIG(CONFIG_PAGE_OWNER)
+LX_CONFIG(CONFIG_SLUB_DEBUG)
+LX_CONFIG(CONFIG_SLAB_FREELIST_HARDENED)
--- /dev/null
+++ a/scripts/gdb/linux/slab.py
@@ -0,0 +1,326 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 MediaTek Inc.
+#
+# Authors:
+#  Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
+#
+
+import gdb
+import re
+import traceback
+from linux import lists, utils, stackdepot, constants, mm
+
+SLAB_RED_ZONE       = constants.LX_SLAB_RED_ZONE
+SLAB_POISON         = constants.LX_SLAB_POISON
+SLAB_KMALLOC        = constants.LX_SLAB_KMALLOC
+SLAB_HWCACHE_ALIGN  = constants.LX_SLAB_HWCACHE_ALIGN
+SLAB_CACHE_DMA      = constants.LX_SLAB_CACHE_DMA
+SLAB_CACHE_DMA32    = constants.LX_SLAB_CACHE_DMA32
+SLAB_STORE_USER     = constants.LX_SLAB_STORE_USER
+SLAB_PANIC          = constants.LX_SLAB_PANIC
+
+OO_SHIFT = 16
+OO_MASK = (1 << OO_SHIFT) - 1
+
+if constants.LX_CONFIG_SLUB_DEBUG:
+    slab_type = utils.CachedType("struct slab")
+    slab_ptr_type = slab_type.get_type().pointer()
+    kmem_cache_type = utils.CachedType("struct kmem_cache")
+    kmem_cache_ptr_type = kmem_cache_type.get_type().pointer()
+    freeptr_t = utils.CachedType("freeptr_t")
+    freeptr_t_ptr = freeptr_t.get_type().pointer()
+
+    track_type = gdb.lookup_type('struct track')
+    track_alloc = int(gdb.parse_and_eval('TRACK_ALLOC'))
+    track_free = int(gdb.parse_and_eval('TRACK_FREE'))
+
+def slab_folio(slab):
+    return slab.cast(gdb.lookup_type("struct folio").pointer())
+
+def slab_address(slab):
+    p_ops = mm.page_ops().ops
+    folio = slab_folio(slab)
+    return p_ops.folio_address(folio)
+
+def for_each_object(cache, addr, slab_objects):
+    p = addr
+    if cache['flags'] & SLAB_RED_ZONE:
+        p += int(cache['red_left_pad'])
+    while p < addr + (slab_objects * cache['size']):
+        yield p
+        p = p + int(cache['size'])
+
+def get_info_end(cache):
+    if (cache['offset'] >= cache['inuse']):
+        return cache['inuse'] + gdb.lookup_type("void").pointer().sizeof
+    else:
+        return cache['inuse']
+
+def get_orig_size(cache, obj):
+    if cache['flags'] & SLAB_STORE_USER and cache['flags'] & SLAB_KMALLOC:
+        p = mm.page_ops().ops.kasan_reset_tag(obj)
+        p += get_info_end(cache)
+        p += gdb.lookup_type('struct track').sizeof * 2
+        p = p.cast(utils.get_uint_type().pointer())
+        return p.dereference()
+    else:
+        return cache['object_size']
+
+def get_track(cache, object_pointer, alloc):
+    p = object_pointer + get_info_end(cache)
+    p += (alloc * track_type.sizeof)
+    return p
+
+def oo_objects(x):
+    return int(x['x']) & OO_MASK
+
+def oo_order(x):
+    return int(x['x']) >> OO_SHIFT
+
+def reciprocal_divide(a, R):
+    t = (a * int(R['m'])) >> 32
+    return (t + ((a - t) >> int(R['sh1']))) >> int(R['sh2'])
+
+def __obj_to_index(cache, addr, obj):
+    return reciprocal_divide(int(mm.page_ops().ops.kasan_reset_tag(obj)) - addr, cache['reciprocal_size'])
+
+def swab64(x):
+    result = (((x & 0x00000000000000ff) << 56) |   \
+    ((x & 0x000000000000ff00) << 40) |   \
+    ((x & 0x0000000000ff0000) << 24) |   \
+    ((x & 0x00000000ff000000) <<  8) |   \
+    ((x & 0x000000ff00000000) >>  8) |   \
+    ((x & 0x0000ff0000000000) >> 24) |   \
+    ((x & 0x00ff000000000000) >> 40) |   \
+    ((x & 0xff00000000000000) >> 56))
+    return result
+
+def freelist_ptr_decode(cache, ptr, ptr_addr):
+    if constants.LX_CONFIG_SLAB_FREELIST_HARDENED:
+        return ptr['v'] ^ cache['random'] ^ swab64(int(ptr_addr))
+    else:
+        return ptr['v']
+
+def get_freepointer(cache, obj):
+    obj = mm.page_ops().ops.kasan_reset_tag(obj)
+    ptr_addr = obj + cache['offset']
+    p = ptr_addr.cast(freeptr_t_ptr).dereference()
+    return freelist_ptr_decode(cache, p, ptr_addr)
+
+def loc_exist(loc_track, addr, handle, waste):
+    for loc in loc_track:
+        if loc['addr'] == addr and loc['handle'] == handle and loc['waste'] == waste:
+            return loc
+    return None
+
+def add_location(loc_track, cache, track, orig_size):
+    jiffies = gdb.parse_and_eval("jiffies_64")
+    age = jiffies - track['when']
+    handle = 0
+    waste = cache['object_size'] - int(orig_size)
+    pid = int(track['pid'])
+    cpuid = int(track['cpu'])
+    addr = track['addr']
+    if constants.LX_CONFIG_STACKDEPOT:
+        handle = track['handle']
+
+    loc = loc_exist(loc_track, addr, handle, waste)
+    if loc:
+        loc['count'] += 1
+        if track['when']:
+            loc['sum_time'] += age
+            loc['min_time'] = min(loc['min_time'], age)
+            loc['max_time'] = max(loc['max_time'], age)
+            loc['min_pid'] = min(loc['min_pid'], pid)
+            loc['max_pid'] = max(loc['max_pid'], pid)
+            loc['cpus'].add(cpuid)
+    else:
+        loc_track.append({
+            'count' : 1,
+            'addr' : addr,
+            'sum_time' : age,
+            'min_time' : age,
+            'max_time' : age,
+            'min_pid' : pid,
+            'max_pid' : pid,
+            'handle' : handle,
+            'waste' : waste,
+            'cpus' : {cpuid}
+            }
+        )
+
+def slabtrace(alloc, cache_name):
+
+    def __fill_map(obj_map, cache, slab):
+        p = slab['freelist']
+        addr = slab_address(slab)
+        while p != gdb.Value(0):
+            index = __obj_to_index(cache, addr, p)
+            obj_map[index] = True # free objects
+            p = get_freepointer(cache, p)
+
+    # process every slab page on the slab_list (partial and full list)
+    def process_slab(loc_track, slab_list, alloc, cache):
+        for slab in lists.list_for_each_entry(slab_list, slab_ptr_type, "slab_list"):
+            obj_map[:] = [False] * oo_objects(cache['oo'])
+            __fill_map(obj_map, cache, slab)
+            addr = slab_address(slab)
+            for object_pointer in for_each_object(cache, addr, slab['objects']):
+                if obj_map[__obj_to_index(cache, addr, object_pointer)] == True:
+                    continue
+                p = get_track(cache, object_pointer, alloc)
+                track = gdb.Value(p).cast(track_type.pointer())
+                if alloc == track_alloc:
+                    size = get_orig_size(cache, object_pointer)
+                else:
+                    size = cache['object_size']
+                add_location(loc_track, cache, track, size)
+                continue
+
+    slab_caches = gdb.parse_and_eval("slab_caches")
+    if mm.page_ops().ops.MAX_NUMNODES > 1:
+        nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
+    else:
+        nr_node_ids = 1
+
+    target_cache = None
+    loc_track = []
+
+    for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
+        if cache['name'].string() == cache_name:
+            target_cache = cache
+            break
+
+    obj_map = [False] * oo_objects(target_cache['oo'])
+
+    if target_cache['flags'] & SLAB_STORE_USER:
+        for i in range(0, nr_node_ids):
+            cache_node = target_cache['node'][i]
+            if cache_node['nr_slabs']['counter'] == 0:
+                continue
+            process_slab(loc_track, cache_node['partial'], alloc, target_cache)
+            process_slab(loc_track, cache_node['full'], alloc, target_cache)
+    else:
+        raise gdb.GdbError("SLAB_STORE_USER is not set in %s" % target_cache['name'].string())
+
+    for loc in sorted(loc_track, key=lambda x:x['count'], reverse=True):
+        if loc['addr']:
+            addr = loc['addr'].cast(utils.get_ulong_type().pointer())
+            gdb.write("%d %s" % (loc['count'], str(addr).split(' ')[-1]))
+        else:
+            gdb.write("%d <not-available>" % loc['count'])
+
+        if loc['waste']:
+            gdb.write(" waste=%d/%d" % (loc['count'] * loc['waste'], loc['waste']))
+
+        if loc['sum_time'] != loc['min_time']:
+            gdb.write(" age=%d/%d/%d" % (loc['min_time'], loc['sum_time']/loc['count'], loc['max_time']))
+        else:
+            gdb.write(" age=%d" % loc['min_time'])
+
+        if loc['min_pid'] != loc['max_pid']:
+            gdb.write(" pid=%d-%d" % (loc['min_pid'], loc['max_pid']))
+        else:
+            gdb.write(" pid=%d" % loc['min_pid'])
+
+        if constants.LX_NR_CPUS > 1:
+            nr_cpu = gdb.parse_and_eval('__num_online_cpus')['counter']
+            if nr_cpu > 1:
+                gdb.write(" cpus=")
+                for i in loc['cpus']:
+                    gdb.write("%d," % i)
+        gdb.write("\n")
+        if constants.LX_CONFIG_STACKDEPOT:
+            if loc['handle']:
+                stackdepot.stack_depot_print(loc['handle'])
+        gdb.write("\n")
+
+def help():
+    t = """Usage: lx-slabtrace --cache_name [cache_name] [Options]
+    Options:
+        --alloc
+            print information of allocation trace of the allocated objects
+        --free
+            print information of freeing trace of the allocated objects
+    Example:
+        lx-slabtrace --cache_name kmalloc-1k --alloc
+        lx-slabtrace --cache_name kmalloc-1k --free\n"""
+    gdb.write("Unrecognized command\n")
+    raise gdb.GdbError(t)
+
+class LxSlabTrace(gdb.Command):
+    """Show specific cache slabtrace"""
+
+    def __init__(self):
+        super(LxSlabTrace, self).__init__("lx-slabtrace", gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        if not constants.LX_CONFIG_SLUB_DEBUG:
+            raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
+
+        argv = gdb.string_to_argv(arg)
+        alloc = track_alloc # default show alloc_traces
+
+        if len(argv) == 3:
+            if argv[2] == '--alloc':
+                alloc = track_alloc
+            elif argv[2] == '--free':
+                alloc = track_free
+            else:
+                help()
+        if len(argv) >= 2 and argv[0] == '--cache_name':
+            slabtrace(alloc, argv[1])
+        else:
+            help()
+LxSlabTrace()
+
+def slabinfo():
+    nr_node_ids = None
+
+    if not constants.LX_CONFIG_SLUB_DEBUG:
+        raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
+
+    def count_free(slab):
+        total_free = 0
+        for slab in lists.list_for_each_entry(slab, slab_ptr_type, 'slab_list'):
+            total_free += int(slab['objects'] - slab['inuse'])
+        return total_free
+
+    gdb.write("{:^18} | {:^20} | {:^12} | {:^12} | {:^8} | {:^11} | {:^13}\n".format('Pointer', 'name', 'active_objs', 'num_objs', 'objsize', 'objperslab', 'pagesperslab'))
+    gdb.write("{:-^18} | {:-^20} | {:-^12} | {:-^12} | {:-^8} | {:-^11} | {:-^13}\n".format('', '', '', '', '', '', ''))
+
+    slab_caches = gdb.parse_and_eval("slab_caches")
+    if mm.page_ops().ops.MAX_NUMNODES > 1:
+        nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
+    else:
+        nr_node_ids = 1
+
+    for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
+        nr_objs = 0
+        nr_free = 0
+        nr_slabs = 0
+        for i in range(0, nr_node_ids):
+            cache_node = cache['node'][i]
+            try:
+                nr_slabs += cache_node['nr_slabs']['counter']
+                nr_objs = int(cache_node['total_objects']['counter'])
+                nr_free = count_free(cache_node['partial'])
+            except:
+                raise gdb.GdbError(traceback.format_exc())
+        active_objs = nr_objs - nr_free
+        num_objs = nr_objs
+        active_slabs = nr_slabs
+        objects_per_slab = oo_objects(cache['oo'])
+        cache_order = oo_order(cache['oo'])
+        gdb.write("{:18s} | {:20.19s} | {:12} | {:12} | {:8} | {:11} | {:13}\n".format(hex(cache), cache['name'].string(), str(active_objs), str(num_objs), str(cache['size']), str(objects_per_slab), str(1 << cache_order)))
+
+class LxSlabInfo(gdb.Command):
+    """Show slabinfo"""
+
+    def __init__(self):
+        super(LxSlabInfo, self).__init__("lx-slabinfo", gdb.COMMAND_DATA)
+
+    def invoke(self, arg, from_tty):
+        slabinfo()
+LxSlabInfo()
--- a/scripts/gdb/vmlinux-gdb.py~scripts-gdb-slab-add-slab-support
+++ a/scripts/gdb/vmlinux-gdb.py
@@ -47,3 +47,4 @@ else:
     import linux.mm
     import linux.stackdepot
     import linux.page_owner
+    import linux.slab
_

Patches currently in -mm which might be from Kuan-Ying.Lee@mediatek.com are

scripts-gdb-fix-lx-lsmod-show-the-wrong-size.patch
scripts-gdb-symbols-add-specific-ko-module-load-command.patch
scripts-gdb-modules-add-get-module-text-support.patch
scripts-gdb-utils-add-common-type-usage.patch
scripts-gdb-aarch64-add-aarch64-page-operation-helper-commands-and-configs.patch
scripts-gdb-stackdepot-add-stackdepot-support.patch
scripts-gdb-page_owner-add-page-owner-support.patch
scripts-gdb-slab-add-slab-support.patch
scripts-gdb-vmalloc-add-vmallocinfo-support.patch


             reply	other threads:[~2023-08-08 19:53 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-08 18:16 Andrew Morton [this message]
  -- strict thread matches above, loose matches on Subject: below --
2023-07-25 18:40 + scripts-gdb-slab-add-slab-support.patch added to mm-nonmm-unstable branch Andrew Morton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230808181653.DE456C433C7@smtp.kernel.org \
    --to=akpm@linux-foundation.org \
    --cc=Kuan-Ying.Lee@mediatek.com \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=chinwen.chang@mediatek.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matthias.bgg@gmail.com \
    --cc=mm-commits@vger.kernel.org \
    --cc=qun-wei.lin@mediatek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.