• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
138
139LxCpus()
140
141
142class PerCpu(gdb.Function):
143    """Return per-cpu variable.
144
145$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
146given CPU number. If CPU is omitted, the CPU of the current context is used.
147Note that VAR has to be quoted as string."""
148
149    def __init__(self):
150        super(PerCpu, self).__init__("lx_per_cpu")
151
152    def invoke(self, var_name, cpu=-1):
153        var_ptr = gdb.parse_and_eval("&" + var_name.string())
154        return per_cpu(var_ptr, cpu)
155
156
157PerCpu()
158
159
160class LxCurrentFunc(gdb.Function):
161    """Return current task.
162
163$lx_current([CPU]): Return the per-cpu task variable for the given CPU
164number. If CPU is omitted, the CPU of the current context is used."""
165
166    def __init__(self):
167        super(LxCurrentFunc, self).__init__("lx_current")
168
169    def invoke(self, cpu=-1):
170        var_ptr = gdb.parse_and_eval("&current_task")
171        return per_cpu(var_ptr, cpu).dereference()
172
173
174LxCurrentFunc()
175