12f060190SKuan-Ying Lee# SPDX-License-Identifier: GPL-2.0 22f060190SKuan-Ying Lee# 32f060190SKuan-Ying Lee# Copyright (c) 2023 MediaTek Inc. 42f060190SKuan-Ying Lee# 52f060190SKuan-Ying Lee# Authors: 62f060190SKuan-Ying Lee# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> 72f060190SKuan-Ying Lee# 82f060190SKuan-Ying Lee 92f060190SKuan-Ying Leeimport gdb 102f060190SKuan-Ying Leefrom linux import utils, stackdepot, constants, mm 112f060190SKuan-Ying Lee 122f060190SKuan-Ying Leeif constants.LX_CONFIG_PAGE_OWNER: 132f060190SKuan-Ying Lee page_ext_t = utils.CachedType('struct page_ext') 142f060190SKuan-Ying Lee page_owner_t = utils.CachedType('struct page_owner') 152f060190SKuan-Ying Lee 162f060190SKuan-Ying Lee PAGE_OWNER_STACK_DEPTH = 16 172f060190SKuan-Ying Lee PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER 182f060190SKuan-Ying Lee PAGE_EXT_INVALID = 0x1 192f060190SKuan-Ying Lee PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED 202f060190SKuan-Ying Lee 212f060190SKuan-Ying Leedef help(): 222f060190SKuan-Ying Lee t = """Usage: lx-dump-page-owner [Option] 232f060190SKuan-Ying Lee Option: 242f060190SKuan-Ying Lee --pfn [Decimal pfn] 252f060190SKuan-Ying Lee Example: 262f060190SKuan-Ying Lee lx-dump-page-owner --pfn 655360\n""" 272f060190SKuan-Ying Lee gdb.write("Unrecognized command\n") 282f060190SKuan-Ying Lee raise gdb.GdbError(t) 292f060190SKuan-Ying Lee 302f060190SKuan-Ying Leeclass DumpPageOwner(gdb.Command): 312f060190SKuan-Ying Lee """Dump page owner""" 322f060190SKuan-Ying Lee 332f060190SKuan-Ying Lee min_pfn = None 342f060190SKuan-Ying Lee max_pfn = None 352f060190SKuan-Ying Lee p_ops = None 362f060190SKuan-Ying Lee migrate_reason_names = None 372f060190SKuan-Ying Lee 382f060190SKuan-Ying Lee def __init__(self): 392f060190SKuan-Ying Lee super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT) 402f060190SKuan-Ying Lee 412f060190SKuan-Ying Lee def invoke(self, args, from_tty): 422f060190SKuan-Ying Lee if not constants.LX_CONFIG_PAGE_OWNER: 432f060190SKuan-Ying Lee raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable') 442f060190SKuan-Ying Lee 452f060190SKuan-Ying Lee page_owner_inited = gdb.parse_and_eval('page_owner_inited') 462f060190SKuan-Ying Lee if page_owner_inited['key']['enabled']['counter'] != 0x1: 472f060190SKuan-Ying Lee raise gdb.GdbError('page_owner_inited is not enabled') 482f060190SKuan-Ying Lee 492f060190SKuan-Ying Lee self.p_ops = mm.page_ops().ops 502f060190SKuan-Ying Lee self.get_page_owner_info() 512f060190SKuan-Ying Lee argv = gdb.string_to_argv(args) 522f060190SKuan-Ying Lee if len(argv) == 0: 532f060190SKuan-Ying Lee self.read_page_owner() 542f060190SKuan-Ying Lee elif len(argv) == 2: 552f060190SKuan-Ying Lee if argv[0] == "--pfn": 562f060190SKuan-Ying Lee pfn = int(argv[1]) 572f060190SKuan-Ying Lee self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn)) 582f060190SKuan-Ying Lee else: 592f060190SKuan-Ying Lee help() 602f060190SKuan-Ying Lee else: 612f060190SKuan-Ying Lee help() 622f060190SKuan-Ying Lee 632f060190SKuan-Ying Lee def get_page_owner_info(self): 642f060190SKuan-Ying Lee self.min_pfn = int(gdb.parse_and_eval("min_low_pfn")) 652f060190SKuan-Ying Lee self.max_pfn = int(gdb.parse_and_eval("max_pfn")) 662f060190SKuan-Ying Lee self.page_ext_size = int(gdb.parse_and_eval("page_ext_size")) 672f060190SKuan-Ying Lee self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names') 682f060190SKuan-Ying Lee 692f060190SKuan-Ying Lee def page_ext_invalid(self, page_ext): 702f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 712f060190SKuan-Ying Lee return True 722f060190SKuan-Ying Lee if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID: 732f060190SKuan-Ying Lee return True 742f060190SKuan-Ying Lee return False 752f060190SKuan-Ying Lee 762f060190SKuan-Ying Lee def get_entry(self, base, index): 772f060190SKuan-Ying Lee return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer()) 782f060190SKuan-Ying Lee 792f060190SKuan-Ying Lee def lookup_page_ext(self, page): 802f060190SKuan-Ying Lee pfn = self.p_ops.page_to_pfn(page) 812f060190SKuan-Ying Lee section = self.p_ops.pfn_to_section(pfn) 822f060190SKuan-Ying Lee page_ext = section["page_ext"] 832f060190SKuan-Ying Lee if self.page_ext_invalid(page_ext): 842f060190SKuan-Ying Lee return gdb.Value(0) 852f060190SKuan-Ying Lee return self.get_entry(page_ext, pfn) 862f060190SKuan-Ying Lee 872f060190SKuan-Ying Lee def page_ext_get(self, page): 882f060190SKuan-Ying Lee page_ext = self.lookup_page_ext(page) 892f060190SKuan-Ying Lee if page_ext != gdb.Value(0): 902f060190SKuan-Ying Lee return page_ext 912f060190SKuan-Ying Lee else: 922f060190SKuan-Ying Lee return gdb.Value(0) 932f060190SKuan-Ying Lee 942f060190SKuan-Ying Lee def get_page_owner(self, page_ext): 952f060190SKuan-Ying Lee addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type()) 962f060190SKuan-Ying Lee return addr.cast(page_owner_t.get_type().pointer()) 972f060190SKuan-Ying Lee 982f060190SKuan-Ying Lee def read_page_owner_by_addr(self, struct_page_addr): 992f060190SKuan-Ying Lee page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) 1002f060190SKuan-Ying Lee pfn = self.p_ops.page_to_pfn(page) 1012f060190SKuan-Ying Lee 1022f060190SKuan-Ying Lee if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)): 1032f060190SKuan-Ying Lee gdb.write("pfn is invalid\n") 1042f060190SKuan-Ying Lee return 1052f060190SKuan-Ying Lee 1062f060190SKuan-Ying Lee page = self.p_ops.pfn_to_page(pfn) 1072f060190SKuan-Ying Lee page_ext = self.page_ext_get(page) 1082f060190SKuan-Ying Lee 1092f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 1102f060190SKuan-Ying Lee gdb.write("page_ext is null\n") 1112f060190SKuan-Ying Lee return 1122f060190SKuan-Ying Lee 1132f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 1142f060190SKuan-Ying Lee gdb.write("page_owner flag is invalid\n") 1152f060190SKuan-Ying Lee raise gdb.GdbError('page_owner info is not present (never set?)\n') 1162f060190SKuan-Ying Lee 1172f060190SKuan-Ying Lee if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address): 1182f060190SKuan-Ying Lee gdb.write('page_owner tracks the page as allocated\n') 1192f060190SKuan-Ying Lee else: 1202f060190SKuan-Ying Lee gdb.write('page_owner tracks the page as freed\n') 1212f060190SKuan-Ying Lee 1222f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 1232f060190SKuan-Ying Lee gdb.write("page_owner is not allocated\n") 1242f060190SKuan-Ying Lee 1252f060190SKuan-Ying Lee page_owner = self.get_page_owner(page_ext) 1262f060190SKuan-Ying Lee gdb.write("Page last allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\ 1272f060190SKuan-Ying Lee (page_owner["order"], page_owner["gfp_mask"],\ 128*e52ec6a2SKuan-Ying Lee page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\ 1292f060190SKuan-Ying Lee page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 1302f060190SKuan-Ying Lee gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 1312f060190SKuan-Ying Lee if page_owner["handle"] == 0: 1322f060190SKuan-Ying Lee gdb.write('page_owner allocation stack trace missing\n') 1332f060190SKuan-Ying Lee else: 1342f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["handle"]) 1352f060190SKuan-Ying Lee 1362f060190SKuan-Ying Lee if page_owner["free_handle"] == 0: 1372f060190SKuan-Ying Lee gdb.write('page_owner free stack trace missing\n') 1382f060190SKuan-Ying Lee else: 1392f060190SKuan-Ying Lee gdb.write('page last free stack trace:\n') 1402f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["free_handle"]) 1412f060190SKuan-Ying Lee if page_owner['last_migrate_reason'] != -1: 1422f060190SKuan-Ying Lee gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']]) 1432f060190SKuan-Ying Lee 1442f060190SKuan-Ying Lee def read_page_owner(self): 1452f060190SKuan-Ying Lee pfn = self.min_pfn 1462f060190SKuan-Ying Lee 1472f060190SKuan-Ying Lee # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area 1482f060190SKuan-Ying Lee while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0: 1492f060190SKuan-Ying Lee pfn += 1 1502f060190SKuan-Ying Lee 1512f060190SKuan-Ying Lee while pfn < self.max_pfn: 1522f060190SKuan-Ying Lee # 1532f060190SKuan-Ying Lee # If the new page is in a new MAX_ORDER_NR_PAGES area, 1542f060190SKuan-Ying Lee # validate the area as existing, skip it if not 1552f060190SKuan-Ying Lee # 1562f060190SKuan-Ying Lee if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)): 1572f060190SKuan-Ying Lee pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1) 1582f060190SKuan-Ying Lee continue; 1592f060190SKuan-Ying Lee 1602f060190SKuan-Ying Lee page = self.p_ops.pfn_to_page(pfn) 1612f060190SKuan-Ying Lee page_ext = self.page_ext_get(page) 1622f060190SKuan-Ying Lee if page_ext == gdb.Value(0): 1632f060190SKuan-Ying Lee pfn += 1 1642f060190SKuan-Ying Lee continue 1652f060190SKuan-Ying Lee 1662f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 1672f060190SKuan-Ying Lee pfn += 1 1682f060190SKuan-Ying Lee continue 1692f060190SKuan-Ying Lee if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 1702f060190SKuan-Ying Lee pfn += 1 1712f060190SKuan-Ying Lee continue 1722f060190SKuan-Ying Lee 1732f060190SKuan-Ying Lee page_owner = self.get_page_owner(page_ext) 1742f060190SKuan-Ying Lee gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\ 1752f060190SKuan-Ying Lee (page_owner["order"], page_owner["gfp_mask"],\ 176*e52ec6a2SKuan-Ying Lee page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\ 1772f060190SKuan-Ying Lee page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 1782f060190SKuan-Ying Lee gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 1792f060190SKuan-Ying Lee stackdepot.stack_depot_print(page_owner["handle"]) 1802f060190SKuan-Ying Lee pfn += (1 << page_owner["order"]) 1812f060190SKuan-Ying Lee 1822f060190SKuan-Ying LeeDumpPageOwner() 183