proxygen
gdb.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 
4 import gdb
5 import gdb.printing
6 import gdb.types
7 import gdb.unwinder
8 import gdb.xmethod
9 import collections
10 import itertools
11 
12 
14  """Print a folly::fibers::Fiber"""
15 
16  def __init__(self, val):
17  self.val = val
18 
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)]
23 
24  def state_to_string(self):
25  if self.state == "folly::fibers::Fiber::INVALID":
26  return "Invalid"
27  if self.state == "folly::fibers::Fiber::NOT_STARTED":
28  return "Not started"
29  if self.state == "folly::fibers::Fiber::READY_TO_RUN":
30  return "Ready to run"
31  if self.state == "folly::fibers::Fiber::RUNNING":
32  return "Running"
33  if self.state == "folly::fibers::Fiber::AWAITING":
34  return "Awaiting"
35  if self.state == "folly::fibers::Fiber::AWAITING_IMMEDIATE":
36  return "Awaiting immediate"
37  if self.state == "folly::fibers::Fiber::YIELDED":
38  return "Yielded"
39  return "Unknown"
40 
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"
45 
46  def children(self):
47  result = collections.OrderedDict()
48  result["state"] = self.state_to_string()
49  result["backtrace available"] = self.backtrace_available()
50  return result.items()
51 
52  def to_string(self):
53  return "folly::fibers::Fiber"
54 
55  def display_hint(self):
56  return "folly::fibers::Fiber"
57 
58 
59 class GetFiberXMethodWorker(gdb.xmethod.XMethodWorker):
60  def get_arg_types(self):
61  return gdb.lookup_type('int')
62 
63  def get_result_type(self):
64  return gdb.lookup_type('int')
65 
66  def __call__(self, *args):
67  fm = args[0]
68  index = int(args[1])
69  fiber = next(itertools.islice(fiber_manager_active_fibers(fm),
70  index,
71  None))
72  if fiber is None:
73  raise gdb.GdbError("Index out of range")
74  else:
75  return fiber
76 
77 
78 class GetFiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
79  def __init__(self):
80  super(GetFiberXMethodMatcher, self).__init__(
81  "Fiber address method matcher")
83 
84  def match(self, class_type, method_name):
85  if class_type.name == "folly::fibers::FiberManager" and \
86  method_name == "get_fiber":
87  return self.worker
88  return None
89 
90 
92  all_fibers = \
93  fm['allFibers_']['data_']['root_plus_size_']['m_header']
94  fiber_hook = all_fibers['next_']
95 
96  fiber_count = 0
97 
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_")
102  fiber = fiber.cast(
103  gdb.lookup_type('folly::fibers::Fiber').pointer()).dereference()
104 
105  if FiberPrinter(fiber).state != "folly::fibers::Fiber::INVALID":
106  yield fiber
107 
108  fiber_hook = fiber_hook.dereference()['next_']
109 
110  fiber_count = fiber_count + 1
111 
112 
114  """Print a folly::fibers::Fiber"""
115 
116  fiber_print_limit = 100
117 
118  def __init__(self, fm):
119  self.fm = fm
120 
121  def children(self):
122  def limit_with_dots(fibers_iterator):
123  num_items = 0
124  for fiber in fibers_iterator:
125  if num_items >= self.fiber_print_limit:
126  yield ('...', '...')
127  return
128 
129  yield (str(fiber.address), fiber)
130  num_items += 1
131 
132  return limit_with_dots(fiber_manager_active_fibers(self.fm))
133 
134  def to_string(self):
135  return "folly::fibers::FiberManager"
136 
137  def display_hint(self):
138  return "folly::fibers::FiberManager"
139 
140 
141 class FiberPrintLimitCommand(gdb.Command):
142  def __init__(self):
143  super(FiberPrintLimitCommand, self).__init__(
144  "fiber-print-limit", gdb.COMMAND_USER)
145 
146  def invoke(self, arg, from_tty):
147  if not arg:
148  print("New limit has to be passed to 'fiber_print_limit' command")
149  return
150  FiberManagerPrinter.fiber_print_limit = int(arg)
151  print("New fiber limit for FiberManager printer set to " +
152  str(FiberManagerPrinter.fiber_print_limit))
153 
154 
156  def __init__(self, sp, pc):
157  self.sp = sp
158  self.pc = pc
159 
160 
162  instance = None
163 
164  @classmethod
165  def set_skip_frame_sp(cls, skip_frame_sp):
166  if cls.instance is None:
168 
169  cls.instance.skip_frame_sp = skip_frame_sp
170 
171  def __init__(self):
172  self.name = "FiberUnwinderFrameFilter"
173  self.priority = 100
174  self.enabled = True
175  gdb.frame_filters[self.name] = self
176 
177  def filter(self, frame_iter):
178  if not self.skip_frame_sp:
179  return frame_iter
180 
181  return self.filter_impl(frame_iter)
182 
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:
187  continue
188  yield frame
189 
190 
191 class FiberUnwinder(gdb.unwinder.Unwinder):
192  instance = None
193 
194  @classmethod
195  def set_fiber(cls, fiber):
196  if cls.instance is None:
197  cls.instance = FiberUnwinder()
198  gdb.unwinder.register_unwinder(None, cls.instance)
199 
200  fiber_impl = fiber['fiberImpl_']
201  cls.instance.fiber_context_ptr = fiber_impl['fiberContext_']
202 
203  def __init__(self):
204  super(FiberUnwinder, self).__init__("Fiber unwinder")
205  self.fiber_context_ptr = None
206 
207  def __call__(self, pending_frame):
208  if not self.fiber_context_ptr:
209  return None
210 
211  orig_sp = pending_frame.read_register('rsp')
212  orig_pc = pending_frame.read_register('rip')
213 
214  void_star_star = gdb.lookup_type('uint64_t').pointer()
215  ptr = self.fiber_context_ptr.cast(void_star_star)
216 
217  # This code may need to be adjusted to newer versions of boost::context.
218  #
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.
223  #
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.
229  #
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:
235  rsp = rbp - 96
236 
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)
242 
243  self.fiber_context_ptr = None
244 
245  FiberUnwinderFrameFilter.set_skip_frame_sp(orig_sp)
246 
247  return unwind_info
248 
249 
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  gdb.invalidate_cached_frames()
257  FiberUnwinder.set_fiber(fiber)
258  return "Fiber 0x{:12x} activated. You can call 'bt' now.".format(int(fiber.address))
259 
260 
262  FiberUnwinderFrameFilter.set_skip_frame_sp(None)
263  gdb.invalidate_cached_frames()
264  return "Fiber de-activated."
265 
266 
267 class FiberActivateCommand(gdb.Command):
268  def __init__(self):
269  super(FiberActivateCommand, self).__init__("fiber", gdb.COMMAND_USER)
270 
271  def invoke(self, arg, from_tty):
272  if not arg:
273  print("folly::fibers::Fiber* has to be passed to 'fiber' command")
274  return
275  fiber_ptr = gdb.parse_and_eval(arg)
276  print(fiber_activate(fiber_ptr))
277 
278 
279 class FiberDeactivateCommand(gdb.Command):
280  def __init__(self):
281  super(FiberDeactivateCommand, self).__init__(
282  "fiber-deactivate", gdb.COMMAND_USER)
283 
284  def invoke(self, arg, from_tty):
285  print(fiber_deactivate())
286 
287 
288 class FiberXMethodWorker(gdb.xmethod.XMethodWorker):
289  def get_arg_types(self):
290  return None
291 
292  def get_result_type(self):
293  return None
294 
295  def __call__(self, *args):
296  return fiber_activate(args[0])
297 
298 
299 class FiberXMethodMatcher(gdb.xmethod.XMethodMatcher):
300  def __init__(self):
301  super(FiberXMethodMatcher, self).__init__("Fiber method matcher")
303 
304  def match(self, class_type, method_name):
305  if class_type.name == "folly::fibers::Fiber" and \
306  method_name == "activate":
307  return self.worker
308  return None
309 
310 
311 class Shortcut(gdb.Function):
312  def __init__(self, function_name, value_lambda):
313  super(Shortcut, self).__init__(function_name)
314  self.value_lambda = value_lambda
315 
316  def invoke(self):
317  return self.value_lambda()
318 
319 
320 def get_fiber_manager_map(evb_type):
321  try:
322  # Exception thrown if unable to find type
323  # Probably because of missing debug symbols
324  global_cache_type = gdb.lookup_type(
325  "folly::fibers::(anonymous namespace)::GlobalCache<" + evb_type + ">")
326  except gdb.error:
327  raise gdb.GdbError("Unable to find types. "
328  "Please make sure debug info is available for this binary.\n"
329  "Have you run 'fbload debuginfo_fbpkg'?")
330 
331  global_cache_instance_ptr_ptr = gdb.parse_and_eval(
332  "&'" + global_cache_type.name + "::instance()::ret'")
333  global_cache_instance_ptr = global_cache_instance_ptr_ptr.cast(
334  global_cache_type.pointer().pointer()).dereference()
335  if global_cache_instance_ptr == 0x0:
336  raise gdb.GdbError("FiberManager map is empty.")
337 
338  global_cache_instance = global_cache_instance_ptr.dereference()
339  return global_cache_instance['map_']
340 
341 
343  return get_fiber_manager_map("folly::EventBase")
344 
345 
347  return get_fiber_manager_map("folly::VirtualEventBase")
348 
349 
351  pp = gdb.printing.RegexpCollectionPrettyPrinter("folly_fibers")
352  pp.add_printer('fibers::Fiber', '^folly::fibers::Fiber$', FiberPrinter)
353  pp.add_printer('fibers::FiberManager', '^folly::fibers::FiberManager$',
354  FiberManagerPrinter)
355  return pp
356 
357 
358 def load():
359  gdb.printing.register_pretty_printer(gdb, build_pretty_printer())
360  gdb.xmethod.register_xmethod_matcher(gdb, FiberXMethodMatcher())
361  gdb.xmethod.register_xmethod_matcher(gdb, GetFiberXMethodMatcher())
365  Shortcut("get_fiber_manager_map_evb", get_fiber_manager_map_evb)
366  Shortcut("get_fiber_manager_map_vevb", get_fiber_manager_map_vevb)
367 
368 
369 def info():
370  return "Pretty printers for folly::fibers"
def get_result_type(self)
Definition: gdb.py:292
def to_string(self)
Definition: gdb.py:134
def build_pretty_printer()
Definition: gdb.py:350
def get_arg_types(self)
Definition: gdb.py:289
void * object
Definition: AtFork.cpp:32
def get_result_type(self)
Definition: gdb.py:63
def backtrace_available(self)
Definition: gdb.py:41
def __init__(self)
Definition: gdb.py:280
def __init__(self, function_name, value_lambda)
Definition: gdb.py:312
def get_fiber_manager_map_vevb()
Definition: gdb.py:346
def __init__(self)
Definition: gdb.py:142
def invoke(self, arg, from_tty)
Definition: gdb.py:284
def get_fiber_manager_map(evb_type)
Definition: gdb.py:320
def get_fiber_manager_map_evb()
Definition: gdb.py:342
def __call__(self, args)
Definition: gdb.py:295
def match(self, class_type, method_name)
Definition: gdb.py:84
def children(self)
Definition: gdb.py:121
def invoke(self, arg, from_tty)
Definition: gdb.py:146
def __init__(self, val)
Definition: gdb.py:16
def invoke(self)
Definition: gdb.py:316
def __init__(self)
Definition: gdb.py:203
def set_fiber(cls, fiber)
Definition: gdb.py:195
def fiber_manager_active_fibers(fm)
Definition: gdb.py:91
value_lambda
Definition: gdb.py:314
def fiber_deactivate()
Definition: gdb.py:261
def __init__(self, fm)
Definition: gdb.py:118
def children(self)
Definition: gdb.py:46
def fiber_activate(fiber)
Definition: gdb.py:250
def __call__(self, pending_frame)
Definition: gdb.py:207
def to_string(self)
Definition: gdb.py:52
def state_to_string(self)
Definition: gdb.py:24
def invoke(self, arg, from_tty)
Definition: gdb.py:271
def __call__(self, args)
Definition: gdb.py:66
def load()
Definition: gdb.py:358
def filter(self, frame_iter)
Definition: gdb.py:177
def info()
Definition: gdb.py:369
def __init__(self)
Definition: gdb.py:268
def __init__(self)
Definition: gdb.py:79
def display_hint(self)
Definition: gdb.py:55
def match(self, class_type, method_name)
Definition: gdb.py:304
Formatter< false, Args... > format(StringPiece fmt, Args &&...args)
Definition: Format.h:271
def filter_impl(self, frame_iter)
Definition: gdb.py:183
def get_arg_types(self)
Definition: gdb.py:60
def __init__(self)
Definition: gdb.py:300
def display_hint(self)
Definition: gdb.py:137
def __init__(self, sp, pc)
Definition: gdb.py:156
def set_skip_frame_sp(cls, skip_frame_sp)
Definition: gdb.py:165
def next(obj)
Definition: ast.py:58
constexpr detail::Dereference dereference
Definition: Base-inl.h:2575