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