Commit | Line | Data |
---|---|---|
fe7f9ed9 JK |
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 | ||
14 | import gdb | |
15 | ||
16 | from linux import tasks, utils | |
17 | ||
18 | ||
526940e3 BS |
19 | task_type = utils.CachedType("struct task_struct") |
20 | ||
21 | ||
fe7f9ed9 JK |
22 | MAX_CPUS = 4096 |
23 | ||
24 | ||
25 | def get_current_cpu(): | |
26 | if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU: | |
27 | return gdb.selected_thread().num - 1 | |
28 | elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB: | |
29 | tid = gdb.selected_thread().ptid[2] | |
30 | if tid > (0x100000000 - MAX_CPUS - 2): | |
31 | return 0x100000000 - tid - 2 | |
32 | else: | |
33 | return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu'] | |
34 | else: | |
35 | raise gdb.GdbError("Sorry, obtaining the current CPU is not yet " | |
36 | "supported with this gdb server.") | |
37 | ||
38 | ||
39 | def per_cpu(var_ptr, cpu): | |
40 | if cpu == -1: | |
41 | cpu = get_current_cpu() | |
42 | if utils.is_target_arch("sparc:v9"): | |
43 | offset = gdb.parse_and_eval( | |
44 | "trap_block[{0}].__per_cpu_base".format(str(cpu))) | |
45 | else: | |
46 | try: | |
47 | offset = gdb.parse_and_eval( | |
48 | "__per_cpu_offset[{0}]".format(str(cpu))) | |
49 | except gdb.error: | |
50 | # !CONFIG_SMP case | |
51 | offset = 0 | |
52 | pointer = var_ptr.cast(utils.get_long_type()) + offset | |
53 | return pointer.cast(var_ptr.type).dereference() | |
54 | ||
55 | ||
3d4cd9c9 JK |
56 | cpu_mask = {} |
57 | ||
58 | ||
59 | def cpu_mask_invalidate(event): | |
60 | global cpu_mask | |
61 | cpu_mask = {} | |
62 | gdb.events.stop.disconnect(cpu_mask_invalidate) | |
63 | if hasattr(gdb.events, 'new_objfile'): | |
64 | gdb.events.new_objfile.disconnect(cpu_mask_invalidate) | |
65 | ||
66 | ||
a77e15e8 JK |
67 | def cpu_list(mask_name): |
68 | global cpu_mask | |
69 | mask = None | |
70 | if mask_name in cpu_mask: | |
71 | mask = cpu_mask[mask_name] | |
72 | if mask is None: | |
73 | mask = gdb.parse_and_eval(mask_name + ".bits") | |
74 | if hasattr(gdb, 'events'): | |
75 | cpu_mask[mask_name] = mask | |
76 | gdb.events.stop.connect(cpu_mask_invalidate) | |
77 | if hasattr(gdb.events, 'new_objfile'): | |
78 | gdb.events.new_objfile.connect(cpu_mask_invalidate) | |
79 | bits_per_entry = mask[0].type.sizeof * 8 | |
80 | num_entries = mask.type.sizeof * 8 / bits_per_entry | |
81 | entry = -1 | |
82 | bits = 0 | |
83 | ||
84 | while True: | |
85 | while bits == 0: | |
86 | entry += 1 | |
87 | if entry == num_entries: | |
88 | return | |
89 | bits = mask[entry] | |
90 | if bits != 0: | |
91 | bit = 0 | |
3d4cd9c9 JK |
92 | break |
93 | ||
a77e15e8 JK |
94 | while bits & 1 == 0: |
95 | bits >>= 1 | |
96 | bit += 1 | |
3d4cd9c9 | 97 | |
a77e15e8 | 98 | cpu = entry * bits_per_entry + bit |
3d4cd9c9 | 99 | |
a77e15e8 JK |
100 | bits >>= 1 |
101 | bit += 1 | |
3d4cd9c9 | 102 | |
4bc393db | 103 | yield int(cpu) |
276d97d9 | 104 | |
3d4cd9c9 | 105 | |
b1503934 KB |
106 | def each_online_cpu(): |
107 | for cpu in cpu_list("__cpu_online_mask"): | |
108 | yield cpu | |
109 | ||
110 | ||
111 | def each_present_cpu(): | |
112 | for cpu in cpu_list("__cpu_present_mask"): | |
113 | yield cpu | |
114 | ||
115 | ||
116 | def each_possible_cpu(): | |
117 | for cpu in cpu_list("__cpu_possible_mask"): | |
118 | yield cpu | |
119 | ||
120 | ||
121 | def each_active_cpu(): | |
122 | for cpu in cpu_list("__cpu_active_mask"): | |
123 | yield cpu | |
124 | ||
125 | ||
126 | class LxCpus(gdb.Command): | |
127 | """List CPU status arrays | |
128 | ||
129 | Displays the known state of each CPU based on the kernel masks | |
130 | and can help identify the state of hotplugged CPUs""" | |
131 | ||
132 | def __init__(self): | |
133 | super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA) | |
134 | ||
135 | def invoke(self, arg, from_tty): | |
136 | gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu()))) | |
137 | gdb.write("Present CPUs : {}\n".format(list(each_present_cpu()))) | |
138 | gdb.write("Online CPUs : {}\n".format(list(each_online_cpu()))) | |
139 | gdb.write("Active CPUs : {}\n".format(list(each_active_cpu()))) | |
140 | ||
494dbe02 | 141 | |
b1503934 KB |
142 | LxCpus() |
143 | ||
144 | ||
fe7f9ed9 JK |
145 | class PerCpu(gdb.Function): |
146 | """Return per-cpu variable. | |
147 | ||
148 | $lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the | |
149 | given CPU number. If CPU is omitted, the CPU of the current context is used. | |
150 | Note that VAR has to be quoted as string.""" | |
151 | ||
152 | def __init__(self): | |
153 | super(PerCpu, self).__init__("lx_per_cpu") | |
154 | ||
db08c53f FR |
155 | def invoke(self, var, cpu=-1): |
156 | return per_cpu(var.address, cpu) | |
fe7f9ed9 JK |
157 | |
158 | ||
159 | PerCpu() | |
116b47b4 | 160 | |
dc958682 | 161 | def get_current_task(cpu): |
526940e3 BS |
162 | task_ptr_type = task_type.get_type().pointer() |
163 | ||
dc958682 | 164 | if utils.is_target_arch("x86"): |
6d51363d GW |
165 | if gdb.lookup_global_symbol("cpu_tasks"): |
166 | # This is a UML kernel, which stores the current task | |
167 | # differently than other x86 sub architectures | |
168 | var_ptr = gdb.parse_and_eval("(struct task_struct *)cpu_tasks[0].task") | |
169 | return var_ptr.dereference() | |
170 | else: | |
171 | var_ptr = gdb.parse_and_eval("&pcpu_hot.current_task") | |
172 | return per_cpu(var_ptr, cpu).dereference() | |
526940e3 | 173 | elif utils.is_target_arch("aarch64"): |
56fe4870 GW |
174 | current_task_addr = gdb.parse_and_eval("$SP_EL0") |
175 | if (current_task_addr >> 63) != 0: | |
176 | current_task = current_task_addr.cast(task_ptr_type) | |
177 | return current_task.dereference() | |
178 | else: | |
179 | raise gdb.GdbError("Sorry, obtaining the current task is not allowed " | |
180 | "while running in userspace(EL0)") | |
cd24f440 DG |
181 | elif utils.is_target_arch("riscv"): |
182 | current_tp = gdb.parse_and_eval("$tp") | |
183 | scratch_reg = gdb.parse_and_eval("$sscratch") | |
184 | ||
185 | # by default tp points to current task | |
186 | current_task = current_tp.cast(task_ptr_type) | |
187 | ||
188 | # scratch register is set 0 in trap handler after entering kernel. | |
189 | # When hart is in user mode, scratch register is pointing to task_struct. | |
190 | # and tp is used by user mode. So when scratch register holds larger value | |
191 | # (negative address as ulong is larger value) than tp, then use scratch register. | |
192 | if (scratch_reg.cast(utils.get_ulong_type()) > current_tp.cast(utils.get_ulong_type())): | |
193 | current_task = scratch_reg.cast(task_ptr_type) | |
194 | ||
195 | return current_task.dereference() | |
dc958682 BS |
196 | else: |
197 | raise gdb.GdbError("Sorry, obtaining the current task is not yet " | |
198 | "supported with this arch") | |
116b47b4 JK |
199 | |
200 | class LxCurrentFunc(gdb.Function): | |
201 | """Return current task. | |
202 | ||
203 | $lx_current([CPU]): Return the per-cpu task variable for the given CPU | |
204 | number. If CPU is omitted, the CPU of the current context is used.""" | |
205 | ||
206 | def __init__(self): | |
207 | super(LxCurrentFunc, self).__init__("lx_current") | |
208 | ||
209 | def invoke(self, cpu=-1): | |
dc958682 | 210 | return get_current_task(cpu) |
116b47b4 JK |
211 | |
212 | ||
213 | LxCurrentFunc() |