forked from osandov/drgn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib: add script to dump page_owner info
Sample output: Page allocated via order 0, gfp_mask: 0x140cca, pid: 74, tgid: 74 (b'kworker/u32:2'), ts 1189257596 ns, free_ts 0 ns PFN: 262203, Flags: 0x3fffe000004003c #0 set_page_owner (./include/linux/page_owner.h:32:3) osandov#1 post_alloc_hook (mm/page_alloc.c:1502:2) osandov#2 prep_new_page (mm/page_alloc.c:1510:2) osandov#3 get_page_from_freelist (mm/page_alloc.c:3489:4) osandov#4 __alloc_pages_noprof (mm/page_alloc.c:4747:9) osandov#5 alloc_pages_mpol_noprof (mm/mempolicy.c:2263:9) osandov#6 folio_alloc_mpol_noprof (mm/mempolicy.c:2281:9) osandov#7 shmem_alloc_folio (mm/shmem.c:1726:10) osandov#8 shmem_alloc_and_add_folio (mm/shmem.c:1786:11) osandov#9 shmem_get_folio_gfp (mm/shmem.c:2192:10) osandov#10 shmem_get_folio (mm/shmem.c:2297:9) osandov#11 shmem_write_begin (mm/shmem.c:2902:8) osandov#12 generic_perform_write (mm/filemap.c:4019:12) osandov#13 shmem_file_write_iter (mm/shmem.c:3078:8) osandov#14 __kernel_write_iter (fs/read_write.c:523:8) osandov#15 __kernel_write (fs/read_write.c:543:9) osandov#16 kernel_write (fs/read_write.c:564:9) osandov#17 kernel_write (fs/read_write.c:554:9) osandov#18 xwrite (init/initramfs.c:33:16) osandov#19 do_copy (init/initramfs.c:405:7) osandov#20 write_buffer (init/initramfs.c:452:10) osandov#21 unpack_to_rootfs (init/initramfs.c:505:14) ... Signed-off-by: Kuan-Ying Lee <kuan-ying.lee@canonical.com>
- Loading branch information
Showing
1 changed file
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
#!/usr/bin/env drgn | ||
# Copyright (c) Canonical Ltd. | ||
# SPDX-License-Identifier: LGPL-2.1-or-later | ||
|
||
""" Script to dump vmallocinfo status using drgn""" | ||
|
||
import re | ||
from typing import Optional | ||
|
||
from drgn import IntegerLike, Program, sizeof | ||
from drgn.helpers.linux.mm import * | ||
from drgn.helpers.linux.kconfig import get_kconfig | ||
from drgn.helpers.linux.stackdepot import stack_depot_fetch | ||
|
||
def DIV_ROUND_UP(n,d): | ||
return ((n) + (d) - 1) // (d) | ||
|
||
try: | ||
MAX_ORDER_NR_PAGES = int(get_kconfig(prog)["CONFIG_ARCH_FORCE_MAX_ORDER"]) | ||
except: | ||
MAX_ORDER_NR_PAGES = 10 | ||
|
||
vmcoreinfo = prog["VMCOREINFO"].string_().decode() | ||
def get_vmcoreinfo_number(item) -> IntegerLike: | ||
match = re.search( | ||
r"^NUMBER\(%s\)=([0-9]+)$" % item, vmcoreinfo, flags=re.M | ||
) | ||
if match: | ||
return int(match.group(1)) | ||
else: | ||
raise Exception("Cannot find %s in vmcoreinfo" % item) | ||
|
||
SECTION_SIZE_BITS = get_vmcoreinfo_number("SECTION_SIZE_BITS") | ||
MAX_PHYSMEM_BITS = get_vmcoreinfo_number("MAX_PHYSMEM_BITS") | ||
SECTIONS_SHIFT = MAX_PHYSMEM_BITS - SECTION_SIZE_BITS | ||
|
||
NR_MEM_SECTIONS = 1 << SECTIONS_SHIFT | ||
PFN_SECTION_SHIFT = SECTION_SIZE_BITS - prog["PAGE_SHIFT"] | ||
|
||
if get_kconfig(prog)["CONFIG_SPARSEMEM_EXTREME"]: | ||
SECTIONS_PER_ROOT = prog["PAGE_SIZE"].value_() // sizeof(prog.type('struct mem_section')) | ||
else: | ||
SECTIONS_PER_ROOT = 1 | ||
|
||
NR_SECTION_ROOTS = DIV_ROUND_UP(NR_MEM_SECTIONS, SECTIONS_PER_ROOT) | ||
SECTION_ROOT_MASK = SECTIONS_PER_ROOT - 1 | ||
|
||
SUBSECTION_SHIFT = 21 | ||
PFN_SUBSECTION_SHIFT = SUBSECTION_SHIFT - prog["PAGE_SHIFT"] | ||
PAGES_PER_SUBSECTION = 1 << PFN_SUBSECTION_SHIFT | ||
|
||
PAGES_PER_SECTION = 1 << PFN_SECTION_SHIFT | ||
PAGE_SECTION_MASK = ~(PAGES_PER_SECTION - 1) | ||
|
||
SECTION_HAS_MEM_MAP = 1 << prog["SECTION_HAS_MEM_MAP_BIT"].value_() | ||
SECTION_IS_EARLY = 1 << prog["SECTION_IS_EARLY_BIT"].value_() | ||
|
||
PAGE_EXT_OWNER = prog["PAGE_EXT_OWNER"].value_() | ||
PAGE_EXT_OWNER_ALLOCATED = prog["PAGE_EXT_OWNER_ALLOCATED"].value_() | ||
|
||
PAGE_EXT_INVALID = 1 | ||
|
||
def pfn_to_section_nr(pfn: Object) -> IntegerLike: | ||
return pfn >> PFN_SECTION_SHIFT | ||
|
||
def section_nr_to_root(sec: IntegerLike) -> IntegerLike: | ||
return sec.value_() // SECTIONS_PER_ROOT | ||
|
||
def nr_to_section(nr: IntegerLike) -> Object: | ||
root = section_nr_to_root(nr) | ||
if root >= NR_SECTION_ROOTS: | ||
raise Exception("root >= NR_SECTION_ROOTS") | ||
return prog["mem_section"][root][nr & SECTION_ROOT_MASK] | ||
|
||
def valid_section(section: Object) -> bool: | ||
return bool(section.address_of_() and (section.section_mem_map & SECTION_HAS_MEM_MAP)) | ||
|
||
def early_section(section: Object) -> bool: | ||
return bool(section.address_of_() and (section.section_mem_map & SECTION_IS_EARLY)) | ||
|
||
def subsection_map_index(pfn: Object) -> IntegerLike: | ||
return (pfn & ~(PAGE_SECTION_MASK)) // PAGES_PER_SUBSECTION | ||
|
||
def pfn_section_valid(ms: Object, pfn: Object) -> IntegerLike: | ||
idx = subsection_map_index(pfn) | ||
usage = ms.usage | ||
if usage: | ||
return (1 << idx) & usage.subsection_map | ||
else: | ||
return 0 | ||
|
||
def pfn_to_section(pfn: Object) -> Optional[Object]: | ||
return nr_to_section(pfn_to_section_nr(pfn)) | ||
|
||
def pfn_valid(pfn: Object) -> IntegerLike: | ||
if PHYS_PFN(PFN_PHYS(pfn)) != pfn: | ||
return 0 | ||
if pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS: | ||
return 0 | ||
ms = pfn_to_section(pfn) | ||
if not valid_section(ms): | ||
return 0 | ||
ret = early_section(ms) or pfn_section_valid(ms, pfn) | ||
return ret | ||
|
||
|
||
def page_ext_invalid(page_ext: Object) -> bool: | ||
if not page_ext: | ||
return True | ||
if page_ext.value_() & PAGE_EXT_INVALID == PAGE_EXT_INVALID: | ||
return True | ||
return False | ||
|
||
def get_entry(base: Object, index: Object) -> Object: | ||
return Object(prog, "struct page_ext *", (cast("unsigned long", base) + prog["page_ext_size"].value_() * index)) | ||
|
||
def lookup_page_ext(page: Object) -> Object: | ||
pfn = page_to_pfn(page) | ||
section = pfn_to_section(pfn) | ||
page_ext = section.page_ext | ||
if page_ext_invalid(page_ext): | ||
return drgn.NULL(prog, "unsigned long") | ||
return get_entry(page_ext, pfn) | ||
|
||
def page_ext_get(page: Object) -> Optional[Object]: | ||
page_ext = lookup_page_ext(page) | ||
if page_ext: | ||
return page_ext | ||
|
||
def get_page_owner(page_ext: Object) -> Object: | ||
addr = cast("unsigned long", page_ext) + prog["page_owner_ops"].offset | ||
return Object(prog, "struct page_owner *", addr) | ||
|
||
min_low_pfn = prog["min_low_pfn"] | ||
max_pfn = prog["max_pfn"] | ||
|
||
def read_page_owner(): | ||
if prog["page_owner_inited"].key.enabled.counter != 1: | ||
raise Exception("page_owner is not enabled") | ||
pfn = min_low_pfn | ||
while (not pfn_valid(pfn)) and (pfn & (MAX_ORDER_NR_PAGES - 1) != 0): | ||
pfn += 1 | ||
while pfn < max_pfn: | ||
# | ||
# If the new page is in a new MAX_ORDER_NR_PAGES area, | ||
# validate the area as existing, skip it if not | ||
# | ||
if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0) and (not pfn_valid(pfn)): | ||
pfn += (MAX_ORDER_NR_PAGES - 1) | ||
continue; | ||
|
||
page = pfn_to_page(pfn) | ||
page_ext = page_ext_get(page) | ||
if not page_ext: | ||
pfn += 1 | ||
continue | ||
|
||
if not (page_ext.flags & (1 << PAGE_EXT_OWNER)): | ||
pfn += 1 | ||
continue | ||
|
||
if not (page_ext.flags & (1 << PAGE_EXT_OWNER_ALLOCATED)): | ||
pfn += 1 | ||
continue | ||
|
||
page_owner = get_page_owner(page_ext) | ||
trace = stack_depot_fetch(page_owner.handle) | ||
print("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns" %\ | ||
(page_owner.order, page_owner.gfp_mask,\ | ||
page_owner.pid, page_owner.tgid, page_owner.comm.string_(),\ | ||
page_owner.ts_nsec, page_owner.free_ts_nsec)) | ||
print("PFN: %d, Flags: 0x%x" % (pfn, page.flags)) | ||
print(trace) | ||
pfn += (1 << page_owner.order) | ||
|
||
read_page_owner() |