[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Libjit] unwinding in gdb
From: |
Tom Tromey |
Subject: |
[Libjit] unwinding in gdb |
Date: |
Wed, 24 Jan 2018 22:15:10 -0700 |
I wanted to be able to at least get a backtrace in gdb when using
libjit, so I wrote a gdb unwinder for it in Python. This works
reasonably well for me and I thought I'd share it, even though it isn't
truly generic.
There are two major bugs with it.
First, it iterates over all the functions in a given context. The
context is hard-coded -- this just looks for a certain global variable.
Ideally, though, it would iterate over all known contexts. Maybe
contexts could be listed in a global somewhere.
Second, I only implemented some minimal x86-64 unwinding. It could be
ported to other platforms and it could try to unwind more registers;
right now it does the bare minimum.
A frame decorator is also included. It just prints the starting and
ending addresses of the jit code, like:
(gdb) bt
[...]
#1 0x00007fffd940683a in JIT[0x7fffd940666a, 0x7fffd9406853] ()
[...]
This is handy since then I know what to pass to "disassemble" in gdb.
I needed a small patch (included) to make this work.
More could be done in this area -- gdb has a couple of ways to interface
with JITs, maybe one of those could be used.
Tom
diff --git a/gdb/libjit/unwind.py b/gdb/libjit/unwind.py
new file mode 100644
index 0000000..80ea34a
--- /dev/null
+++ b/gdb/libjit/unwind.py
@@ -0,0 +1,126 @@
+# Unwinder for libjit.
+
+# FIXME
+# * x86-64 only for now
+# * should make a cache of all functions for further unwinding
+# and then invalidate on gdb.events.cont
+# * Extract function names
+
+import gdb
+import gdb.types
+import gdb.unwinder
+from gdb.FrameDecorator import FrameDecorator
+
+# Python 3 compat.
+try:
+ long
+except NameError:
+ long = int
+
+# Python 3 compat.
+try:
+ from itertools import imap
+except ImportError:
+ imap = map
+
+info_cache = {}
+
+def find_by_sp(sp):
+ if sp in info_cache:
+ return info_cache[sp]
+ return None
+
+def add_function_range(sp, start, end):
+ info_cache[sp] = [start, end]
+
+def clear_cache(*args, **kwargs):
+ global info_cache
+ info_cache = {}
+
+gdb.events.cont.connect(clear_cache)
+
+class FrameId(object):
+ def __init__(self, sp, pc):
+ self.sp = sp
+ self.pc = pc
+
+class LibjitFrameDecorator(FrameDecorator):
+ def __init__(self, base, start, end):
+ super(LibjitFrameDecorator, self).__init__(base)
+ self.start = start
+ self.end = end
+
+ def function(self):
+ return "JIT[0x%x, 0x%x]" % (self.start, self.end)
+
+class LibjitFrameFilter(object):
+ def __init__(self):
+ self.name = "Libjit"
+ self.enabled = True
+ self.priority = 100
+
+ def maybe_wrap(self, frame):
+ rbp = long(frame.inferior_frame().read_register("rbp"))
+ vals = find_by_sp(rbp)
+ if vals is None:
+ return frame
+ return LibjitFrameDecorator(frame, vals[0], vals[1])
+
+ def filter(self, frame_iter):
+ return imap(self.maybe_wrap, frame_iter)
+
+class LibjitUnwinder(gdb.unwinder.Unwinder):
+ def __init__(self):
+ super(LibjitUnwinder, self).__init__("Libjit")
+ self.enabled = True
+
+ def our_frame(self, pc, rbp):
+ pc = long(pc)
+ # FIXME - there's no way to get this generally,
+ # so this is Emacs-specific.
+ context = gdb.lookup_global_symbol("emacs_jit_context").value()
+ if long(context) == 0:
+ return False
+ func = context['functions']
+ while long(func) != 0:
+ if pc >= long(func["entry_point"]) and pc < long(func["code_end"]):
+ add_function_range(long(rbp), long(func["entry_point"]),
+ long(func["code_end"]))
+ return True
+ func = func["next"]
+ return False
+
+ def __call__(self, pending_frame):
+ # Just x86-64 for now.
+ pc = pending_frame.read_register("rip")
+ rbp = pending_frame.read_register("rbp")
+ if not self.our_frame(pc, rbp):
+ return None
+
+ # Convenient type to work with.
+ ptr = gdb.lookup_type("void").pointer().pointer()
+
+ # Previous frame pointer is at 0(%rbp).
+ as_ptr = rbp.cast(ptr)
+ prev_rbp = as_ptr.dereference()
+ # Previous PC is at 8(%rbp)
+ prev_pc = (as_ptr + 1).dereference()
+
+ frame_id = FrameId(prev_rbp, prev_pc)
+ unwind_info = pending_frame.create_unwind_info(frame_id)
+ unwind_info.add_saved_register("rip", prev_pc)
+ unwind_info.add_saved_register("rbp", prev_rbp)
+
+ return unwind_info
+
+def register_unwinder(objf):
+ # Register the unwinder.
+ unwinder = LibjitUnwinder()
+ gdb.unwinder.register_unwinder(objf, unwinder, replace=True)
+ # Register the frame filter.
+ filt = LibjitFrameFilter()
+ if objf is None:
+ objf = gdb
+ objf.frame_filters[filt.name] = filt
+
+register_unwinder(None)
diff --git a/jit/jit-compile.c b/jit/jit-compile.c
index ba310e4..2b6a153 100644
--- a/jit/jit-compile.c
+++ b/jit/jit-compile.c
@@ -841,6 +841,7 @@ jit_compile(jit_function_t func)
if(result == JIT_RESULT_OK)
{
func->entry_point = state.gen.code_start;
+ func->code_end = state.gen.code_end;
func->is_compiled = 1;
/* Free the builder structure, which we no longer require */
diff --git a/jit/jit-internal.h b/jit/jit-internal.h
index b1fa3b8..7b3054e 100644
--- a/jit/jit-internal.h
+++ b/jit/jit-internal.h
@@ -492,6 +492,7 @@ struct _jit_function
/* The entry point for the function's compiled code */
void * volatile entry_point;
+ void * volatile code_end;
/* The function to call to perform on-demand compilation */
jit_on_demand_func on_demand;
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Libjit] unwinding in gdb,
Tom Tromey <=