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 cpu
101
102
103class PerCpu(gdb.Function):
104    """Return per-cpu variable.
105
106$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
107given CPU number. If CPU is omitted, the CPU of the current context is used.
108Note that VAR has to be quoted as string."""
109
110    def __init__(self):
111        super(PerCpu, self).__init__("lx_per_cpu")
112
113    def invoke(self, var_name, cpu=-1):
114        var_ptr = gdb.parse_and_eval("&" + var_name.string())
115        return per_cpu(var_ptr, cpu)
116
117
118PerCpu()
119
120
121class LxCurrentFunc(gdb.Function):
122    """Return current task.
123
124$lx_current([CPU]): Return the per-cpu task variable for the given CPU
125number. If CPU is omitted, the CPU of the current context is used."""
126
127    def __init__(self):
128        super(LxCurrentFunc, self).__init__("lx_current")
129
130    def invoke(self, cpu=-1):
131        var_ptr = gdb.parse_and_eval("&current_task")
132        return per_cpu(var_ptr, cpu).dereference()
133
134
135LxCurrentFunc()
136