Commit adc2ca50 authored by Mike Gerdts's avatar Mike Gerdts Committed by Jim Harris
Browse files

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: default avatarMike Gerdts <mgerdts@nvidia.com>
Change-Id: I4f903c588d9384c4005eec01348fa5c2d3cab5db
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16000


Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <smatsumoto@nvidia.com>
parent 531258aa
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -125,6 +125,54 @@ nqn "nqn.2016-06.io.spdk.umgmt:cnode1", '\000' <repeats 191 times>
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 <g_bdev_mgr+80>) 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 <spdk_spin_init+213> 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 <spdk_spin_lock+436> thread.c:2909
     0x40ca9c <bdev_name_add+129> /build/spdk/spdk-review-public/lib/bdev/bdev.c:3855
     0x411a3c <bdev_register+641> /build/spdk/spdk-review-public/lib/bdev/bdev.c:6660
     0x412e1e <spdk_bdev_register+24> /build/spdk/spdk-review-public/lib/bdev/bdev.c:7171
     0x417895 <num_blocks_test+119> bdev_ut.c:878
     0x7ffff7bc38cb <run_single_test.constprop+379>
     0x7ffff7bc3b61 <run_single_suite.constprop+433>
     0x7ffff7bc3f76 <CU_run_all_tests+118>
     0x43351f <main+1439> 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 <spdk_spin_lock+436> /build/spdk/spdk-review-public/lib/thread/thread.c:2909
 0x413f48 <spdk_spin+552> thread_ut.c:1831
 0x7ffff7bc38cb <run_single_test.constprop+379>
 0x7ffff7bc3b61 <run_single_suite.constprop+433>
 0x7ffff7bc3f76 <CU_run_all_tests+118>
 0x4148fa <main+547> 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.
+68 −0
Original line number Diff line number Diff line
@@ -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