forked from mirrors/linux
		
	 e52ec6a2db
			
		
	
	
		e52ec6a2db
		
	
	
	
	
		
			
			1. When we crash on a page, we want to check what happened on this page instead of skipping this page by try-except block. Thus, removing the try-except block. 2. Remove redundant comma and print the task name properly. Link: https://lkml.kernel.org/r/20231127070404.4192-4-Kuan-Ying.Lee@mediatek.com Signed-off-by: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> Cc: Andrey Konovalov <andreyknvl@google.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Chinwen Chang <chinwen.chang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Qun-Wei Lin <qun-wei.lin@mediatek.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
		
			
				
	
	
		
			182 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # SPDX-License-Identifier: GPL-2.0
 | |
| #
 | |
| # Copyright (c) 2023 MediaTek Inc.
 | |
| #
 | |
| # Authors:
 | |
| #  Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
 | |
| #
 | |
| 
 | |
| import gdb
 | |
| from linux import utils, stackdepot, constants, mm
 | |
| 
 | |
| if constants.LX_CONFIG_PAGE_OWNER:
 | |
|     page_ext_t = utils.CachedType('struct page_ext')
 | |
|     page_owner_t = utils.CachedType('struct page_owner')
 | |
| 
 | |
|     PAGE_OWNER_STACK_DEPTH = 16
 | |
|     PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER
 | |
|     PAGE_EXT_INVALID = 0x1
 | |
|     PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED
 | |
| 
 | |
| def help():
 | |
|     t = """Usage: lx-dump-page-owner [Option]
 | |
|     Option:
 | |
|         --pfn [Decimal pfn]
 | |
|     Example:
 | |
|         lx-dump-page-owner --pfn 655360\n"""
 | |
|     gdb.write("Unrecognized command\n")
 | |
|     raise gdb.GdbError(t)
 | |
| 
 | |
| class DumpPageOwner(gdb.Command):
 | |
|     """Dump page owner"""
 | |
| 
 | |
|     min_pfn = None
 | |
|     max_pfn = None
 | |
|     p_ops = None
 | |
|     migrate_reason_names = None
 | |
| 
 | |
|     def __init__(self):
 | |
|         super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT)
 | |
| 
 | |
|     def invoke(self, args, from_tty):
 | |
|         if not constants.LX_CONFIG_PAGE_OWNER:
 | |
|             raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable')
 | |
| 
 | |
|         page_owner_inited = gdb.parse_and_eval('page_owner_inited')
 | |
|         if page_owner_inited['key']['enabled']['counter'] != 0x1:
 | |
|             raise gdb.GdbError('page_owner_inited is not enabled')
 | |
| 
 | |
|         self.p_ops = mm.page_ops().ops
 | |
|         self.get_page_owner_info()
 | |
|         argv = gdb.string_to_argv(args)
 | |
|         if len(argv) == 0:
 | |
|               self.read_page_owner()
 | |
|         elif len(argv) == 2:
 | |
|             if argv[0] == "--pfn":
 | |
|                 pfn = int(argv[1])
 | |
|                 self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn))
 | |
|             else:
 | |
|                 help()
 | |
|         else:
 | |
|             help()
 | |
| 
 | |
|     def get_page_owner_info(self):
 | |
|         self.min_pfn = int(gdb.parse_and_eval("min_low_pfn"))
 | |
|         self.max_pfn = int(gdb.parse_and_eval("max_pfn"))
 | |
|         self.page_ext_size = int(gdb.parse_and_eval("page_ext_size"))
 | |
|         self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names')
 | |
| 
 | |
|     def page_ext_invalid(self, page_ext):
 | |
|         if page_ext == gdb.Value(0):
 | |
|             return True
 | |
|         if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID:
 | |
|             return True
 | |
|         return False
 | |
| 
 | |
|     def get_entry(self, base, index):
 | |
|         return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer())
 | |
| 
 | |
|     def lookup_page_ext(self, page):
 | |
|         pfn = self.p_ops.page_to_pfn(page)
 | |
|         section = self.p_ops.pfn_to_section(pfn)
 | |
|         page_ext = section["page_ext"]
 | |
|         if self.page_ext_invalid(page_ext):
 | |
|             return gdb.Value(0)
 | |
|         return self.get_entry(page_ext, pfn)
 | |
| 
 | |
|     def page_ext_get(self, page):
 | |
|         page_ext = self.lookup_page_ext(page)
 | |
|         if page_ext != gdb.Value(0):
 | |
|             return page_ext
 | |
|         else:
 | |
|             return gdb.Value(0)
 | |
| 
 | |
|     def get_page_owner(self, page_ext):
 | |
|         addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type())
 | |
|         return addr.cast(page_owner_t.get_type().pointer())
 | |
| 
 | |
|     def read_page_owner_by_addr(self, struct_page_addr):
 | |
|         page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
 | |
|         pfn = self.p_ops.page_to_pfn(page)
 | |
| 
 | |
|         if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)):
 | |
|             gdb.write("pfn is invalid\n")
 | |
|             return
 | |
| 
 | |
|         page = self.p_ops.pfn_to_page(pfn)
 | |
|         page_ext = self.page_ext_get(page)
 | |
| 
 | |
|         if page_ext == gdb.Value(0):
 | |
|             gdb.write("page_ext is null\n")
 | |
|             return
 | |
| 
 | |
|         if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
 | |
|             gdb.write("page_owner flag is invalid\n")
 | |
|             raise gdb.GdbError('page_owner info is not present (never set?)\n')
 | |
| 
 | |
|         if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address):
 | |
|             gdb.write('page_owner tracks the page as allocated\n')
 | |
|         else:
 | |
|             gdb.write('page_owner tracks the page as freed\n')
 | |
| 
 | |
|         if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
 | |
|             gdb.write("page_owner is not allocated\n")
 | |
| 
 | |
|         page_owner = self.get_page_owner(page_ext)
 | |
|         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" %\
 | |
|                 (page_owner["order"], page_owner["gfp_mask"],\
 | |
|                 page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
 | |
|                 page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
 | |
|         gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
 | |
|         if page_owner["handle"] == 0:
 | |
|             gdb.write('page_owner allocation stack trace missing\n')
 | |
|         else:
 | |
|             stackdepot.stack_depot_print(page_owner["handle"])
 | |
| 
 | |
|         if page_owner["free_handle"] == 0:
 | |
|             gdb.write('page_owner free stack trace missing\n')
 | |
|         else:
 | |
|             gdb.write('page last free stack trace:\n')
 | |
|             stackdepot.stack_depot_print(page_owner["free_handle"])
 | |
|         if page_owner['last_migrate_reason'] != -1:
 | |
|             gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])
 | |
| 
 | |
|     def read_page_owner(self):
 | |
|         pfn = self.min_pfn
 | |
| 
 | |
|         # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area
 | |
|         while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0:
 | |
|             pfn += 1
 | |
| 
 | |
|         while pfn < self.max_pfn:
 | |
|             #
 | |
|             # If the new page is in a new MAX_ORDER_NR_PAGES area,
 | |
|             # validate the area as existing, skip it if not
 | |
|             #
 | |
|             if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)):
 | |
|                 pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1)
 | |
|                 continue;
 | |
| 
 | |
|             page = self.p_ops.pfn_to_page(pfn)
 | |
|             page_ext = self.page_ext_get(page)
 | |
|             if page_ext == gdb.Value(0):
 | |
|                 pfn += 1
 | |
|                 continue
 | |
| 
 | |
|             if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
 | |
|                 pfn += 1
 | |
|                 continue
 | |
|             if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
 | |
|                 pfn += 1
 | |
|                 continue
 | |
| 
 | |
|             page_owner = self.get_page_owner(page_ext)
 | |
|             gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\
 | |
|                     (page_owner["order"], page_owner["gfp_mask"],\
 | |
|                     page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
 | |
|                     page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
 | |
|             gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
 | |
|             stackdepot.stack_depot_print(page_owner["handle"])
 | |
|             pfn += (1 << page_owner["order"])
 | |
| 
 | |
| DumpPageOwner()
 |