]> Git Repo - linux.git/blob - scripts/gdb/linux/page_owner.py
Merge tag 'input-for-v6.6-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor...
[linux.git] / scripts / gdb / linux / page_owner.py
1 # SPDX-License-Identifier: GPL-2.0
2 #
3 # Copyright (c) 2023 MediaTek Inc.
4 #
5 # Authors:
6 #  Kuan-Ying Lee <[email protected]>
7 #
8
9 import gdb
10 from linux import utils, stackdepot, constants, mm
11
12 if 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
21 def 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
30 class 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
190 DumpPageOwner()
This page took 0.04351 seconds and 4 git commands to generate.