xref: /linux-6.15/scripts/gdb/linux/cpus.py (revision 3d4cd9c9)
1#
2# gdb helper commands and functions for Linux kernel debugging
3#
4#  per-cpu tools
5#
6# Copyright (c) Siemens AG, 2011-2013
7#
8# Authors:
9#  Jan Kiszka <[email protected]>
10#
11# This work is licensed under the terms of the GNU GPL version 2.
12#
13
14import gdb
15
16from linux import tasks, utils
17
18
19MAX_CPUS = 4096
20
21
22def get_current_cpu():
23    if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU:
24        return gdb.selected_thread().num - 1
25    elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB:
26        tid = gdb.selected_thread().ptid[2]
27        if tid > (0x100000000 - MAX_CPUS - 2):
28            return 0x100000000 - tid - 2
29        else:
30            return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu']
31    else:
32        raise gdb.GdbError("Sorry, obtaining the current CPU is not yet "
33                           "supported with this gdb server.")
34
35
36def per_cpu(var_ptr, cpu):
37    if cpu == -1:
38        cpu = get_current_cpu()
39    if utils.is_target_arch("sparc:v9"):
40        offset = gdb.parse_and_eval(
41            "trap_block[{0}].__per_cpu_base".format(str(cpu)))
42    else:
43        try:
44            offset = gdb.parse_and_eval(
45                "__per_cpu_offset[{0}]".format(str(cpu)))
46        except gdb.error:
47            # !CONFIG_SMP case
48            offset = 0
49    pointer = var_ptr.cast(utils.get_long_type()) + offset
50    return pointer.cast(var_ptr.type).dereference()
51
52
53cpu_mask = {}
54
55
56def cpu_mask_invalidate(event):
57    global cpu_mask
58    cpu_mask = {}
59    gdb.events.stop.disconnect(cpu_mask_invalidate)
60    if hasattr(gdb.events, 'new_objfile'):
61        gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
62
63
64class CpuList():
65    def __init__(self, mask_name):
66        global cpu_mask
67        self.mask = None
68        if mask_name in cpu_mask:
69            self.mask = cpu_mask[mask_name]
70        if self.mask is None:
71            self.mask = gdb.parse_and_eval(mask_name + ".bits")
72            if hasattr(gdb, 'events'):
73                cpu_mask[mask_name] = self.mask
74                gdb.events.stop.connect(cpu_mask_invalidate)
75                if hasattr(gdb.events, 'new_objfile'):
76                    gdb.events.new_objfile.connect(cpu_mask_invalidate)
77        self.bits_per_entry = self.mask[0].type.sizeof * 8
78        self.num_entries = self.mask.type.sizeof * 8 / self.bits_per_entry
79        self.entry = -1
80        self.bits = 0
81
82    def __iter__(self):
83        return self
84
85    def next(self):
86        while self.bits == 0:
87            self.entry += 1
88            if self.entry == self.num_entries:
89                raise StopIteration
90            self.bits = self.mask[self.entry]
91            if self.bits != 0:
92                self.bit = 0
93                break
94
95        while self.bits & 1 == 0:
96            self.bits >>= 1
97            self.bit += 1
98
99        cpu = self.entry * self.bits_per_entry + self.bit
100
101        self.bits >>= 1
102        self.bit += 1
103
104        return cpu
105
106
107class PerCpu(gdb.Function):
108    """Return per-cpu variable.
109
110$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
111given CPU number. If CPU is omitted, the CPU of the current context is used.
112Note that VAR has to be quoted as string."""
113
114    def __init__(self):
115        super(PerCpu, self).__init__("lx_per_cpu")
116
117    def invoke(self, var_name, cpu=-1):
118        var_ptr = gdb.parse_and_eval("&" + var_name.string())
119        return per_cpu(var_ptr, cpu)
120
121
122PerCpu()
123
124
125class LxCurrentFunc(gdb.Function):
126    """Return current task.
127
128$lx_current([CPU]): Return the per-cpu task variable for the given CPU
129number. If CPU is omitted, the CPU of the current context is used."""
130
131    def __init__(self):
132        super(LxCurrentFunc, self).__init__("lx_current")
133
134    def invoke(self, cpu=-1):
135        var_ptr = gdb.parse_and_eval("&current_task")
136        return per_cpu(var_ptr, cpu).dereference()
137
138
139LxCurrentFunc()
140