From 376c1ae299e55835a6708fb5f0320e0fa5c51d90 Mon Sep 17 00:00:00 2001 From: Mike Gerdts Date: Fri, 16 Dec 2022 17:19:28 -0600 Subject: [PATCH] scripts: gdb needs a pretty printer for spinlocks In debug builds, SPDK spinlocks will have stack traces that track where they were allocated, last locked, and last unlocked. This adds gdb pretty printers to make that information easily visible. See the updates in doc/gdb_macros.md for details. Signed-off-by: Mike Gerdts Change-Id: I4f903c588d9384c4005eec01348fa5c2d3cab5db Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16000 Reviewed-by: Jim Harris Tested-by: SPDK CI Jenkins Reviewed-by: Shuhei Matsumoto --- doc/gdb_macros.md | 48 ++++++++++++++++++++++++++++++ scripts/gdb_macros.py | 68 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/doc/gdb_macros.md b/doc/gdb_macros.md index 3c2260073..b0fb82e8b 100644 --- a/doc/gdb_macros.md +++ b/doc/gdb_macros.md @@ -125,6 +125,54 @@ nqn "nqn.2016-06.io.spdk.umgmt:cnode1", '\000' ID 1 ~~~ +Printing SPDK spinlocks: + +In this example, the spinlock has been initialized and locked but has never been unlocked. +After it is unlocked the first time the last unlocked stack will be present and the +`Locked by spdk_thread` line will say `not locked`. + +~~~{.sh} +Breakpoint 2, spdk_spin_unlock (sspin=0x655110 ) at thread.c:2915 +2915 struct spdk_thread *thread = spdk_get_thread(); +(gdb) print *sspin +$2 = struct spdk_spinlock: + Locked by spdk_thread: 0x658080 + Initialized at: + 0x43e677 thread.c:2878 + 0x404feb <_bdev_init+16> /build/spdk/spdk-review-public/lib/bdev/bdev.c:116 + 0x44483d <__libc_csu_init+77> + 0x7ffff62c9d18 <__libc_start_main+120> + 0x40268e <_start+46> + Last locked at: + 0x43e936 thread.c:2909 + 0x40ca9c /build/spdk/spdk-review-public/lib/bdev/bdev.c:3855 + 0x411a3c /build/spdk/spdk-review-public/lib/bdev/bdev.c:6660 + 0x412e1e /build/spdk/spdk-review-public/lib/bdev/bdev.c:7171 + 0x417895 bdev_ut.c:878 + 0x7ffff7bc38cb + 0x7ffff7bc3b61 + 0x7ffff7bc3f76 + 0x43351f bdev_ut.c:6295 + 0x7ffff62c9d85 <__libc_start_main+229> + 0x40268e <_start+46> + Last unlocked at: +~~~ + +Print a single spinlock stack: + +~~~{.sh} +(gdb) print sspin->internal.lock_stack +$1 = struct sspin_stack: + 0x40c6a1 /build/spdk/spdk-review-public/lib/thread/thread.c:2909 + 0x413f48 thread_ut.c:1831 + 0x7ffff7bc38cb + 0x7ffff7bc3b61 + 0x7ffff7bc3f76 + 0x4148fa thread_ut.c:1948 + 0x7ffff62c9d85 <__libc_start_main+229> + 0x40248e <_start+46> +~~~ + ## Loading The gdb Macros Copy the gdb macros to the host where you are about to debug. diff --git a/scripts/gdb_macros.py b/scripts/gdb_macros.py index ee2d13a22..977cb112e 100644 --- a/scripts/gdb_macros.py +++ b/scripts/gdb_macros.py @@ -4,6 +4,7 @@ # import gdb +import gdb.printing class SpdkTailqList(object): @@ -307,6 +308,64 @@ class spdk_print_threads(SpdkPrintCommand): super(spdk_print_threads, self).__init__(name, threads) +class SpdkSpinlockStackPrinter(object): + + def __init__(self, val): + self.val = val + + def to_array(self): + ret = [] + count = self.val['depth'] + for i in range(count): + line = '' + addr = self.val['addrs'][i] + line += ' ' + str(addr) + # Source and line (sal) only exists for objects with debug info + sal = gdb.find_pc_line(int(addr)) + try: + line += ' ' + str(sal.symtab.filename) + line += ':' + str(sal.line) + except AttributeError as e: + pass + ret.append(line) + return ret + + def to_string(self): + return 'struct sspin_stack:\n' + '\n'.join(self.to_array()) + + def display_hint(self): + return 'struct sspin_stack' + + +class SpdkSpinlockPrinter(object): + + def __init__(self, val): + self.val = val + + def to_string(self): + thread = self.val['thread'] + internal = self.val['internal'] + s = 'struct spdk_spinlock:' + s += '\n Locked by spdk_thread: ' + if thread == 0: + s += 'not locked' + else: + s += str(thread) + if internal != 0: + stacks = [ + ['Initialized', 'init_stack'], + ['Last locked', 'lock_stack'], + ['Last unlocked', 'unlock_stack']] + for stack in stacks: + s += '\n ' + stack[0] + ' at:\n ' + frames = SpdkSpinlockStackPrinter(internal[stack[1]]) + s += '\n '.join(frames.to_array()) + return s + + def display_hint(self): + return 'struct spdk_spinlock' + + class spdk_load_macros(gdb.Command): def __init__(self): @@ -316,12 +375,21 @@ class spdk_load_macros(gdb.Command): True) self.loaded = False + def load_pretty_printers(self): + pp = gdb.printing.RegexpCollectionPrettyPrinter("spdk_library") + pp.add_printer('sspin_stack', '^sspin_stack$', + SpdkSpinlockStackPrinter) + pp.add_printer('spdk_spinlock', '^spdk_spinlock$', SpdkSpinlockPrinter) + gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) + def invoke(self, arg, from_tty): if arg == '--reload': print('Reloading spdk information') reload = True else: reload = False + # These can only load once + self.load_pretty_printers() if self.loaded and not reload: return