libjit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Libjit] [PATCH] Unwinder for gdb


From: Tom Tromey
Subject: [Libjit] [PATCH] Unwinder for gdb
Date: Tue, 27 Feb 2018 21:09:58 -0700

This is a new version of the gdb unwinder patch.

This version installs the unwinder where gdb will automatically find
it.  This is very convenient when building against an installed
libjit.

This version also adds the necessary global state so that the unwinder
can unwind via any jit context.

While this has only been ported to x86-64, it should be easy to port
to other architectures.  Also, I wasn't able to reproduce the gdb bug
I encountered while working on this earlier, so it should be suitable
for pretty regular use now.
---
 jit/Makefile.am    |  14 +++++-
 jit/jit-compile.c  |   1 +
 jit/jit-context.c  |  24 ++++++++++
 jit/jit-internal.h |   5 +++
 jit/libjit-gdb.py  | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 jit/libjit-gdb.py

diff --git a/jit/Makefile.am b/jit/Makefile.am
index 93b884e..e650634 100644
--- a/jit/Makefile.am
+++ b/jit/Makefile.am
@@ -83,12 +83,24 @@ EXTRA_DIST = \
        jit-interp-opcodes.ops \
        jit-rules-arm.ins \
        jit-rules-x86.ins \
-       jit-rules-x86-64.ins
+       jit-rules-x86-64.ins \
+       libjit-gdb.py
 
 AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir)
 
 libjit_la_LDFLAGS = -version-info $(LIBJIT_VERSION) -no-undefined
 
+install-exec-hook:
+       for name in `cd "$(DESTDIR)$(libdir)" && ls libjit.*`; do \
+         case "$$name" in \
+           libjit.a | libjit.la) ;; \
+           *) \
+             if test ! -h "$(DESTDIR)$(libdir)/$$name"; then \
+               $(INSTALL_DATA) $(srcdir)/libjit-gdb.py 
"$(DESTDIR)$(libdir)/$$name-gdb.py"; \
+             fi ;;\
+         esac \
+       done
+
 jit-interp.lo: jit-interp-labels.h
 
 jit-interp-labels.h: $(top_builddir)/include/jit/jit-opcode.h \
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-context.c b/jit/jit-context.c
index a38d93f..4fd8209 100644
--- a/jit/jit-context.c
+++ b/jit/jit-context.c
@@ -64,6 +64,10 @@ ultimately destroy JIT contexts:
 
 @*/
 
+/* All contexts are kept on a linked list.  This is used by the gdb
+   support, which is why this is not static.  */
+jit_context_t _jit_context_list;
+
 /*@
  * @deftypefun jit_context_t jit_context_create (void)
  * Create a new context block for the JIT.  Returns NULL
@@ -92,6 +96,16 @@ jit_context_create(void)
        context->last_function = 0;
        context->on_demand_driver = _jit_function_compile_on_demand;
        context->memory_manager = jit_default_memory_manager();
+
+       /* Link the context into the global list.  */
+       jit_mutex_lock(&_jit_global_lock);
+       context->next = _jit_context_list;
+       _jit_context_list = context;
+       context->prev = NULL;
+       if (context->next)
+         context->next->prev = context;
+       jit_mutex_unlock(&_jit_global_lock);
+
        return context;
 }
 
@@ -112,6 +126,16 @@ jit_context_destroy(jit_context_t context)
                return;
        }
 
+       /* Unlink the context from the global list.  */
+       jit_mutex_lock(&_jit_global_lock);
+       if (context->next)
+         context->next->prev = context->prev;
+       if (context->prev)
+         context->prev->next = context->next;
+       else
+         _jit_context_list = context->next;
+       jit_mutex_unlock(&_jit_global_lock);
+
        for(sym = 0; sym < context->num_registered_symbols; ++sym)
        {
                jit_free(context->registered_symbols[sym]);
diff --git a/jit/jit-internal.h b/jit/jit-internal.h
index b1fa3b8..9b69af6 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;
@@ -587,6 +588,10 @@ struct _jit_context
 
        /* On-demand compilation driver */
        jit_on_demand_driver_func       on_demand_driver;
+
+       /* Contexts are kept on a linked list for gdb.  */
+       jit_context_t           next;
+       jit_context_t           prev;
 };
 
 void *_jit_malloc_exec(unsigned int size);
diff --git a/jit/libjit-gdb.py b/jit/libjit-gdb.py
new file mode 100644
index 0000000..4244bdb
--- /dev/null
+++ b/jit/libjit-gdb.py
@@ -0,0 +1,125 @@
+# 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
+# * If _jit_function carried a name, we could print it
+# * Could recover more registers with more knowledge of the frame layout
+
+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)
+        context = gdb.lookup_global_symbol("_jit_context_list").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(gdb.current_objfile())
-- 
2.13.6




reply via email to

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