13 """Print a folly::fibers::Fiber"""
15 def __init__(self, val):
18 state = self.val['state_']
19 d = gdb.types.make_enum_dict(state.type)
20 d = dict((v, k) for k, v in d.items())
21 self.state = d[int(state)]
23 def state_to_string(self):
24 if self.state == "folly::fibers::Fiber::INVALID":
26 if self.state == "folly::fibers::Fiber::NOT_STARTED":
28 if self.state == "folly::fibers::Fiber::READY_TO_RUN":
30 if self.state == "folly::fibers::Fiber::RUNNING":
32 if self.state == "folly::fibers::Fiber::AWAITING":
34 if self.state == "folly::fibers::Fiber::AWAITING_IMMEDIATE":
35 return "Awaiting immediate"
36 if self.state == "folly::fibers::Fiber::YIELDED":
40 def backtrace_available(self):
41 return self.state != "folly::fibers::Fiber::INVALID" and \
42 self.state != "folly::fibers::Fiber::NOT_STARTED" and \
43 self.state != "folly::fibers::Fiber::RUNNING"
46 result = collections.OrderedDict()
47 result["state"] = self.state_to_string()
48 result["backtrace available"] = self.backtrace_available()
52 return "folly::fibers::Fiber"
54 def display_hint(self):
55 return "folly::fibers::Fiber"
58 class FiberManagerPrinter:
59 """Print a folly::fibers::Fiber"""
61 fiber_print_limit = 100
63 def __init__(self, fm):
68 self.fm['allFibers_']['data_']['root_plus_size_']['m_header']
69 fiber_hook = all_fibers['next_']
71 active_fibers = collections.OrderedDict()
75 while fiber_hook != all_fibers.address:
76 if fiber_count == FiberManagerPrinter.fiber_print_limit:
77 active_fibers["..."] = "..."
80 fiber = fiber_hook.cast(gdb.lookup_type("int64_t"))
81 fiber = fiber - gdb.parse_and_eval(
82 "(int64_t)&'folly::fibers::Fiber'::globalListHook_")
84 gdb.lookup_type('folly::fibers::Fiber').pointer()).dereference()
86 if FiberPrinter(fiber).state != "folly::fibers::Fiber::INVALID":
87 active_fibers[str(fiber.address)] = fiber
89 fiber_hook = fiber_hook.dereference()['next_']
91 fiber_count = fiber_count + 1
93 return active_fibers.items()
96 return "folly::fibers::FiberManager"
98 def display_hint(self):
99 return "folly::fibers::FiberManager"
102 class FiberPrintLimitCommand(gdb.Command):
104 super(FiberPrintLimitCommand, self).__init__(
105 "fiber-print-limit", gdb.COMMAND_USER)
107 def invoke(self, arg, from_tty):
109 print("New limit has to be passed to 'fiber_print_limit' command")
111 FiberManagerPrinter.fiber_print_limit = int(arg)
112 print("New fiber limit for FiberManager printer set to " +
113 str(FiberManagerPrinter.fiber_print_limit))
116 class FrameId(object):
117 def __init__(self, sp, pc):
122 class FiberUnwinderFrameFilter:
126 def set_skip_frame_sp(cls, skip_frame_sp):
127 if cls.instance is None:
128 cls.instance = FiberUnwinderFrameFilter()
130 cls.instance.skip_frame_sp = skip_frame_sp
133 self.name = "FiberUnwinderFrameFilter"
136 gdb.frame_filters[self.name] = self
138 def filter(self, frame_iter):
139 if not self.skip_frame_sp:
142 return self.filter_impl(frame_iter)
144 def filter_impl(self, frame_iter):
145 for frame in frame_iter:
146 frame_sp = frame.inferior_frame().read_register("rsp")
147 if frame_sp == self.skip_frame_sp:
152 class FiberUnwinder(gdb.unwinder.Unwinder):
156 def set_fiber(cls, fiber):
157 if cls.instance is None:
158 cls.instance = FiberUnwinder()
159 gdb.unwinder.register_unwinder(None, cls.instance)
161 fiber_impl = fiber['fiberImpl_']
162 cls.instance.fiber_context_ptr = fiber_impl['fiberContext_']
165 super(FiberUnwinder, self).__init__("Fiber unwinder")
166 self.fiber_context_ptr = None
168 def __call__(self, pending_frame):
169 if not self.fiber_context_ptr:
172 orig_sp = pending_frame.read_register('rsp')
173 orig_pc = pending_frame.read_register('rip')
175 void_star_star = gdb.lookup_type('uint64_t').pointer()
176 ptr = self.fiber_context_ptr.cast(void_star_star)
178 # This code may need to be adjusted to newer versions of boost::context.
180 # The easiest way to get these offsets is to first attach to any
181 # program which uses folly::fibers and add a break point in
182 # boost::context::jump_fcontext. You then need to save information about
183 # frame 1 via 'info frame 1' command.
185 # After that you need to resume program until fiber switch is complete
186 # and expore the contents of saved fiber context via
187 # 'x/16gx {fiber pointer}->fiberImpl_.fiberContext_' command.
188 # You then need to match those to the following values you've previously
189 # observed in the output of 'info frame 1' command.
191 # Value found at "rbp at X" of 'info frame 1' output:
192 rbp = (ptr + 6).dereference()
193 # Value found at "rip = X" of 'info frame 1' output:
194 rip = (ptr + 7).dereference()
195 # Value found at "caller of frame at X" of 'info frame 1' output:
198 frame_id = FrameId(rsp, orig_pc)
199 unwind_info = pending_frame.create_unwind_info(frame_id)
200 unwind_info.add_saved_register('rbp', rbp)
201 unwind_info.add_saved_register('rsp', rsp)
202 unwind_info.add_saved_register('rip', rip)
204 self.fiber_context_ptr = None
206 FiberUnwinderFrameFilter.set_skip_frame_sp(orig_sp)
211 def fiber_activate(fiber_ptr):
212 fiber_type = gdb.lookup_type("folly::fibers::Fiber")
213 fiber = fiber_ptr.cast(fiber_type.pointer()).dereference()
214 if not FiberPrinter(fiber).backtrace_available():
215 return "Can not activate a non-waiting fiber."
216 FiberUnwinder.set_fiber(fiber)
217 return "Fiber " + str(fiber_ptr) + " activated. You can call 'bt' now."
220 def fiber_deactivate():
221 FiberUnwinderFrameFilter.set_skip_frame_sp(None)
222 gdb.invalidate_cached_frames()
223 return "Fiber de-activated."
226 class FiberActivateCommand(gdb.Command):
228 super(FiberActivateCommand, self).__init__("fiber", gdb.COMMAND_USER)
230 def invoke(self, arg, from_tty):
232 print("folly::fibers::Fiber* has to be passed to 'fiber' command")
234 fiber_ptr = gdb.parse_and_eval(arg)
235 print(fiber_activate(fiber_ptr))
238 class FiberDeactivateCommand(gdb.Command):
240 super(FiberDeactivateCommand, self).__init__(
241 "fiber-deactivate", gdb.COMMAND_USER)
243 def invoke(self, arg, from_tty):
244 print(fiber_deactivate())
247 class FiberXMethodWorker(gdb.xmethod.XMethodWorker):
248 def get_arg_types(self):
251 def get_result_type(self):
254 def __call__(self, *args):
255 return fiber_activate(args[0])
258 class FiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
260 super(FiberXMethodMatcher, self).__init__("Fiber method matcher")
261 self.worker = FiberXMethodWorker()
263 def match(self, class_type, method_name):
264 if class_type.name == "folly::fibers::Fiber" and \
265 method_name == "activate":
270 class Shortcut(gdb.Function):
271 def __init__(self, function_name, value_lambda):
272 super(Shortcut, self).__init__(function_name)
273 self.value_lambda = value_lambda
276 return self.value_lambda()
279 def get_fiber_manager_map(evb_type):
280 global_cache_type = gdb.lookup_type(
281 "folly::fibers::(anonymous namespace)::GlobalCache<" + evb_type + ">")
282 global_cache_instance_ptr_ptr = gdb.parse_and_eval(
283 "&'" + global_cache_type.name + "::instance()::ret'")
284 global_cache_instance = global_cache_instance_ptr_ptr.cast(
285 global_cache_type.pointer().pointer()).dereference().dereference()
286 return global_cache_instance['map_']
289 def get_fiber_manager_map_evb():
290 return get_fiber_manager_map("folly::EventBase")
293 def get_fiber_manager_map_vevb():
294 return get_fiber_manager_map("folly::VirtualEventBase")
297 def build_pretty_printer():
298 pp = gdb.printing.RegexpCollectionPrettyPrinter("folly_fibers")
299 pp.add_printer('fibers::Fiber', '^folly::fibers::Fiber$', FiberPrinter)
300 pp.add_printer('fibers::FiberManager', '^folly::fibers::FiberManager$',
306 gdb.printing.register_pretty_printer(gdb, build_pretty_printer())
307 gdb.xmethod.register_xmethod_matcher(gdb, FiberXMethodMatcher())
308 FiberPrintLimitCommand()
309 FiberActivateCommand()
310 FiberDeactivateCommand()
311 Shortcut("get_fiber_manager_map_evb", get_fiber_manager_map_evb)
312 Shortcut("get_fiber_manager_map_vevb", get_fiber_manager_map_vevb)
316 return "Pretty printers for folly::fibers"