| # |
| # 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() |