14 """Print a folly::fibers::Fiber"""
16 def __init__(self, val):
19 state = self.val['state_']
20 d = gdb.types.make_enum_dict(state.type)
21 d = dict((v, k) for k, v in d.items())
22 self.state = d[int(state)]
24 def state_to_string(self):
25 if self.state == "folly::fibers::Fiber::INVALID":
27 if self.state == "folly::fibers::Fiber::NOT_STARTED":
29 if self.state == "folly::fibers::Fiber::READY_TO_RUN":
31 if self.state == "folly::fibers::Fiber::RUNNING":
33 if self.state == "folly::fibers::Fiber::AWAITING":
35 if self.state == "folly::fibers::Fiber::AWAITING_IMMEDIATE":
36 return "Awaiting immediate"
37 if self.state == "folly::fibers::Fiber::YIELDED":
41 def backtrace_available(self):
42 return self.state != "folly::fibers::Fiber::INVALID" and \
43 self.state != "folly::fibers::Fiber::NOT_STARTED" and \
44 self.state != "folly::fibers::Fiber::RUNNING"
47 result = collections.OrderedDict()
48 result["state"] = self.state_to_string()
49 result["backtrace available"] = self.backtrace_available()
53 return "folly::fibers::Fiber"
55 def display_hint(self):
56 return "folly::fibers::Fiber"
59 class GetFiberXMethodWorker(gdb.xmethod.XMethodWorker):
60 def get_arg_types(self):
61 return gdb.lookup_type('int')
63 def get_result_type(self):
64 return gdb.lookup_type('int')
66 def __call__(self, *args):
69 fiber = next(itertools.islice(fiber_manager_active_fibers(fm),
73 raise gdb.GdbError("Index out of range")
78 class GetFiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
80 super(GetFiberXMethodMatcher, self).__init__(
81 "Fiber address method matcher")
82 self.worker = GetFiberXMethodWorker()
84 def match(self, class_type, method_name):
85 if class_type.name == "folly::fibers::FiberManager" and \
86 method_name == "get_fiber":
91 def fiber_manager_active_fibers(fm):
93 fm['allFibers_']['data_']['root_plus_size_']['m_header']
94 fiber_hook = all_fibers['next_']
98 while fiber_hook != all_fibers.address:
99 fiber = fiber_hook.cast(gdb.lookup_type("int64_t"))
100 fiber = fiber - gdb.parse_and_eval(
101 "(int64_t)&'folly::fibers::Fiber'::globalListHook_")
103 gdb.lookup_type('folly::fibers::Fiber').pointer()).dereference()
105 if FiberPrinter(fiber).state != "folly::fibers::Fiber::INVALID":
108 fiber_hook = fiber_hook.dereference()['next_']
110 fiber_count = fiber_count + 1
113 class FiberManagerPrinter:
114 """Print a folly::fibers::Fiber"""
116 fiber_print_limit = 100
118 def __init__(self, fm):
122 def limit_with_dots(fibers_iterator):
124 for fiber in fibers_iterator:
125 if num_items >= self.fiber_print_limit:
129 yield (str(fiber.address), fiber)
132 return limit_with_dots(fiber_manager_active_fibers(self.fm))
135 return "folly::fibers::FiberManager"
137 def display_hint(self):
138 return "folly::fibers::FiberManager"
141 class FiberPrintLimitCommand(gdb.Command):
143 super(FiberPrintLimitCommand, self).__init__(
144 "fiber-print-limit", gdb.COMMAND_USER)
146 def invoke(self, arg, from_tty):
148 print("New limit has to be passed to 'fiber_print_limit' command")
150 FiberManagerPrinter.fiber_print_limit = int(arg)
151 print("New fiber limit for FiberManager printer set to " +
152 str(FiberManagerPrinter.fiber_print_limit))
155 class FrameId(object):
156 def __init__(self, sp, pc):
161 class FiberUnwinderFrameFilter:
165 def set_skip_frame_sp(cls, skip_frame_sp):
166 if cls.instance is None:
167 cls.instance = FiberUnwinderFrameFilter()
169 cls.instance.skip_frame_sp = skip_frame_sp
172 self.name = "FiberUnwinderFrameFilter"
175 gdb.frame_filters[self.name] = self
177 def filter(self, frame_iter):
178 if not self.skip_frame_sp:
181 return self.filter_impl(frame_iter)
183 def filter_impl(self, frame_iter):
184 for frame in frame_iter:
185 frame_sp = frame.inferior_frame().read_register("rsp")
186 if frame_sp == self.skip_frame_sp:
191 class FiberUnwinder(gdb.unwinder.Unwinder):
195 def set_fiber(cls, fiber):
196 if cls.instance is None:
197 cls.instance = FiberUnwinder()
198 gdb.unwinder.register_unwinder(None, cls.instance)
200 fiber_impl = fiber['fiberImpl_']
201 cls.instance.fiber_context_ptr = fiber_impl['fiberContext_']
204 super(FiberUnwinder, self).__init__("Fiber unwinder")
205 self.fiber_context_ptr = None
207 def __call__(self, pending_frame):
208 if not self.fiber_context_ptr:
211 orig_sp = pending_frame.read_register('rsp')
212 orig_pc = pending_frame.read_register('rip')
214 void_star_star = gdb.lookup_type('uint64_t').pointer()
215 ptr = self.fiber_context_ptr.cast(void_star_star)
217 # This code may need to be adjusted to newer versions of boost::context.
219 # The easiest way to get these offsets is to first attach to any
220 # program which uses folly::fibers and add a break point in
221 # boost::context::jump_fcontext. You then need to save information about
222 # frame 1 via 'info frame 1' command.
224 # After that you need to resume program until fiber switch is complete
225 # and expore the contents of saved fiber context via
226 # 'x/16gx {fiber pointer}->fiberImpl_.fiberContext_' command.
227 # You then need to match those to the following values you've previously
228 # observed in the output of 'info frame 1' command.
230 # Value found at "rbp at X" of 'info frame 1' output:
231 rbp = (ptr + 6).dereference()
232 # Value found at "rip = X" of 'info frame 1' output:
233 rip = (ptr + 7).dereference()
234 # Value found at "caller of frame at X" of 'info frame 1' output:
237 frame_id = FrameId(rsp, orig_pc)
238 unwind_info = pending_frame.create_unwind_info(frame_id)
239 unwind_info.add_saved_register('rbp', rbp)
240 unwind_info.add_saved_register('rsp', rsp)
241 unwind_info.add_saved_register('rip', rip)
243 self.fiber_context_ptr = None
245 FiberUnwinderFrameFilter.set_skip_frame_sp(orig_sp)
250 def fiber_activate(fiber):
251 fiber_type = gdb.lookup_type("folly::fibers::Fiber")
252 if fiber.type != fiber_type:
253 fiber = fiber.cast(fiber_type.pointer()).dereference()
254 if not FiberPrinter(fiber).backtrace_available():
255 return "Can not activate a non-waiting fiber."
256 FiberUnwinder.set_fiber(fiber)
257 return "Fiber 0x{:12x} activated. You can call 'bt' now.".format(int(fiber.address))
260 def fiber_deactivate():
261 FiberUnwinderFrameFilter.set_skip_frame_sp(None)
262 gdb.invalidate_cached_frames()
263 return "Fiber de-activated."
266 class FiberActivateCommand(gdb.Command):
268 super(FiberActivateCommand, self).__init__("fiber", gdb.COMMAND_USER)
270 def invoke(self, arg, from_tty):
272 print("folly::fibers::Fiber* has to be passed to 'fiber' command")
274 fiber_ptr = gdb.parse_and_eval(arg)
275 print(fiber_activate(fiber_ptr))
278 class FiberDeactivateCommand(gdb.Command):
280 super(FiberDeactivateCommand, self).__init__(
281 "fiber-deactivate", gdb.COMMAND_USER)
283 def invoke(self, arg, from_tty):
284 print(fiber_deactivate())
287 class FiberXMethodWorker(gdb.xmethod.XMethodWorker):
288 def get_arg_types(self):
291 def get_result_type(self):
294 def __call__(self, *args):
295 return fiber_activate(args[0])
298 class FiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
300 super(FiberXMethodMatcher, self).__init__("Fiber method matcher")
301 self.worker = FiberXMethodWorker()
303 def match(self, class_type, method_name):
304 if class_type.name == "folly::fibers::Fiber" and \
305 method_name == "activate":
310 class Shortcut(gdb.Function):
311 def __init__(self, function_name, value_lambda):
312 super(Shortcut, self).__init__(function_name)
313 self.value_lambda = value_lambda
316 return self.value_lambda()
319 def get_fiber_manager_map(evb_type):
321 # Exception thrown if unable to find type
322 # Probably because of missing debug symbols
323 global_cache_type = gdb.lookup_type(
324 "folly::fibers::(anonymous namespace)::GlobalCache<" + evb_type + ">")
326 raise gdb.GdbError("Unable to find types. "
327 "Please make sure debug info is available for this binary.\n"
328 "Have you run 'fbload debuginfo_fbpkg'?")
330 global_cache_instance_ptr_ptr = gdb.parse_and_eval(
331 "&'" + global_cache_type.name + "::instance()::ret'")
332 global_cache_instance_ptr = global_cache_instance_ptr_ptr.cast(
333 global_cache_type.pointer().pointer()).dereference()
334 if global_cache_instance_ptr == 0x0:
335 raise gdb.GdbError("FiberManager map is empty.")
337 global_cache_instance = global_cache_instance_ptr.dereference()
338 return global_cache_instance['map_']
341 def get_fiber_manager_map_evb():
342 return get_fiber_manager_map("folly::EventBase")
345 def get_fiber_manager_map_vevb():
346 return get_fiber_manager_map("folly::VirtualEventBase")
349 def build_pretty_printer():
350 pp = gdb.printing.RegexpCollectionPrettyPrinter("folly_fibers")
351 pp.add_printer('fibers::Fiber', '^folly::fibers::Fiber$', FiberPrinter)
352 pp.add_printer('fibers::FiberManager', '^folly::fibers::FiberManager$',
358 gdb.printing.register_pretty_printer(gdb, build_pretty_printer())
359 gdb.xmethod.register_xmethod_matcher(gdb, FiberXMethodMatcher())
360 gdb.xmethod.register_xmethod_matcher(gdb, GetFiberXMethodMatcher())
361 FiberPrintLimitCommand()
362 FiberActivateCommand()
363 FiberDeactivateCommand()
364 Shortcut("get_fiber_manager_map_evb", get_fiber_manager_map_evb)
365 Shortcut("get_fiber_manager_map_vevb", get_fiber_manager_map_vevb)
369 return "Pretty printers for folly::fibers"