aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorLinus Torvalds2023-08-29 14:53:51 -0700
committerLinus Torvalds2023-08-29 14:53:51 -0700
commitd68b4b6f307d155475cce541f2aee938032ed22e (patch)
treec2a6487ac8b1bce963b5b352b42e461a6fa8da15 /scripts
parentb96a3e9142fdf346b05b20e867b4f0dfca119e96 (diff)
parentdce8f8ed1de1d9d6d27c5ccd202ce4ec163b100c (diff)
Merge tag 'mm-nonmm-stable-2023-08-28-22-48' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull non-MM updates from Andrew Morton: - An extensive rework of kexec and crash Kconfig from Eric DeVolder ("refactor Kconfig to consolidate KEXEC and CRASH options") - kernel.h slimming work from Andy Shevchenko ("kernel.h: Split out a couple of macros to args.h") - gdb feature work from Kuan-Ying Lee ("Add GDB memory helper commands") - vsprintf inclusion rationalization from Andy Shevchenko ("lib/vsprintf: Rework header inclusions") - Switch the handling of kdump from a udev scheme to in-kernel handling, by Eric DeVolder ("crash: Kernel handling of CPU and memory hot un/plug") - Many singleton patches to various parts of the tree * tag 'mm-nonmm-stable-2023-08-28-22-48' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (81 commits) document while_each_thread(), change first_tid() to use for_each_thread() drivers/char/mem.c: shrink character device's devlist[] array x86/crash: optimize CPU changes crash: change crash_prepare_elf64_headers() to for_each_possible_cpu() crash: hotplug support for kexec_load() x86/crash: add x86 crash hotplug support crash: memory and CPU hotplug sysfs attributes kexec: exclude elfcorehdr from the segment digest crash: add generic infrastructure for crash hotplug support crash: move a few code bits to setup support of crash hotplug kstrtox: consistently use _tolower() kill do_each_thread() nilfs2: fix WARNING in mark_buffer_dirty due to discarded buffer reuse scripts/bloat-o-meter: count weak symbol sizes treewide: drop CONFIG_EMBEDDED lockdep: fix static memory detection even more lib/vsprintf: declare no_hash_pointers in sprintf.h lib/vsprintf: split out sprintf() and friends kernel/fork: stop playing lockless games for exe_file replacement adfs: delete unused "union adfs_dirtail" definition ...
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/bloat-o-meter10
-rwxr-xr-xscripts/checkpatch.pl22
-rw-r--r--scripts/gdb/linux/constants.py.in55
-rw-r--r--scripts/gdb/linux/mm.py582
-rw-r--r--scripts/gdb/linux/modules.py44
-rw-r--r--scripts/gdb/linux/page_owner.py190
-rw-r--r--scripts/gdb/linux/pgtable.py222
-rw-r--r--scripts/gdb/linux/slab.py326
-rw-r--r--scripts/gdb/linux/stackdepot.py55
-rw-r--r--scripts/gdb/linux/symbols.py40
-rw-r--r--scripts/gdb/linux/utils.py20
-rw-r--r--scripts/gdb/linux/vmalloc.py56
-rw-r--r--scripts/gdb/vmlinux-gdb.py7
-rwxr-xr-xscripts/headers_install.sh1
14 files changed, 1406 insertions, 224 deletions
diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter
index 36303afa9dfc..888ce286a351 100755
--- a/scripts/bloat-o-meter
+++ b/scripts/bloat-o-meter
@@ -100,12 +100,12 @@ def print_result(symboltype, symbolformat):
print("Total: Before=%d, After=%d, chg %+.2f%%" % (otot, ntot, percent))
if args.c:
- print_result("Function", "tT")
- print_result("Data", "dDbB")
+ print_result("Function", "tTwW")
+ print_result("Data", "dDbBvV")
print_result("RO Data", "rR")
elif args.d:
- print_result("Data", "dDbBrR")
+ print_result("Data", "dDbBrRvV")
elif args.t:
- print_result("Function", "tT")
+ print_result("Function", "tTwW")
else:
- print_result("Function", "tTdDbBrR")
+ print_result("Function", "tTdDbBrRvVwW")
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index a9841148cde2..7d16f863edf1 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -74,6 +74,8 @@ my $git_command ='export LANGUAGE=en_US.UTF-8; git';
my $tabsize = 8;
my ${CONFIG_} = "CONFIG_";
+my %maybe_linker_symbol; # for externs in c exceptions, when seen in *vmlinux.lds.h
+
sub help {
my ($exitcode) = @_;
@@ -3270,7 +3272,7 @@ sub process {
# A Fixes:, link or signature tag line
$commit_log_possible_stack_dump)) {
WARN("COMMIT_LOG_LONG_LINE",
- "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
+ "Prefer a maximum 75 chars per line (possible unwrapped commit description?)\n" . $herecurr);
$commit_log_long_line = 1;
}
@@ -6051,6 +6053,9 @@ sub process {
# check for line continuations outside of #defines, preprocessor #, and asm
+ } elsif ($realfile =~ m@/vmlinux.lds.h$@) {
+ $line =~ s/(\w+)/$maybe_linker_symbol{$1}++/ge;
+ #print "REAL: $realfile\nln: $line\nkeys:", sort keys %maybe_linker_symbol;
} else {
if ($prevline !~ /^..*\\$/ &&
$line !~ /^\+\s*\#.*\\$/ && # preprocessor
@@ -7120,6 +7125,21 @@ sub process {
}
} elsif ($realfile =~ /\.c$/ && defined $stat &&
+ $stat =~ /^\+extern struct\s+(\w+)\s+(\w+)\[\];/)
+ {
+ my ($st_type, $st_name) = ($1, $2);
+
+ for my $s (keys %maybe_linker_symbol) {
+ #print "Linker symbol? $st_name : $s\n";
+ goto LIKELY_LINKER_SYMBOL
+ if $st_name =~ /$s/;
+ }
+ WARN("AVOID_EXTERNS",
+ "found a file-scoped extern type:$st_type name:$st_name in .c file\n"
+ . "is this a linker symbol ?\n" . $herecurr);
+ LIKELY_LINKER_SYMBOL:
+
+ } elsif ($realfile =~ /\.c$/ && defined $stat &&
$stat =~ /^.\s*extern\s+/)
{
WARN("AVOID_EXTERNS",
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 50a92c4e9984..e3517d4ab8ec 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -18,8 +18,11 @@
#include <linux/irq.h>
#include <linux/mount.h>
#include <linux/of_fdt.h>
+#include <linux/page_ext.h>
#include <linux/radix-tree.h>
+#include <linux/slab.h>
#include <linux/threads.h>
+#include <linux/vmalloc.h>
/* We need to stringify expanded macros so that they can be parsed */
@@ -64,6 +67,9 @@ LX_GDBPARSED(IRQ_HIDDEN)
/* linux/module.h */
LX_GDBPARSED(MOD_TEXT)
+LX_GDBPARSED(MOD_DATA)
+LX_GDBPARSED(MOD_RODATA)
+LX_GDBPARSED(MOD_RO_AFTER_INIT)
/* linux/mount.h */
LX_VALUE(MNT_NOSUID)
@@ -86,6 +92,28 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE)
LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
LX_GDBPARSED(RADIX_TREE_MAP_MASK)
+/* linux/vmalloc.h */
+LX_VALUE(VM_IOREMAP)
+LX_VALUE(VM_ALLOC)
+LX_VALUE(VM_MAP)
+LX_VALUE(VM_USERMAP)
+LX_VALUE(VM_DMA_COHERENT)
+
+/* linux/page_ext.h */
+if IS_BUILTIN(CONFIG_PAGE_OWNER):
+ LX_GDBPARSED(PAGE_EXT_OWNER)
+ LX_GDBPARSED(PAGE_EXT_OWNER_ALLOCATED)
+
+/* linux/slab.h */
+LX_GDBPARSED(SLAB_RED_ZONE)
+LX_GDBPARSED(SLAB_POISON)
+LX_GDBPARSED(SLAB_KMALLOC)
+LX_GDBPARSED(SLAB_HWCACHE_ALIGN)
+LX_GDBPARSED(SLAB_CACHE_DMA)
+LX_GDBPARSED(SLAB_CACHE_DMA32)
+LX_GDBPARSED(SLAB_STORE_USER)
+LX_GDBPARSED(SLAB_PANIC)
+
/* Kernel Configs */
LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS)
LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
@@ -102,3 +130,30 @@ LX_CONFIG(CONFIG_X86_MCE_AMD)
LX_CONFIG(CONFIG_X86_MCE)
LX_CONFIG(CONFIG_X86_IO_APIC)
LX_CONFIG(CONFIG_HAVE_KVM)
+LX_CONFIG(CONFIG_NUMA)
+LX_CONFIG(CONFIG_ARM64)
+LX_CONFIG(CONFIG_ARM64_4K_PAGES)
+LX_CONFIG(CONFIG_ARM64_16K_PAGES)
+LX_CONFIG(CONFIG_ARM64_64K_PAGES)
+if IS_BUILTIN(CONFIG_ARM64):
+ LX_VALUE(CONFIG_ARM64_PA_BITS)
+ LX_VALUE(CONFIG_ARM64_VA_BITS)
+ LX_VALUE(CONFIG_ARM64_PAGE_SHIFT)
+ LX_VALUE(CONFIG_ARCH_FORCE_MAX_ORDER)
+LX_CONFIG(CONFIG_SPARSEMEM)
+LX_CONFIG(CONFIG_SPARSEMEM_EXTREME)
+LX_CONFIG(CONFIG_SPARSEMEM_VMEMMAP)
+LX_CONFIG(CONFIG_KASAN)
+LX_CONFIG(CONFIG_KASAN_GENERIC)
+LX_CONFIG(CONFIG_KASAN_SW_TAGS)
+LX_CONFIG(CONFIG_KASAN_HW_TAGS)
+if IS_BUILTIN(CONFIG_KASAN_GENERIC) or IS_BUILTIN(CONFIG_KASAN_SW_TAGS):
+ LX_VALUE(CONFIG_KASAN_SHADOW_OFFSET)
+LX_CONFIG(CONFIG_VMAP_STACK)
+if IS_BUILTIN(CONFIG_NUMA):
+ LX_VALUE(CONFIG_NODES_SHIFT)
+LX_CONFIG(CONFIG_DEBUG_VIRTUAL)
+LX_CONFIG(CONFIG_STACKDEPOT)
+LX_CONFIG(CONFIG_PAGE_OWNER)
+LX_CONFIG(CONFIG_SLUB_DEBUG)
+LX_CONFIG(CONFIG_SLAB_FREELIST_HARDENED)
diff --git a/scripts/gdb/linux/mm.py b/scripts/gdb/linux/mm.py
index 30d837f3dfae..ad5641dcb068 100644
--- a/scripts/gdb/linux/mm.py
+++ b/scripts/gdb/linux/mm.py
@@ -1,222 +1,398 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: GPL-2.0
#
-# gdb helper commands and functions for Linux kernel debugging
-#
-# routines to introspect page table
+# Copyright (c) 2023 MediaTek Inc.
#
# Authors:
-# Dmitrii Bundin <dmitrii.bundin.a@gmail.com>
+# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
#
import gdb
+import math
+from linux import utils, constants
+
+def DIV_ROUND_UP(n,d):
+ return ((n) + (d) - 1) // (d)
-from linux import utils
+def test_bit(nr, addr):
+ if addr.dereference() & (0x1 << nr):
+ return True
+ else:
+ return False
-PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff')
+class page_ops():
+ ops = None
+ def __init__(self):
+ if not constants.LX_CONFIG_SPARSEMEM_VMEMMAP:
+ raise gdb.GdbError('Only support CONFIG_SPARSEMEM_VMEMMAP now')
+ if constants.LX_CONFIG_ARM64 and utils.is_target_arch('aarch64'):
+ self.ops = aarch64_page_ops()
+ else:
+ raise gdb.GdbError('Only support aarch64 now')
+class aarch64_page_ops():
+ def __init__(self):
+ self.SUBSECTION_SHIFT = 21
+ self.SEBSECTION_SIZE = 1 << self.SUBSECTION_SHIFT
+ self.MODULES_VSIZE = 128 * 1024 * 1024
-def page_mask(level=1):
- # 4KB
- if level == 1:
- return gdb.parse_and_eval('(u64) ~0xfff')
- # 2MB
- elif level == 2:
- return gdb.parse_and_eval('(u64) ~0x1fffff')
- # 1GB
- elif level == 3:
- return gdb.parse_and_eval('(u64) ~0x3fffffff')
- else:
- raise Exception(f'Unknown page level: {level}')
-
-
-#page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled
-POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000'
-def _page_offset_base():
- pob_symbol = gdb.lookup_global_symbol('page_offset_base')
- pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT
- return gdb.parse_and_eval(pob)
-
-
-def is_bit_defined_tupled(data, offset):
- return offset, bool(data >> offset & 1)
-
-def content_tupled(data, bit_start, bit_end):
- return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1)
-
-def entry_va(level, phys_addr, translating_va):
- def start_bit(level):
- if level == 5:
- return 48
- elif level == 4:
- return 39
- elif level == 3:
- return 30
- elif level == 2:
- return 21
- elif level == 1:
- return 12
+ if constants.LX_CONFIG_ARM64_64K_PAGES:
+ self.SECTION_SIZE_BITS = 29
+ else:
+ self.SECTION_SIZE_BITS = 27
+ self.MAX_PHYSMEM_BITS = constants.LX_CONFIG_ARM64_VA_BITS
+
+ self.PAGE_SHIFT = constants.LX_CONFIG_ARM64_PAGE_SHIFT
+ self.PAGE_SIZE = 1 << self.PAGE_SHIFT
+ self.PAGE_MASK = (~(self.PAGE_SIZE - 1)) & ((1 << 64) - 1)
+
+ self.VA_BITS = constants.LX_CONFIG_ARM64_VA_BITS
+ if self.VA_BITS > 48:
+ self.VA_BITS_MIN = 48
+ self.vabits_actual = gdb.parse_and_eval('vabits_actual')
+ else:
+ self.VA_BITS_MIN = self.VA_BITS
+ self.vabits_actual = self.VA_BITS
+ self.kimage_voffset = gdb.parse_and_eval('kimage_voffset') & ((1 << 64) - 1)
+
+ self.SECTIONS_SHIFT = self.MAX_PHYSMEM_BITS - self.SECTION_SIZE_BITS
+
+ if str(constants.LX_CONFIG_ARCH_FORCE_MAX_ORDER).isdigit():
+ self.MAX_ORDER = constants.LX_CONFIG_ARCH_FORCE_MAX_ORDER
+ else:
+ self.MAX_ORDER = 11
+
+ self.MAX_ORDER_NR_PAGES = 1 << (self.MAX_ORDER - 1)
+ self.PFN_SECTION_SHIFT = self.SECTION_SIZE_BITS - self.PAGE_SHIFT
+ self.NR_MEM_SECTIONS = 1 << self.SECTIONS_SHIFT
+ self.PAGES_PER_SECTION = 1 << self.PFN_SECTION_SHIFT
+ self.PAGE_SECTION_MASK = (~(self.PAGES_PER_SECTION - 1)) & ((1 << 64) - 1)
+
+ if constants.LX_CONFIG_SPARSEMEM_EXTREME:
+ self.SECTIONS_PER_ROOT = self.PAGE_SIZE // gdb.lookup_type("struct mem_section").sizeof
+ else:
+ self.SECTIONS_PER_ROOT = 1
+
+ self.NR_SECTION_ROOTS = DIV_ROUND_UP(self.NR_MEM_SECTIONS, self.SECTIONS_PER_ROOT)
+ self.SECTION_ROOT_MASK = self.SECTIONS_PER_ROOT - 1
+ self.SUBSECTION_SHIFT = 21
+ self.SEBSECTION_SIZE = 1 << self.SUBSECTION_SHIFT
+ self.PFN_SUBSECTION_SHIFT = self.SUBSECTION_SHIFT - self.PAGE_SHIFT
+ self.PAGES_PER_SUBSECTION = 1 << self.PFN_SUBSECTION_SHIFT
+
+ self.SECTION_HAS_MEM_MAP = 1 << int(gdb.parse_and_eval('SECTION_HAS_MEM_MAP_BIT'))
+ self.SECTION_IS_EARLY = 1 << int(gdb.parse_and_eval('SECTION_IS_EARLY_BIT'))
+
+ self.struct_page_size = utils.get_page_type().sizeof
+ self.STRUCT_PAGE_MAX_SHIFT = (int)(math.log(self.struct_page_size, 2))
+
+ self.PAGE_OFFSET = self._PAGE_OFFSET(self.VA_BITS)
+ self.MODULES_VADDR = self._PAGE_END(self.VA_BITS_MIN)
+ self.MODULES_END = self.MODULES_VADDR + self.MODULES_VSIZE
+
+ self.VMEMMAP_SHIFT = (self.PAGE_SHIFT - self.STRUCT_PAGE_MAX_SHIFT)
+ self.VMEMMAP_SIZE = ((self._PAGE_END(self.VA_BITS_MIN) - self.PAGE_OFFSET) >> self.VMEMMAP_SHIFT)
+ self.VMEMMAP_START = (-(1 << (self.VA_BITS - self.VMEMMAP_SHIFT))) & 0xffffffffffffffff
+ self.VMEMMAP_END = self.VMEMMAP_START + self.VMEMMAP_SIZE
+
+ self.VMALLOC_START = self.MODULES_END
+ self.VMALLOC_END = self.VMEMMAP_START - 256 * 1024 * 1024
+
+ self.memstart_addr = gdb.parse_and_eval("memstart_addr")
+ self.PHYS_OFFSET = self.memstart_addr
+ self.vmemmap = gdb.Value(self.VMEMMAP_START).cast(utils.get_page_type().pointer()) - (self.memstart_addr >> self.PAGE_SHIFT)
+
+ self.KERNEL_START = gdb.parse_and_eval("_text")
+ self.KERNEL_END = gdb.parse_and_eval("_end")
+
+ if constants.LX_CONFIG_KASAN_GENERIC or constants.LX_CONFIG_KASAN_SW_TAGS:
+ if constants.LX_CONFIG_KASAN_GENERIC:
+ self.KASAN_SHADOW_SCALE_SHIFT = 3
else:
- raise Exception(f'Unknown level {level}')
-
- entry_offset = ((translating_va >> start_bit(level)) & 511) * 8
- entry_va = _page_offset_base() + phys_addr + entry_offset
- return entry_va
-
-class Cr3():
- def __init__(self, cr3, page_levels):
- self.cr3 = cr3
- self.page_levels = page_levels
- self.page_level_write_through = is_bit_defined_tupled(cr3, 3)
- self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4)
- self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask()
-
- def next_entry(self, va):
- next_level = self.page_levels
- return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
-
- def mk_string(self):
- return f"""\
-cr3:
- {'cr3 binary data': <30} {hex(self.cr3)}
- {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
- ---
- {'bit' : <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
- {'bit' : <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
-"""
-
-
-class PageHierarchyEntry():
- def __init__(self, address, level):
- data = int.from_bytes(
- memoryview(gdb.selected_inferior().read_memory(address, 8)),
- "little"
- )
- if level == 1:
- self.is_page = True
- self.entry_present = is_bit_defined_tupled(data, 0)
- self.read_write = is_bit_defined_tupled(data, 1)
- self.user_access_allowed = is_bit_defined_tupled(data, 2)
- self.page_level_write_through = is_bit_defined_tupled(data, 3)
- self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
- self.entry_was_accessed = is_bit_defined_tupled(data, 5)
- self.dirty = is_bit_defined_tupled(data, 6)
- self.pat = is_bit_defined_tupled(data, 7)
- self.global_translation = is_bit_defined_tupled(data, 8)
- self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level)
- self.next_entry_physical_address = None
- self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
- self.protection_key = content_tupled(data, 59, 62)
- self.executed_disable = is_bit_defined_tupled(data, 63)
+ self.KASAN_SHADOW_SCALE_SHIFT = 4
+ self.KASAN_SHADOW_OFFSET = constants.LX_CONFIG_KASAN_SHADOW_OFFSET
+ self.KASAN_SHADOW_END = (1 << (64 - self.KASAN_SHADOW_SCALE_SHIFT)) + self.KASAN_SHADOW_OFFSET
+ self.PAGE_END = self.KASAN_SHADOW_END - (1 << (self.vabits_actual - self.KASAN_SHADOW_SCALE_SHIFT))
+ else:
+ self.PAGE_END = self._PAGE_END(self.VA_BITS_MIN)
+
+ if constants.LX_CONFIG_NUMA and constants.LX_CONFIG_NODES_SHIFT:
+ self.NODE_SHIFT = constants.LX_CONFIG_NODES_SHIFT
+ else:
+ self.NODE_SHIFT = 0
+
+ self.MAX_NUMNODES = 1 << self.NODE_SHIFT
+
+ def SECTION_NR_TO_ROOT(self, sec):
+ return sec // self.SECTIONS_PER_ROOT
+
+ def __nr_to_section(self, nr):
+ root = self.SECTION_NR_TO_ROOT(nr)
+ mem_section = gdb.parse_and_eval("mem_section")
+ return mem_section[root][nr & self.SECTION_ROOT_MASK]
+
+ def pfn_to_section_nr(self, pfn):
+ return pfn >> self.PFN_SECTION_SHIFT
+
+ def section_nr_to_pfn(self, sec):
+ return sec << self.PFN_SECTION_SHIFT
+
+ def __pfn_to_section(self, pfn):
+ return self.__nr_to_section(self.pfn_to_section_nr(pfn))
+
+ def pfn_to_section(self, pfn):
+ return self.__pfn_to_section(pfn)
+
+ def subsection_map_index(self, pfn):
+ return (pfn & ~(self.PAGE_SECTION_MASK)) // self.PAGES_PER_SUBSECTION
+
+ def pfn_section_valid(self, ms, pfn):
+ if constants.LX_CONFIG_SPARSEMEM_VMEMMAP:
+ idx = self.subsection_map_index(pfn)
+ return test_bit(idx, ms['usage']['subsection_map'])
+ else:
+ return True
+
+ def valid_section(self, mem_section):
+ if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_HAS_MEM_MAP):
+ return True
+ return False
+
+ def early_section(self, mem_section):
+ if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_IS_EARLY):
+ return True
+ return False
+
+ def pfn_valid(self, pfn):
+ ms = None
+ if self.PHYS_PFN(self.PFN_PHYS(pfn)) != pfn:
+ return False
+ if self.pfn_to_section_nr(pfn) >= self.NR_MEM_SECTIONS:
+ return False
+ ms = self.__pfn_to_section(pfn)
+
+ if not self.valid_section(ms):
+ return False
+ return self.early_section(ms) or self.pfn_section_valid(ms, pfn)
+
+ def _PAGE_OFFSET(self, va):
+ return (-(1 << (va))) & 0xffffffffffffffff
+
+ def _PAGE_END(self, va):
+ return (-(1 << (va - 1))) & 0xffffffffffffffff
+
+ def kasan_reset_tag(self, addr):
+ if constants.LX_CONFIG_KASAN_SW_TAGS or constants.LX_CONFIG_KASAN_HW_TAGS:
+ return int(addr) | (0xff << 56)
+ else:
+ return addr
+
+ def __is_lm_address(self, addr):
+ if (addr - self.PAGE_OFFSET) < (self.PAGE_END - self.PAGE_OFFSET):
+ return True
+ else:
+ return False
+ def __lm_to_phys(self, addr):
+ return addr - self.PAGE_OFFSET + self.PHYS_OFFSET
+
+ def __kimg_to_phys(self, addr):
+ return addr - self.kimage_voffset
+
+ def __virt_to_phys_nodebug(self, va):
+ untagged_va = self.kasan_reset_tag(va)
+ if self.__is_lm_address(untagged_va):
+ return self.__lm_to_phys(untagged_va)
+ else:
+ return self.__kimg_to_phys(untagged_va)
+
+ def __virt_to_phys(self, va):
+ if constants.LX_CONFIG_DEBUG_VIRTUAL:
+ if not self.__is_lm_address(self.kasan_reset_tag(va)):
+ raise gdb.GdbError("Warning: virt_to_phys used for non-linear address: 0x%lx\n" % va)
+ return self.__virt_to_phys_nodebug(va)
+
+ def virt_to_phys(self, va):
+ return self.__virt_to_phys(va)
+
+ def PFN_PHYS(self, pfn):
+ return pfn << self.PAGE_SHIFT
+
+ def PHYS_PFN(self, phys):
+ return phys >> self.PAGE_SHIFT
+
+ def __phys_to_virt(self, pa):
+ return (pa - self.PHYS_OFFSET) | self.PAGE_OFFSET
+
+ def __phys_to_pfn(self, pa):
+ return self.PHYS_PFN(pa)
+
+ def __pfn_to_phys(self, pfn):
+ return self.PFN_PHYS(pfn)
+
+ def __pa_symbol_nodebug(self, x):
+ return self.__kimg_to_phys(x)
+
+ def __phys_addr_symbol(self, x):
+ if constants.LX_CONFIG_DEBUG_VIRTUAL:
+ if x < self.KERNEL_START or x > self.KERNEL_END:
+ raise gdb.GdbError("0x%x exceed kernel range" % x)
+ return self.__pa_symbol_nodebug(x)
+
+ def __pa_symbol(self, x):
+ return self.__phys_addr_symbol(x)
+
+ def __va(self, pa):
+ return self.__phys_to_virt(pa)
+
+ def pfn_to_kaddr(self, pfn):
+ return self.__va(pfn << self.PAGE_SHIFT)
+
+ def virt_to_pfn(self, va):
+ return self.__phys_to_pfn(self.__virt_to_phys(va))
+
+ def sym_to_pfn(self, x):
+ return self.__phys_to_pfn(self.__pa_symbol(x))
+
+ def page_to_pfn(self, page):
+ return int(page.cast(utils.get_page_type().pointer()) - self.vmemmap.cast(utils.get_page_type().pointer()))
+
+ def page_to_phys(self, page):
+ return self.__pfn_to_phys(self.page_to_pfn(page))
+
+ def pfn_to_page(self, pfn):
+ return (self.vmemmap + pfn).cast(utils.get_page_type().pointer())
+
+ def page_to_virt(self, page):
+ if constants.LX_CONFIG_DEBUG_VIRTUAL:
+ return self.__va(self.page_to_phys(page))
else:
- page_size = is_bit_defined_tupled(data, 7)
- page_size_bit = page_size[1]
- self.is_page = page_size_bit
- self.entry_present = is_bit_defined_tupled(data, 0)
- self.read_write = is_bit_defined_tupled(data, 1)
- self.user_access_allowed = is_bit_defined_tupled(data, 2)
- self.page_level_write_through = is_bit_defined_tupled(data, 3)
- self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
- self.entry_was_accessed = is_bit_defined_tupled(data, 5)
- self.page_size = page_size
- self.dirty = is_bit_defined_tupled(
- data, 6) if page_size_bit else None
- self.global_translation = is_bit_defined_tupled(
- data, 8) if page_size_bit else None
- self.pat = is_bit_defined_tupled(
- data, 12) if page_size_bit else None
- self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) if page_size_bit else None
- self.next_entry_physical_address = None if page_size_bit else data & PHYSICAL_ADDRESS_MASK & page_mask()
- self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
- self.protection_key = content_tupled(data, 59, 62) if page_size_bit else None
- self.executed_disable = is_bit_defined_tupled(data, 63)
- self.address = address
- self.page_entry_binary_data = data
- self.page_hierarchy_level = level
-
- def next_entry(self, va):
- if self.is_page or not self.entry_present[1]:
- return None
-
- next_level = self.page_hierarchy_level - 1
- return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
-
-
- def mk_string(self):
- if not self.entry_present[1]:
- return f"""\
-level {self.page_hierarchy_level}:
- {'entry address': <30} {hex(self.address)}
- {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
- ---
- PAGE ENTRY IS NOT PRESENT!
-"""
- elif self.is_page:
- def page_size_line(ps_bit, ps, level):
- return "" if level == 1 else f"{'bit': <3} {ps_bit: <5} {'page size': <30} {ps}"
-
- return f"""\
-level {self.page_hierarchy_level}:
- {'entry address': <30} {hex(self.address)}
- {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
- {'page size': <30} {'1GB' if self.page_hierarchy_level == 3 else '2MB' if self.page_hierarchy_level == 2 else '4KB' if self.page_hierarchy_level == 1 else 'Unknown page size for level:' + self.page_hierarchy_level}
- {'page physical address': <30} {hex(self.page_physical_address)}
- ---
- {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
- {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
- {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
- {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
- {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
- {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
- {"" if self.page_hierarchy_level == 1 else f"{'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}"}
- {'bit': <4} {self.dirty[0]: <10} {'page dirty': <30} {self.dirty[1]}
- {'bit': <4} {self.global_translation[0]: <10} {'global translation': <30} {self.global_translation[1]}
- {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
- {'bit': <4} {self.pat[0]: <10} {'pat': <30} {self.pat[1]}
- {'bits': <4} {str(self.protection_key[0]): <10} {'protection key': <30} {self.protection_key[1]}
- {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
-"""
+ __idx = int((page.cast(gdb.lookup_type("unsigned long")) - self.VMEMMAP_START).cast(utils.get_ulong_type())) // self.struct_page_size
+ return self.PAGE_OFFSET + (__idx * self.PAGE_SIZE)
+
+ def virt_to_page(self, va):
+ if constants.LX_CONFIG_DEBUG_VIRTUAL:
+ return self.pfn_to_page(self.virt_to_pfn(va))
else:
- return f"""\
-level {self.page_hierarchy_level}:
- {'entry address': <30} {hex(self.address)}
- {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
- {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
- ---
- {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
- {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
- {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
- {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
- {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
- {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
- {'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}
- {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
- {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
-"""
-
-
-class TranslateVM(gdb.Command):
- """Prints the entire paging structure used to translate a given virtual address.
-
-Having an address space of the currently executed process translates the virtual address
-and prints detailed information of all paging structure levels used for the transaltion.
-Currently supported arch: x86"""
+ __idx = int(self.kasan_reset_tag(va) - self.PAGE_OFFSET) // self.PAGE_SIZE
+ addr = self.VMEMMAP_START + (__idx * self.struct_page_size)
+ return gdb.Value(addr).cast(utils.get_page_type().pointer())
+
+ def page_address(self, page):
+ return self.page_to_virt(page)
+
+ def folio_address(self, folio):
+ return self.page_address(folio['page'].address)
+
+class LxPFN2Page(gdb.Command):
+ """PFN to struct page"""
def __init__(self):
- super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER)
+ super(LxPFN2Page, self).__init__("lx-pfn_to_page", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
- if utils.is_target_arch("x86"):
- vm_address = gdb.parse_and_eval(f'{arg}')
- cr3_data = gdb.parse_and_eval('$cr3')
- cr4 = gdb.parse_and_eval('$cr4')
- page_levels = 5 if cr4 & (1 << 12) else 4
- page_entry = Cr3(cr3_data, page_levels)
- while page_entry:
- gdb.write(page_entry.mk_string())
- page_entry = page_entry.next_entry(vm_address)
- else:
- gdb.GdbError("Virtual address translation is not"
- "supported for this arch")
+ argv = gdb.string_to_argv(arg)
+ pfn = int(argv[0])
+ page = page_ops().ops.pfn_to_page(pfn)
+ gdb.write("pfn_to_page(0x%x) = 0x%x\n" % (pfn, page))
+
+LxPFN2Page()
+
+class LxPage2PFN(gdb.Command):
+ """struct page to PFN"""
+
+ def __init__(self):
+ super(LxPage2PFN, self).__init__("lx-page_to_pfn", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ struct_page_addr = int(argv[0], 16)
+ page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
+ pfn = page_ops().ops.page_to_pfn(page)
+ gdb.write("page_to_pfn(0x%x) = 0x%x\n" % (page, pfn))
+
+LxPage2PFN()
+
+class LxPageAddress(gdb.Command):
+ """struct page to linear mapping address"""
+
+ def __init__(self):
+ super(LxPageAddress, self).__init__("lx-page_address", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ struct_page_addr = int(argv[0], 16)
+ page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
+ addr = page_ops().ops.page_address(page)
+ gdb.write("page_address(0x%x) = 0x%x\n" % (page, addr))
+LxPageAddress()
+
+class LxPage2Phys(gdb.Command):
+ """struct page to physical address"""
+
+ def __init__(self):
+ super(LxPage2Phys, self).__init__("lx-page_to_phys", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ struct_page_addr = int(argv[0], 16)
+ page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
+ phys_addr = page_ops().ops.page_to_phys(page)
+ gdb.write("page_to_phys(0x%x) = 0x%x\n" % (page, phys_addr))
+
+LxPage2Phys()
+
+class LxVirt2Phys(gdb.Command):
+ """virtual address to physical address"""
+
+ def __init__(self):
+ super(LxVirt2Phys, self).__init__("lx-virt_to_phys", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ linear_addr = int(argv[0], 16)
+ phys_addr = page_ops().ops.virt_to_phys(linear_addr)
+ gdb.write("virt_to_phys(0x%x) = 0x%x\n" % (linear_addr, phys_addr))
+
+LxVirt2Phys()
+
+class LxVirt2Page(gdb.Command):
+ """virtual address to struct page"""
+
+ def __init__(self):
+ super(LxVirt2Page, self).__init__("lx-virt_to_page", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ linear_addr = int(argv[0], 16)
+ page = page_ops().ops.virt_to_page(linear_addr)
+ gdb.write("virt_to_page(0x%x) = 0x%x\n" % (linear_addr, page))
+
+LxVirt2Page()
+
+class LxSym2PFN(gdb.Command):
+ """symbol address to PFN"""
+
+ def __init__(self):
+ super(LxSym2PFN, self).__init__("lx-sym_to_pfn", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ sym_addr = int(argv[0], 16)
+ pfn = page_ops().ops.sym_to_pfn(sym_addr)
+ gdb.write("sym_to_pfn(0x%x) = %d\n" % (sym_addr, pfn))
+
+LxSym2PFN()
+
+class LxPFN2Kaddr(gdb.Command):
+ """PFN to kernel address"""
+
+ def __init__(self):
+ super(LxPFN2Kaddr, self).__init__("lx-pfn_to_kaddr", gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ pfn = int(argv[0])
+ kaddr = page_ops().ops.pfn_to_kaddr(pfn)
+ gdb.write("pfn_to_kaddr(%d) = 0x%x\n" % (pfn, kaddr))
-TranslateVM()
+LxPFN2Kaddr()
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 261f28640f4c..298dfcc25eae 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -73,11 +73,17 @@ class LxLsmod(gdb.Command):
" " if utils.get_long_type().sizeof == 8 else ""))
for module in module_list():
- layout = module['mem'][constants.LX_MOD_TEXT]
+ text = module['mem'][constants.LX_MOD_TEXT]
+ text_addr = str(text['base']).split()[0]
+ total_size = 0
+
+ for i in range(constants.LX_MOD_TEXT, constants.LX_MOD_RO_AFTER_INIT + 1):
+ total_size += module['mem'][i]['size']
+
gdb.write("{address} {name:<19} {size:>8} {ref}".format(
- address=str(layout['base']).split()[0],
+ address=text_addr,
name=module['name'].string(),
- size=str(layout['size']),
+ size=str(total_size),
ref=str(module['refcnt']['counter'] - 1)))
t = self._module_use_type.get_type().pointer()
@@ -91,5 +97,35 @@ class LxLsmod(gdb.Command):
gdb.write("\n")
-
LxLsmod()
+
+def help():
+ t = """Usage: lx-getmod-by-textaddr [Heximal Address]
+ Example: lx-getmod-by-textaddr 0xffff800002d305ac\n"""
+ gdb.write("Unrecognized command\n")
+ raise gdb.GdbError(t)
+
+class LxFindTextAddrinMod(gdb.Command):
+ '''Look up loaded kernel module by text address.'''
+
+ def __init__(self):
+ super(LxFindTextAddrinMod, self).__init__('lx-getmod-by-textaddr', gdb.COMMAND_SUPPORT)
+
+ def invoke(self, arg, from_tty):
+ args = gdb.string_to_argv(arg)
+
+ if len(args) != 1:
+ help()
+
+ addr = gdb.Value(int(args[0], 16)).cast(utils.get_ulong_type())
+ for mod in module_list():
+ mod_text_start = mod['mem'][constants.LX_MOD_TEXT]['base']
+ mod_text_end = mod_text_start + mod['mem'][constants.LX_MOD_TEXT]['size'].cast(utils.get_ulong_type())
+
+ if addr >= mod_text_start and addr < mod_text_end:
+ s = "0x%x" % addr + " is in " + mod['name'].string() + ".ko\n"
+ gdb.write(s)
+ return
+ gdb.write("0x%x is not in any module text section\n" % addr)
+
+LxFindTextAddrinMod()
diff --git a/scripts/gdb/linux/page_owner.py b/scripts/gdb/linux/page_owner.py
new file mode 100644
index 000000000000..844fd5d0c912
--- /dev/null
+++ b/scripts/gdb/linux/page_owner.py
@@ -0,0 +1,190 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 MediaTek Inc.
+#
+# Authors:
+# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
+#
+
+import gdb
+from linux import utils, stackdepot, constants, mm
+
+if constants.LX_CONFIG_PAGE_OWNER:
+ page_ext_t = utils.CachedType('struct page_ext')
+ page_owner_t = utils.CachedType('struct page_owner')
+
+ PAGE_OWNER_STACK_DEPTH = 16
+ PAGE_EXT_OWNER = constants.LX_PAGE_EXT_OWNER
+ PAGE_EXT_INVALID = 0x1
+ PAGE_EXT_OWNER_ALLOCATED = constants.LX_PAGE_EXT_OWNER_ALLOCATED
+
+def help():
+ t = """Usage: lx-dump-page-owner [Option]
+ Option:
+ --pfn [Decimal pfn]
+ Example:
+ lx-dump-page-owner --pfn 655360\n"""
+ gdb.write("Unrecognized command\n")
+ raise gdb.GdbError(t)
+
+class DumpPageOwner(gdb.Command):
+ """Dump page owner"""
+
+ min_pfn = None
+ max_pfn = None
+ p_ops = None
+ migrate_reason_names = None
+
+ def __init__(self):
+ super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT)
+
+ def invoke(self, args, from_tty):
+ if not constants.LX_CONFIG_PAGE_OWNER:
+ raise gdb.GdbError('CONFIG_PAGE_OWNER does not enable')
+
+ page_owner_inited = gdb.parse_and_eval('page_owner_inited')
+ if page_owner_inited['key']['enabled']['counter'] != 0x1:
+ raise gdb.GdbError('page_owner_inited is not enabled')
+
+ self.p_ops = mm.page_ops().ops
+ self.get_page_owner_info()
+ argv = gdb.string_to_argv(args)
+ if len(argv) == 0:
+ self.read_page_owner()
+ elif len(argv) == 2:
+ if argv[0] == "--pfn":
+ pfn = int(argv[1])
+ self.read_page_owner_by_addr(self.p_ops.pfn_to_page(pfn))
+ else:
+ help()
+ else:
+ help()
+
+ def get_page_owner_info(self):
+ self.min_pfn = int(gdb.parse_and_eval("min_low_pfn"))
+ self.max_pfn = int(gdb.parse_and_eval("max_pfn"))
+ self.page_ext_size = int(gdb.parse_and_eval("page_ext_size"))
+ self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names')
+
+ def page_ext_invalid(self, page_ext):
+ if page_ext == gdb.Value(0):
+ return True
+ if page_ext.cast(utils.get_ulong_type()) & PAGE_EXT_INVALID == PAGE_EXT_INVALID:
+ return True
+ return False
+
+ def get_entry(self, base, index):
+ return (base.cast(utils.get_ulong_type()) + self.page_ext_size * index).cast(page_ext_t.get_type().pointer())
+
+ def lookup_page_ext(self, page):
+ pfn = self.p_ops.page_to_pfn(page)
+ section = self.p_ops.pfn_to_section(pfn)
+ page_ext = section["page_ext"]
+ if self.page_ext_invalid(page_ext):
+ return gdb.Value(0)
+ return self.get_entry(page_ext, pfn)
+
+ def page_ext_get(self, page):
+ page_ext = self.lookup_page_ext(page)
+ if page_ext != gdb.Value(0):
+ return page_ext
+ else:
+ return gdb.Value(0)
+
+ def get_page_owner(self, page_ext):
+ addr = page_ext.cast(utils.get_ulong_type()) + gdb.parse_and_eval("page_owner_ops")["offset"].cast(utils.get_ulong_type())
+ return addr.cast(page_owner_t.get_type().pointer())
+
+ def read_page_owner_by_addr(self, struct_page_addr):
+ page = gdb.Value(struct_page_addr).cast(utils.get_page_type().pointer())
+ pfn = self.p_ops.page_to_pfn(page)
+
+ if pfn < self.min_pfn or pfn > self.max_pfn or (not self.p_ops.pfn_valid(pfn)):
+ gdb.write("pfn is invalid\n")
+ return
+
+ page = self.p_ops.pfn_to_page(pfn)
+ page_ext = self.page_ext_get(page)
+
+ if page_ext == gdb.Value(0):
+ gdb.write("page_ext is null\n")
+ return
+
+ if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER)):
+ gdb.write("page_owner flag is invalid\n")
+ raise gdb.GdbError('page_owner info is not present (never set?)\n')
+
+ if mm.test_bit(PAGE_EXT_OWNER_ALLOCATED, page_ext['flags'].address):
+ gdb.write('page_owner tracks the page as allocated\n')
+ else:
+ gdb.write('page_owner tracks the page as freed\n')
+
+ if not (page_ext['flags'] & (1 << PAGE_EXT_OWNER_ALLOCATED)):
+ gdb.write("page_owner is not allocated\n")
+
+ try:
+ page_owner = self.get_page_owner(page_ext)
+ 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" %\
+ (page_owner["order"], page_owner["gfp_mask"],\
+ page_owner["pid"], page_owner["tgid"], page_owner["comm"],\
+ page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
+ gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
+ if page_owner["handle"] == 0:
+ gdb.write('page_owner allocation stack trace missing\n')
+ else:
+ stackdepot.stack_depot_print(page_owner["handle"])
+
+ if page_owner["free_handle"] == 0:
+ gdb.write('page_owner free stack trace missing\n')
+ else:
+ gdb.write('page last free stack trace:\n')
+ stackdepot.stack_depot_print(page_owner["free_handle"])
+ if page_owner['last_migrate_reason'] != -1:
+ gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])
+ except:
+ gdb.write("\n")
+
+ def read_page_owner(self):
+ pfn = self.min_pfn
+
+ # Find a valid PFN or the start of a MAX_ORDER_NR_PAGES area
+ while ((not self.p_ops.pfn_valid(pfn)) and (pfn & (self.p_ops.MAX_ORDER_NR_PAGES - 1))) != 0:
+ pfn += 1
+
+ while pfn < self.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 & (self.p_ops.MAX_ORDER_NR_PAGES - 1)) == 0) and (not self.p_ops.pfn_valid(pfn)):
+ pfn += (self.p_ops.MAX_ORDER_NR_PAGES - 1)
+ continue;
+
+ page = self.p_ops.pfn_to_page(pfn)
+ page_ext = self.page_ext_get(page)
+ if page_ext == gdb.Value(0):
+ 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
+
+ try:
+ page_owner = self.get_page_owner(page_ext)
+ gdb.write("Page allocated via order %d, gfp_mask: 0x%x, pid: %d, tgid: %d (%s), ts %u ns, free_ts %u ns\n" %\
+ (page_owner["order"], page_owner["gfp_mask"],\
+ page_owner["pid"], page_owner["tgid"], page_owner["comm"],\
+ page_owner["ts_nsec"], page_owner["free_ts_nsec"]))
+ gdb.write("PFN: %d, Flags: 0x%x\n" % (pfn, page['flags']))
+ stackdepot.stack_depot_print(page_owner["handle"])
+ pfn += (1 << page_owner["order"])
+ continue
+ except:
+ gdb.write("\n")
+ pfn += 1
+
+DumpPageOwner()
diff --git a/scripts/gdb/linux/pgtable.py b/scripts/gdb/linux/pgtable.py
new file mode 100644
index 000000000000..30d837f3dfae
--- /dev/null
+++ b/scripts/gdb/linux/pgtable.py
@@ -0,0 +1,222 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# routines to introspect page table
+#
+# Authors:
+# Dmitrii Bundin <dmitrii.bundin.a@gmail.com>
+#
+
+import gdb
+
+from linux import utils
+
+PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff')
+
+
+def page_mask(level=1):
+ # 4KB
+ if level == 1:
+ return gdb.parse_and_eval('(u64) ~0xfff')
+ # 2MB
+ elif level == 2:
+ return gdb.parse_and_eval('(u64) ~0x1fffff')
+ # 1GB
+ elif level == 3:
+ return gdb.parse_and_eval('(u64) ~0x3fffffff')
+ else:
+ raise Exception(f'Unknown page level: {level}')
+
+
+#page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled
+POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000'
+def _page_offset_base():
+ pob_symbol = gdb.lookup_global_symbol('page_offset_base')
+ pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT
+ return gdb.parse_and_eval(pob)
+
+
+def is_bit_defined_tupled(data, offset):
+ return offset, bool(data >> offset & 1)
+
+def content_tupled(data, bit_start, bit_end):
+ return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1)
+
+def entry_va(level, phys_addr, translating_va):
+ def start_bit(level):
+ if level == 5:
+ return 48
+ elif level == 4:
+ return 39
+ elif level == 3:
+ return 30
+ elif level == 2:
+ return 21
+ elif level == 1:
+ return 12
+ else:
+ raise Exception(f'Unknown level {level}')
+
+ entry_offset = ((translating_va >> start_bit(level)) & 511) * 8
+ entry_va = _page_offset_base() + phys_addr + entry_offset
+ return entry_va
+
+class Cr3():
+ def __init__(self, cr3, page_levels):
+ self.cr3 = cr3
+ self.page_levels = page_levels
+ self.page_level_write_through = is_bit_defined_tupled(cr3, 3)
+ self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4)
+ self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask()
+
+ def next_entry(self, va):
+ next_level = self.page_levels
+ return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
+
+ def mk_string(self):
+ return f"""\
+cr3:
+ {'cr3 binary data': <30} {hex(self.cr3)}
+ {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
+ ---
+ {'bit' : <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
+ {'bit' : <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
+"""
+
+
+class PageHierarchyEntry():
+ def __init__(self, address, level):
+ data = int.from_bytes(
+ memoryview(gdb.selected_inferior().read_memory(address, 8)),
+ "little"
+ )
+ if level == 1:
+ self.is_page = True
+ self.entry_present = is_bit_defined_tupled(data, 0)
+ self.read_write = is_bit_defined_tupled(data, 1)
+ self.user_access_allowed = is_bit_defined_tupled(data, 2)
+ self.page_level_write_through = is_bit_defined_tupled(data, 3)
+ self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
+ self.entry_was_accessed = is_bit_defined_tupled(data, 5)
+ self.dirty = is_bit_defined_tupled(data, 6)
+ self.pat = is_bit_defined_tupled(data, 7)
+ self.global_translation = is_bit_defined_tupled(data, 8)
+ self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level)
+ self.next_entry_physical_address = None
+ self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
+ self.protection_key = content_tupled(data, 59, 62)
+ self.executed_disable = is_bit_defined_tupled(data, 63)
+ else:
+ page_size = is_bit_defined_tupled(data, 7)
+ page_size_bit = page_size[1]
+ self.is_page = page_size_bit
+ self.entry_present = is_bit_defined_tupled(data, 0)
+ self.read_write = is_bit_defined_tupled(data, 1)
+ self.user_access_allowed = is_bit_defined_tupled(data, 2)
+ self.page_level_write_through = is_bit_defined_tupled(data, 3)
+ self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
+ self.entry_was_accessed = is_bit_defined_tupled(data, 5)
+ self.page_size = page_size
+ self.dirty = is_bit_defined_tupled(
+ data, 6) if page_size_bit else None
+ self.global_translation = is_bit_defined_tupled(
+ data, 8) if page_size_bit else None
+ self.pat = is_bit_defined_tupled(
+ data, 12) if page_size_bit else None
+ self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) if page_size_bit else None
+ self.next_entry_physical_address = None if page_size_bit else data & PHYSICAL_ADDRESS_MASK & page_mask()
+ self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
+ self.protection_key = content_tupled(data, 59, 62) if page_size_bit else None
+ self.executed_disable = is_bit_defined_tupled(data, 63)
+ self.address = address
+ self.page_entry_binary_data = data
+ self.page_hierarchy_level = level
+
+ def next_entry(self, va):
+ if self.is_page or not self.entry_present[1]:
+ return None
+
+ next_level = self.page_hierarchy_level - 1
+ return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
+
+
+ def mk_string(self):
+ if not self.entry_present[1]:
+ return f"""\
+level {self.page_hierarchy_level}:
+ {'entry address': <30} {hex(self.address)}
+ {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
+ ---
+ PAGE ENTRY IS NOT PRESENT!
+"""
+ elif self.is_page:
+ def page_size_line(ps_bit, ps, level):
+ return "" if level == 1 else f"{'bit': <3} {ps_bit: <5} {'page size': <30} {ps}"
+
+ return f"""\
+level {self.page_hierarchy_level}:
+ {'entry address': <30} {hex(self.address)}
+ {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
+ {'page size': <30} {'1GB' if self.page_hierarchy_level == 3 else '2MB' if self.page_hierarchy_level == 2 else '4KB' if self.page_hierarchy_level == 1 else 'Unknown page size for level:' + self.page_hierarchy_level}
+ {'page physical address': <30} {hex(self.page_physical_address)}
+ ---
+ {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
+ {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
+ {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
+ {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
+ {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
+ {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
+ {"" if self.page_hierarchy_level == 1 else f"{'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}"}
+ {'bit': <4} {self.dirty[0]: <10} {'page dirty': <30} {self.dirty[1]}
+ {'bit': <4} {self.global_translation[0]: <10} {'global translation': <30} {self.global_translation[1]}
+ {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
+ {'bit': <4} {self.pat[0]: <10} {'pat': <30} {self.pat[1]}
+ {'bits': <4} {str(self.protection_key[0]): <10} {'protection key': <30} {self.protection_key[1]}
+ {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
+"""
+ else:
+ return f"""\
+level {self.page_hierarchy_level}:
+ {'entry address': <30} {hex(self.address)}
+ {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
+ {'next entry physical address': <30} {hex(self.next_entry_physical_address)}
+ ---
+ {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
+ {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
+ {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
+ {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
+ {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
+ {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
+ {'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}
+ {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
+ {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
+"""
+
+
+class TranslateVM(gdb.Command):
+ """Prints the entire paging structure used to translate a given virtual address.
+
+Having an address space of the currently executed process translates the virtual address
+and prints detailed information of all paging structure levels used for the transaltion.
+Currently supported arch: x86"""
+
+ def __init__(self):
+ super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER)
+
+ def invoke(self, arg, from_tty):
+ if utils.is_target_arch("x86"):
+ vm_address = gdb.parse_and_eval(f'{arg}')
+ cr3_data = gdb.parse_and_eval('$cr3')
+ cr4 = gdb.parse_and_eval('$cr4')
+ page_levels = 5 if cr4 & (1 << 12) else 4
+ page_entry = Cr3(cr3_data, page_levels)
+ while page_entry:
+ gdb.write(page_entry.mk_string())
+ page_entry = page_entry.next_entry(vm_address)
+ else:
+ gdb.GdbError("Virtual address translation is not"
+ "supported for this arch")
+
+
+TranslateVM()
diff --git a/scripts/gdb/linux/slab.py b/scripts/gdb/linux/slab.py
new file mode 100644
index 000000000000..f012ba38c7d9
--- /dev/null
+++ b/scripts/gdb/linux/slab.py
@@ -0,0 +1,326 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 MediaTek Inc.
+#
+# Authors:
+# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
+#
+
+import gdb
+import re
+import traceback
+from linux import lists, utils, stackdepot, constants, mm
+
+SLAB_RED_ZONE = constants.LX_SLAB_RED_ZONE
+SLAB_POISON = constants.LX_SLAB_POISON
+SLAB_KMALLOC = constants.LX_SLAB_KMALLOC
+SLAB_HWCACHE_ALIGN = constants.LX_SLAB_HWCACHE_ALIGN
+SLAB_CACHE_DMA = constants.LX_SLAB_CACHE_DMA
+SLAB_CACHE_DMA32 = constants.LX_SLAB_CACHE_DMA32
+SLAB_STORE_USER = constants.LX_SLAB_STORE_USER
+SLAB_PANIC = constants.LX_SLAB_PANIC
+
+OO_SHIFT = 16
+OO_MASK = (1 << OO_SHIFT) - 1
+
+if constants.LX_CONFIG_SLUB_DEBUG:
+ slab_type = utils.CachedType("struct slab")
+ slab_ptr_type = slab_type.get_type().pointer()
+ kmem_cache_type = utils.CachedType("struct kmem_cache")
+ kmem_cache_ptr_type = kmem_cache_type.get_type().pointer()
+ freeptr_t = utils.CachedType("freeptr_t")
+ freeptr_t_ptr = freeptr_t.get_type().pointer()
+
+ track_type = gdb.lookup_type('struct track')
+ track_alloc = int(gdb.parse_and_eval('TRACK_ALLOC'))
+ track_free = int(gdb.parse_and_eval('TRACK_FREE'))
+
+def slab_folio(slab):
+ return slab.cast(gdb.lookup_type("struct folio").pointer())
+
+def slab_address(slab):
+ p_ops = mm.page_ops().ops
+ folio = slab_folio(slab)
+ return p_ops.folio_address(folio)
+
+def for_each_object(cache, addr, slab_objects):
+ p = addr
+ if cache['flags'] & SLAB_RED_ZONE:
+ p += int(cache['red_left_pad'])
+ while p < addr + (slab_objects * cache['size']):
+ yield p
+ p = p + int(cache['size'])
+
+def get_info_end(cache):
+ if (cache['offset'] >= cache['inuse']):
+ return cache['inuse'] + gdb.lookup_type("void").pointer().sizeof
+ else:
+ return cache['inuse']
+
+def get_orig_size(cache, obj):
+ if cache['flags'] & SLAB_STORE_USER and cache['flags'] & SLAB_KMALLOC:
+ p = mm.page_ops().ops.kasan_reset_tag(obj)
+ p += get_info_end(cache)
+ p += gdb.lookup_type('struct track').sizeof * 2
+ p = p.cast(utils.get_uint_type().pointer())
+ return p.dereference()
+ else:
+ return cache['object_size']
+
+def get_track(cache, object_pointer, alloc):
+ p = object_pointer + get_info_end(cache)
+ p += (alloc * track_type.sizeof)
+ return p
+
+def oo_objects(x):
+ return int(x['x']) & OO_MASK
+
+def oo_order(x):
+ return int(x['x']) >> OO_SHIFT
+
+def reciprocal_divide(a, R):
+ t = (a * int(R['m'])) >> 32
+ return (t + ((a - t) >> int(R['sh1']))) >> int(R['sh2'])
+
+def __obj_to_index(cache, addr, obj):
+ return reciprocal_divide(int(mm.page_ops().ops.kasan_reset_tag(obj)) - addr, cache['reciprocal_size'])
+
+def swab64(x):
+ result = (((x & 0x00000000000000ff) << 56) | \
+ ((x & 0x000000000000ff00) << 40) | \
+ ((x & 0x0000000000ff0000) << 24) | \
+ ((x & 0x00000000ff000000) << 8) | \
+ ((x & 0x000000ff00000000) >> 8) | \
+ ((x & 0x0000ff0000000000) >> 24) | \
+ ((x & 0x00ff000000000000) >> 40) | \
+ ((x & 0xff00000000000000) >> 56))
+ return result
+
+def freelist_ptr_decode(cache, ptr, ptr_addr):
+ if constants.LX_CONFIG_SLAB_FREELIST_HARDENED:
+ return ptr['v'] ^ cache['random'] ^ swab64(int(ptr_addr))
+ else:
+ return ptr['v']
+
+def get_freepointer(cache, obj):
+ obj = mm.page_ops().ops.kasan_reset_tag(obj)
+ ptr_addr = obj + cache['offset']
+ p = ptr_addr.cast(freeptr_t_ptr).dereference()
+ return freelist_ptr_decode(cache, p, ptr_addr)
+
+def loc_exist(loc_track, addr, handle, waste):
+ for loc in loc_track:
+ if loc['addr'] == addr and loc['handle'] == handle and loc['waste'] == waste:
+ return loc
+ return None
+
+def add_location(loc_track, cache, track, orig_size):
+ jiffies = gdb.parse_and_eval("jiffies_64")
+ age = jiffies - track['when']
+ handle = 0
+ waste = cache['object_size'] - int(orig_size)
+ pid = int(track['pid'])
+ cpuid = int(track['cpu'])
+ addr = track['addr']
+ if constants.LX_CONFIG_STACKDEPOT:
+ handle = track['handle']
+
+ loc = loc_exist(loc_track, addr, handle, waste)
+ if loc:
+ loc['count'] += 1
+ if track['when']:
+ loc['sum_time'] += age
+ loc['min_time'] = min(loc['min_time'], age)
+ loc['max_time'] = max(loc['max_time'], age)
+ loc['min_pid'] = min(loc['min_pid'], pid)
+ loc['max_pid'] = max(loc['max_pid'], pid)
+ loc['cpus'].add(cpuid)
+ else:
+ loc_track.append({
+ 'count' : 1,
+ 'addr' : addr,
+ 'sum_time' : age,
+ 'min_time' : age,
+ 'max_time' : age,
+ 'min_pid' : pid,
+ 'max_pid' : pid,
+ 'handle' : handle,
+ 'waste' : waste,
+ 'cpus' : {cpuid}
+ }
+ )
+
+def slabtrace(alloc, cache_name):
+
+ def __fill_map(obj_map, cache, slab):
+ p = slab['freelist']
+ addr = slab_address(slab)
+ while p != gdb.Value(0):
+ index = __obj_to_index(cache, addr, p)
+ obj_map[index] = True # free objects
+ p = get_freepointer(cache, p)
+
+ # process every slab page on the slab_list (partial and full list)
+ def process_slab(loc_track, slab_list, alloc, cache):
+ for slab in lists.list_for_each_entry(slab_list, slab_ptr_type, "slab_list"):
+ obj_map[:] = [False] * oo_objects(cache['oo'])
+ __fill_map(obj_map, cache, slab)
+ addr = slab_address(slab)
+ for object_pointer in for_each_object(cache, addr, slab['objects']):
+ if obj_map[__obj_to_index(cache, addr, object_pointer)] == True:
+ continue
+ p = get_track(cache, object_pointer, alloc)
+ track = gdb.Value(p).cast(track_type.pointer())
+ if alloc == track_alloc:
+ size = get_orig_size(cache, object_pointer)
+ else:
+ size = cache['object_size']
+ add_location(loc_track, cache, track, size)
+ continue
+
+ slab_caches = gdb.parse_and_eval("slab_caches")
+ if mm.page_ops().ops.MAX_NUMNODES > 1:
+ nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
+ else:
+ nr_node_ids = 1
+
+ target_cache = None
+ loc_track = []
+
+ for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
+ if cache['name'].string() == cache_name:
+ target_cache = cache
+ break
+
+ obj_map = [False] * oo_objects(target_cache['oo'])
+
+ if target_cache['flags'] & SLAB_STORE_USER:
+ for i in range(0, nr_node_ids):
+ cache_node = target_cache['node'][i]
+ if cache_node['nr_slabs']['counter'] == 0:
+ continue
+ process_slab(loc_track, cache_node['partial'], alloc, target_cache)
+ process_slab(loc_track, cache_node['full'], alloc, target_cache)
+ else:
+ raise gdb.GdbError("SLAB_STORE_USER is not set in %s" % target_cache['name'].string())
+
+ for loc in sorted(loc_track, key=lambda x:x['count'], reverse=True):
+ if loc['addr']:
+ addr = loc['addr'].cast(utils.get_ulong_type().pointer())
+ gdb.write("%d %s" % (loc['count'], str(addr).split(' ')[-1]))
+ else:
+ gdb.write("%d <not-available>" % loc['count'])
+
+ if loc['waste']:
+ gdb.write(" waste=%d/%d" % (loc['count'] * loc['waste'], loc['waste']))
+
+ if loc['sum_time'] != loc['min_time']:
+ gdb.write(" age=%d/%d/%d" % (loc['min_time'], loc['sum_time']/loc['count'], loc['max_time']))
+ else:
+ gdb.write(" age=%d" % loc['min_time'])
+
+ if loc['min_pid'] != loc['max_pid']:
+ gdb.write(" pid=%d-%d" % (loc['min_pid'], loc['max_pid']))
+ else:
+ gdb.write(" pid=%d" % loc['min_pid'])
+
+ if constants.LX_NR_CPUS > 1:
+ nr_cpu = gdb.parse_and_eval('__num_online_cpus')['counter']
+ if nr_cpu > 1:
+ gdb.write(" cpus=")
+ for i in loc['cpus']:
+ gdb.write("%d," % i)
+ gdb.write("\n")
+ if constants.LX_CONFIG_STACKDEPOT:
+ if loc['handle']:
+ stackdepot.stack_depot_print(loc['handle'])
+ gdb.write("\n")
+
+def help():
+ t = """Usage: lx-slabtrace --cache_name [cache_name] [Options]
+ Options:
+ --alloc
+ print information of allocation trace of the allocated objects
+ --free
+ print information of freeing trace of the allocated objects
+ Example:
+ lx-slabtrace --cache_name kmalloc-1k --alloc
+ lx-slabtrace --cache_name kmalloc-1k --free\n"""
+ gdb.write("Unrecognized command\n")
+ raise gdb.GdbError(t)
+
+class LxSlabTrace(gdb.Command):
+ """Show specific cache slabtrace"""
+
+ def __init__(self):
+ super(LxSlabTrace, self).__init__("lx-slabtrace", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ if not constants.LX_CONFIG_SLUB_DEBUG:
+ raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
+
+ argv = gdb.string_to_argv(arg)
+ alloc = track_alloc # default show alloc_traces
+
+ if len(argv) == 3:
+ if argv[2] == '--alloc':
+ alloc = track_alloc
+ elif argv[2] == '--free':
+ alloc = track_free
+ else:
+ help()
+ if len(argv) >= 2 and argv[0] == '--cache_name':
+ slabtrace(alloc, argv[1])
+ else:
+ help()
+LxSlabTrace()
+
+def slabinfo():
+ nr_node_ids = None
+
+ if not constants.LX_CONFIG_SLUB_DEBUG:
+ raise gdb.GdbError("CONFIG_SLUB_DEBUG is not enabled")
+
+ def count_free(slab):
+ total_free = 0
+ for slab in lists.list_for_each_entry(slab, slab_ptr_type, 'slab_list'):
+ total_free += int(slab['objects'] - slab['inuse'])
+ return total_free
+
+ gdb.write("{:^18} | {:^20} | {:^12} | {:^12} | {:^8} | {:^11} | {:^13}\n".format('Pointer', 'name', 'active_objs', 'num_objs', 'objsize', 'objperslab', 'pagesperslab'))
+ gdb.write("{:-^18} | {:-^20} | {:-^12} | {:-^12} | {:-^8} | {:-^11} | {:-^13}\n".format('', '', '', '', '', '', ''))
+
+ slab_caches = gdb.parse_and_eval("slab_caches")
+ if mm.page_ops().ops.MAX_NUMNODES > 1:
+ nr_node_ids = int(gdb.parse_and_eval("nr_node_ids"))
+ else:
+ nr_node_ids = 1
+
+ for cache in lists.list_for_each_entry(slab_caches, kmem_cache_ptr_type, 'list'):
+ nr_objs = 0
+ nr_free = 0
+ nr_slabs = 0
+ for i in range(0, nr_node_ids):
+ cache_node = cache['node'][i]
+ try:
+ nr_slabs += cache_node['nr_slabs']['counter']
+ nr_objs = int(cache_node['total_objects']['counter'])
+ nr_free = count_free(cache_node['partial'])
+ except:
+ raise gdb.GdbError(traceback.format_exc())
+ active_objs = nr_objs - nr_free
+ num_objs = nr_objs
+ active_slabs = nr_slabs
+ objects_per_slab = oo_objects(cache['oo'])
+ cache_order = oo_order(cache['oo'])
+ gdb.write("{:18s} | {:20.19s} | {:12} | {:12} | {:8} | {:11} | {:13}\n".format(hex(cache), cache['name'].string(), str(active_objs), str(num_objs), str(cache['size']), str(objects_per_slab), str(1 << cache_order)))
+
+class LxSlabInfo(gdb.Command):
+ """Show slabinfo"""
+
+ def __init__(self):
+ super(LxSlabInfo, self).__init__("lx-slabinfo", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ slabinfo()
+LxSlabInfo()
diff --git a/scripts/gdb/linux/stackdepot.py b/scripts/gdb/linux/stackdepot.py
new file mode 100644
index 000000000000..047d329a6a12
--- /dev/null
+++ b/scripts/gdb/linux/stackdepot.py
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 MediaTek Inc.
+#
+# Authors:
+# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
+#
+
+import gdb
+from linux import utils, constants
+
+if constants.LX_CONFIG_STACKDEPOT:
+ stack_record_type = utils.CachedType('struct stack_record')
+ DEPOT_STACK_ALIGN = 4
+
+def stack_depot_fetch(handle):
+ global DEPOT_STACK_ALIGN
+ global stack_record_type
+
+ stack_depot_disabled = gdb.parse_and_eval('stack_depot_disabled')
+
+ if stack_depot_disabled:
+ raise gdb.GdbError("stack_depot_disabled\n")
+
+ handle_parts_t = gdb.lookup_type("union handle_parts")
+ parts = handle.cast(handle_parts_t)
+ offset = parts['offset'] << DEPOT_STACK_ALIGN
+ pool_index_cached = gdb.parse_and_eval('pool_index')
+
+ if parts['pool_index'] > pool_index_cached:
+ gdb.write("pool index %d out of bounds (%d) for stack id 0x%08x\n" % (parts['pool_index'], pool_index_cached, handle))
+ return gdb.Value(0), 0
+
+ stack_pools = gdb.parse_and_eval('stack_pools')
+
+ try:
+ pool = stack_pools[parts['pool_index']]
+ stack = (pool + gdb.Value(offset).cast(utils.get_size_t_type())).cast(stack_record_type.get_type().pointer())
+ size = int(stack['size'].cast(utils.get_ulong_type()))
+ return stack['entries'], size
+ except Exception as e:
+ gdb.write("%s\n" % e)
+ return gdb.Value(0), 0
+
+def stack_depot_print(handle):
+ if not constants.LX_CONFIG_STACKDEPOT:
+ raise gdb.GdbError("CONFIG_STACKDEPOT is not enabled")
+
+ entries, nr_entries = stack_depot_fetch(handle)
+ if nr_entries > 0:
+ for i in range(0, nr_entries):
+ try:
+ gdb.execute("x /i 0x%x" % (int(entries[i])))
+ except Exception as e:
+ gdb.write("%s\n" % e)
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index fdad3f32c747..5179edd1b627 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -89,29 +89,34 @@ lx-symbols command."""
return name
return None
- def _section_arguments(self, module):
+ def _section_arguments(self, module, module_addr):
try:
sect_attrs = module['sect_attrs'].dereference()
except gdb.error:
- return ""
+ return str(module_addr)
+
attrs = sect_attrs['attrs']
section_name_to_address = {
attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
for n in range(int(sect_attrs['nsections']))}
+
+ textaddr = section_name_to_address.get(".text", module_addr)
args = []
for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
- ".text", ".text.hot", ".text.unlikely"]:
+ ".text.hot", ".text.unlikely"]:
address = section_name_to_address.get(section_name)
if address:
args.append(" -s {name} {addr}".format(
name=section_name, addr=str(address)))
- return "".join(args)
+ return "{textaddr} {sections}".format(
+ textaddr=textaddr, sections="".join(args))
- def load_module_symbols(self, module):
+ def load_module_symbols(self, module, module_file=None):
module_name = module['name'].string()
module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0]
- module_file = self._get_module_file(module_name)
+ if not module_file:
+ module_file = self._get_module_file(module_name)
if not module_file and not self.module_files_updated:
self._update_module_files()
module_file = self._get_module_file(module_name)
@@ -125,16 +130,28 @@ lx-symbols command."""
module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
gdb.write("loading @{addr}: {filename}\n".format(
addr=module_addr, filename=module_file))
- cmdline = "add-symbol-file {filename} {addr}{sections}".format(
+ cmdline = "add-symbol-file {filename} {sections}".format(
filename=module_file,
- addr=module_addr,
- sections=self._section_arguments(module))
+ sections=self._section_arguments(module, module_addr))
gdb.execute(cmdline, to_string=True)
if module_name not in self.loaded_modules:
self.loaded_modules.append(module_name)
else:
gdb.write("no module object found for '{0}'\n".format(module_name))
+ def load_ko_symbols(self, mod_path):
+ self.loaded_modules = []
+ module_list = modules.module_list()
+
+ for module in module_list:
+ module_name = module['name'].string()
+ module_pattern = ".*/{0}\.ko(?:.debug)?$".format(
+ module_name.replace("_", r"[_\-]"))
+ if re.match(module_pattern, mod_path) and os.path.exists(mod_path):
+ self.load_module_symbols(module, mod_path)
+ return
+ raise gdb.GdbError("%s is not a valid .ko\n" % mod_path)
+
def load_all_symbols(self):
gdb.write("loading vmlinux\n")
@@ -173,6 +190,11 @@ lx-symbols command."""
self.module_files = []
self.module_files_updated = False
+ argv = gdb.string_to_argv(arg)
+ if len(argv) == 1:
+ self.load_ko_symbols(argv[0])
+ return
+
self.load_all_symbols()
if hasattr(gdb, 'Breakpoint'):
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 9f44df13761e..7d5278d815fa 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -35,12 +35,32 @@ class CachedType:
long_type = CachedType("long")
+ulong_type = CachedType("unsigned long")
+uint_type = CachedType("unsigned int")
atomic_long_type = CachedType("atomic_long_t")
+size_t_type = CachedType("size_t")
+struct_page_type = CachedType("struct page")
+
+def get_uint_type():
+ global uint_type
+ return uint_type.get_type()
+
+def get_page_type():
+ global struct_page_type
+ return struct_page_type.get_type()
def get_long_type():
global long_type
return long_type.get_type()
+def get_ulong_type():
+ global ulong_type
+ return ulong_type.get_type()
+
+def get_size_t_type():
+ global size_t_type
+ return size_t_type.get_type()
+
def offset_of(typeobj, field):
element = gdb.Value(0).cast(typeobj)
return int(str(element[field].address).split()[0], 16)
diff --git a/scripts/gdb/linux/vmalloc.py b/scripts/gdb/linux/vmalloc.py
new file mode 100644
index 000000000000..48e4a4fae7bb
--- /dev/null
+++ b/scripts/gdb/linux/vmalloc.py
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2023 MediaTek Inc.
+#
+# Authors:
+# Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
+#
+
+import gdb
+import re
+from linux import lists, utils, stackdepot, constants, mm
+
+vmap_area_type = utils.CachedType('struct vmap_area')
+vmap_area_ptr_type = vmap_area_type.get_type().pointer()
+
+def is_vmalloc_addr(x):
+ pg_ops = mm.page_ops().ops
+ addr = pg_ops.kasan_reset_tag(x)
+ return addr >= pg_ops.VMALLOC_START and addr < pg_ops.VMALLOC_END
+
+class LxVmallocInfo(gdb.Command):
+ """Show vmallocinfo"""
+
+ def __init__(self):
+ super(LxVmallocInfo, self).__init__("lx-vmallocinfo", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ vmap_area_list = gdb.parse_and_eval('vmap_area_list')
+ for vmap_area in lists.list_for_each_entry(vmap_area_list, vmap_area_ptr_type, "list"):
+ if not vmap_area['vm']:
+ gdb.write("0x%x-0x%x %10d vm_map_ram\n" % (vmap_area['va_start'], vmap_area['va_end'],
+ vmap_area['va_end'] - vmap_area['va_start']))
+ continue
+ v = vmap_area['vm']
+ gdb.write("0x%x-0x%x %10d" % (v['addr'], v['addr'] + v['size'], v['size']))
+ if v['caller']:
+ gdb.write(" %s" % str(v['caller']).split(' ')[-1])
+ if v['nr_pages']:
+ gdb.write(" pages=%d" % v['nr_pages'])
+ if v['phys_addr']:
+ gdb.write(" phys=0x%x" % v['phys_addr'])
+ if v['flags'] & constants.LX_VM_IOREMAP:
+ gdb.write(" ioremap")
+ if v['flags'] & constants.LX_VM_ALLOC:
+ gdb.write(" vmalloc")
+ if v['flags'] & constants.LX_VM_MAP:
+ gdb.write(" vmap")
+ if v['flags'] & constants.LX_VM_USERMAP:
+ gdb.write(" user")
+ if v['flags'] & constants.LX_VM_DMA_COHERENT:
+ gdb.write(" dma-coherent")
+ if is_vmalloc_addr(v['pages']):
+ gdb.write(" vpages")
+ gdb.write("\n")
+
+LxVmallocInfo()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 2d32308c3f7a..fc53cdf286f1 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -41,6 +41,11 @@ else:
import linux.genpd
import linux.device
import linux.vfs
- import linux.mm
+ import linux.pgtable
import linux.radixtree
import linux.interrupts
+ import linux.mm
+ import linux.stackdepot
+ import linux.page_owner
+ import linux.slab
+ import linux.vmalloc
diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh
index 36b56b746fce..afdddc82f02b 100755
--- a/scripts/headers_install.sh
+++ b/scripts/headers_install.sh
@@ -76,7 +76,6 @@ arch/arc/include/uapi/asm/swab.h:CONFIG_ARC_HAS_SWAPE
arch/arm/include/uapi/asm/ptrace.h:CONFIG_CPU_ENDIAN_BE8
arch/hexagon/include/uapi/asm/ptrace.h:CONFIG_HEXAGON_ARCH_VERSION
arch/hexagon/include/uapi/asm/user.h:CONFIG_HEXAGON_ARCH_VERSION
-arch/ia64/include/uapi/asm/cmpxchg.h:CONFIG_IA64_DEBUG_CMPXCHG
arch/m68k/include/uapi/asm/ptrace.h:CONFIG_COLDFIRE
arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_NO
arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_SUPPORT