libjit
[Top][All Lists]
Advanced

[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;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]