xref: /freebsd/sys/tools/gdb/pcpu.py (revision 8ccc0d235c226d84112561d453c49904398d085c)
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