scylla-gdb.py: scylla fiber: add support for walking over seastar threads

Currently seastar threads end any attempt to follow waited-on-futures.
Seastar threads need special handling because it allocates the wake up
task on its stack. This patch adds this special handling.
This commit is contained in:
Botond Dénes
2022-10-19 16:43:14 +03:00
parent fcc63965ed
commit 437f888ba0

View File

@@ -3608,10 +3608,24 @@ class scylla_fiber(gdb.Command):
return ptr_meta, maybe_vptr, resolved_symbol
# Find futures waiting on this task
def _walk_forward(self, ptr_meta, i, max_depth, scanned_region_size, using_seastar_allocator, verbose):
def _walk_forward(self, ptr_meta, name, i, max_depth, scanned_region_size, using_seastar_allocator, verbose):
ptr = ptr_meta.ptr
region_start = ptr + self._vptr_type.sizeof # ignore our own vtable
region_end = region_start + (ptr_meta.size - ptr_meta.size % self._vptr_type.sizeof)
# thread_context has a self reference in its `_func` field which will be
# found before the ref to the next task if the whole object is scanned.
# Exploit that thread_context is a type we can cast to and scan the
# `_done` field explicitly to avoid that.
if 'thread_context' in name:
self._maybe_log("Current task is a thread, using its _done field to continue\n", verbose)
thread_context_ptr_type = gdb.lookup_type('seastar::thread_context').pointer()
ctxt = gdb.Value(int(ptr_meta.ptr)).reinterpret_cast(thread_context_ptr_type).dereference()
pr = ctxt['_done']
region_start = pr.address
region_end = region_start + pr.type.sizeof
else:
region_start = ptr + self._vptr_type.sizeof # ignore our own vtable
region_end = region_start + (ptr_meta.size - ptr_meta.size % self._vptr_type.sizeof)
self._maybe_log("Scanning task #{} @ 0x{:016x}: {}\n".format(i, ptr, str(ptr_meta)), verbose)
for it in range(region_start, region_end, self._vptr_type.sizeof):
@@ -3626,8 +3640,22 @@ class scylla_fiber(gdb.Command):
return None
# Find futures waited-on by this task
def _walk_backward(self, ptr_meta, i, max_depth, scanned_region_size, using_seastar_allocator, verbose):
def _walk_backward(self, ptr_meta, name, i, max_depth, scanned_region_size, using_seastar_allocator, verbose):
res = None
# Threads need special handling as they allocate the thread_wait_task object on their stack.
if 'thread_context' in name:
context = gdb.parse_and_eval('(seastar::thread_context*)0x{:x}'.format(ptr_meta.ptr))
stack_ptr = int(std_unique_ptr(context['_stack']).get())
self._maybe_log("Current task is a thread, trying to find the thread_wake_task on its stack: 0x{:x}\n".format(stack_ptr), verbose)
stack_meta = scylla_ptr.analyze(stack_ptr)
# stack grows downwards, so walk from end of buffer towards the beginning
for maybe_tptr in range(stack_ptr + stack_meta.size - self._vptr_type.sizeof, stack_ptr - self._vptr_type.sizeof, -self._vptr_type.sizeof):
res = self._probe_pointer(maybe_tptr, scanned_region_size, using_seastar_allocator, verbose)
if not res is None and 'thread_wake_task' in res[2]:
return res
return None
for maybe_tptr_meta, _ in scylla_find.find(ptr_meta.ptr):
maybe_tptr_meta.ptr -= maybe_tptr_meta.offset_in_object
res = self._probe_pointer(maybe_tptr_meta.ptr, scanned_region_size, using_seastar_allocator, verbose)
@@ -3635,7 +3663,7 @@ class scylla_fiber(gdb.Command):
return res
return res
def _walk(self, walk_method, tptr_meta, max_depth, scanned_region_size, using_seastar_allocator, verbose):
def _walk(self, walk_method, tptr_meta, name, max_depth, scanned_region_size, using_seastar_allocator, verbose):
i = 0
fiber = []
known_tasks = set()
@@ -3643,7 +3671,8 @@ class scylla_fiber(gdb.Command):
if max_depth > -1 and i >= max_depth:
break
res = walk_method(tptr_meta, i + 1, max_depth, scanned_region_size, using_seastar_allocator, verbose)
self._maybe_log("_walk() 0x{:x} {}\n".format(int(tptr_meta.ptr), name), verbose)
res = walk_method(tptr_meta, name, i + 1, max_depth, scanned_region_size, using_seastar_allocator, verbose)
if res is None:
break
@@ -3692,7 +3721,7 @@ class scylla_fiber(gdb.Command):
gdb.write("Provided pointer 0x{:016x} is not an object managed by seastar or not a task pointer\n".format(initial_task_ptr))
return
backwards_fiber = self._walk(self._walk_backward, this_task[0], args.max_depth, args.scanned_region_size, using_seastar_allocator, args.verbose)
backwards_fiber = self._walk(self._walk_backward, this_task[0], this_task[2], args.max_depth, args.scanned_region_size, using_seastar_allocator, args.verbose)
for i, (tptr, vptr, name) in enumerate(reversed(backwards_fiber)):
gdb.write("#{:<2d} (task*) 0x{:016x} 0x{:016x} {}\n".format(i - len(backwards_fiber), int(tptr), int(vptr), name))
@@ -3700,7 +3729,7 @@ class scylla_fiber(gdb.Command):
tptr, vptr, name = this_task
gdb.write("#0 (task*) 0x{:016x} 0x{:016x} {}\n".format(int(tptr.ptr), int(vptr), name))
forward_fiber = self._walk(self._walk_forward, this_task[0], args.max_depth, args.scanned_region_size, using_seastar_allocator, args.verbose)
forward_fiber = self._walk(self._walk_forward, this_task[0], this_task[2], args.max_depth, args.scanned_region_size, using_seastar_allocator, args.verbose)
for i, (tptr, vptr, name) in enumerate(forward_fiber):
gdb.write("#{:<2d} (task*) 0x{:016x} 0x{:016x} {}\n".format(i + 1, int(tptr), int(vptr), name))