1# 2# Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org> 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6 7import gdb 8from freebsd import * 9 10class pcpu(gdb.Function): 11 """ 12 Register a function to lookup PCPU and DPCPU variables by name. 13 14 To look up the value of the PCPU field foo on CPU n, use 15 $PCPU("foo", n). This works for DPCPU fields too. If the CPU ID is 16 omitted, and the currently selected thread is on-CPU, that CPU is 17 used, otherwise an error is raised. 18 """ 19 def __init__(self): 20 super(pcpu, self).__init__("PCPU") 21 22 def invoke(self, field, cpuid=-1): 23 if cpuid == -1: 24 cpuid = tdfind(gdb.selected_thread().ptid[2])['td_oncpu'] 25 if cpuid == -1: 26 raise gdb.error("Currently selected thread is off-CPU") 27 if cpuid < 0 or cpuid > symval("mp_maxid"): 28 raise gdb.error(f"Currently selected on invalid CPU {cpuid}") 29 pcpu = symval("cpuid_to_pcpu")[cpuid] 30 31 # Are we dealing with a PCPU or DPCPU field? 32 field = field.string() 33 for f in gdb.lookup_type("struct pcpu").fields(): 34 if f.name == "pc_" + field: 35 return pcpu["pc_" + field] 36 37 def uintptr_t(val): 38 return val.cast(gdb.lookup_type("uintptr_t")) 39 40 # We're dealing with a DPCPU field. This is handled similarly 41 # to VNET symbols, see vnet.py for comments. 42 pcpu_base = pcpu['pc_dynamic'] 43 pcpu_entry = symval("pcpu_entry_" + field) 44 pcpu_entry_addr = uintptr_t(pcpu_entry.address) 45 46 for lf in linker_file_foreach(): 47 block = gdb.block_for_pc(lf['ops']['cls']['methods'][0]['func']) 48 elf_file_t = gdb.lookup_type("elf_file_t", block).target() 49 ef = lf.cast(elf_file_t) 50 51 file_type = lf['ops']['cls']['name'].string() 52 if file_type == "elf64": 53 start = uintptr_t(ef['pcpu_start']) 54 if start == 0: 55 continue 56 end = uintptr_t(ef['pcpu_stop']) 57 base = uintptr_t(ef['pcpu_base']) 58 elif file_type == "elf64_obj": 59 for i in range(ef['nprogtab']): 60 pe = ef['progtab'][i] 61 if pe['name'].string() == "set_pcpu": 62 start = uintptr_t(pe['origaddr']) 63 end = start + uintptr_t(pe['size']) 64 base = uintptr_t(pe['addr']) 65 break 66 else: 67 continue 68 else: 69 path = lf['pathname'].string() 70 raise gdb.error(f"{path} has unexpected linker file type {file_type}") 71 72 if pcpu_entry_addr >= start and pcpu_entry_addr < end: 73 obj = gdb.Value(pcpu_base + pcpu_entry_addr - start + base) 74 return obj.cast(pcpu_entry.type.pointer()).dereference() 75 76# Register with gdb. 77pcpu() 78