libjit
[Top][All Lists]
Advanced

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

Re: [Libjit] unwinding in gdb


From: Aleksey Demakov
Subject: Re: [Libjit] unwinding in gdb
Date: Sat, 10 Feb 2018 06:39:12 +0300

This is cool, thanks for sharing. Do you prefer this to be merged in?
I think a better pace for the python script would be in the tools
subdir.

Regards,
Aleksey

On Thu, Jan 25, 2018 at 8:15 AM, Tom Tromey <address@hidden> wrote:
> 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]