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 <jan.kiszka@siemens.com>
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
64def cpu_list(mask_name):
65    global cpu_mask
66    mask = None
67    if mask_name in cpu_mask:
68        mask = cpu_mask[mask_name]
69    if mask is None:
70        mask = gdb.parse_and_eval(mask_name + ".bits")
71        if hasattr(gdb, 'events'):
72            cpu_mask[mask_name] = mask
73            gdb.events.stop.connect(cpu_mask_invalidate)
74            if hasattr(gdb.events, 'new_objfile'):
75                gdb.events.new_objfile.connect(cpu_mask_invalidate)
76    bits_per_entry = mask[0].type.sizeof * 8
77    num_entries = mask.type.sizeof * 8 / bits_per_entry
78    entry = -1
79    bits = 0
80
81    while True:
82        while bits == 0:
83            entry += 1
84            if entry == num_entries:
85                return
86            bits = mask[entry]
87            if bits != 0:
88                bit = 0
89                break
90
91        while bits & 1 == 0:
92            bits >>= 1
93            bit += 1
94
95        cpu = entry * bits_per_entry + bit
96
97        bits >>= 1
98        bit += 1
99
100        yield int(cpu)
101
102
103def each_online_cpu():
104    for cpu in cpu_list("__cpu_online_mask"):
105        yield cpu
106
107
108def each_present_cpu():
109    for cpu in cpu_list("__cpu_present_mask"):
110        yield cpu
111
112
113def each_possible_cpu():
114    for cpu in cpu_list("__cpu_possible_mask"):
115        yield cpu
116
117
118def each_active_cpu():
119    for cpu in cpu_list("__cpu_active_mask"):
120        yield cpu
121
122
123class LxCpus(gdb.Command):
124    """List CPU status arrays
125
126Displays the known state of each CPU based on the kernel masks
127and can help identify the state of hotplugged CPUs"""
128
129    def __init__(self):
130        super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA)
131
132    def invoke(self, arg, from_tty):
133        gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu())))
134        gdb.write("Present CPUs  : {}\n".format(list(each_present_cpu())))
135        gdb.write("Online CPUs   : {}\n".format(list(each_online_cpu())))
136        gdb.write("Active CPUs   : {}\n".format(list(each_active_cpu())))
137
138LxCpus()
139
140
141class PerCpu(gdb.Function):
142    """Return per-cpu variable.
143
144$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
145given CPU number. If CPU is omitted, the CPU of the current context is used.
146Note that VAR has to be quoted as string."""
147
148    def __init__(self):
149        super(PerCpu, self).__init__("lx_per_cpu")
150
151    def invoke(self, var_name, cpu=-1):
152        var_ptr = gdb.parse_and_eval("&" + var_name.string())
153        return per_cpu(var_ptr, cpu)
154
155
156PerCpu()
157
158
159class LxCurrentFunc(gdb.Function):
160    """Return current task.
161
162$lx_current([CPU]): Return the per-cpu task variable for the given CPU
163number. If CPU is omitted, the CPU of the current context is used."""
164
165    def __init__(self):
166        super(LxCurrentFunc, self).__init__("lx_current")
167
168    def invoke(self, cpu=-1):
169        var_ptr = gdb.parse_and_eval("&current_task")
170        return per_cpu(var_ptr, cpu).dereference()
171
172
173LxCurrentFunc()
174