From: Sergey Bronnikov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Kaplun <skaplun@tarantool.org>,
Mikhail Elhimov <m.elhimov@vk.team>,
Evgeniy Temirgaleev <e.temirgaleev@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH v2 luajit 2/6] lldb: refactor extension
Date: Wed, 27 May 2026 15:27:16 +0300 [thread overview]
Message-ID: <e7707d21-9192-4469-89ef-fee56964ef67@tarantool.org> (raw)
In-Reply-To: <20260519123913.178775-3-skaplun@tarantool.org>
[-- Attachment #1: Type: text/plain, Size: 32617 bytes --]
Hi, Sergey,
thanks for the patch! LGTM
Sergey
On 5/19/26 15:39, Sergey Kaplun wrote:
> This patch refactors lldb python extension C structure casts and type
> handling. Now it uses a monkey-patched lldb.value class instead of a
> handwritten one. Hence, there is no need for hardcoded C type structure
> fields. All LuaJIT-related functions use the same semantics as in the
> gdb extension. Also, this patch changes all fields' access to item
> access instead of attribute access.
>
> This commit fixes all tests except the initialization check since the
> order of loaded commands is different from gdb. This will be fixed in
> the next commit.
> ---
> src/luajit_lldb.py | 605 +++++++-----------
> .../debug-extension-tests.py | 33 +-
> 2 files changed, 256 insertions(+), 382 deletions(-)
>
> diff --git a/src/luajit_lldb.py b/src/luajit_lldb.py
> index 6e66954f..218cc8d1 100644
> --- a/src/luajit_lldb.py
> +++ b/src/luajit_lldb.py
> @@ -5,6 +5,7 @@
> import abc
> import re
> import lldb
> +import struct
>
> LJ_64 = None
> LJ_GC64 = None
> @@ -24,213 +25,132 @@ LJ_TISNUM = None
> target = None
>
>
> -class Ptr:
> - def __init__(self, value, normal_type):
> - self.value = value
> - self.normal_type = normal_type
> -
> - @property
> - def __deref(self):
> - return self.normal_type(self.value.Dereference())
> -
> - def __add__(self, other):
> - assert isinstance(other, int)
> - return self.__class__(
> - cast(
> - self.normal_type.__name__ + ' *',
> - cast(
> - 'uintptr_t',
> - self.value.unsigned + other * self.value.deref.size,
> - ),
> - ),
> - )
> +def lldb_tp_isfp(tp):
> + return tp.GetBasicType() in [
> + lldb.eBasicTypeFloat,
> + lldb.eBasicTypeDouble,
> + lldb.eBasicTypeLongDouble
> + ]
>
> - def __sub__(self, other):
> - assert isinstance(other, int) or isinstance(other, Ptr)
> - if isinstance(other, int):
> - return self.__add__(-other)
> - else:
> - return int((self.value.unsigned - other.value.unsigned)
> - / sizeof(self.normal_type.__name__))
>
> - def __eq__(self, other):
> - assert isinstance(other, Ptr) or isinstance(other, int) and other >= 0
> - if isinstance(other, Ptr):
> - return self.value.unsigned == other.value.unsigned
> - else:
> - return self.value.unsigned == other
> -
> - def __ne__(self, other):
> - return not self == other
> -
> - def __gt__(self, other):
> - assert isinstance(other, Ptr)
> - return self.value.unsigned > other.value.unsigned
> -
> - def __ge__(self, other):
> - assert isinstance(other, Ptr)
> - return self.value.unsigned >= other.value.unsigned
> -
> - def __bool__(self):
> - return self.value.unsigned != 0
> -
> - def __int__(self):
> - return self.value.unsigned
> -
> - def __str__(self):
> - return self.value.value
> -
> - def __getattr__(self, name):
> - if name != '__deref':
> - return getattr(self.__deref, name)
> - return self.__deref
> -
> -
> -class MetaStruct(type):
> - def __init__(cls, name, bases, nmspc):
> - super(MetaStruct, cls).__init__(name, bases, nmspc)
> -
> - def make_general(field, tp):
> - builtin = {
> - 'uint': 'unsigned',
> - 'int': 'signed',
> - 'string': 'value',
> - }
> - if tp in builtin.keys():
> - return lambda self: getattr(self[field], builtin[tp])
> - else:
> - return lambda self: globals()[tp](self[field])
> -
> - if hasattr(cls, 'metainfo'):
> - for field in cls.metainfo:
> - if not isinstance(field[0], str):
> - setattr(cls, field[1], field[0])
> - else:
> - setattr(
> - cls,
> - field[1],
> - property(make_general(field[1], field[0])),
> - )
> -
> -
> -class Struct(metaclass=MetaStruct):
> - def __init__(self, value):
> - self.value = value
> -
> - def __getitem__(self, name):
> - return self.value.GetChildMemberWithName(name)
> -
> - @property
> - def addr(self):
> - return self.value.address_of
> -
> -
> -c_structs = {
> - 'MRef': [
> - (property(lambda self: self['ptr64'].unsigned if LJ_GC64
> - else self['ptr32'].unsigned), 'ptr')
> - ],
> - 'GCRef': [
> - (property(lambda self: self['gcptr64'].unsigned if LJ_GC64
> - else self['gcptr32'].unsigned), 'gcptr')
> - ],
> - 'TValue': [
> - ('GCRef', 'gcr'),
> - ('uint', 'it'),
> - ('uint', 'i'),
> - ('int', 'it64'),
> - ('string', 'n'),
> - (property(lambda self: FR(self['fr']) if not LJ_GC64 else None), 'fr'),
> - (property(lambda self: self['ftsz'].signed if LJ_GC64 else None),
> - 'ftsz')
> - ],
> - 'GCState': [
> - ('GCRef', 'root'),
> - ('GCRef', 'gray'),
> - ('GCRef', 'grayagain'),
> - ('GCRef', 'weak'),
> - ('GCRef', 'mmudata'),
> - ('uint', 'state'),
> - ('uint', 'total'),
> - ('uint', 'threshold'),
> - ('uint', 'debt'),
> - ('uint', 'estimate'),
> - ('uint', 'stepmul'),
> - ('uint', 'pause'),
> - ('uint', 'sweepstr')
> - ],
> - 'lua_State': [
> - ('MRef', 'glref'),
> - ('MRef', 'stack'),
> - ('MRef', 'maxstack'),
> - ('TValuePtr', 'top'),
> - ('TValuePtr', 'base')
> - ],
> - 'global_State': [
> - ('GCState', 'gc'),
> - ('uint', 'vmstate'),
> - ('uint', 'strmask')
> - ],
> - 'jit_State': [
> - ('uint', 'state')
> - ],
> - 'GChead': [
> - ('GCRef', 'nextgc')
> - ],
> - 'GCobj': [
> - ('GChead', 'gch')
> - ],
> - 'GCstr': [
> - ('uint', 'hash'),
> - ('uint', 'len')
> - ],
> - 'FrameLink': [
> - ('MRef', 'pcr'),
> - ('int', 'ftsz')
> - ],
> - 'FR': [
> - ('FrameLink', 'tp')
> - ],
> - 'GCfuncC': [
> - ('MRef', 'pc'),
> - ('uint', 'ffid'),
> - ('uint', 'nupvalues'),
> - ('uint', 'f')
> - ],
> - 'GCtab': [
> - ('MRef', 'array'),
> - ('MRef', 'node'),
> - ('GCRef', 'metatable'),
> - ('uint', 'asize'),
> - ('uint', 'hmask')
> - ],
> - 'GCproto': [
> - ('GCRef', 'chunkname'),
> - ('int', 'firstline')
> - ],
> - 'GCtrace': [
> - ('uint', 'traceno')
> - ],
> - 'Node': [
> - ('TValue', 'key'),
> - ('TValue', 'val'),
> - ('MRef', 'next')
> - ],
> - 'BCIns': []
> -}
> +def lldb_value_from_raw(raw_value, size, tp):
> + isfp = lldb_tp_isfp(tp)
> + pack_flag = '<d' if isfp else '<Q'
> + raw_data = struct.pack(pack_flag, raw_value)
> + sbdata = lldb.SBData()
> + sbdata.SetData(
> + lldb.SBError(),
> + raw_data,
> + lldb.eByteOrderLittle,
> + size
> + )
> + sbval_res = target.CreateValueFromData(
> + # XXX: The name is required. Let's make it meaningful.
> + '({tp}){val}'.format(
> + tp=tp.name,
> + val=raw_value if isfp else hex(raw_value)
> + ),
> + sbdata,
> + tp
> + )
> + return lldb.value(sbval_res)
>
>
> -for cls in c_structs.keys():
> - globals()[cls] = type(cls, (Struct, ), {'metainfo': c_structs[cls]})
> +def lldb__add__(self, other):
> + other = int(other)
> + sbvalue = self.sbvalue
> + if sbvalue.TypeIsPointerType():
> + tp = sbvalue.GetType()
> + sz = sbvalue.deref.size
> + addr = sbvalue.GetValueAsUnsigned() + other * sz
> + return lldb_value_from_raw(addr, sbvalue.GetByteSize(), tp)
> + else:
> + return int(self) + other
>
>
> -for cls in Struct.__subclasses__():
> - ptr_name = cls.__name__ + 'Ptr'
> +def lldb__bool__(self):
> + return int(self) != 0
>
> - globals()[ptr_name] = type(ptr_name, (Ptr,), {
> - '__init__':
> - lambda self, value: super(type(self), self).__init__(value, cls)
> - })
> +
> +def lldb__ge__(self, other):
> + return int(self) >= int(other)
> +
> +
> +def lldb__getitem__(self, key):
> + if type(key) is lldb.value:
> + key = int(key)
> + if type(key) is int:
> + # Allow array access.
> + return lldb.value(self.sbvalue.GetValueForExpressionPath('[%i]' % key))
> + elif type(key) is str:
> + return lldb.value(self.sbvalue.GetChildMemberWithName(key))
> + raise Exception(TypeError('No item of type %s' % str(type(key))))
> +
> +
> +def lldb__gt__(self, other):
> + return int(self) > int(other)
> +
> +
> +def lldb__le__(self, other):
> + return int(self) <= int(other)
> +
> +
> +def lldb__lt__(self, other):
> + return int(self) < int(other)
> +
> +
> +def lldb__str__(self):
> + # Instead of default GetSummary.
> + if not self.sbvalue.TypeIsPointerType():
> + tp = self.sbvalue.GetType()
> + is_float = lldb_tp_isfp(tp)
> + if is_float:
> + return self.sbvalue.GetValue()
> + else:
> + return str(int(self))
> +
> + s = self.sbvalue.GetValue()
> + if s[:2] == '0x':
> + # Strip useless leading zeros.
> + res = s[2:].lstrip('0')
> + return '0x' + (res if res else '0')
> + return s
> +
> +
> +def lldb__sub__(self, other):
> + if type(other) is not lldb.value or \
> + type(other) is lldb.value and not other.sbvalue.TypeIsPointerType():
> + other = int(other)
> + if type(other) is int:
> + return lldb__add__(self, -other)
> + elif self.sbvalue.TypeIsPointerType():
> + ssbval = self.sbvalue
> + osbval = other.sbvalue
> + self_tp = ssbval.GetType()
> + other_tp = osbval.GetType()
> + # Subtract pointers of the same size only.
> + elsz = self_tp.GetDereferencedType().size
> + if other_tp.GetDereferencedType().size != elsz:
> + raise Exception('Attempt to substruct {otp} from {stp}'.format(
> + stp=self_tp.name,
> + otp=other_tp.name
> + ))
> + diff = ssbval.GetValueAsUnsigned() - osbval.GetValueAsUnsigned()
> + return int(diff / elsz)
> + else:
> + return int(self) - int(other)
> +
> +
> +# Monkey-patch the lldb.value class.
> +lldb.value.__add__ = lldb__add__
> +lldb.value.__bool__ = lldb__bool__
> +lldb.value.__ge__ = lldb__ge__
> +lldb.value.__getitem__ = lldb__getitem__
> +lldb.value.__gt__ = lldb__gt__
> +lldb.value.__le__ = lldb__le__
> +lldb.value.__lt__ = lldb__lt__
> +lldb.value.__str__ = lldb__str__
> +lldb.value.__sub__ = lldb__sub__
>
>
> class Command(object):
> @@ -280,52 +200,38 @@ class Command(object):
> """
>
>
> -def cast(typename, value):
> - pointer_type = False
> - name = None
> - if isinstance(value, Struct) or isinstance(value, Ptr):
> - # Get underlying value, if passed object is a wrapper.
> - value = value.value
> +gtype_cache = {}
>
> - # Obtain base type name, decide whether it's a pointer.
> - if isinstance(typename, type):
> - name = typename.__name__
> - if name.endswith('Ptr'):
> - pointer_type = True
> - name = name[:-3]
> - else:
> - name = typename
> - if name[-1] == '*':
> - name = name[:-1].strip()
> - pointer_type = True
> -
> - # Get the lldb type representation.
> - t = target.FindFirstType(name)
> - if pointer_type:
> - t = t.GetPointerType()
> -
> - if isinstance(value, int):
> - # Integer casts require some black magic for lldb to behave properly.
> - if pointer_type:
> - casted = target.CreateValueFromAddress(
> - 'value',
> - lldb.SBAddress(value, target),
> - t.GetPointeeType(),
> - ).address_of
> - else:
> - casted = target.CreateValueFromData(
> - name='value',
> - data=lldb.SBData.CreateDataFromInt(value, size=8),
> - type=t,
> - )
> - else:
> - casted = value.Cast(t)
>
> - if isinstance(typename, type):
> - # Wrap lldb object, if possible
> - return typename(casted)
> - else:
> - return casted
> +def gtype(typestr):
> + if typestr in gtype_cache:
> + return gtype_cache[typestr]
> +
> + m = re.match(r'((?:(?:struct|union) )?\S*)\s*[*]', typestr)
> +
> + gtype = target.FindFirstType(typestr) if m is None \
> + else target.FindFirstType(m.group(1)).GetPointerType()
> +
> + gtype_cache[typestr] = gtype
> + return gtype
> +
> +
> +def cast(typestr, val):
> + if isinstance(val, lldb.value):
> + val = val.sbvalue
> + elif type(val) is int:
> + tp = gtype(typestr)
> + return lldb_value_from_raw(val, tp.GetByteSize(), tp)
> + elif not isinstance(val, lldb.SBValue):
> + raise Exception('unexpected cast from type: {t}'.format(t=type(val)))
> +
> + # XXX: Simply SBValue.Cast() works incorrectly since it may
> + # take the 8 bytes of memory instead of 4, before the cast.
> + # Construct the value on the fly.
> + tp = gtype(typestr)
> + is_fp = lldb_tp_isfp(tp)
> + rawval = float(val.GetValue()) if is_fp else val.GetValueAsUnsigned()
> + return lldb_value_from_raw(rawval, val.GetByteSize(), tp)
>
>
> def lookup_global(name):
> @@ -336,28 +242,20 @@ def type_member(type_obj, name):
> return next((x for x in type_obj.members if x.name == name), None)
>
>
> -def find_type(typename):
> - return target.FindFirstType(typename)
> -
> -
> def offsetof(typename, membername):
> - type_obj = find_type(typename)
> + type_obj = gtype(typename)
> member = type_member(type_obj, membername)
> assert member is not None
> return member.GetOffsetInBytes()
>
>
> def sizeof(typename):
> - type_obj = find_type(typename)
> + type_obj = gtype(typename)
> return type_obj.GetByteSize()
>
>
> -def vtou64(value):
> - return value.unsigned & 0xFFFFFFFFFFFFFFFF
> -
> -
> -def vtoi(value):
> - return value.signed
> +def tou64(val):
> + return cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF
>
>
> def dbg_eval(expr):
> @@ -371,17 +269,17 @@ def dbg_eval(expr):
>
>
> def gcval(obj):
> - return cast(GCobjPtr, cast('uintptr_t', obj.gcptr & LJ_GCVMASK) if LJ_GC64
> - else cast('uintptr_t', obj.gcptr))
> + return cast('GCobj *', obj['gcptr64'] & LJ_GCVMASK if LJ_GC64
> + else cast('uintptr_t', obj['gcptr32']))
>
>
> def gcref(obj):
> - return cast(GCobjPtr, obj.gcptr if LJ_GC64
> - else cast('uintptr_t', obj.gcptr))
> + return cast('GCobj *', obj['gcptr64'] if LJ_GC64
> + else cast('uintptr_t', obj['gcptr32']))
>
>
> def gcnext(obj):
> - return gcref(obj).gch.nextgc
> + return gcref(obj)['gch']['nextgc']
>
>
> def gclistlen(root, end=0x0):
> @@ -412,15 +310,15 @@ gclen = {
>
>
> def dump_gc(g):
> - gc = g.gc
> + gc = g['gc']
> stats = ['{key}: {value}'.format(key=f, value=getattr(gc, f)) for f in (
> 'total', 'threshold', 'debt', 'estimate', 'stepmul', 'pause'
> )]
>
> stats += ['sweepstr: {sweepstr}/{strmask}'.format(
> - sweepstr=gc.sweepstr,
> + sweepstr=gc['sweepstr'],
> # String hash mask (size of hash table - 1).
> - strmask=g.strmask + 1,
> + strmask=g['strmask'] + 1,
> )]
>
> stats += ['{key}: {number} objects'.format(
> @@ -431,20 +329,17 @@ def dump_gc(g):
>
>
> def mref(typename, obj):
> - return cast(typename, obj.ptr)
> + return cast(typename, obj['ptr64'] if LJ_GC64 else obj['ptr32'])
>
>
> def J(g):
> g_offset = offsetof('GG_State', 'g')
> J_offset = offsetof('GG_State', 'J')
> - return cast(
> - jit_StatePtr,
> - vtou64(cast('char *', g)) - g_offset + J_offset,
> - )
> + return cast('jit_State *', (cast('char *', g) - g_offset + J_offset))
>
>
> def G(L):
> - return mref(global_StatePtr, L.glref)
> + return mref('global_State *', L['glref'])
>
>
> def L(L=None):
> @@ -459,7 +354,7 @@ def L(L=None):
> # TODO: Add more
> ))):
> if lstate:
> - return lua_State(lstate)
> + return cast('lua_State *', lstate)
>
>
> def tou32(val):
> @@ -481,7 +376,7 @@ def vm_state(g):
> i2notu32(6): 'RECORD',
> i2notu32(7): 'OPT',
> i2notu32(8): 'ASM',
> - }.get(int(tou32(g.vmstate)), 'TRACE')
> + }.get(int(tou32(g['vmstate'])), 'TRACE')
>
>
> def gc_state(g):
> @@ -493,7 +388,7 @@ def gc_state(g):
> 4: 'SWEEP',
> 5: 'FINALIZE',
> 6: 'LAST',
> - }.get(g.gc.state, 'INVALID')
> + }.get(int(g['gc']['state']), 'INVALID')
>
>
> def jit_state(g):
> @@ -505,31 +400,29 @@ def jit_state(g):
> 0x13: 'END',
> 0x14: 'ASM',
> 0x15: 'ERR',
> - }.get(J(g).state, 'INVALID')
> + }.get(int(J(g).state), 'INVALID')
>
>
> def strx64(val):
> return re.sub('L?$', '',
> - hex(int(val) & 0xFFFFFFFFFFFFFFFF))
> + hex(int(cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF)))
>
>
> def funcproto(func):
> assert func.ffid == 0
> - proto_size = sizeof('GCproto')
> - value = cast('uintptr_t', vtou64(mref('char *', func.pc)) - proto_size)
> - return cast(GCprotoPtr, value)
> + return cast('GCproto *', mref('char *', func.pc) - sizeof('GCproto'))
>
>
> def strdata(obj):
> try:
> - ptr = cast('char *', obj + 1)
> - return ptr.summary
> + ptr = cast('char *', cast('GCstr *', obj) + 1)
> + return ptr.sbvalue.summary
> except UnicodeEncodeError:
> return "<luajit-lldb: error occurred while rendering non-ascii slot>"
>
>
> def itype(o):
> - return tou32(o.it64 >> 47) if LJ_GC64 else o.it
> + return tou32(o['it64'] >> 47) if LJ_GC64 else o['it']
>
>
> def tvisint(o):
> @@ -538,7 +431,7 @@ def tvisint(o):
>
> def tvislightud(o):
> if LJ_64 and not LJ_GC64:
> - return (vtoi(cast('int32_t', itype(o))) >> 15) == -2
> + return (int(cast('int32_t', itype(o))) >> 15) == -2
> else:
> return itype(o) == LJ_T['LIGHTUD']
>
> @@ -560,80 +453,80 @@ def dump_lj_ttrue(tv):
>
>
> def dump_lj_tlightud(tv):
> - return 'light userdata @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'light userdata @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_tstr(tv):
> return 'string {body} @ {address}'.format(
> - body=strdata(cast(GCstrPtr, gcval(tv.gcr))),
> - address=strx64(gcval(tv.gcr))
> + body=strdata(cast('GCstr *', gcval(tv['gcr']))),
> + address=strx64(gcval(tv['gcr']))
> )
>
>
> def dump_lj_tupval(tv):
> - return 'upvalue @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'upvalue @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_tthread(tv):
> - return 'thread @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'thread @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_tproto(tv):
> - return 'proto @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'proto @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_tfunc(tv):
> - func = cast(GCfuncCPtr, gcval(tv.gcr))
> - ffid = func.ffid
> + func = cast('GCfuncC *', gcval(tv['gcr']))
> + ffid = func['ffid']
>
> if ffid == 0:
> pt = funcproto(func)
> return 'Lua function @ {addr}, {nups} upvalues, {chunk}:{line}'.format(
> addr=strx64(func),
> - nups=func.nupvalues,
> - chunk=strdata(cast(GCstrPtr, gcval(pt.chunkname))),
> - line=pt.firstline
> + nups=func['nupvalues'],
> + chunk=strdata(cast('GCstr *', gcval(pt['chunkname']))),
> + line=pt['firstline']
> )
> elif ffid == 1:
> - return 'C function @ {}'.format(strx64(func.f))
> + return 'C function @ {}'.format(strx64(func['f']))
> else:
> return 'fast function #{}'.format(ffid)
>
>
> def dump_lj_ttrace(tv):
> - trace = cast(GCtracePtr, gcval(tv.gcr))
> + trace = cast('GCtrace *', gcval(tv['gcr']))
> return 'trace {traceno} @ {addr}'.format(
> - traceno=strx64(trace.traceno),
> + traceno=strx64(trace['traceno']),
> addr=strx64(trace)
> )
>
>
> def dump_lj_tcdata(tv):
> - return 'cdata @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'cdata @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_ttab(tv):
> - table = cast(GCtabPtr, gcval(tv.gcr))
> + table = cast('GCtab *', gcval(tv['gcr']))
> return 'table @ {gcr} (asize: {asize}, hmask: {hmask})'.format(
> gcr=strx64(table),
> - asize=table.asize,
> - hmask=strx64(table.hmask),
> + asize=table['asize'],
> + hmask=strx64(table['hmask']),
> )
>
>
> def dump_lj_tudata(tv):
> - return 'userdata @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'userdata @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> def dump_lj_tnumx(tv):
> if tvisint(tv):
> - return 'integer {}'.format(cast('int32_t', tv.i))
> + return 'integer {}'.format(cast('int32_t', tv['i']))
> else:
> - return 'number {}'.format(tv.n)
> + return 'number {}'.format(cast('double', tv['n']))
>
>
> def dump_lj_invalid(tv):
> - return 'not valid type @ {}'.format(strx64(gcval(tv.gcr)))
> + return 'not valid type @ {}'.format(strx64(gcval(tv['gcr'])))
>
>
> dumpers = {
> @@ -686,8 +579,8 @@ def typenames(value):
> }.get(int(value), 'LJ_TINVALID')
>
>
> -def dump_tvalue(tvptr):
> - return dumpers.get(typenames(itypemap(tvptr)), dump_lj_invalid)(tvptr)
> +def dump_tvalue(tvalue):
> + return dumpers.get(typenames(itypemap(tvalue)), dump_lj_invalid)(tvalue)
>
>
> FRAME_TYPE = 0x3
> @@ -720,23 +613,17 @@ def bc_a(ins):
>
>
> def frame_ftsz(framelink):
> - return vtou64(cast('ptrdiff_t', framelink.ftsz if LJ_FR2
> - else framelink.fr.tp.ftsz))
> + return cast('ptrdiff_t', framelink['ftsz'] if LJ_FR2
> + else framelink['fr']['tp']['ftsz'])
>
>
> def frame_pc(framelink):
> - return cast(BCInsPtr, frame_ftsz(framelink)) if LJ_FR2 \
> - else mref(BCInsPtr, framelink.fr.tp.pcr)
> + return cast('BCIns *', frame_ftsz(framelink)) if LJ_FR2 \
> + else mref('BCIns *', framelink['fr']['tp']['pcr'])
>
>
> def frame_prevl(framelink):
> - # We are evaluating the `frame_pc(framelink)[-1])` with lldb's
> - # REPL, because the lldb API is faulty and it's not possible to cast
> - # a struct member of 32-bit type to 64-bit type without getting onto
> - # the next property bits, despite the fact that it's an actual value, not
> - # a pointer to it.
> - bcins = vtou64(dbg_eval('((BCIns *)' + str(frame_pc(framelink)) + ')[-1]'))
> - return framelink - (1 + LJ_FR2 + bc_a(bcins))
> + return framelink - (1 + LJ_FR2 + bc_a(frame_pc(framelink)[-1]))
>
>
> def frame_ispcall(framelink):
> @@ -770,14 +657,14 @@ def frame_prev(framelink):
>
>
> def frame_sentinel(L):
> - return mref(TValuePtr, L.stack) + LJ_FR2
> + return mref('TValue *', L['stack']) + LJ_FR2
>
>
> # The generator that implements frame iterator.
> # Every frame is represented as a tuple of framelink and frametop.
> def frames(L):
> - frametop = L.top
> - framelink = L.base - 1
> + frametop = L['top']
> + framelink = L['base'] - 1
> framelink_sentinel = frame_sentinel(L)
> while True:
> yield framelink, frametop
> @@ -788,14 +675,8 @@ def frames(L):
>
>
> def dump_framelink_slot_address(fr):
> - return '{start:{padding}}:{end:{padding}}'.format(
> - start=hex(int(fr - 1)),
> - end=hex(int(fr)),
> - padding=len(PADDING),
> - ) if LJ_FR2 else '{addr:{padding}}'.format(
> - addr=hex(int(fr)),
> - padding=len(PADDING),
> - )
> + return '{}:{}'.format(fr - 1, fr) if LJ_FR2 \
> + else '{}'.format(fr) + PADDING
>
>
> def dump_framelink(L, fr):
> @@ -809,30 +690,30 @@ def dump_framelink(L, fr):
> frname=frametypes(int(frame_type(fr))),
> p='P' if frame_typep(fr) & FRAME_P else ''
> ),
> - d=fr - frame_prev(fr),
> + d=cast('TValue *', fr) - cast('TValue *', frame_prev(fr)),
> f=dump_lj_tfunc(fr - LJ_FR2),
> )
>
>
> def dump_stack_slot(L, slot, base=None, top=None):
> - base = base or L.base
> - top = top or L.top
> + base = base or L['base']
> + top = top or L['top']
>
> - return '{addr:{padding}} [ {B}{T}{M}] VALUE: {value}'.format(
> + return '{addr}{padding} [ {B}{T}{M}] VALUE: {value}'.format(
> addr=strx64(slot),
> - padding=2 * len(PADDING) + 1,
> + padding=PADDING,
> B='B' if slot == base else ' ',
> T='T' if slot == top else ' ',
> - M='M' if slot == mref(TValuePtr, L.maxstack) else ' ',
> + M='M' if slot == mref('TValue *', L['maxstack']) else ' ',
> value=dump_tvalue(slot),
> )
>
>
> def dump_stack(L, base=None, top=None):
> - base = base or L.base
> - top = top or L.top
> - stack = mref(TValuePtr, L.stack)
> - maxstack = mref(TValuePtr, L.maxstack)
> + base = base or L['base']
> + top = top or L['top']
> + stack = mref('TValue *', L['stack'])
> + maxstack = mref('TValue *', L['maxstack'])
> red = 5 + 3 * LJ_FR2
>
> dump = [
> @@ -848,19 +729,13 @@ def dump_stack(L, base=None, top=None):
> dump.extend([
> '{padding} Stack: {nstackslots: >5} slots {padding}'.format(
> padding='-' * len(PADDING),
> - nstackslots=int((maxstack - stack) >> 3),
> + nstackslots=int((tou64(maxstack) - tou64(stack)) >> 3),
> ),
> dump_stack_slot(L, maxstack, base, top),
> '{start}:{end} [ ] {nfreeslots} slots: Free stack slots'.format(
> - start='{address:{padding}}'.format(
> - address=strx64(top + 1),
> - padding=len(PADDING),
> - ),
> - end='{address:{padding}}'.format(
> - address=strx64(maxstack - 1),
> - padding=len(PADDING),
> - ),
> - nfreeslots=int((maxstack - top - 8) >> 3),
> + start=strx64(top + 1),
> + end=strx64(maxstack - 1),
> + nfreeslots=int((tou64(maxstack) - tou64(top) - 8) >> 3),
> ),
> ])
>
> @@ -905,7 +780,7 @@ Whether the type of the given address differs from the listed above, then
> error message occurs.
> '''
> def execute(self, debugger, args, result):
> - tvptr = TValuePtr(cast('TValue *', self.parse(args)))
> + tvptr = cast('TValue *', self.parse(args))
> print('{}'.format(dump_tvalue(tvptr)))
>
>
> @@ -984,11 +859,11 @@ the payload, size in bytes and hash.
> is replaced with the corresponding error when decoding fails.
> '''
> def execute(self, debugger, args, result):
> - string_ptr = GCstrPtr(cast('GCstr *', self.parse(args)))
> + string = cast('GCstr *', self.parse(args))
> print("String: {body} [{len} bytes] with hash {hash}".format(
> - body=strdata(string_ptr),
> - hash=strx64(string_ptr.hash),
> - len=string_ptr.len,
> + body=strdata(string),
> + hash=strx64(string['hash']),
> + len=string['len'],
> ))
>
>
> @@ -1004,13 +879,13 @@ The command receives a GCtab address and dumps the table contents:
> <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr>
> '''
> def execute(self, debugger, args, result):
> - t = GCtabPtr(cast('GCtab *', self.parse(args)))
> - array = mref(TValuePtr, t.array)
> - nodes = mref(NodePtr, t.node)
> - mt = gcval(t.metatable)
> + t = cast('GCtab *', self.parse(args))
> + array = mref('TValue *', t['array'])
> + nodes = mref('Node *', t['node'])
> + mt = gcval(t['metatable'])
> capacity = {
> - 'apart': int(t.asize),
> - 'hpart': int(t.hmask + 1) if t.hmask > 0 else 0
> + 'apart': int(t['asize']),
> + 'hpart': int(t['hmask'] + 1) if t['hmask'] > 0 else 0
> }
>
> if mt:
> @@ -1031,9 +906,9 @@ The command receives a GCtab address and dumps the table contents:
> node = nodes + i
> print('{ptr}: {{ {key} }} => {{ {val} }}; next = {n}'.format(
> ptr=strx64(node),
> - key=dump_tvalue(TValuePtr(node.key.addr)),
> - val=dump_tvalue(TValuePtr(node.val.addr)),
> - n=strx64(mref(NodePtr, node.next))
> + key=dump_tvalue(node['key']),
> + val=dump_tvalue(node['val']),
> + n=strx64(mref('Node *', node['next']))
> ))
>
>
> @@ -1070,9 +945,7 @@ coroutine guest stack:
> If L is omitted the main coroutine is used.
> '''
> def execute(self, debugger, args, result):
> - lstate = self.parse(args)
> - lstate_ptr = cast('lua_State *', lstate) if coro is not None else None
> - print('{}'.format(dump_stack(L(lstate_ptr))))
> + print('{}'.format(dump_stack(L(self.parse(args)))))
>
>
> def register_commands(debugger, commands):
> @@ -1106,7 +979,7 @@ def configure(debugger):
> 'no debugging symbols found for libluajit')
> return
>
> - PADDING = ' ' * len(strx64((TValuePtr(L().addr))))
> + PADDING = ' ' * len(':' + hex((1 << (47 if LJ_GC64 else 32)) - 1))
> LJ_TISNUM = 0xfffeffff if LJ_64 and not LJ_GC64 else LJ_T['NUMX']
>
>
> diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py
> index 6094c535..11c2492b 100644
> --- a/test/tarantool-debugger-tests/debug-extension-tests.py
> +++ b/test/tarantool-debugger-tests/debug-extension-tests.py
> @@ -125,20 +125,22 @@ class TestCaseBase(unittest.TestCase):
> self.assertRegex(self.output, self.pattern.strip())
>
>
> -class TestLoad(TestCaseBase):
> - extension_cmds = ''
> - location = 'lj_cf_print'
> - lua_script = 'print(1)'
> - pattern = (
> - r'lj-arch command initialized\n'
> - r'lj-tv command initialized\n'
> - r'lj-str command initialized\n'
> - r'lj-tab command initialized\n'
> - r'lj-stack command initialized\n'
> - r'lj-state command initialized\n'
> - r'lj-gc command initialized\n'
> - r'.*is successfully loaded'
> - )
> +# FIXME: Skip for LLDB since it has different order.
> +if not LLDB:
> + class TestLoad(TestCaseBase):
> + extension_cmds = ''
> + location = 'lj_cf_print'
> + lua_script = 'print(1)'
> + pattern = (
> + r'lj-arch command initialized\n'
> + r'lj-tv command initialized\n'
> + r'lj-str command initialized\n'
> + r'lj-tab command initialized\n'
> + r'lj-stack command initialized\n'
> + r'lj-state command initialized\n'
> + r'lj-gc command initialized\n'
> + r'.*is successfully loaded'
> + )
>
>
> class TestLJArch(TestCaseBase):
> @@ -281,6 +283,5 @@ class TestLJTab(TestCaseBase):
> for test_cls in TestCaseBase.__subclasses__():
> test_cls.test = lambda self: self.check()
>
> -# FIXME: skip for LLDB since most commands are not working anyway.
> -if __name__ == '__main__' and not LLDB:
> +if __name__ == '__main__':
> unittest.main(verbosity=2)
[-- Attachment #2: Type: text/html, Size: 30964 bytes --]
next prev parent reply other threads:[~2026-05-27 12:27 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-19 12:39 [Tarantool-patches] [PATCH v2 luajit 0/6] Unified extension for debuggers Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 1/6] test: introduce tests for debugging extensions Sergey Kaplun via Tarantool-patches
2026-05-20 13:38 ` Sergey Bronnikov via Tarantool-patches
2026-05-25 9:14 ` Sergey Kaplun via Tarantool-patches
2026-05-27 9:54 ` Sergey Bronnikov via Tarantool-patches
2026-05-27 10:52 ` Sergey Kaplun via Tarantool-patches
2026-05-26 13:50 ` Evgeniy Temirgaleev via Tarantool-patches
2026-05-26 14:41 ` Sergey Kaplun via Tarantool-patches
2026-05-26 18:52 ` Evgeniy Temirgaleev via Tarantool-patches
2026-05-27 7:56 ` Sergey Kaplun via Tarantool-patches
2026-05-27 12:41 ` Sergey Bronnikov via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 2/6] lldb: refactor extension Sergey Kaplun via Tarantool-patches
2026-05-27 12:27 ` Sergey Bronnikov via Tarantool-patches [this message]
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 3/6] dbg: sort initialization of commands Sergey Kaplun via Tarantool-patches
2026-05-20 13:43 ` Sergey Bronnikov via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 4/6] lldb: support full-range 64-bit lightuserdata Sergey Kaplun via Tarantool-patches
2026-05-27 12:28 ` Sergey Bronnikov via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 5/6] dbg: generalize extension Sergey Kaplun via Tarantool-patches
2026-05-27 12:38 ` Sergey Bronnikov via Tarantool-patches
2026-05-27 12:55 ` Sergey Kaplun via Tarantool-patches
2026-05-19 12:39 ` [Tarantool-patches] [PATCH v2 luajit 6/6] ci: introduce workflow to test debugger extension Sergey Kaplun via Tarantool-patches
2026-05-20 13:52 ` Sergey Bronnikov via Tarantool-patches
2026-05-25 7:00 ` Sergey Kaplun via Tarantool-patches
2026-05-27 10:57 ` Sergey Bronnikov via Tarantool-patches
2026-05-27 11:58 ` Sergey Kaplun via Tarantool-patches
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e7707d21-9192-4469-89ef-fee56964ef67@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=e.temirgaleev@tarantool.org \
--cc=m.elhimov@vk.team \
--cc=sergeyb@tarantool.org \
--cc=skaplun@tarantool.org \
--subject='Re: [Tarantool-patches] [PATCH v2 luajit 2/6] lldb: refactor extension' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox