[Tarantool-patches] [PATCH luajit v6 1/2] debug: generalized extension

Sergey Kaplun skaplun at tarantool.org
Wed Apr 17 19:00:00 MSK 2024


Hi, Maxim!
Thanks for the patch!
Please consider my comments below.

On 04.04.24, Maxim Kokryashkin wrote:

<snipped>

> ---
>  src/luajit-gdb.py                     | 885 --------------------------
>  src/{luajit_lldb.py => luajit_dbg.py} | 616 ++++++++++++------

Since luajit_lldb.py is gone, please change the comment in <.flake8rc>.

>  2 files changed, 416 insertions(+), 1085 deletions(-)
>  delete mode 100644 src/luajit-gdb.py
>  rename src/{luajit_lldb.py => luajit_dbg.py} (63%)
> 
> diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py
> deleted file mode 100644

<snipped>

> diff --git a/src/luajit_lldb.py b/src/luajit_dbg.py
> similarity index 63%
> rename from src/luajit_lldb.py
> rename to src/luajit_dbg.py
> index 5ac11b65..a42d8f25 100644
> --- a/src/luajit_lldb.py
> +++ b/src/luajit_dbg.py
> @@ -1,10 +1,230 @@
> -# LLDB extension for LuaJIT post-mortem analysis.
> -# To use, just put 'command script import <path-to-repo>/src/luajit_lldb.py'
> -# in lldb.
> +# Debug extension for LuaJIT post-mortem analysis.
> +# To use in LLDB: 'command script import <path-to-repo>/src/luajit_dbg.py'
> +# To use in GDB: 'source <path-to-repo>/src/luajit_dbg.py'
>  
>  import abc
>  import re
> -import lldb
> +import sys
> +import types
> +
> +from importlib import import_module
> +
> +# make script compatible with the ancient Python {{{

Typo: s/make script/Make the script/

<snipped>

> +class Debugger(object):
> +    def __init__(self):
> +        self.GDB = False
> +        self.LLDB = False
> +
> +        debuggers = {
> +            'gdb': lambda lib: True,
> +            'lldb': lambda lib: lib.debugger is not None,
> +        }
> +        for name, healthcheck in debuggers.items():
> +            lib = None
> +            try:
> +                lib = import_module(name)
> +                if healthcheck(lib):

Why do we need this healthcheck? Why just import of the module isn't
enough?
Please add a comment near `debuggers` definition.

> +                    setattr(self, name.upper(), True)
> +                    globals()[name] = lib
> +                    self.name = name
> +            except Exception:
> +                continue
> +
> +        assert self.LLDB != self.GDB

<snipped>

> +
> +    def find_type(self, typename):
> +        if self.LLDB:
> +            return target.FindFirstType(typename)
> +        else:
> +            return gdb.lookup_type(typename)

Why do you drop the cache for types here? It may be critical when
running scripts for the search of objects on big coredumps or the
attached process.

> +
> +    def type_to_pointer_type(self, tp):
> +        if self.LLDB:
> +            return tp.GetPointerType()
> +        else:
> +            return tp.pointer()

<snipped>

> +
> +    def type_member_offset(self, member):
> +        if self.LLDB:
> +            return member.GetOffsetInBytes()
> +        else:
> +            return member.bitpos / 8

Should it be `//`?

<snipped>

> +class Struct(object):

Should we do this part for GDB too? I thought that this class generation
may be skipped for GDB.

>      def __init__(self, value):
>          self.value = value
>  
>      def __getitem__(self, name):

<snipped>

> +def make_property_from_metadata(field, tp):
> +    builtin = {
> +        'uint':   dbg.to_unsigned,
> +        'int':    dbg.to_signed,
> +        'string': dbg.to_str,
> +    }
> +    if tp in builtin.keys():
> +        return lambda self: builtin[tp](self[field])
> +    else:
> +        return lambda self: globals()[tp](self[field])
> +
> +
> +for cls, metainfo in c_structs.items():
> +    cls_dict = {}
> +    for field in metainfo:

May you please name field[0], field[1] as local variables for better
readability?

> +        if not isinstance(field[0], str):
> +            cls_dict[field[1]] = field[0]
> +        else:
> +            cls_dict[field[1]] = property(
> +                make_property_from_metadata(field[1], field[0])
> +            )
> +    globals()[cls] = type(cls, (Struct, ), cls_dict)
>  
>  
>  for cls in Struct.__subclasses__():
>      ptr_name = cls.__name__ + 'Ptr'
>  


<snipped>

> -
> -        ret = frame.EvaluateExpression(command)
> -        return ret
> +        return dbg.to_unsigned(dbg.eval(command))

Why do we need return unsigned here?

>  
>      @abc.abstractproperty
>      def command(self):
> @@ -270,7 +491,7 @@ class Command(object):

<snipped>

> @@ -278,6 +499,11 @@ class Command(object):
>          properly routed to LLDB frontend. Any unhandled exception will be
>          automatically transformed into proper errors.
>          """
> +    def invoke(self, arg, from_tty):
> +        try:
> +            self.execute(arg)
> +        except Exception as e:
> +            dbg.write(e)

Why do we need this change?

The error message for such situation is changed and non informative:

| Breakpoint 1, lj_cf_dofile (L=0x2) at /home/burii/reviews/luajit/lj-dbg/src/lib_base.c:429
| 429     {
| (gdb) lj-stack L
| Python Exception <class 'TypeError'>: unsupported operand type(s) for +: 'MemoryError' and 'str'
| Error occurred in Python: unsupported operand type(s) for +: 'MemoryError' and 'str'

Within the following implementation all works as expected.
|    def invoke(self, arg, from_tty):
|        self.execute(arg)

This produces more understandable reason of an error:
| (gdb) lj-stack L
| Python Exception <class 'gdb.MemoryError'>: Cannot access memory at address 0x26
| Error occurred in Python: Cannot access memory at address 0x26

Also, maybe it is good to add a test for this error.

<snipped>

-- 
Best regards,
Sergey Kaplun


More information about the Tarantool-patches mailing list