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("¤t_task") 171 return per_cpu(var_ptr, cpu).dereference() 172 173 174LxCurrentFunc() 175