From: Andrii Grynenko Date: Tue, 13 Dec 2016 21:57:32 +0000 (-0800) Subject: Move GDB scripts for fibers into folly X-Git-Tag: v2016.12.19.00~24 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=8753361e48dbb5918d5f1837fef6f59292f9327a;p=folly.git Move GDB scripts for fibers into folly Reviewed By: igorsugak Differential Revision: D4320762 fbshipit-source-id: 2028de6ccc6fb129381b03d694b88677a5aa50d6 --- diff --git a/folly/fibers/scripts/gdb.py b/folly/fibers/scripts/gdb.py new file mode 100644 index 00000000..1f5e9f95 --- /dev/null +++ b/folly/fibers/scripts/gdb.py @@ -0,0 +1,291 @@ +#!/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" diff --git a/folly/fibers/scripts/utils.gdb b/folly/fibers/scripts/utils.gdb deleted file mode 100644 index a3bd0c8c..00000000 --- a/folly/fibers/scripts/utils.gdb +++ /dev/null @@ -1,99 +0,0 @@ -# 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::fibers::(anonymous namespace)::GlobalCache::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