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 <[email protected]> 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 64class CpuList(): 65 def __init__(self, mask_name): 66 global cpu_mask 67 self.mask = None 68 if mask_name in cpu_mask: 69 self.mask = cpu_mask[mask_name] 70 if self.mask is None: 71 self.mask = gdb.parse_and_eval(mask_name + ".bits") 72 if hasattr(gdb, 'events'): 73 cpu_mask[mask_name] = self.mask 74 gdb.events.stop.connect(cpu_mask_invalidate) 75 if hasattr(gdb.events, 'new_objfile'): 76 gdb.events.new_objfile.connect(cpu_mask_invalidate) 77 self.bits_per_entry = self.mask[0].type.sizeof * 8 78 self.num_entries = self.mask.type.sizeof * 8 / self.bits_per_entry 79 self.entry = -1 80 self.bits = 0 81 82 def __iter__(self): 83 return self 84 85 def __next__(self): 86 while self.bits == 0: 87 self.entry += 1 88 if self.entry == self.num_entries: 89 raise StopIteration 90 self.bits = self.mask[self.entry] 91 if self.bits != 0: 92 self.bit = 0 93 break 94 95 while self.bits & 1 == 0: 96 self.bits >>= 1 97 self.bit += 1 98 99 cpu = self.entry * self.bits_per_entry + self.bit 100 101 self.bits >>= 1 102 self.bit += 1 103 104 return cpu 105 106 def next(self): 107 return self.__next__() 108 109 110class PerCpu(gdb.Function): 111 """Return per-cpu variable. 112 113$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the 114given CPU number. If CPU is omitted, the CPU of the current context is used. 115Note that VAR has to be quoted as string.""" 116 117 def __init__(self): 118 super(PerCpu, self).__init__("lx_per_cpu") 119 120 def invoke(self, var_name, cpu=-1): 121 var_ptr = gdb.parse_and_eval("&" + var_name.string()) 122 return per_cpu(var_ptr, cpu) 123 124 125PerCpu() 126 127 128class LxCurrentFunc(gdb.Function): 129 """Return current task. 130 131$lx_current([CPU]): Return the per-cpu task variable for the given CPU 132number. If CPU is omitted, the CPU of the current context is used.""" 133 134 def __init__(self): 135 super(LxCurrentFunc, self).__init__("lx_current") 136 137 def invoke(self, cpu=-1): 138 var_ptr = gdb.parse_and_eval("¤t_task") 139 return per_cpu(var_ptr, cpu).dereference() 140 141 142LxCurrentFunc() 143