| # | |
| # gdb helper commands and functions for Linux kernel debugging | |
| # | |
| # per-cpu tools | |
| # | |
| # Copyright (c) Siemens AG, 2011-2013 | |
| # | |
| # Authors: | |
| # Jan Kiszka <jan.kiszka@siemens.com> | |
| # | |
| # This work is licensed under the terms of the GNU GPL version 2. | |
| # | |
| import gdb | |
| from linux import tasks, utils | |
| MAX_CPUS = 4096 | |
| def get_current_cpu(): | |
| if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU: | |
| return gdb.selected_thread().num - 1 | |
| elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB: | |
| tid = gdb.selected_thread().ptid[2] | |
| if tid > (0x100000000 - MAX_CPUS - 2): | |
| return 0x100000000 - tid - 2 | |
| else: | |
| return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu'] | |
| else: | |
| raise gdb.GdbError("Sorry, obtaining the current CPU is not yet " | |
| "supported with this gdb server.") | |
| def per_cpu(var_ptr, cpu): | |
| if cpu == -1: | |
| cpu = get_current_cpu() | |
| if utils.is_target_arch("sparc:v9"): | |
| offset = gdb.parse_and_eval( | |
| "trap_block[{0}].__per_cpu_base".format(str(cpu))) | |
| else: | |
| try: | |
| offset = gdb.parse_and_eval( | |
| "__per_cpu_offset[{0}]".format(str(cpu))) | |
| except gdb.error: | |
| # !CONFIG_SMP case | |
| offset = 0 | |
| pointer = var_ptr.cast(utils.get_long_type()) + offset | |
| return pointer.cast(var_ptr.type).dereference() | |
| cpu_mask = {} | |
| def cpu_mask_invalidate(event): | |
| global cpu_mask | |
| cpu_mask = {} | |
| gdb.events.stop.disconnect(cpu_mask_invalidate) | |
| if hasattr(gdb.events, 'new_objfile'): | |
| gdb.events.new_objfile.disconnect(cpu_mask_invalidate) | |
| def cpu_list(mask_name): | |
| global cpu_mask | |
| mask = None | |
| if mask_name in cpu_mask: | |
| mask = cpu_mask[mask_name] | |
| if mask is None: | |
| mask = gdb.parse_and_eval(mask_name + ".bits") | |
| if hasattr(gdb, 'events'): | |
| cpu_mask[mask_name] = mask | |
| gdb.events.stop.connect(cpu_mask_invalidate) | |
| if hasattr(gdb.events, 'new_objfile'): | |
| gdb.events.new_objfile.connect(cpu_mask_invalidate) | |
| bits_per_entry = mask[0].type.sizeof * 8 | |
| num_entries = mask.type.sizeof * 8 / bits_per_entry | |
| entry = -1 | |
| bits = 0 | |
| while True: | |
| while bits == 0: | |
| entry += 1 | |
| if entry == num_entries: | |
| return | |
| bits = mask[entry] | |
| if bits != 0: | |
| bit = 0 | |
| break | |
| while bits & 1 == 0: | |
| bits >>= 1 | |
| bit += 1 | |
| cpu = entry * bits_per_entry + bit | |
| bits >>= 1 | |
| bit += 1 | |
| yield int(cpu) | |
| def each_online_cpu(): | |
| for cpu in cpu_list("__cpu_online_mask"): | |
| yield cpu | |
| def each_present_cpu(): | |
| for cpu in cpu_list("__cpu_present_mask"): | |
| yield cpu | |
| def each_possible_cpu(): | |
| for cpu in cpu_list("__cpu_possible_mask"): | |
| yield cpu | |
| def each_active_cpu(): | |
| for cpu in cpu_list("__cpu_active_mask"): | |
| yield cpu | |
| class LxCpus(gdb.Command): | |
| """List CPU status arrays | |
| Displays the known state of each CPU based on the kernel masks | |
| and can help identify the state of hotplugged CPUs""" | |
| def __init__(self): | |
| super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA) | |
| def invoke(self, arg, from_tty): | |
| gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu()))) | |
| gdb.write("Present CPUs : {}\n".format(list(each_present_cpu()))) | |
| gdb.write("Online CPUs : {}\n".format(list(each_online_cpu()))) | |
| gdb.write("Active CPUs : {}\n".format(list(each_active_cpu()))) | |
| LxCpus() | |
| class PerCpu(gdb.Function): | |
| """Return per-cpu variable. | |
| $lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the | |
| given CPU number. If CPU is omitted, the CPU of the current context is used. | |
| Note that VAR has to be quoted as string.""" | |
| def __init__(self): | |
| super(PerCpu, self).__init__("lx_per_cpu") | |
| def invoke(self, var_name, cpu=-1): | |
| var_ptr = gdb.parse_and_eval("&" + var_name.string()) | |
| return per_cpu(var_ptr, cpu) | |
| PerCpu() | |
| class LxCurrentFunc(gdb.Function): | |
| """Return current task. | |
| $lx_current([CPU]): Return the per-cpu task variable for the given CPU | |
| number. If CPU is omitted, the CPU of the current context is used.""" | |
| def __init__(self): | |
| super(LxCurrentFunc, self).__init__("lx_current") | |
| def invoke(self, cpu=-1): | |
| var_ptr = gdb.parse_and_eval("¤t_task") | |
| return per_cpu(var_ptr, cpu).dereference() | |
| LxCurrentFunc() |