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