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