From nobody Fri Sep 20 13:18:37 2024 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A54CC05051 for ; Tue, 8 Aug 2023 16:16:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232279AbjHHQQE (ORCPT ); Tue, 8 Aug 2023 12:16:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57472 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232146AbjHHQOQ (ORCPT ); Tue, 8 Aug 2023 12:14:16 -0400 Received: from mailgw02.mediatek.com (unknown [210.61.82.184]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8DF3983CC; Tue, 8 Aug 2023 08:48:01 -0700 (PDT) X-UUID: df44968235c511eeb20a276fd37b9834-20230808 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=hGt7NeNlML1MGMWhcOV6ROE/8hYT1LCj/C58WEanJig=; b=UZ0fwCL3Iktwnfr82e/B9uUO+D5oysKiU41pabgBiutGFgNf2EmQ9+oPahLoLuHK/RRAcgW7dbBchZjQIpws2LGR+3LR9ZVGBrGLI0dlVUQ1CNIVvaLy7q1JrKphyTkWXaatelcvo2YSXfFCE2ZKhuZY+6So5IL/gCHfYSVBuFk=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.31,REQID:7fafc22f-d69e-45df-8b80-e2b30dc585fc,IP:0,U RL:0,TC:0,Content:0,EDM:0,RT:0,SF:95,FILE:0,BULK:0,RULE:Release_Ham,ACTION :release,TS:95 X-CID-INFO: VERSION:1.1.31,REQID:7fafc22f-d69e-45df-8b80-e2b30dc585fc,IP:0,URL :0,TC:0,Content:0,EDM:0,RT:0,SF:95,FILE:0,BULK:0,RULE:Spam_GS981B3D,ACTION :quarantine,TS:95 X-CID-META: VersionHash:0ad78a4,CLOUDID:a8e7cfd2-cd77-4e67-bbfd-aa4eaace762f,B ulkID:230808163049VCAOXA3Y,BulkQuantity:0,Recheck:0,SF:28|17|19|48|38|29,T C:nil,Content:0,EDM:-3,IP:nil,URL:11|1,File:nil,Bulk:nil,QS:nil,BEC:nil,CO L:0,OSI:0,OSA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0 X-CID-BVR: 0 X-CID-BAS: 0,_,0,_ X-CID-FACTOR: TF_CID_SPAM_SNR,TF_CID_SPAM_SDM,TF_CID_SPAM_ASC,TF_CID_SPAM_FAS, TF_CID_SPAM_FSD,TF_CID_SPAM_ULN X-UUID: df44968235c511eeb20a276fd37b9834-20230808 Received: from mtkmbs10n1.mediatek.inc [(172.21.101.34)] by mailgw02.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256) with ESMTP id 1422125080; Tue, 08 Aug 2023 16:30:46 +0800 Received: from mtkmbs13n2.mediatek.inc (172.21.101.108) by mtkmbs10n2.mediatek.inc (172.21.101.183) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Tue, 8 Aug 2023 16:30:45 +0800 Received: from mtksdccf07.mediatek.inc (172.21.84.99) by mtkmbs13n2.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Tue, 8 Aug 2023 16:30:45 +0800 From: Kuan-Ying Lee To: Jan Kiszka , Kieran Bingham , Matthias Brugger , AngeloGioacchino Del Regno CC: , , , , , , , Kuan-Ying Lee , , Subject: [PATCH v2 7/8] scripts/gdb/slab: Add slab support Date: Tue, 8 Aug 2023 16:30:17 +0800 Message-ID: <20230808083020.22254-8-Kuan-Ying.Lee@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20230808083020.22254-1-Kuan-Ying.Lee@mediatek.com> References: <20230808083020.22254-1-Kuan-Ying.Lee@mediatek.com> MIME-Version: 1.0 X-MTK: N Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 | o= bjsize | objperslab | pagesperslab ------------------ | -------------------- | ------------ | ------------ | -= ------- | ----------- | ------------- 0xffff0000c59df480 | p9_req_t | 0 | 0 | 2= 80 | 29 | 2 0xffff0000c59df280 | isp1760_qh | 0 | 0 | 1= 60 | 25 | 1 0xffff0000c59df080 | isp1760_qtd | 0 | 0 | 1= 84 | 22 | 1 0xffff0000c59dee80 | isp1760_urb_listite | 0 | 0 | 1= 36 | 30 | 1 0xffff0000c59dec80 | asd_sas_event | 0 | 0 | 2= 56 | 32 | 2 0xffff0000c59dea80 | sas_task | 0 | 0 | 4= 48 | 36 | 4 0xffff0000c59de880 | bio-120 | 18 | 21 | 3= 84 | 21 | 2 0xffff0000c59de680 | io_kiocb | 0 | 0 | 4= 48 | 36 | 4 0xffff0000c59de480 | bfq_io_cq | 0 | 0 | 1= 504 | 21 | 8 0xffff0000c59de280 | bfq_queue | 0 | 0 | 7= 20 | 22 | 4 0xffff0000c59de080 | mqueue_inode_cache | 1 | 28 | 1= 152 | 28 | 8 0xffff0000c59dde80 | v9fs_inode_cache | 0 | 0 | 8= 32 | 39 | 8 ... (gdb) lx-slabtrace --cache_name kmalloc-1k 63 waste=3D16632/264 age=3D46856/46871/46888= pid=3D1 cpus=3D6, 0xffff800008720240 <__kmem_cache_alloc_node+236>: mov x22, x0 0xffff80000862a4fc : mov x21, x0 0xffff8000095d086c : mov x19, x0 0xffff8000095d0f98 : cmn x0, #0x1, l= sl #12 0xffff80000c2677e8 : Cannot access memory at address 0xf= fff80000c2677e8 0xffff80000c265a10 : Cannot access memory at address 0xf= fff80000c265a10 0xffff80000c26d3c4 : Cannot access memory at add= ress 0xffff80000c26d3c4 0xffff8000080161d4 : mov w21, w0 0xffff80000c1c1b58 : Cannot access memor= y at address 0xffff80000c1c1b58 0xffff80000acf1334 : bl 0xffff8000081ac040 0xffff800008018d00 : mrs x28, sp_el0 (gdb) lx-slabtrace --cache_name kmalloc-1k --free 428 age=3D4294958600 pid=3D0 cpus=3D0, Signed-off-by: Kuan-Ying Lee --- scripts/gdb/linux/constants.py.in | 13 ++ scripts/gdb/linux/slab.py | 326 ++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 3 files changed, 340 insertions(+) create mode 100644 scripts/gdb/linux/slab.py diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constant= s.py.in index 52f61d65f430..03fa6d2cfe01 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -20,6 +20,7 @@ #include #include #include +#include #include =20 /* 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) =20 +/* 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) diff --git a/scripts/gdb/linux/slab.py b/scripts/gdb/linux/slab.py new file mode 100644 index 000000000000..f012ba38c7d9 --- /dev/null +++ b/scripts/gdb/linux/slab.py @@ -0,0 +1,326 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2023 MediaTek Inc. +# +# Authors: +# Kuan-Ying Lee +# + +import gdb +import re +import traceback +from linux import lists, utils, stackdepot, constants, mm + +SLAB_RED_ZONE =3D constants.LX_SLAB_RED_ZONE +SLAB_POISON =3D constants.LX_SLAB_POISON +SLAB_KMALLOC =3D constants.LX_SLAB_KMALLOC +SLAB_HWCACHE_ALIGN =3D constants.LX_SLAB_HWCACHE_ALIGN +SLAB_CACHE_DMA =3D constants.LX_SLAB_CACHE_DMA +SLAB_CACHE_DMA32 =3D constants.LX_SLAB_CACHE_DMA32 +SLAB_STORE_USER =3D constants.LX_SLAB_STORE_USER +SLAB_PANIC =3D constants.LX_SLAB_PANIC + +OO_SHIFT =3D 16 +OO_MASK =3D (1 << OO_SHIFT) - 1 + +if constants.LX_CONFIG_SLUB_DEBUG: + slab_type =3D utils.CachedType("struct slab") + slab_ptr_type =3D slab_type.get_type().pointer() + kmem_cache_type =3D utils.CachedType("struct kmem_cache") + kmem_cache_ptr_type =3D kmem_cache_type.get_type().pointer() + freeptr_t =3D utils.CachedType("freeptr_t") + freeptr_t_ptr =3D freeptr_t.get_type().pointer() + + track_type =3D gdb.lookup_type('struct track') + track_alloc =3D int(gdb.parse_and_eval('TRACK_ALLOC')) + track_free =3D 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 =3D mm.page_ops().ops + folio =3D slab_folio(slab) + return p_ops.folio_address(folio) + +def for_each_object(cache, addr, slab_objects): + p =3D addr + if cache['flags'] & SLAB_RED_ZONE: + p +=3D int(cache['red_left_pad']) + while p < addr + (slab_objects * cache['size']): + yield p + p =3D p + int(cache['size']) + +def get_info_end(cache): + if (cache['offset'] >=3D 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 =3D mm.page_ops().ops.kasan_reset_tag(obj) + p +=3D get_info_end(cache) + p +=3D gdb.lookup_type('struct track').sizeof * 2 + p =3D p.cast(utils.get_uint_type().pointer()) + return p.dereference() + else: + return cache['object_size'] + +def get_track(cache, object_pointer, alloc): + p =3D object_pointer + get_info_end(cache) + p +=3D (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 =3D (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 =3D (((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 =3D mm.page_ops().ops.kasan_reset_tag(obj) + ptr_addr =3D obj + cache['offset'] + p =3D 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'] =3D=3D addr and loc['handle'] =3D=3D handle and loc= ['waste'] =3D=3D waste: + return loc + return None + +def add_location(loc_track, cache, track, orig_size): + jiffies =3D gdb.parse_and_eval("jiffies_64") + age =3D jiffies - track['when'] + handle =3D 0 + waste =3D cache['object_size'] - int(orig_size) + pid =3D int(track['pid']) + cpuid =3D int(track['cpu']) + addr =3D track['addr'] + if constants.LX_CONFIG_STACKDEPOT: + handle =3D track['handle'] + + loc =3D loc_exist(loc_track, addr, handle, waste) + if loc: + loc['count'] +=3D 1 + if track['when']: + loc['sum_time'] +=3D age + loc['min_time'] =3D min(loc['min_time'], age) + loc['max_time'] =3D max(loc['max_time'], age) + loc['min_pid'] =3D min(loc['min_pid'], pid) + loc['max_pid'] =3D 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 =3D slab['freelist'] + addr =3D slab_address(slab) + while p !=3D gdb.Value(0): + index =3D __obj_to_index(cache, addr, p) + obj_map[index] =3D True # free objects + p =3D 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, "s= lab_list"): + obj_map[:] =3D [False] * oo_objects(cache['oo']) + __fill_map(obj_map, cache, slab) + addr =3D slab_address(slab) + for object_pointer in for_each_object(cache, addr, slab['objec= ts']): + if obj_map[__obj_to_index(cache, addr, object_pointer)] = =3D=3D True: + continue + p =3D get_track(cache, object_pointer, alloc) + track =3D gdb.Value(p).cast(track_type.pointer()) + if alloc =3D=3D track_alloc: + size =3D get_orig_size(cache, object_pointer) + else: + size =3D cache['object_size'] + add_location(loc_track, cache, track, size) + continue + + slab_caches =3D gdb.parse_and_eval("slab_caches") + if mm.page_ops().ops.MAX_NUMNODES > 1: + nr_node_ids =3D int(gdb.parse_and_eval("nr_node_ids")) + else: + nr_node_ids =3D 1 + + target_cache =3D None + loc_track =3D [] + + for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_typ= e, 'list'): + if cache['name'].string() =3D=3D cache_name: + target_cache =3D cache + break + + obj_map =3D [False] * oo_objects(target_cache['oo']) + + if target_cache['flags'] & SLAB_STORE_USER: + for i in range(0, nr_node_ids): + cache_node =3D target_cache['node'][i] + if cache_node['nr_slabs']['counter'] =3D=3D 0: + continue + process_slab(loc_track, cache_node['partial'], alloc, target_c= ache) + process_slab(loc_track, cache_node['full'], alloc, target_cach= e) + else: + raise gdb.GdbError("SLAB_STORE_USER is not set in %s" % target_cac= he['name'].string()) + + for loc in sorted(loc_track, key=3Dlambda x:x['count'], reverse=3DTrue= ): + if loc['addr']: + addr =3D loc['addr'].cast(utils.get_ulong_type().pointer()) + gdb.write("%d %s" % (loc['count'], str(addr).split(' ')[-1])) + else: + gdb.write("%d " % loc['count']) + + if loc['waste']: + gdb.write(" waste=3D%d/%d" % (loc['count'] * loc['waste'], loc= ['waste'])) + + if loc['sum_time'] !=3D loc['min_time']: + gdb.write(" age=3D%d/%d/%d" % (loc['min_time'], loc['sum_time'= ]/loc['count'], loc['max_time'])) + else: + gdb.write(" age=3D%d" % loc['min_time']) + + if loc['min_pid'] !=3D loc['max_pid']: + gdb.write(" pid=3D%d-%d" % (loc['min_pid'], loc['max_pid'])) + else: + gdb.write(" pid=3D%d" % loc['min_pid']) + + if constants.LX_NR_CPUS > 1: + nr_cpu =3D gdb.parse_and_eval('__num_online_cpus')['counter'] + if nr_cpu > 1: + gdb.write(" cpus=3D") + 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 =3D """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 =3D gdb.string_to_argv(arg) + alloc =3D track_alloc # default show alloc_traces + + if len(argv) =3D=3D 3: + if argv[2] =3D=3D '--alloc': + alloc =3D track_alloc + elif argv[2] =3D=3D '--free': + alloc =3D track_free + else: + help() + if len(argv) >=3D 2 and argv[0] =3D=3D '--cache_name': + slabtrace(alloc, argv[1]) + else: + help() +LxSlabTrace() + +def slabinfo(): + nr_node_ids =3D None + + if not constants.LX_CONFIG_SLUB_DEBUG: + raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled") + + def count_free(slab): + total_free =3D 0 + for slab in lists.list_for_each_entry(slab, slab_ptr_type, 'slab_l= ist'): + total_free +=3D 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', 'objper= slab', 'pagesperslab')) + gdb.write("{:-^18} | {:-^20} | {:-^12} | {:-^12} | {:-^8} | {:-^11} | = {:-^13}\n".format('', '', '', '', '', '', '')) + + slab_caches =3D gdb.parse_and_eval("slab_caches") + if mm.page_ops().ops.MAX_NUMNODES > 1: + nr_node_ids =3D int(gdb.parse_and_eval("nr_node_ids")) + else: + nr_node_ids =3D 1 + + for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_typ= e, 'list'): + nr_objs =3D 0 + nr_free =3D 0 + nr_slabs =3D 0 + for i in range(0, nr_node_ids): + cache_node =3D cache['node'][i] + try: + nr_slabs +=3D cache_node['nr_slabs']['counter'] + nr_objs =3D int(cache_node['total_objects']['counter']) + nr_free =3D count_free(cache_node['partial']) + except: + raise gdb.GdbError(traceback.format_exc()) + active_objs =3D nr_objs - nr_free + num_objs =3D nr_objs + active_slabs =3D nr_slabs + objects_per_slab =3D oo_objects(cache['oo']) + cache_order =3D oo_order(cache['oo']) + gdb.write("{:18s} | {:20.19s} | {:12} | {:12} | {:8} | {:11} | {:1= 3}\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() diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py index 89465f0de548..2526364f31fd 100644 --- a/scripts/gdb/vmlinux-gdb.py +++ b/scripts/gdb/vmlinux-gdb.py @@ -47,3 +47,4 @@ else: import linux.mm import linux.stackdepot import linux.page_owner + import linux.slab --=20 2.18.0