xref: /linux/scripts/gdb/linux/page_owner.py (revision e52ec6a2)
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