Commit 8753361e authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

Move GDB scripts for fibers into folly

Reviewed By: igorsugak

Differential Revision: D4320762

fbshipit-source-id: 2028de6ccc6fb129381b03d694b88677a5aa50d6
parent 168487dc
#!/usr/bin/env python3
import gdb
import gdb.printing
import gdb.types
import gdb.unwinder
import gdb.xmethod
import collections
class FiberPrinter:
"""Print a folly::fibers::Fiber"""
def __init__(self, val):
self.val = val
state = self.val['state_']
d = gdb.types.make_enum_dict(state.type)
d = dict((v, k) for k, v in d.items())
self.state = d[int(state)]
def state_to_string(self):
if self.state == "folly::fibers::Fiber::INVALID":
return "Invalid"
if self.state == "folly::fibers::Fiber::NOT_STARTED":
return "Not started"
if self.state == "folly::fibers::Fiber::READY_TO_RUN":
return "Ready to run"
if self.state == "folly::fibers::Fiber::RUNNING":
return "Running"
if self.state == "folly::fibers::Fiber::AWAITING":
return "Awaiting"
if self.state == "folly::fibers::Fiber::AWAITING_IMMEDIATE":
return "Awaiting immediate"
if self.state == "folly::fibers::Fiber::YIELDED":
return "Yielded"
return "Unknown"
def backtrace_available(self):
return self.state != "folly::fibers::Fiber::INVALID" and \
self.state != "folly::fibers::Fiber::NOT_STARTED" and \
self.state != "folly::fibers::Fiber::RUNNING"
def children(self):
result = collections.OrderedDict()
result["state"] = self.state_to_string()
result["backtrace available"] = self.backtrace_available()
return result.items()
def to_string(self):
return "folly::fibers::Fiber"
def display_hint(self):
return "folly::fibers::Fiber"
class FiberManagerPrinter:
"""Print a folly::fibers::Fiber"""
def __init__(self, fm):
self.fm = fm
def children(self):
all_fibers = \
self.fm['allFibers_']['data_']['root_plus_size_']['m_header']
fiber_hook = all_fibers['next_']
active_fibers = collections.OrderedDict()
while fiber_hook != all_fibers.address:
fiber = fiber_hook.cast(gdb.lookup_type("int64_t"))
fiber = fiber - gdb.parse_and_eval(
"(int64_t)&folly::fibers::Fiber::globalListHook_")
fiber = fiber.cast(
gdb.lookup_type('folly::fibers::Fiber').pointer()).dereference()
if FiberPrinter(fiber).state != "folly::fibers::Fiber::INVALID":
active_fibers[str(fiber.address)] = fiber
fiber_hook = fiber_hook.dereference()['next_']
return active_fibers.items()
def to_string(self):
return "folly::fibers::FiberManager"
def display_hint(self):
return "folly::fibers::FiberManager"
class FrameId(object):
def __init__(self, sp, pc):
self.sp = sp
self.pc = pc
class FiberUnwinderFrameFilter:
instance = None
@classmethod
def set_skip_frame_sp(cls, skip_frame_sp):
if cls.instance is None:
cls.instance = FiberUnwinderFrameFilter()
cls.instance.skip_frame_sp = skip_frame_sp
def __init__(self):
self.name = "FiberUnwinderFrameFilter"
self.priority = 100
self.enabled = True
gdb.frame_filters[self.name] = self
def filter(self, frame_iter):
if not self.skip_frame_sp:
return frame_iter
return self.filter_impl(frame_iter)
def filter_impl(self, frame_iter):
for frame in frame_iter:
frame_sp = frame.inferior_frame().read_register("rsp")
if frame_sp == self.skip_frame_sp:
continue
yield frame
class FiberUnwinder(gdb.unwinder.Unwinder):
instance = None
@classmethod
def set_fiber(cls, fiber):
if cls.instance is None:
cls.instance = FiberUnwinder()
gdb.unwinder.register_unwinder(None, cls.instance)
fiber_impl = fiber['fiberImpl_']
cls.instance.fiber_context_ptr = fiber_impl['fiberContext_']
def __init__(self):
super(FiberUnwinder, self).__init__("Fiber unwinder")
self.fiber_context_ptr = None
def __call__(self, pending_frame):
if not self.fiber_context_ptr:
return None
orig_sp = pending_frame.read_register('rsp')
orig_pc = pending_frame.read_register('rip')
void_star_star = gdb.lookup_type('uint64_t').pointer()
ptr = self.fiber_context_ptr.cast(void_star_star)
# This code may need to be adjusted to newer versions of boost::context.
#
# The easiest way to get these offsets is to first attach to any
# program which uses folly::fibers and add a break point in
# boost::context::jump_fcontext. You then need to save information about
# frame 1 via 'info frame 1' command.
#
# After that you need to resume program until fiber switch is complete
# and expore the contents of saved fiber context via
# 'x/16gx {fiber pointer}->fiberImpl_.fiberContext_' command.
# You then need to match those to the following values you've previously
# observed in the output of 'info frame 1' command.
#
# Value found at "rbp at X" of 'info frame 1' output:
rbp = (ptr + 6).dereference()
# Value found at "rip = X" of 'info frame 1' output:
rip = (ptr + 7).dereference()
# Value found at "caller of frame at X" of 'info frame 1' output:
rsp = rbp - 96
frame_id = FrameId(rsp, orig_pc)
unwind_info = pending_frame.create_unwind_info(frame_id)
unwind_info.add_saved_register('rbp', rbp)
unwind_info.add_saved_register('rsp', rsp)
unwind_info.add_saved_register('rip', rip)
self.fiber_context_ptr = None
FiberUnwinderFrameFilter.set_skip_frame_sp(orig_sp)
return unwind_info
def fiber_activate(fiber_ptr):
fiber_type = gdb.lookup_type("folly::fibers::Fiber")
fiber = fiber_ptr.cast(fiber_type.pointer()).dereference()
if not FiberPrinter(fiber).backtrace_available():
return "Can not activate a non-waiting fiber."
FiberUnwinder.set_fiber(fiber)
return "Fiber " + str(fiber_ptr) + " activated. You can call 'bt' now."
def fiber_deactivate():
FiberUnwinderFrameFilter.set_skip_frame_sp(None)
gdb.invalidate_cached_frames()
return "Fiber de-activated."
class FiberActivateCommand(gdb.Command):
def __init__(self):
super(FiberActivateCommand, self).__init__("fiber", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
if not arg:
print("folly::fibers::Fiber* has to be passed to 'fiber' command")
return
fiber_ptr = gdb.parse_and_eval(arg)
print(fiber_activate(fiber_ptr))
class FiberDeactivateCommand(gdb.Command):
def __init__(self):
super(FiberDeactivateCommand, self).__init__(
"fiber-deactivate", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
print(fiber_deactivate())
class FiberXMethodWorker(gdb.xmethod.XMethodWorker):
def get_arg_types(self):
return None
def get_result_type(self):
return None
def __call__(self, *args):
return fiber_activate(args[0])
class FiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
def __init__(self):
super(FiberXMethodMatcher, self).__init__("Fiber method matcher")
self.worker = FiberXMethodWorker()
def match(self, class_type, method_name):
if class_type.name == "folly::fibers::Fiber" and \
method_name == "activate":
return self.worker
return None
class Shortcut(gdb.Function):
def __init__(self, function_name, value_lambda):
super(Shortcut, self).__init__(function_name)
self.value_lambda = value_lambda
def invoke(self):
return self.value_lambda()
def get_fiber_manager_map(evb_type):
global_cache_type = gdb.lookup_type(
"folly::fibers::(anonymous namespace)::GlobalCache<" + evb_type + ">")
global_cache_instance_ptr_ptr = gdb.parse_and_eval(
"&'" + global_cache_type.name + "::instance()::ret'")
global_cache_instance = global_cache_instance_ptr_ptr.cast(
global_cache_type.pointer().pointer()).dereference().dereference()
return global_cache_instance['map_']
def get_fiber_manager_map_evb():
return get_fiber_manager_map("folly::EventBase")
def get_fiber_manager_map_vevb():
return get_fiber_manager_map("folly::VirtualEventBase")
def build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter("folly_fibers")
pp.add_printer('fibers::Fiber', '^folly::fibers::Fiber$', FiberPrinter)
pp.add_printer('fibers::FiberManager', '^folly::fibers::FiberManager$',
FiberManagerPrinter)
return pp
def load():
gdb.printing.register_pretty_printer(gdb, build_pretty_printer())
gdb.xmethod.register_xmethod_matcher(gdb, FiberXMethodMatcher())
FiberActivateCommand()
FiberDeactivateCommand()
Shortcut("get_fiber_manager_map_evb", get_fiber_manager_map_evb)
Shortcut("get_fiber_manager_map_vevb", get_fiber_manager_map_vevb)
def info():
return "Pretty printers for folly::fibers"
# Print given Fiber state
# arg0 folly::fibers::Fiber*
define print_folly_fiber_state
set $fiber = (folly::fibers::Fiber*)$arg0
if $fiber->state_ == folly::fibers::Fiber::INVALID
printf "Invalid"
end
if $fiber->state_ == folly::fibers::Fiber::NOT_STARTED
printf "Not started"
end
if $fiber->state_ == folly::fibers::Fiber::READY_TO_RUN
printf "Ready to run"
end
if $fiber->state_ == folly::fibers::Fiber::RUNNING
printf "Running"
end
if $fiber->state_ == folly::fibers::Fiber::AWAITING
printf "Awaiting"
end
if $fiber->state_ == folly::fibers::Fiber::AWAITING_IMMEDIATE
printf "Awaiting immediate"
end
if $fiber->state_ == folly::fibers::Fiber::YIELDED
printf "Yielded"
end
end
# Print given Fiber
# arg0 folly::fibers::Fiber*
define print_folly_fiber
set $fiber = (folly::fibers::Fiber*)$arg0
printf " (folly::fibers::Fiber*)%p\n\n", $fiber
printf " State: "
print_folly_fiber_state $fiber
printf "\n"
if $fiber->state_ != folly::fibers::Fiber::INVALID && \
$fiber->state_ != folly::fibers::Fiber::NOT_STARTED && \
$fiber->state_ != folly::fibers::Fiber::RUNNING
printf " Backtrace:\n"
set $frameptr = ((uint64_t*)$fiber->fiberImpl_.fiberContext_)[6]
set $k = 0
while $frameptr != 0
printf " #%d at %p in ", $k, *((void**)($frameptr+8))
set $k = $k + 1
info symbol *((void**)($frameptr+8))
set $frameptr = *((void**)($frameptr))
end
end
end
# Print given FiberManager
# arg0 folly::fibers::FiberManager*
define print_folly_fiber_manager
set $fiberManager = (folly::fibers::FiberManager*)$arg0
printf " (folly::fibers::FiberManager*)%p\n\n", $fiberManager
printf " Fibers active: %d\n", $fiberManager->fibersActive_
printf " Fibers allocated: %d\n", $fiberManager->fibersAllocated_
printf " Fibers pool size: %d\n", $fiberManager->fibersPoolSize_
printf " Active fiber: (folly::fibers::Fiber*)%p\n", \
$fiberManager->activeFiber_
printf " Current fiber: (folly::fibers::Fiber*)%p\n", \
$fiberManager->currentFiber_
set $all_fibers = &($fiberManager->allFibers_.data_.root_plus_size_.m_header)
set $fiber_hook = $all_fibers->next_
printf "\n Active fibers:\n"
while $fiber_hook != $all_fibers
set $fiber = (folly::fibers::Fiber*) \
((int64_t)$fiber_hook - \
(int64_t)&folly::fibers::Fiber::globalListHook_)
if $fiber->state_ != folly::fibers::Fiber::INVALID
printf " (folly::fibers::Fiber*)%p State: ", $fiber
print_folly_fiber_state $fiber
printf "\n"
end
set $fiber_hook = $fiber_hook->next_
end
end
# Print global FiberManager map
define print_folly_fiber_manager_map
set $global_cache=*(('folly::fibers::(anonymous namespace)::GlobalCache<folly::EventBase>'**) \
&'folly::fibers::(anonymous namespace)::GlobalCache<folly::EventBase>::instance()::ret')
printf " Global FiberManager map has %d entries.\n", \
$global_cache->map_->_M_h->_M_element_count
set $item = $global_cache->map_->_M_h->_M_before_begin._M_nxt
while $item != 0
set $evb = ((folly::EventBase**)$item)[1]
set $fiberManager = ((folly::fibers::FiberManager**)$item)[2]
printf " (folly::EventBase*)%p -> (folly::fibers::FiberManager*)%p\n", \
$evb, $fiberManager
set $item = $item->_M_nxt
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment