1# SPDX-License-Identifier: GPL-2.0 2# 3# Copyright (c) 2023 MediaTek Inc. 4# 5# Authors: 6# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> 7# 8 9import gdb 10from linux import utils, stackdepot, constants, mm 11 12if constants.LX_CONFIG_PAGE_OWNER: 13 page_ext_t = utils.CachedType('struct page_ext') 14 page_owner_t = utils.CachedType('struct page_owner') 15 16 PAGE_OWNER_STACK_DEPTH = 16 17 PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER 18 PAGE_EXT_INVALID = 0x1 19 PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED 20 21def help(): 22 t = """Usage: lx-dump-page-owner [Option] 23 Option: 24 --pfn [Decimal pfn] 25 Example: 26 lx-dump-page-owner --pfn 655360\n""" 27 gdb.write("Unrecognized command\n") 28 raise gdb.GdbError(t) 29 30class DumpPageOwner(gdb.Command): 31 """Dump page owner""" 32 33 min_pfn = None 34 max_pfn = None 35 p_ops = None 36 migrate_reason_names = None 37 38 def __init__(self): 39 super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT) 40 41 def invoke(self, args, from_tty): 42 if not constants.LX_CONFIG_PAGE_OWNER: 43 raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable') 44 45 page_owner_inited = gdb.parse_and_eval('page_owner_inited') 46 if page_owner_inited['key']['enabled']['counter'] != 0x1: 47 raise gdb.GdbError('page_owner_inited is not enabled') 48 49 self.p_ops = mm.page_ops().ops 50 self.get_page_owner_info() 51 argv = gdb.string_to_argv(args) 52 if len(argv) == 0: 53 self.read_page_owner() 54 elif len(argv) == 2: 55 if argv[0] == "--pfn": 56 pfn = int(argv[1]) 57 self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn)) 58 else: 59 help() 60 else: 61 help() 62 63 def get_page_owner_info(self): 64 self.min_pfn = int(gdb.parse_and_eval("min_low_pfn")) 65 self.max_pfn = int(gdb.parse_and_eval("max_pfn")) 66 self.page_ext_size = int(gdb.parse_and_eval("page_ext_size")) 67 self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names') 68 69 def page_ext_invalid(self, page_ext): 70 if page_ext == gdb.Value(0): 71 return True 72 if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID: 73 return True 74 return False 75 76 def get_entry(self, base, index): 77 return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer()) 78 79 def lookup_page_ext(self, page): 80 pfn = self.p_ops.page_to_pfn(page) 81 section = self.p_ops.pfn_to_section(pfn) 82 page_ext = section["page_ext"] 83 if self.page_ext_invalid(page_ext): 84 return gdb.Value(0) 85 return self.get_entry(page_ext, pfn) 86 87 def page_ext_get(self, page): 88 page_ext = self.lookup_page_ext(page) 89 if page_ext != gdb.Value(0): 90 return page_ext 91 else: 92 return gdb.Value(0) 93 94 def get_page_owner(self, page_ext): 95 addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type()) 96 return addr.cast(page_owner_t.get_type().pointer()) 97 98 def read_page_owner_by_addr(self, struct_page_addr): 99 page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer()) 100 pfn = self.p_ops.page_to_pfn(page) 101 102 if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)): 103 gdb.write("pfn is invalid\n") 104 return 105 106 page = self.p_ops.pfn_to_page(pfn) 107 page_ext = self.page_ext_get(page) 108 109 if page_ext == gdb.Value(0): 110 gdb.write("page_ext is null\n") 111 return 112 113 if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 114 gdb.write("page_owner flag is invalid\n") 115 raise gdb.GdbError('page_owner info is not present (never set?)\n') 116 117 if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address): 118 gdb.write('page_owner tracks the page as allocated\n') 119 else: 120 gdb.write('page_owner tracks the page as freed\n') 121 122 if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 123 gdb.write("page_owner is not allocated\n") 124 125 try: 126 page_owner = self.get_page_owner(page_ext) 127 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" %\ 128 (page_owner["order"], page_owner["gfp_mask"],\ 129 page_owner["pid"], page_owner["tgid"], page_owner["comm"],\ 130 page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 131 gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 132 if page_owner["handle"] == 0: 133 gdb.write('page_owner allocation stack trace missing\n') 134 else: 135 stackdepot.stack_depot_print(page_owner["handle"]) 136 137 if page_owner["free_handle"] == 0: 138 gdb.write('page_owner free stack trace missing\n') 139 else: 140 gdb.write('page last free stack trace:\n') 141 stackdepot.stack_depot_print(page_owner["free_handle"]) 142 if page_owner['last_migrate_reason'] != -1: 143 gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']]) 144 except: 145 gdb.write("\n") 146 147 def read_page_owner(self): 148 pfn = self.min_pfn 149 150 # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area 151 while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0: 152 pfn += 1 153 154 while pfn < self.max_pfn: 155 # 156 # If the new page is in a new MAX_ORDER_NR_PAGES area, 157 # validate the area as existing, skip it if not 158 # 159 if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)): 160 pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1) 161 continue; 162 163 page = self.p_ops.pfn_to_page(pfn) 164 page_ext = self.page_ext_get(page) 165 if page_ext == gdb.Value(0): 166 pfn += 1 167 continue 168 169 if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)): 170 pfn += 1 171 continue 172 if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)): 173 pfn += 1 174 continue 175 176 try: 177 page_owner = self.get_page_owner(page_ext) 178 gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\ 179 (page_owner["order"], page_owner["gfp_mask"],\ 180 page_owner["pid"], page_owner["tgid"], page_owner["comm"],\ 181 page_owner["ts_nsec"], page_owner["free_ts_nsec"])) 182 gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags'])) 183 stackdepot.stack_depot_print(page_owner["handle"]) 184 pfn += (1 << page_owner["order"]) 185 continue 186 except: 187 gdb.write("\n") 188 pfn += 1 189 190DumpPageOwner() 191