Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Kaplun via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Bronnikov <sergeyb@tarantool.org>,
	Evgeniy Temirgaleev <e.temirgaleev@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [PATCH luajit 2/3] dbg: introduce lj-ctype command, extend cdata dump
Date: Thu, 25 Jun 2026 23:29:02 +0300	[thread overview]
Message-ID: <20260625202903.3157425-3-skaplun@tarantool.org> (raw)
In-Reply-To: <20260625202903.3157425-1-skaplun@tarantool.org>

This patch extends dumped information for the given cdata object. Now
it resolves the given `CType` and prints it in the format similar to the
`__tostring` metamethod. The `lj-ctype` command is introduced to dump
this information where there is only the `CType` pointer but no cdata
associated with it.

`__or__` and `__ror__` metamethods are monkey-patched for the LLDB value
object. In `__sub__` metamethod for LLDB pointers `GetPointeeType()` is
used to get the pointee type instead of the incorrectly used
`GetDereferencedType()` which always returns the same type with size 8.
Casting from negative values to the unsigned values is supported to
check `CTF_UCHAR`.

Part of tarantool/tarantool#4808
---
 src/luajit_dbg.py                             | 333 +++++++++++++++++-
 .../debug-extension-tests.py                  | 208 ++++++++++-
 2 files changed, 535 insertions(+), 6 deletions(-)

diff --git a/src/luajit_dbg.py b/src/luajit_dbg.py
index fd6ca8a5..62cd65d5 100644
--- a/src/luajit_dbg.py
+++ b/src/luajit_dbg.py
@@ -386,6 +386,8 @@ class _LLDBDebugger(Debugger):
             pack_flag = '<q'
         else:
             pack_flag = '<Q'
+            # Cast to unsigned.
+            raw_value &= 0xFFFFFFFFFFFFFFFF
         raw_data = struct.pack(pack_flag, raw_value)
         sbdata = lldb.SBData()
         sbdata.SetData(
@@ -482,6 +484,9 @@ class _LLDBDebugger(Debugger):
         def lldb__lt__(lldbval, other):
             return int(lldbval) < int(other)
 
+        def lldb__or__(lldbval, other):
+            return int(lldbval) | int(other)
+
         def lldb__str__(lldbval):
             # Instead of default GetSummary.
             if not lldbval.sbvalue.TypeIsPointerType():
@@ -512,8 +517,8 @@ class _LLDBDebugger(Debugger):
                 lldbval_tp = sbval.GetType()
                 other_tp = osbval.GetType()
                 # Subtract pointers of the same size only.
-                elsz = lldbval_tp.GetDereferencedType().size
-                if other_tp.GetDereferencedType().size != elsz:
+                elsz = lldbval_tp.GetPointeeType().size
+                if other_tp.GetPointeeType().size != elsz:
                     raise Exception(
                         'Attempt to substruct {otp} from {stp}'.format(
                             stp=lldbval_tp.name,
@@ -536,6 +541,8 @@ class _LLDBDebugger(Debugger):
         lldb.value.__index__ = lldb__index__
         lldb.value.__le__ = lldb__le__
         lldb.value.__lt__ = lldb__lt__
+        lldb.value.__or__ = lldb__or__
+        lldb.value.__ror__ = lldb__or__  # Same semantics.
         lldb.value.__str__ = lldb__str__
         lldb.value.__sub__ = lldb__sub__
 
@@ -1352,6 +1359,119 @@ def lightudV(tv):
         return gcval(tv['gcr'])
 
 
+# FFI.
+
+
+def ctype_ctsG(g):
+    return mref('CTState *', g['ctype_state'])
+
+
+def ctype_get(cts, id):
+    return dbg.address(cts['tab'][id])
+
+
+# Externally visible types.
+CT_NUM = 0  # Integer or floating-point numbers.
+CT_STRUCT = 1  # Struct or union.
+CT_PTR = 2  # Pointer or reference.
+CT_ARRAY = 3  # Array or complex type.
+CT_MAYCONVERT = CT_ARRAY
+CT_VOID = 4  # Void type.
+CT_ENUM = 5  # Enumeration.
+CT_HASSIZE = CT_ENUM  # Last type where ct->size holds the actual size.
+CT_FUNC = 6  # Function.
+CT_TYPEDEF = 7  # Typedef.
+CT_ATTRIB = 8  # Miscellaneous attributes.
+
+# Common types.
+CTID_CTYPEID = 21
+
+# C type info flags.
+CTF_BOOL = 0x08000000  # Boolean: NUM, BITFIELD.
+CTF_FP = 0x04000000  # Floating-point: NUM.
+CTF_CONST = 0x02000000  # Const qualifier.
+CTF_VOLATILE = 0x01000000  # Volatile qualifier.
+CTF_UNSIGNED = 0x00800000  # Unsigned: NUM, BITFIELD.
+CTF_LONG = 0x00400000  # Long: NUM.
+CTF_VLA = 0x00100000  # Variable-length: ARRAY, STRUCT.
+CTF_REF = 0x00800000  # Reference: PTR.
+CTF_VECTOR = 0x08000000  # Vector: ARRAY.
+CTF_COMPLEX = 0x04000000  # Complex: ARRAY.
+CTF_UNION = 0x00800000  # Union: STRUCT.
+CTF_VARARG = 0x00800000  # Vararg: FUNC.
+CTF_SSEREGPARM = 0x00400000  # SSE register parameters: FUNC.
+
+CTF_UCHAR = CTF_UNSIGNED if int(dbg.cast('char', -1)) > 0 else 0
+
+CTMASK_ATTRIB = 255  # Max. 256 attributes.
+CTSHIFT_ATTRIB = 16
+
+# Attribute numbers.
+CTA_QUAL = 1  # Unmerged qualifiers.
+
+CTSHIFT_NUM = 28
+CTMASK_CID = 0x0000ffff
+CTMASK_NUM = 0xf0000000  # Max. 16 type numbers.
+
+# Special sizes.
+CTSIZE_INVALID = 0xffffffff
+DWORDSZ = 4
+QWORDSZ = 8
+
+
+def ctype_type(info):
+    return info >> CTSHIFT_NUM
+
+
+def ctype_attrib(info):
+    return (info >> CTSHIFT_ATTRIB) & CTMASK_ATTRIB
+
+
+def ctinfo(ct, flags):
+    return (tou32(ct) << CTSHIFT_NUM) + flags
+
+
+def ctype_isptr(info):
+    return ctype_type(info) == CT_PTR
+
+
+def ctype_iscomplex(info):
+    return (info & (CTMASK_NUM | CTF_COMPLEX)) == ctinfo(CT_ARRAY, CTF_COMPLEX)
+
+
+def ctype_isinteger(info):
+    return (info & (CTMASK_NUM | CTF_BOOL | CTF_FP)) == ctinfo(CT_NUM, 0)
+
+
+def ctype_isrefarray(info):
+    return (info & (CTMASK_NUM | CTF_VECTOR | CTF_COMPLEX)) == \
+           ctinfo(CT_ARRAY, 0)
+
+
+def ctype_cid(info):
+    return info & CTMASK_CID
+
+
+def ctype_child(cts, ctype):
+    return ctype_get(cts, ctype_cid(ctype['info']))
+
+
+def cdataptr(cd):
+    return dbg.cast('void *', (cd + 1))
+
+
+def cdata_getptr(p, size):
+    if LJ_64 and size == 4:
+        return dbg.cast('void *', dbg.cast('uint32_t *', p)[0])
+    else:
+        return dbg.cast('void *', dbg.cast('uint64_t *', p)[0])
+
+
+# Get C type ID for a C type.
+def ctype_typeid(cts, ct):
+    return ct - cts['tab']
+
+
 # JIT engine.
 
 
@@ -1951,7 +2071,26 @@ def dump_lj_gco_trace(gcobj):
 
 
 def dump_lj_gco_cdata(gcobj):
-    return 'cdata @ {}'.format(strx64(gcobj))
+    cdata = dbg.cast('struct GCcdata *', gcobj)
+    cts = ctype_ctsG(G(L()))
+    cid = cdata['ctypeid']
+    ctype = ctype_get(cts, cid)
+    info = ctype['info']
+    size = ctype['size']
+    value = ''
+    if ctype_iscomplex(info):
+        value = cdata_val_complex(cdata, ctype)
+    elif size == 8 and ctype_isinteger(info):
+        value = cdata_val_int64(cdata, ctype)
+    else:
+        value = cdataptr(cdata)
+        if ctype_isptr(info):
+            value = cdata_getptr(value, size)
+    return 'cdata @ {addr} {ctype} {value}'.format(
+        addr=strx64(gcobj),
+        ctype=dump_ctype(ctype),
+        value=value,
+    )
 
 
 def dump_lj_gco_tab(gcobj):
@@ -2281,6 +2420,176 @@ def dump_func(func):
         return 'fast function #{}\n'.format(int(ffid))
 
 
+# FFI dumpers.
+
+
+def cdata_val_int64(cdata, ctype):
+    info = ctype['info']
+    isunsigned = info & CTF_UNSIGNED
+    cdataval = cdataptr(cdata)
+    valueptr = None
+    usuffix = ''
+    if isunsigned:
+        usuffix = 'U'
+        valueptr = dbg.cast('uint64_t *', cdataval)
+    else:
+        valueptr = dbg.cast('int64_t *', cdataval)
+    return str(valueptr[0]) + usuffix + 'LL'
+
+
+def cdata_val_complex(cdata, ctype):
+    size = ctype['size']
+    cdataval = cdataptr(cdata)
+    casttype = None
+    if size == QWORDSZ * 2:
+        casttype = 'double *'
+    else:
+        assert size == DWORDSZ * 2, 'bad (complex float) size'
+        casttype = 'float *'
+    re = dbg.cast(casttype, cdataval)[0]
+    im = dbg.cast(casttype, cdataval)[1]
+    sign = '+' if im > 0 else ''
+    return '{re}{sign}{im}i'.format(re=re, im=im, sign=sign)
+
+
+def ctype_preplit(ctypestr, lit):
+    # Prevent extra space in the end of the string.
+    space = ' ' if ctypestr != '' else ''
+    return lit + space + ctypestr
+
+
+def ctype_prepqual(ctypestr, info):
+    if (info & CTF_VOLATILE):
+        ctypestr = ctype_preplit(ctypestr, 'volatile')
+    if (info & CTF_CONST):
+        ctypestr = ctype_preplit(ctypestr, 'const')
+    return ctypestr
+
+
+def ctype_preptype(cts, ctypestr, ctype, qual, tp):
+    nameref = gcref(ctype['name'])
+    if nameref:
+        ctypestr = ctype_preplit(ctypestr, re.sub('"', '', strdata(nameref)))
+    else:
+        ctypestr = ctype_preplit(ctypestr, str(ctype_typeid(cts, ctype)))
+    ctypestr = ctype_preplit(ctypestr, tp)
+    ctypestr = ctype_prepqual(ctypestr, qual)
+    return ctypestr
+
+
+def ctype_prepnum(ctypestr, info, size):
+    if info & CTF_BOOL:
+        ctypestr = ctype_preplit(ctypestr, 'bool')
+    elif info & CTF_FP:
+        if size == QWORDSZ:
+            ctypestr = ctype_preplit(ctypestr, 'double')
+        elif size == DWORDSZ:
+            ctypestr = ctype_preplit(ctypestr, 'float')
+        else:
+            assert size == QWORDSZ * 2, 'bad (long double) size'
+            ctypestr = ctype_preplit(ctypestr, 'long double')
+    elif size == 1:
+        if not ((info ^ CTF_UCHAR) & CTF_UNSIGNED):
+            ctypestr = ctype_preplit(ctypestr, 'char')
+        elif CTF_UCHAR:
+            ctypestr = ctype_preplit(ctypestr, 'signed char')
+        else:
+            ctypestr = ctype_preplit(ctypestr, 'unsigned char')
+    elif size < 8:
+        if size == 4:
+            ctypestr = ctype_preplit(ctypestr, 'int')
+        else:
+            assert size == DWORDSZ // 2, 'bad (short) size'
+            ctypestr = ctype_preplit(ctypestr, 'short')
+        if info & CTF_UNSIGNED:
+            ctypestr = ctype_preplit(ctypestr, 'unsigned')
+    else:
+        size_t = '{u}int{sz}_t'.format(
+            u='u' if info & CTF_UNSIGNED else '',
+            sz=size * 8,
+        )
+        ctypestr = ctype_preplit(ctypestr, size_t)
+    return ctypestr
+
+
+def ctype_repr(cts, id):
+    ctype = ctype_get(cts, id)
+    ctypestr = ''
+    qual = 0
+    ptrto = 0
+    while True:
+        info = ctype['info']
+        size = ctype['size']
+        ctp = ctype_type(info)
+        if ctp == CT_NUM:
+            ctypestr = ctype_prepnum(ctypestr, info, size)
+            return ctype_prepqual(ctypestr, qual | info)
+        elif ctp == CT_VOID:
+            ctypestr = ctype_preplit(ctypestr, 'void')
+            return ctype_prepqual(ctypestr, qual | info)
+        elif ctp == CT_STRUCT:
+            tp = 'union' if (info & CTF_UNION) else 'struct'
+            return ctype_preptype(cts, ctypestr, ctype, qual, tp)
+        elif ctp == CT_ENUM:
+            if id == CTID_CTYPEID:
+                return ctype_preplit(ctypestr, 'ctype')
+            return ctype_preptype(cts, ctypestr, ctype, qual, 'enum')
+        elif ctp == CT_ATTRIB:
+            if ctype_attrib(info) == CTA_QUAL:
+                qual |= size
+        elif ctp == CT_PTR:
+            if info & CTF_REF:
+                ctypestr = ctype_preplit(ctypestr, '&')
+            else:
+                ctypestr = ctype_prepqual(ctypestr, qual | info)
+                if LJ_64 and size == 4:
+                    ctypestr = ctype_preplit(ctypestr, '__ptr32')
+                ctypestr = ctype_preplit(ctypestr, '*')
+            qual = 0
+            ptrto = 1
+        elif ctp == CT_ARRAY:
+            if ctype_isrefarray(info):
+                if ptrto:
+                    ptrto = 0
+                    ctypestr = '(' + ctypestr + ')'
+                arrsize = ''
+                if size != CTSIZE_INVALID:
+                    child_size = ctype_child(cts, ctype)['size']
+                    arrsize = str(int(size / child_size) if child_size > 0
+                                  else 0)
+                elif info & CTF_VLA:
+                    arrsize = '?'
+                ctypestr = ctypestr + '[{}]'.format(arrsize)
+            elif ctype_iscomplex(info):
+                if size == DWORDSZ * 2:
+                    ctypestr = ctype_preplit(ctypestr, 'float')
+                else:
+                    assert size == QWORDSZ * 2, 'bad (complex double) size'
+                return ctype_preplit(ctypestr, 'complex')
+            else:
+                ctypestr = ctype_preplit(
+                    ctypestr,
+                    '__attribute__((vector_size({})))'.format(size)
+                )
+        elif ctp == CT_FUNC:
+            if ptrto:
+                ptrto = 0
+                ctypestr = '(' + ctypestr + ')'
+            ctypestr += '()'
+        ctype = ctype_child(cts, ctype)
+    return 'NYI'
+
+
+def dump_ctype(ct):
+    cts = ctype_ctsG(G(L()))
+    cid = ctype_typeid(cts, ct)
+    name = ctype_repr(cts, cid)
+    return '[{id}] <{name}>'.format(
+        id=cid,
+        name=name,
+    )
+
+
 # JIT dumpers.
 
 
@@ -2294,7 +2603,8 @@ def dump_call_func(trace, callop):
             assert IRS[cdt_idx_irk['o']] == 'KINT', \
                    'unexpected IR for ctype storage'
             ctype_idx = cdt_idx_irk['i']
-            ctype = 'ctype: {}'.format(ctype_idx)
+            cts = ctype_ctsG(G(L()))
+            ctype = 'ctype: {}'.format(dump_ctype(ctype_get(cts, ctype_idx)))
 
     func_str = ''
     if callop < 0:
@@ -2652,6 +2962,20 @@ https://github.com/tarantool/tarantool/wiki/LuaJIT-Bytecodes.
         ))
 
 
+class LJDumpCType(dbg.LJBase):
+    '''
+lj-ctype <CType *>
+
+The command receives a pointer <ctype> of the corresponding CType
+and dumps the ID and the name for this C data type.
+    '''
+
+    def execute(self, arg):
+        dbg.write('{}\n'.format(
+            dump_ctype(dbg.cast('CType *', dbg.eval(arg)))
+        ))
+
+
 class LJDumpFunc(dbg.LJBase):
     '''
 lj-func <GCfunc *>
@@ -2979,6 +3303,7 @@ def load(event=None):
     dbg.initialize_extension({
         'lj-arch':   LJDumpArch,
         'lj-bc':     LJDumpBC,
+        'lj-ctype':  LJDumpCType,
         'lj-func':   LJDumpFunc,
         'lj-gc':     LJGC,
         'lj-gco':    LJDumpGCobj,
diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py b/test/tarantool-debugger-tests/debug-extension-tests.py
index 76543daa..fc5d2c7b 100644
--- a/test/tarantool-debugger-tests/debug-extension-tests.py
+++ b/test/tarantool-debugger-tests/debug-extension-tests.py
@@ -227,6 +227,7 @@ class TestLoad(TestCaseBase):
     pattern = (
         r'lj-arch command initialized\n'
         r'lj-bc command initialized\n'
+        r'lj-ctype command initialized\n'
         r'lj-func command initialized\n'
         r'lj-gc command initialized\n'
         r'lj-gco command initialized\n'
@@ -331,7 +332,7 @@ GCO_RX = (
     r'Lua function @ ' + RX_ADDR + r', [0-9]+ upvalues, .+:[0-9]+\n'
     r'C function @ ' + RX_ADDR + r'\n'
     r'fast function #[0-9]+\n'
-    r'cdata @ ' + RX_ADDR + r'\n'
+    r'cdata @ ' + RX_ADDR + r' \[\d+\] <int \*> 0x0\n'
     r'table @ ' + RX_ADDR + r' \(asize: \d+, hmask: ' + RX_HASH + r'\)\n'
     r'userdata @ ' + RX_ADDR + r'\n'
 )
@@ -817,7 +818,9 @@ class TestLJIRCallXSCType(TestCaseBase):
         'trace()\n'
         'print()\n'
     )
-    pattern = r'int CALLXS .* [' + RX_ADDR + r'\]\(.*\) ctype: \d+'
+    pattern = (
+        r'int CALLXS .* [' + RX_ADDR + r'\]\(.*\) ctype: \[\d+\] <int \(\)>'
+    )
 
 
 class TestLJJSlotsBase(TestCaseBase):
@@ -838,6 +841,207 @@ class TestLJJSlotsBase(TestCaseBase):
     )
 
 
+def cdata_rx(tpstr, suffix=None):
+    return r'cdata @ ' + RX_ADDR + r' \[\d+\] <' + tpstr + '> ' + (
+        RX_ADDR if not suffix else suffix
+    )
+
+
+CHAR_SIGNED = machine in ['arm64', 'aarch64'] and sys.platform != 'darwin'
+HAS_LONG_DOUBLE = not (machine in ['arm64', 'aarch64'] and
+                       sys.platform == 'darwin')
+
+
+class TestLJCTypePrim(TestCaseBase):
+    location = 'lj_cf_print'
+    extension_cmds = (
+        'n\n'  # Load L.
+        'lj-tv L->base\n'
+        'lj-tv L->base + 1\n'
+        'lj-tv L->base + 2\n'
+        'lj-tv L->base + 3\n'
+        'lj-tv L->base + 4\n'
+        'lj-tv L->base + 5\n'
+        'lj-tv L->base + 6\n'
+        'lj-tv L->base + 7\n'
+        'lj-tv L->base + 8\n'
+        'lj-tv L->base + 9\n'
+        'lj-tv L->base + 10\n'
+        'lj-tv L->base + 11\n'
+        'lj-tv L->base + 12\n'
+        'lj-tv L->base + 13\n'
+        'lj-tv L->base + 14\n'
+        'lj-tv L->base + 15\n'
+        'lj-tv L->base + 16\n'
+        'lj-tv L->base + 17\n'
+        'lj-tv L->base + 18\n'
+        'lj-tv L->base + 19\n'
+        'lj-tv L->base + 20\n'
+        'lj-tv L->base + 21\n'
+        'lj-tv L->base + 22\n'
+    )
+    lua_script = (
+        'local ffi = require("ffi")\n'
+        'print(\n'
+        '  ffi.new("bool"),\n'
+        '  ffi.new("char"),\n'
+        '  ffi.new("signed char"),\n'
+        '  ffi.new("unsigned char"),\n'
+        '  ffi.new("int"),\n'
+        '  ffi.new("short"),\n'
+        '  ffi.new("unsigned"),\n'
+        '  ffi.new("int8_t"),\n'
+        '  ffi.new("int16_t"),\n'
+        '  ffi.new("int32_t"),\n'
+        '  ffi.new("int64_t"),\n'
+        '  ffi.new("uint8_t"),\n'
+        '  ffi.new("uint64_t"),\n'
+        '  ffi.new("float"),\n'
+        '  ffi.new("double"),\n'
+        '  ffi.new("long double"),\n'
+        '  1i,\n'
+        '  ffi.new("complex float", 1, -2),\n'
+        '  ffi.new("const volatile int"),\n'
+        '  ffi.new("void *"),\n'
+        '  ffi.new("void * __ptr32"),\n'
+        '  ffi.new("int &"),\n'
+        '  ffi.typeof(1LL)\n'
+        ')\n'
+    )
+    pattern = (
+        cdata_rx('bool') + r'\n' +
+        cdata_rx('char') + r'\n' +
+        cdata_rx(('signed ' if CHAR_SIGNED else '') + 'char') + r'\n' +
+        cdata_rx(('unsigned ' if not CHAR_SIGNED else '') + 'char') + r'\n' +
+        cdata_rx('int') + r'\n' +
+        cdata_rx('short') + r'\n' +
+        cdata_rx('unsigned int') + r'\n' +
+        cdata_rx(('signed ' if CHAR_SIGNED else '') + 'char') + r'\n' +
+        cdata_rx('short') + r'\n' +
+        cdata_rx('int') + r'\n' +
+        cdata_rx('int64_t', '0LL') + r'\n' +
+        cdata_rx(('unsigned ' if not CHAR_SIGNED else '') + 'char') + r'\n' +
+        cdata_rx('uint64_t', '0ULL') + r'\n' +
+        cdata_rx('float') + r'\n' +
+        cdata_rx('double') + r'\n' +
+        cdata_rx(('long ' if HAS_LONG_DOUBLE else '') + 'double') + r'\n' +
+        cdata_rx('complex', r'0\+1i') + r'\n' +
+        cdata_rx('complex float', '1-2i') + r'\n' +
+        cdata_rx('const volatile int') + r'\n' +
+        cdata_rx(r'void \*') + r'\n' +
+        cdata_rx(r'void \* __ptr32') + r'\n' +
+        cdata_rx('int &') + r'\n' +
+        cdata_rx('ctype') + r'\n'
+    )
+
+
+class TestLJCTypeStructUnionEnum(TestCaseBase):
+    location = 'lj_cf_print'
+    extension_cmds = (
+        'n\n'  # Load L.
+        'lj-tv L->base\n'
+        'lj-tv L->base + 1\n'
+        'lj-tv L->base + 2\n'
+        'lj-tv L->base + 3\n'
+    )
+    lua_script = (
+        'local ffi = require("ffi")\n'
+        'ffi.cdef[[\n'
+        '  struct test {int a;};\n'
+        ']]\n'
+        'print(\n'
+        '  ffi.new("struct test"),\n'
+        '  ffi.new("struct {int a;}"),\n'
+        '  ffi.new("union {int a;}"),\n'
+        '  ffi.new("enum {ENUM1}")\n'
+        ')\n'
+    )
+    pattern = (
+        cdata_rx('struct test') + r'\n' +
+        cdata_rx(r'struct \d+') + r'\n' +
+        cdata_rx(r'union \d+') + r'\n' +
+        cdata_rx(r'enum \d+') + r'\n'
+    )
+
+
+class TestLJCTypeArray(TestCaseBase):
+    location = 'lj_cf_print'
+    extension_cmds = (
+        'n\n'  # Load L.
+        'lj-tv L->base\n'
+        'lj-tv L->base + 1\n'
+        'lj-tv L->base + 2\n'
+        'lj-tv L->base + 3\n'
+        'lj-tv L->base + 4\n'
+        'lj-tv L->base + 5\n'
+        'lj-tv L->base + 6\n'
+    )
+    lua_script = (
+        'local ffi = require("ffi")\n'
+        'print(\n'
+        '  ffi.new("char [0]"),\n'
+        '  ffi.new("int [1]"),\n'
+        '  ffi.new("complex [2]"),\n'
+        '  ffi.new("complex float [3]"),\n'
+        '  ffi.new("float __attribute__((vector_size(4)))"),\n'
+        '  ffi.new("int (&)[5]"),\n'
+        '  ffi.new("int[?]", 6)\n'
+        ')\n'
+    )
+    pattern = (
+        cdata_rx(r'char \[0\]') + r'\n' +
+        cdata_rx(r'int \[1\]') + r'\n' +
+        cdata_rx(r'complex \[2\]') + r'\n' +
+        cdata_rx(r'complex float \[3\]') + r'\n' +
+        cdata_rx(r'float __attribute__\(\(vector_size\(4\)\)\)') + r'\n' +
+        cdata_rx(r'int \(&\)\[5\]') + r'\n' +
+        cdata_rx(r'int \[\?\]') + r'\n'
+    )
+
+
+class TestLJCTypeFunc(TestCaseBase):
+    location = 'lj_cf_print'
+    extension_cmds = (
+        'n\n'  # Load L.
+        'lj-tv L->base\n'
+        'lj-tv L->base + 1\n'
+        'lj-tv L->base + 2\n'
+    )
+    lua_script = (
+        'local ffi = require("ffi")\n'
+        'ffi.cdef[[void getpid(void);]]\n'
+        'print(\n'
+        '  ffi.C.getpid,\n'
+        '  ffi.new("int (*)()"),\n'
+        '  ffi.new("int (*(*)(void))[2]")\n'
+        ')\n'
+    )
+    pattern = (
+        cdata_rx(r'void \(\)') + r'\n' +
+        cdata_rx(r'int \(\*\)\(\)') + r'\n' +
+        cdata_rx(r'int \(\* \(\*\)\(\)\)\[2\]') + r'\n'
+    )
+
+
+class TestLJCTypeBase(TestCaseBase):
+    location = 'lj_cf_ffi_new'
+    extension_cmds = (
+        # Load `ct`. Skip inlined functions for LLDB.
+        'n\n'
+        'n\n'
+        'n\n'
+        'n\n'
+        'n\n'
+        'n\n'
+        'lj-ctype ct\n'
+    )
+    lua_script = (
+        'local ffi = require("ffi")\n'
+        'ffi.new("int")\n'
+    )
+    pattern = r'\[\d+\] <int>'
+
+
 for test_cls in TestCaseBase.__subclasses__():
     test_cls.test = lambda self: self.check()
 
-- 
2.54.0


  parent reply	other threads:[~2026-06-25 20:30 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-25 20:29 [Tarantool-patches] [PATCH luajit 0/3] Extend debug extension Sergey Kaplun via Tarantool-patches
2026-06-25 20:29 ` [Tarantool-patches] [PATCH luajit 1/3] dbg: introduce lj-ir, lj-jslots, lj-trace dumpers Sergey Kaplun via Tarantool-patches
2026-06-25 20:29 ` Sergey Kaplun via Tarantool-patches [this message]
2026-06-25 20:29 ` [Tarantool-patches] [PATCH luajit 3/3] test: add verbose mode for debug extension tests 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=20260625202903.3157425-3-skaplun@tarantool.org \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=e.temirgaleev@tarantool.org \
    --cc=sergeyb@tarantool.org \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH luajit 2/3] dbg: introduce lj-ctype command, extend cdata dump' \
    /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