page_owner.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. import gdb
  9. from linux import utils, stackdepot, constants, mm
  10. if constants.LX_CONFIG_PAGE_OWNER:
  11. page_ext_t = utils.CachedType('struct page_ext')
  12. page_owner_t = utils.CachedType('struct page_owner')
  13. PAGE_OWNER_STACK_DEPTH = 16
  14. PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER
  15. PAGE_EXT_INVALID = 0x1
  16. PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED
  17. def help():
  18. t = """Usage: lx-dump-page-owner [Option]
  19. Option:
  20. --pfn [Decimal pfn]
  21. Example:
  22. lx-dump-page-owner --pfn 655360\n"""
  23. gdb.write("Unrecognized command\n")
  24. raise gdb.GdbError(t)
  25. class DumpPageOwner(gdb.Command):
  26. """Dump page owner"""
  27. min_pfn = None
  28. max_pfn = None
  29. p_ops = None
  30. migrate_reason_names = None
  31. def __init__(self):
  32. super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT)
  33. def invoke(self, args, from_tty):
  34. if not constants.LX_CONFIG_PAGE_OWNER:
  35. raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable')
  36. page_owner_inited = gdb.parse_and_eval('page_owner_inited')
  37. if page_owner_inited['key']['enabled']['counter'] != 0x1:
  38. raise gdb.GdbError('page_owner_inited is not enabled')
  39. self.p_ops = mm.page_ops().ops
  40. self.get_page_owner_info()
  41. argv = gdb.string_to_argv(args)
  42. if len(argv) == 0:
  43. self.read_page_owner()
  44. elif len(argv) == 2:
  45. if argv[0] == "--pfn":
  46. pfn = int(argv[1])
  47. self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn))
  48. else:
  49. help()
  50. else:
  51. help()
  52. def get_page_owner_info(self):
  53. self.min_pfn = int(gdb.parse_and_eval("min_low_pfn"))
  54. self.max_pfn = int(gdb.parse_and_eval("max_pfn"))
  55. self.page_ext_size = int(gdb.parse_and_eval("page_ext_size"))
  56. self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names')
  57. def page_ext_invalid(self, page_ext):
  58. if page_ext == gdb.Value(0):
  59. return True
  60. if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID:
  61. return True
  62. return False
  63. def get_entry(self, base, index):
  64. return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer())
  65. def lookup_page_ext(self, page):
  66. pfn = self.p_ops.page_to_pfn(page)
  67. section = self.p_ops.pfn_to_section(pfn)
  68. page_ext = section["page_ext"]
  69. if self.page_ext_invalid(page_ext):
  70. return gdb.Value(0)
  71. return self.get_entry(page_ext, pfn)
  72. def page_ext_get(self, page):
  73. page_ext = self.lookup_page_ext(page)
  74. if page_ext != gdb.Value(0):
  75. return page_ext
  76. else:
  77. return gdb.Value(0)
  78. def get_page_owner(self, page_ext):
  79. addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type())
  80. return addr.cast(page_owner_t.get_type().pointer())
  81. def read_page_owner_by_addr(self, struct_page_addr):
  82. page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
  83. pfn = self.p_ops.page_to_pfn(page)
  84. if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)):
  85. gdb.write("pfn is invalid\n")
  86. return
  87. page = self.p_ops.pfn_to_page(pfn)
  88. page_ext = self.page_ext_get(page)
  89. if page_ext == gdb.Value(0):
  90. gdb.write("page_ext is null\n")
  91. return
  92. if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
  93. gdb.write("page_owner flag is invalid\n")
  94. raise gdb.GdbError('page_owner info is not present (never set?)\n')
  95. if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address):
  96. gdb.write('page_owner tracks the page as allocated\n')
  97. else:
  98. gdb.write('page_owner tracks the page as freed\n')
  99. if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
  100. gdb.write("page_owner is not allocated\n")
  101. page_owner = self.get_page_owner(page_ext)
  102. 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" %\
  103. (page_owner["order"], page_owner["gfp_mask"],\
  104. page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
  105. page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
  106. gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
  107. if page_owner["handle"] == 0:
  108. gdb.write('page_owner allocation stack trace missing\n')
  109. else:
  110. stackdepot.stack_depot_print(page_owner["handle"])
  111. if page_owner["free_handle"] == 0:
  112. gdb.write('page_owner free stack trace missing\n')
  113. else:
  114. gdb.write('page last free stack trace:\n')
  115. stackdepot.stack_depot_print(page_owner["free_handle"])
  116. if page_owner['last_migrate_reason'] != -1:
  117. gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])
  118. def read_page_owner(self):
  119. pfn = self.min_pfn
  120. # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area
  121. while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0:
  122. pfn += 1
  123. while pfn < self.max_pfn:
  124. #
  125. # If the new page is in a new MAX_ORDER_NR_PAGES area,
  126. # validate the area as existing, skip it if not
  127. #
  128. if ((pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)):
  129. pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1)
  130. continue;
  131. page = self.p_ops.pfn_to_page(pfn)
  132. page_ext = self.page_ext_get(page)
  133. if page_ext == gdb.Value(0):
  134. pfn += 1
  135. continue
  136. if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
  137. pfn += 1
  138. continue
  139. if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
  140. pfn += 1
  141. continue
  142. page_owner = self.get_page_owner(page_ext)
  143. gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\
  144. (page_owner["order"], page_owner["gfp_mask"],\
  145. page_owner["pid"], page_owner["tgid"], page_owner["comm"].string(),\
  146. page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
  147. gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
  148. stackdepot.stack_depot_print(page_owner["handle"])
  149. pfn += (1 << page_owner["order"])
  150. DumpPageOwner()