1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright (C) 2019 Intel Corporation. 3# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. 4# 5 6import gdb 7import gdb.printing 8 9 10class SpdkTailqList(object): 11 12 def __init__(self, list_pointer, list_member, tailq_name_list): 13 self.list_pointer = list_pointer 14 self.tailq_name_list = tailq_name_list 15 self.list_member = list_member 16 self.list = gdb.parse_and_eval(self.list_pointer) 17 18 def __iter__(self): 19 curr = self.list['tqh_first'] 20 while curr: 21 yield self.list_member(curr) 22 for tailq_name in self.tailq_name_list: 23 curr = curr[tailq_name] 24 curr = curr['tqe_next'] 25 26 27class SpdkNormalTailqList(SpdkTailqList): 28 29 def __init__(self, list_pointer, list_member): 30 super(SpdkNormalTailqList, self).__init__(list_pointer, list_member, 31 ['tailq']) 32 33 34class SpdkRbTree(object): 35 36 def __init__(self, tree_pointer, tree_member, tree_name_list): 37 self.tree_pointer = tree_pointer 38 self.tree_name_list = tree_name_list 39 self.tree_member = tree_member 40 self.tree = gdb.parse_and_eval(self.tree_pointer) 41 42 def get_left_node(self, node): 43 return node['node']['rbe_left'] 44 45 def get_right_node(self, node): 46 return node['node']['rbe_right'] 47 48 def traverse_rb_tree(self, node): 49 if node: 50 self.rb_list.append(node) 51 self.traverse_rb_tree(self.get_left_node(node)) 52 self.traverse_rb_tree(self.get_right_node(node)) 53 54 def __iter__(self): 55 self.rb_list = [] 56 tree_top = self.tree['rbh_root'] 57 if tree_top: 58 self.traverse_rb_tree(tree_top) 59 for rb_node in self.rb_list: 60 yield self.tree_member(rb_node) 61 else: 62 yield 63 64 65class SpdkArr(object): 66 67 def __init__(self, arr_pointer, num_elements, element_type): 68 self.arr_pointer = arr_pointer 69 self.num_elements = num_elements 70 self.element_type = element_type 71 72 def __iter__(self): 73 for i in range(0, self.num_elements): 74 curr = (self.arr_pointer + i).dereference() 75 if (curr == 0x0): 76 continue 77 yield self.element_type(curr) 78 79 80class SpdkPrintCommand(gdb.Command): 81 82 def __init__(self, name, element_list): 83 self.element_list = element_list 84 gdb.Command.__init__(self, name, 85 gdb.COMMAND_DATA, 86 gdb.COMPLETE_SYMBOL, 87 True) 88 89 def print_element_list(self, element_list): 90 first = True 91 for element in element_list: 92 if first: 93 first = False 94 else: 95 print("---------------") 96 print("\n" + str(element) + "\n") 97 98 def invoke(self, arg, from_tty): 99 self.print_element_list(self.element_list) 100 101 102class SpdkObject(object): 103 104 def __init__(self, gdb_obj): 105 self.obj = gdb_obj 106 107 def get_name(self): 108 return self.obj['name'] 109 110 def __str__(self): 111 s = "SPDK object of type %s at %s" % (self.type_name, str(self.obj)) 112 s += '\n((%s*) %s)' % (self.type_name, str(self.obj)) 113 s += '\nname %s' % self.get_name() 114 return s 115 116 117class IoDevice(SpdkObject): 118 119 type_name = 'struct io_device' 120 121 122class IoDevices(SpdkRbTree): 123 124 def __init__(self): 125 super(IoDevices, self).__init__('g_io_devices', IoDevice, ['rbh_root']) 126 127 128class spdk_print_io_devices(SpdkPrintCommand): 129 130 def __init__(self): 131 try: 132 io_devices = IoDevices() 133 except RuntimeError as e: 134 print("Cannot load IO devices: " + str(e)) 135 return 136 name = 'spdk_print_io_devices' 137 super(spdk_print_io_devices, self).__init__(name, io_devices) 138 139 140class Bdev(SpdkObject): 141 142 type_name = 'struct spdk_bdev' 143 144 145class BdevMgrBdevs(SpdkTailqList): 146 147 def __init__(self): 148 tailq_name_list = ['internal', 'link'] 149 super(BdevMgrBdevs, self).__init__('g_bdev_mgr->bdevs', Bdev, tailq_name_list) 150 151 152class spdk_print_bdevs(SpdkPrintCommand): 153 name = 'spdk_print_bdevs' 154 155 def __init__(self): 156 try: 157 bdevs = BdevMgrBdevs() 158 except RuntimeError as e: 159 print("Cannot load bdevs: " + str(e)) 160 return 161 super(spdk_print_bdevs, self).__init__(self.name, bdevs) 162 163 164class spdk_find_bdev(spdk_print_bdevs): 165 166 name = 'spdk_find_bdev' 167 168 def invoke(self, arg, from_tty): 169 print(arg) 170 bdev_query = [bdev for bdev in self.element_list 171 if str(bdev.get_name()).find(arg) != -1] 172 if bdev_query == []: 173 print("Cannot find bdev with name %s" % arg) 174 return 175 176 self.print_element_list(bdev_query) 177 178 179class NvmfSubsystem(SpdkObject): 180 181 type_name = 'struct spdk_nvmf_subsystem' 182 183 def __init__(self, ptr): 184 self.ptr = ptr 185 gdb_obj = self.ptr.cast(gdb.lookup_type(self.type_name).pointer()) 186 super(NvmfSubsystem, self).__init__(gdb_obj) 187 188 def get_name(self): 189 return self.obj['subnqn'] 190 191 def get_id(self): 192 return int(self.obj['id']) 193 194 def get_ns_list(self): 195 max_nsid = int(self.obj['max_nsid']) 196 ns_list = [] 197 for i in range(0, max_nsid): 198 nsptr = (self.obj['ns'] + i).dereference() 199 if nsptr == 0x0: 200 continue 201 ns = nsptr.cast(gdb.lookup_type('struct spdk_nvmf_ns').pointer()) 202 ns_list.append(ns) 203 return ns_list 204 205 def __str__(self): 206 s = super(NvmfSubsystem, self).__str__() 207 s += '\nnqn %s' % self.get_name() 208 s += '\nID %d' % self.get_id() 209 for ns in self.get_ns_list(): 210 s += '\t%s' % str(ns) 211 return s 212 213 214class SpdkNvmfTgtSubsystems(SpdkArr): 215 216 def get_num_subsystems(self): 217 try: # version >= 18.11 218 return int(self.spdk_nvmf_tgt['max_subsystems']) 219 except RuntimeError: # version < 18.11 220 return int(self.spdk_nvmf_tgt['opts']['max_subsystems']) 221 222 def __init__(self): 223 try: 224 self.spdk_nvmf_tgt = gdb.parse_and_eval("g_spdk_nvmf_tgt") 225 except RuntimeError as e: 226 print("Cannot load nvmf target subsystems: " + str(e)) 227 return 228 subsystems = gdb.parse_and_eval("g_spdk_nvmf_tgt->subsystems") 229 super(SpdkNvmfTgtSubsystems, self).__init__(subsystems, 230 self.get_num_subsystems(), 231 NvmfSubsystem) 232 233 234class spdk_print_nvmf_subsystems(SpdkPrintCommand): 235 236 def __init__(self): 237 name = 'spdk_print_nvmf_subsystems' 238 nvmf_tgt_subsystems = SpdkNvmfTgtSubsystems() 239 super(spdk_print_nvmf_subsystems, self).__init__(name, nvmf_tgt_subsystems) 240 241 242class IoChannel(SpdkObject): 243 244 type_name = 'struct spdk_io_channel' 245 246 def get_ref(self): 247 248 return int(self.obj['ref']) 249 250 def get_device(self): 251 return self.obj['dev'] 252 253 def get_device_name(self): 254 return self.obj['dev']['name'] 255 256 def get_name(self): 257 return "" 258 259 def __str__(self): 260 s = super(IoChannel, self).__str__() + '\n' 261 s += 'ref %d\n' % self.get_ref() 262 s += 'device %s (%s)\n' % (self.get_device(), self.get_device_name()) 263 return s 264 265 266class IoChannels(SpdkRbTree): 267 268 def __init__(self, tree_obj): 269 self.tree_name_list = ['rbh_root'] 270 self.tree_member = IoChannel 271 self.tree = tree_obj 272 273 274class SpdkThread(SpdkObject): 275 276 type_name = 'struct spdk_thread' 277 278 def __init__(self, gdb_obj): 279 super(SpdkThread, self).__init__(gdb_obj) 280 self.io_channels = IoChannels(self.obj['io_channels']) 281 282 def __str__(self): 283 s = super(SpdkThread, self).__str__() + '\n' 284 s += "IO Channels:\n" 285 for io_channel in self.get_io_channels(): 286 channel_lines = str(io_channel).split('\n') 287 s += '\n'.join('\t%s' % line for line in channel_lines if line != '') 288 s += '\n' 289 s += '\t---------------\n' 290 s += '\n' 291 return s 292 293 def get_io_channels(self): 294 return self.io_channels 295 296 297class SpdkThreads(SpdkNormalTailqList): 298 299 def __init__(self): 300 super(SpdkThreads, self).__init__('g_threads', SpdkThread) 301 302 303class spdk_print_threads(SpdkPrintCommand): 304 305 def __init__(self): 306 name = "spdk_print_threads" 307 threads = SpdkThreads() 308 super(spdk_print_threads, self).__init__(name, threads) 309 310 311class SpdkSpinlockStackPrinter(object): 312 313 def __init__(self, val): 314 self.val = val 315 316 def to_array(self): 317 ret = [] 318 count = self.val['depth'] 319 for i in range(count): 320 line = '' 321 addr = self.val['addrs'][i] 322 line += ' ' + str(addr) 323 # Source and line (sal) only exists for objects with debug info 324 sal = gdb.find_pc_line(int(addr)) 325 try: 326 line += ' ' + str(sal.symtab.filename) 327 line += ':' + str(sal.line) 328 except AttributeError as e: 329 pass 330 ret.append(line) 331 return ret 332 333 def to_string(self): 334 return 'struct sspin_stack:\n' + '\n'.join(self.to_array()) 335 336 def display_hint(self): 337 return 'struct sspin_stack' 338 339 340class SpdkSpinlockPrinter(object): 341 342 def __init__(self, val): 343 self.val = val 344 345 def to_string(self): 346 thread = self.val['thread'] 347 internal = self.val['internal'] 348 s = 'struct spdk_spinlock:' 349 s += '\n Locked by spdk_thread: ' 350 if thread == 0: 351 s += 'not locked' 352 else: 353 s += str(thread) 354 if internal != 0: 355 stacks = [ 356 ['Initialized', 'init_stack'], 357 ['Last locked', 'lock_stack'], 358 ['Last unlocked', 'unlock_stack']] 359 for stack in stacks: 360 s += '\n ' + stack[0] + ' at:\n ' 361 frames = SpdkSpinlockStackPrinter(internal[stack[1]]) 362 s += '\n '.join(frames.to_array()) 363 return s 364 365 def display_hint(self): 366 return 'struct spdk_spinlock' 367 368 369class spdk_load_macros(gdb.Command): 370 371 def __init__(self): 372 gdb.Command.__init__(self, 'spdk_load_macros', 373 gdb.COMMAND_DATA, 374 gdb.COMPLETE_SYMBOL, 375 True) 376 self.loaded = False 377 378 def load_pretty_printers(self): 379 pp = gdb.printing.RegexpCollectionPrettyPrinter("spdk_library") 380 pp.add_printer('sspin_stack', '^sspin_stack$', 381 SpdkSpinlockStackPrinter) 382 pp.add_printer('spdk_spinlock', '^spdk_spinlock$', SpdkSpinlockPrinter) 383 gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) 384 385 def invoke(self, arg, from_tty): 386 if arg == '--reload': 387 print('Reloading spdk information') 388 reload = True 389 else: 390 reload = False 391 # These can only load once 392 self.load_pretty_printers() 393 394 if self.loaded and not reload: 395 return 396 397 spdk_print_threads() 398 spdk_print_bdevs() 399 spdk_print_io_devices() 400 spdk_print_nvmf_subsystems() 401 spdk_find_bdev() 402 403 404spdk_load_macros() 405