Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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        page_owner = self.get_page_owner(page_ext)
126        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" %\
127                (page_owner["order"], page_owner["gfp_mask"],\
128                page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
129                page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
130        gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
131        if page_owner["handle"] == 0:
132            gdb.write('page_owner allocation stack trace missing\n')
133        else:
134            stackdepot.stack_depot_print(page_owner["handle"])
135
136        if page_owner["free_handle"] == 0:
137            gdb.write('page_owner free stack trace missing\n')
138        else:
139            gdb.write('page last free stack trace:\n')
140            stackdepot.stack_depot_print(page_owner["free_handle"])
141        if page_owner['last_migrate_reason'] != -1:
142            gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])
143
144    def read_page_owner(self):
145        pfn = self.min_pfn
146
147        # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area
148        while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0:
149            pfn += 1
150
151        while pfn < self.max_pfn:
152            #
153            # If the new page is in a new MAX_ORDER_NR_PAGES area,
154            # validate the area as existing, skip it if not
155            #
156            if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)):
157                pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1)
158                continue;
159
160            page = self.p_ops.pfn_to_page(pfn)
161            page_ext = self.page_ext_get(page)
162            if page_ext == gdb.Value(0):
163                pfn += 1
164                continue
165
166            if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
167                pfn += 1
168                continue
169            if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
170                pfn += 1
171                continue
172
173            page_owner = self.get_page_owner(page_ext)
174            gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\
175                    (page_owner["order"], page_owner["gfp_mask"],\
176                    page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
177                    page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
178            gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
179            stackdepot.stack_depot_print(page_owner["handle"])
180            pfn += (1 << page_owner["order"])
181
182DumpPageOwner()