From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 79E80111A00D; Tue, 19 May 2026 15:41:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 79E80111A00D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1779194460; bh=u1rJomMFiYbqB9/fmhcnR+Lc8XB3ONQRnfJilnPcFXs=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=S2pVZUivGpE7h8WQf15i48z764LnogKfQtMMxBaFwdet8dW0ljQAFxEHV9e09r0mT mOk1E43mmZxejke2QLap4+c2W/3J4S4JX/PFmA294rWgV7Jvi0BphJhCIu1uI9JmHm 0aA5bTcZgBKfoyhQw3uk761unWA9Ox2T1aI6pdlQ= Received: from send218.i.mail.ru (send218.i.mail.ru [95.163.59.57]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 106D81769CFC for ; Tue, 19 May 2026 15:40:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 106D81769CFC Received: by exim-smtp-8466d5ddfc-zbcjt with esmtpa (envelope-from ) id 1wPJjr-00000000Fgc-40dR; Tue, 19 May 2026 15:40:00 +0300 To: Mikhail Elhimov , Sergey Bronnikov , Evgeniy Temirgaleev Date: Tue, 19 May 2026 15:39:09 +0300 Message-ID: <20260519123913.178775-3-skaplun@tarantool.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260519123913.178775-1-skaplun@tarantool.org> References: <20260519123913.178775-1-skaplun@tarantool.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-618D5548: A9C608D391E90E6D411046492FDDF8063A46E30F728E3CDC9D98F6840A4EFB7836DFA22A52D5CFAC X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD98DC58EE55F2C5C37C466666A746DEFC1146EFBBC61D4393C00894C459B0CD1B9A9C608D391E90E6D411046492FDDF8067230E33ED53BFC539D98F6840A4EFB78785316FF18B29525 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE782A779A89F7D69B2C2099A533E45F2D0395957E7521B51C2CFCAF695D4D8E9FCEA1F7E6F0F101C6759CC434672EE6371C2A783ECEC0211ADC4224003CC836476D5A39DEEDB180909611E41BBFE2FEB2BB050F1020F9ACBC86BCA5AE7846BF0580B952328D5491973483764958AC68F249FA2833FD35BB23D9E625A9149C048EEB1593CA6EC85F86DBDFBBEFFF4125B51D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BE96A3A8AAADC8934A471835C12D1D977C4224003CC8364762BB6847A3DEAEFB0F43C7A68FF6260569E8FC8737B5C2249D082881546D93491E827F84554CEF50127C277FBC8AE2E8BA83251EDC214901ED5E8D9A59859A8B64468F871CB5E8762089D37D7C0E48F6C5571747095F342E88FB05168BE4CE3AF X-87b9d050: 1 X-B7AD71C0: 6FEFE4C63DFE2D851629A3278334FCEC2AD323669803AD9FE6E38E9349F0B96635BB2938A49559595798A3C46E872714 X-C1DE0DAB: 0D63561A33F958A5419FB044AC5374005002B1117B3ED696FF60197A8B55E10EBFF4097FFC9E796F823CB91A9FED034534781492E4B8EEAD1B93E4F2B95EAF71C79554A2A72441328621D336A7BC284946AD531847A6065A535571D14F44ED41 X-C8649E89: 1C3962B70DF3F0ADB58128AB1E6D661A716CD42B3DD1D34CB8C0E004AB9358F525B6776AC983F447FC0B9F89525902EE6F57B2FD27647F25E66C117BDB76D659DD8314A6668D9ADD5A077BE911E4C8CF10DB38C787808D7C15F2A602E5084C0280FC5AC9DAAF8DE2B8341EE9D5BE9A0A5E65E14FB1361C344B43B2A6B51D093C0F43AAF0FB82FB1A9350E1BD188BD56E4C41F94D744909CECFA6C6B0C050A61A8CAF69B82BA93681CD72808BE417F3B9E0E7457915DAA85F X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVax5M0mESuWmi1ADw550AkM= X-DA7885C5: FE04AA142BDF59A0F255D290C0D534F908B2FFA22253F0BED318804566ECF9C676CAC6AE0DC10FB65B1A4C17EAA7BC4BEF2421ABFA55128DAF83EF9164C44C7E X-Mailru-Sender: 689FA8AB762F7393520AF17B8A65FDE2706A3276CA4BEB03228D337503C33EE53116993464A365C2E49D44BB4BD9522A059A1ED8796F048DB274557F927329BE89D5A3BC2B10C37545BD1C3CC395C826B4A721A3011E896F X-Mras: Ok Subject: [Tarantool-patches] [PATCH v2 luajit 2/6] lldb: refactor extension X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" 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 = '= 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 "" 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: : { } => { }; next = ''' 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) -- 2.53.0