* [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT @ 2020-02-05 16:22 Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension Igor Munkin ` (7 more replies) 0 siblings, 8 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-05 16:22 UTC (permalink / raw) To: tarantool-patches The series provides a gdb extension with commands for inspecting LuaJIT internals. To use it, just put 'source <path-to-repo>/src/luajit-gdb.py' in gdb. The extension obliges the one to provide gdbinfo for libluajit, otherwise loading can fail with the error: | (gdb) source <path-to-repo>/src/luajit-gdb.py | luajit-gdb.py failed to load: no debugging symbols found for libluajit When the loading succeeds the one see the following: | (gdb) source <path-to-repo>/src/luajit-gdb.py | lj-arch command initialized | lj-tv command initialized | lj-str command initialized | lj-tab command initialized | lj-stack command initialized | lj-state command initialized | lj-gc command initialized | luajit-gdb.py is successfully loaded Below is a description for the set of implemented commands below: ### lj-arch The command requires no args and dumps values of LJ_64 and LJ_GC64 compile-time flags. These values define the sizes of host and GC pointers respectively. NB: Compile-time defines are checked when script is being loaded: * When LJ_64 is enabled IRT_PTR is an IRT_P64 alias and an IRT_P32 one otherwise * When LJ_64 is enabled IRT_PGC is an IRT_P64 alias and an IRT_P32 one otherwise ### lj-tv The command recieves a <tv address> (TValue address) and dumps the type and some info related to it. * LJ_TNIL: nil * LJ_TFALSE: false * LJ_TTRUE: true * LJ_TLIGHTUD: light userdata @ <gcr> * LJ_TSTR: string <string payload> @ <gcr> * LJ_TUPVAL: upvalue @ <gcr> * LJ_TTHREAD: thread @ <gcr> * LJ_TPROTO: proto @ <gcr> * LJ_TFUNC: <LFUNC|CFUNC|FFUNC> - <LFUNC>: Lua function @ <gcr>, <nupvals> upvalues, <chunk:line> - <CFUNC>: C function <mcode address> - <FFUNC>: fast function #<ffid> * LJ_TTRACE: trace <traceno> @ <gcr> * LJ_TCDATA: cdata @ <gcr> * LJ_TTAB: table @ <gcr> (asize: <asize>, hmask: <hmask>) * LJ_TUDATA: userdata @ <gcr> * LJ_TNUMX: number <numeric payload> Whether the type of the given address differs from the listed above, then error message occurs. ### lj-str The command recieves a <gcr> of the corresponding GCstr object and dumps the payload, size in bytes and hash. *Caveat*: Since Python 2 provides no native Unicode support, the payload is replaced with the corresponding error when decoding fails. ### lj-tab The command recieves a GCtab address and dumps the table contents: * Metatable address whether the one is set * Array part <asize> slots: <aslot ptr>: [<index>]: <tv> * Hash part <hsize> nodes: <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> ### lj-stack The command recieves a lua_State address and dumps the given Lua coroutine guest stack: <slot ptr> [<slot attributes>] <VALUE|FRAME> * <slot ptr>: guest stack slot address * <slot attributes>: - S: Bottom of the stack (the slot L->stack points to) - B: Base of the current guest frame (the slot L->base points to) - T: Top of the current guest frame (the slot L->top points to) - M: Last slot of the stack (the slot L->maxstack points to) * <VALUE>: see help lj-tv for more info * <FRAME>: framelink slot differs from the value slot: it contains info related to the function being executed within this guest frame, its type and link to the parent guest frame [<frame type>] delta=<slots in frame>, <lj-tv for LJ_TFUNC slot> - <frame type>: + L: VM performs a call as a result of bytecode execution + C: VM performs a call as a result of lj_vm_call + M: VM performs a call to a metamethod as a result of bytecode execution + V: Variable-length frame for storing arguments of a variadic function + CP: Protected C frame + PP: VM performs a call as a result of executinig pcall or xpcall If L is ommited the main coroutine is used. ### lj-state The command requires no args and dumps current VM and GC states * VM state: <INTERP|C|GC|EXIT|RECORD|OPT|ASM|TRACE> * GC state: <PAUSE|PROPAGATE|ATOMIC|SWEEPSTRING|SWEEP|FINALIZE|LAST> * JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> ### lj-gc The command requires no args and dumps current GC stats: * total: <total number of allocated bytes in GC area> * threshold: <limit when gc step is triggered> * debt: <how much GC is behind schedule> * estimate: <estimate of memory actually in use> * stepmul: <incremental GC step granularity> * pause: <pause between successive GC cycles> * sweepstr: <sweep position in string table> * root: <number of all collectable objects> * gray: <number of gray objects> * grayagain: <number of objects for atomic traversal> * weak: <number of weak tables (to be cleared)> Signed-off-by: Igor Munkin <imun@tarantool.org> -- Changes in v2: * squashed "fixup" commits with the first one * added loading errors handling v1: https://lists.tarantool.org/pipermail/tarantool-patches/2020-January/013801.html Branch: https://github.com/tarantool/luajit/tree/imun/luajit-gdb Igor Munkin (3): gdb: introduce luajit-gdb extension gdb: adjust the extension to be used with Python 2 gdb: enhance the extension loading src/luajit-gdb.py | 687 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 687 insertions(+) create mode 100644 src/luajit-gdb.py -- 2.24.0 ^ permalink raw reply [flat|nested] 23+ messages in thread
* [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin @ 2020-02-05 16:22 ` Igor Munkin 2020-02-13 13:24 ` Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 2/3] gdb: adjust the extension to be used with Python 2 Igor Munkin ` (6 subsequent siblings) 7 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-02-05 16:22 UTC (permalink / raw) To: tarantool-patches The provided luajit-gdb extenstion contains the following additional commands: * lj-arch -- dumps values of LJ_64 and LJ_GC64 macro definitions * lj-tv -- dumps the type and some GCobj info related to the given TValue * lj-str -- dumps the contents of the given GCstr * lj-tab -- dumps the contents of the given GCtab * lj-stack -- dumps Lua stack of the given lua_State * lj-state -- shows current VM, GC and JIT states * lj-gc -- shows current GC stats Currently extension supports only x64 builds but respects LJ_GC64 value Signed-off-by: Igor Munkin <imun@tarantool.org> --- src/luajit-gdb.py | 684 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 684 insertions(+) create mode 100644 src/luajit-gdb.py diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py new file mode 100644 index 0000000..77da5e6 --- /dev/null +++ b/src/luajit-gdb.py @@ -0,0 +1,684 @@ +import re +import gdb + +gtype_cache = {} + +def gtype(typestr): + global gtype_cache + if typestr in gtype_cache: + return gtype_cache[typestr] + + m = re.match(r'((?:(?:struct|union) )?\S*)\s*[*]', typestr) + + gtype = gdb.lookup_type(typestr) if m is None \ + else gdb.lookup_type(m.group(1)).pointer() + + gtype_cache[typestr] = gtype + return gtype + +def cast(typestr, val): + return gdb.Value(val).cast(gtype(typestr)) + +def lookup(symbol): + variable, _ = gdb.lookup_symbol(symbol) + return variable.value() if variable else None + +def parse_arg(arg): + if not arg: + return None + + ret = gdb.parse_and_eval(arg) + + if not ret: + raise gdb.GdbError('table argument empty') + + return ret + +def tou64(val): + return cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF + +def tou32(val): + return cast('uint32_t', val) & 0xFFFFFFFF + +def i2notu64(val): + return ~int(val) & 0xFFFFFFFFFFFFFFFF + +def i2notu32(val): + return ~int(val) & 0xFFFFFFFF + +def strx64(val): + return hex(cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF) + +# Types {{{ + +LJ_T = { + 'NIL' : i2notu32(0), + 'FALSE' : i2notu32(1), + 'TRUE' : i2notu32(2), + 'LIGHTUD' : i2notu32(3), + 'STR' : i2notu32(4), + 'UPVAL' : i2notu32(5), + 'THREAD' : i2notu32(6), + 'PROTO' : i2notu32(7), + 'FUNC' : i2notu32(8), + 'TRACE' : i2notu32(9), + 'CDATA' : i2notu32(10), + 'TAB' : i2notu32(11), + 'UDATA' : i2notu32(12), + 'NUMX' : i2notu32(13), +} + +def typenames(value): + return { + LJ_T[k]: 'LJ_T' + k for k in LJ_T.keys() + }.get(int(value), 'LJ_TINVALID') + +# }}} + +# Frames {{{ + +FRAME_TYPE = 0x3 +FRAME_P = 0x4 +FRAME_TYPEP = FRAME_TYPE | FRAME_P + +FRAME = { + 'LUA': 0x0, + 'C': 0x1, + 'CONT': 0x2, + 'VARG': 0x3, + 'LUAP': 0x4, + 'CP': 0x5, + 'PCALL': 0x6, + 'PCALLH': 0x7, +} + +def frametypes(ft): + return { + FRAME['LUA'] : 'L', + FRAME['C'] : 'C', + FRAME['CONT'] : 'M', + FRAME['VARG'] : 'V', + }.get(ft, '?') + +def bc_a(ins): + return (ins >> 8) & 0xff + +def frame_ftsz(framelink): + return cast('ptrdiff_t', framelink['ftsz'] if LJ_FR2 \ + else framelink['fr']['tp']['ftsz']) + +def frame_pc(framelink): + return cast('BCIns *', frame_ftsz(framelink)) if LJ_FR2 \ + else mref('BCIns *', framelink['fr']['tp']['pcr']) + +def frame_prevl(framelink): + return framelink - (1 + LJ_FR2 + bc_a(frame_pc(framelink)[-1])) + +def frame_ispcall(framelink): + return (frame_ftsz(framelink) & FRAME['PCALL']) == FRAME['PCALL'] + +def frame_sized(framelink): + return (frame_ftsz(framelink) & ~FRAME_TYPEP) + +def frame_prevd(framelink): + return cast('TValue *', cast('char *', framelink) - frame_sized(framelink)) + +def frame_type(framelink): + return frame_ftsz(framelink) & FRAME_TYPE + +def frame_typep(framelink): + return frame_ftsz(framelink) & FRAME_TYPEP + +def frame_islua(framelink): + return frametypes(int(frame_type(framelink))) == 'L' \ + and int(frame_ftsz(framelink)) > 0 + +def frame_prev(framelink): + return frame_prevl(framelink) if frame_islua(framelink) \ + else frame_prevd(framelink) + +# }}} + +# Const {{{ + +LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' + +LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' + +LJ_FR2 = LJ_GC64 + +LJ_GCVMASK = ((1 << 47) - 1) + +PADDING = ' ' * len(':' + hex((1 << (47 if LJ_GC64 else 32)) - 1)) + +# }}} + +def itype(o): + return cast('uint32_t', o['it64'] >> 47) if LJ_GC64 else o['it'] + +def mref(typename, obj): + return cast(typename, obj['ptr64'] if LJ_GC64 else obj['ptr32']) + +def gcref(obj): + return cast('GCobj *', obj['gcptr64'] if LJ_GC64 + else cast('uintptr_t', obj['gcptr32'])) + +def gcval(obj): + return cast('GCobj *', obj['gcptr64'] & LJ_GCVMASK if LJ_GC64 + else cast('uintptr_t', obj['gcptr32'])) + +def L(L=None): + # lookup a symbol for the main coroutine considering the host app + for l in (L, *map(lambda l: lookup(l), ( + # LuaJIT main coro (see luajit/src/luajit.c) + 'globalL', + # Tarantool main coro (see tarantool/src/lua/init.h) + 'tarantool_L', + # TODO: Add more + ))): + if l: + return cast('lua_State *', l) + +def G(L): + return mref('global_State *', L['glref']) + +def J(g): + typeGG = gtype('GG_State') + + return cast('jit_State *', int(cast('char *', g)) + - int(typeGG['g'].bitpos / 8) + + int(typeGG['J'].bitpos / 8) + ) + +def vm_state(g): + return { + i2notu64(0): 'INTERP', + i2notu64(1): 'C', + i2notu64(2): 'GC', + i2notu64(3): 'EXIT', + i2notu64(4): 'RECORD', + i2notu64(5): 'OPT', + i2notu64(6): 'ASM', + }.get(int(tou64(g['vmstate'])), 'TRACE') + +def gc_state(g): + return { + 0: 'PAUSE', + 1: 'PROPAGATE', + 2: 'ATOMIC', + 3: 'SWEEPSTRING', + 4: 'SWEEP', + 5: 'FINALIZE', + 6: 'LAST', + }.get(int(g['gc']['state']), 'INVALID') + +def jit_state(g): + return { + 0: 'IDLE', + 0x10: 'ACTIVE', + 0x11: 'RECORD', + 0x12: 'START', + 0x13: 'END', + 0x14: 'ASM', + 0x15: 'ERR', + }.get(int(J(g)['state']), 'INVALID') + +def tvisnumber(o): + return itype(o) <= (0xfffeffff if LJ_64 and not LJ_GC64 else LJ_T['NUMX']) + +def tvislightud(o): + if LJ_64 and not LJ_GC64: + return (cast('int32_t', itype(o)) >> 15) == -2 + else: + return itype(o) == LJ_T['LIGHTUD'] + +def strdata(obj): + # String is printed with pointer to it, thanks to gdb. Just strip it. + return str(cast('char *', cast('GCstr *', obj) + 1))[len(PADDING):] + +def itypemap(o): + if LJ_64 and not LJ_GC64: + return LJ_T['NUMX'] if tvisnumber(o) \ + else LJ_T['LIGHTUD'] if tvislightud(o) \ + else itype(o) + else: + return LJ_T['NUMX'] if tvisnumber(o) else itype(o) + +def funcproto(func): + assert(func['ffid'] == 0) + + return cast('GCproto *', + mref('char *', func['pc']) - gdb.lookup_type('GCproto').sizeof) + +def gclistlen(root): + count = 0 + while(gcref(root)): + count += 1 + root = gcref(root)['gch']['nextgc'] + return count + +# Dumpers {{{ + +def dump_lj_tnil(tv): + return 'nil' + +def dump_lj_tfalse(tv): + return 'false' + +def dump_lj_ttrue(tv): + return 'true' + +def dump_lj_tlightud(tv): + return 'light userdata @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_tstr(tv): + return 'string {body} @ {address}'.format( + body = strdata(gcval(tv['gcr'])), + address = strx64(gcval(tv['gcr'])) + ) + +def dump_lj_tupval(tv): + return 'upvalue @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_tthread(tv): + return 'thread @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_tproto(tv): + return 'proto @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_tfunc(tv): + func = cast('struct GCfuncC *', gcval(tv['gcr'])) + ffid = func['ffid'] + + if ffid == 0: + pt = funcproto(func) + return 'Lua function @ {addr}, {nupvals} upvalues, {chunk}:{line}'.format( + addr = strx64(func), + nupvals = int(func['nupvalues']), + chunk = strdata(cast('GCstr *', gcval(pt['chunkname']))), + line = pt['firstline'] + ) + elif ffid == 1: + return 'C function @ {}'.format(strx64(func['f'])) + else: + return 'fast function #{}'.format(int(ffid)) + +def dump_lj_ttrace(tv): + trace = cast('struct GCtrace *', gcval(tv['gcr'])) + return 'trace {traceno} @ {addr}'.format( + traceno = strx64(trace['traceno']), + addr = strx64(trace) + ) + +def dump_lj_tcdata(tv): + return 'cdata @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_ttab(tv): + table = cast('GCtab *', gcval(tv['gcr'])) + return 'table @ {gcr} (asize: {asize}, hmask: {hmask})'.format( + gcr = strx64(table), + asize = table['asize'], + hmask = strx64(table['hmask']), + ) + +def dump_lj_tudata(tv): + return 'userdata @ {}'.format(strx64(gcval(tv['gcr']))) + +def dump_lj_tnumx(tv): + return 'number {}'.format(cast('double', tv['n'])) + +def dump_lj_invalid(tv): + return 'not valid type @ {}'.format(strx64(gcval(tv['gcr']))) + +# }}} + +dumpers = { + 'LJ_TNIL': dump_lj_tnil, + 'LJ_TFALSE': dump_lj_tfalse, + 'LJ_TTRUE': dump_lj_ttrue, + 'LJ_TLIGHTUD': dump_lj_tlightud, + 'LJ_TSTR': dump_lj_tstr, + 'LJ_TUPVAL': dump_lj_tupval, + 'LJ_TTHREAD': dump_lj_tthread, + 'LJ_TPROTO': dump_lj_tproto, + 'LJ_TFUNC': dump_lj_tfunc, + 'LJ_TTRACE': dump_lj_ttrace, + 'LJ_TCDATA': dump_lj_tcdata, + 'LJ_TTAB': dump_lj_ttab, + 'LJ_TUDATA': dump_lj_tudata, + 'LJ_TNUMX': dump_lj_tnumx, +} + +def dump_tvalue(tvalue): + return dumpers.get(typenames(itypemap(tvalue)), dump_lj_invalid)(tvalue) + +def dump_framelink(L, fr): + fr2 = fr + LJ_FR2 + + return '{fr}{padding} [ ] FRAME: [{pp}] delta={d}, {f}\n'.format( + fr = fr, + padding = ':{fr2}'.format(fr2 = fr2) if LJ_FR2 else PADDING, + pp = 'PP' if frame_ispcall(fr2) else '{frname}{p}'.format( + frname = frametypes(int(frame_type(fr2))), + p = 'P' if frame_typep(fr2) & FRAME_P else '' + ), + d = cast('TValue *', fr2) - cast('TValue *', frame_prev(fr2)), + f = dump_lj_tfunc(fr), + ) + +def dump_stack_slot(L, slot, base=None, top=None): + base = base or L['base'] + top = top or L['top'] + + return '{addr}{padding} [ {B}{T}{M}] VALUE: {value}\n'.format( + addr = strx64(slot), + padding = PADDING, + B = 'B' if slot == base else ' ', + T = 'T' if slot == top 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'] + maxstack = mref('TValue *', L['maxstack']) + red = 5 + 2 * LJ_FR2 + + dump = '\n'.join([ + '{start}:{end} [ ] {n} slots: Red zone'.format( + start = strx64(maxstack + 1), + end = strx64(maxstack + red), + n = red, + ), + '{maxstack}{padding} [ M]'.format( + maxstack = strx64(maxstack), + padding = PADDING, + ), + '{start}:{end} [ ] {nfreeslots} slots: Free stack slots'.format( + start = strx64(top + 1), + end = strx64(maxstack - 1), + nfreeslots = int((tou64(maxstack) - tou64(top) - 8) >> 3), + ), + '{top}{padding} [ T ]'.format( + top = strx64(top), + padding = PADDING, + ) + ]) + '\n' + + slot = top - 1 + framelink = base - (1 + LJ_FR2) + + while framelink > mref('TValue *', L['stack']): + while slot > framelink + LJ_FR2: + dump += dump_stack_slot(L, slot, base, top) + slot -= 1 + dump += dump_framelink(L, framelink) + framelink = frame_prev(framelink + LJ_FR2) - LJ_FR2 + slot -= 1 + LJ_FR2 + + dump += '{fr}{padding} [S ] FRAME: dummy L'.format( + fr = slot, + padding = ':{nilslot}'.format(nilslot = slot + 1) if LJ_FR2 else PADDING + ) + + return dump + +def dump_gc(g): + gc = g['gc'] + stats = [ '{key}: {value}'.format(key = f, value = gc[f]) for f in ( + 'total', 'threshold', 'debt', 'estimate', 'stepmul', 'pause' + ) ] + + stats += [ 'sweepstr: {sweepstr}/{strmask}'.format( + sweepstr = gc['sweepstr'], + # String hash mask (size of hash table - 1). + strmask = g['strmask'] + 1, + ) ] + + stats += [ '{key}: {number} objects'.format( + key = f, + number = gclistlen(gc[f]), + ) for f in ('root', 'gray', 'grayagain', 'weak') ] + + # TODO: mmudata + + return '\n'.join(map(lambda s: '\t' + s, stats)) + +class LJDumpArch(gdb.Command): + ''' +lj-arch + +The command requires no args and dumps values of LJ_64 and LJ_GC64 +compile-time flags. These values define the sizes of host and GC +pointers respectively. + ''' + + def __init__(self): + super(LJDumpArch, self).__init__( + 'lj-arch', gdb.COMMAND_DATA + ) + + def invoke(self, arg, from_tty): + gdb.write('LJ_64: {LJ_64}, LJ_GC64: {LJ_GC64}\n'.format( + LJ_64 = LJ_64, + LJ_GC64 = LJ_GC64 + )) + +LJDumpArch() + +class LJDumpTValue(gdb.Command): + ''' +lj-tv <TValue *> + +The command recieves a pointer to <tv> (TValue address) and dumps +the type and some info related to it. + +* LJ_TNIL: nil +* LJ_TFALSE: false +* LJ_TTRUE: true +* LJ_TLIGHTUD: light userdata @ <gcr> +* LJ_TSTR: string <string payload> @ <gcr> +* LJ_TUPVAL: upvalue @ <gcr> +* LJ_TTHREAD: thread @ <gcr> +* LJ_TPROTO: proto @ <gcr> +* LJ_TFUNC: <LFUNC|CFUNC|FFUNC> + <LFUNC>: Lua function @ <gcr>, <nupvals> upvalues, <chunk:line> + <CFUNC>: C function <mcode address> + <FFUNC>: fast function #<ffid> +* LJ_TTRACE: trace <traceno> @ <gcr> +* LJ_TCDATA: cdata @ <gcr> +* LJ_TTAB: table @ <gcr> (asize: <asize>, hmask: <hmask>) +* LJ_TUDATA: userdata @ <gcr> +* LJ_TNUMX: number <numeric payload> + +Whether the type of the given address differs from the listed above, then +error message occurs. + ''' + + def __init__(self): + super(LJDumpTValue, self).__init__( + 'lj-tv', gdb.COMMAND_DATA + ) + + def invoke(self, arg, from_tty): + tv = cast('TValue *', parse_arg(arg)) + gdb.write('{}\n'.format(dump_tvalue(tv))) + +LJDumpTValue() + +class LJDumpString(gdb.Command): + ''' +lj-str <GCstr *> + +The command recieves a <gcr> of the corresponding GCstr object and dumps +the payload, size in bytes and hash. + +*Caveat*: Since Python 2 provides no native Unicode support, the payload +is replaced with the corresponding error when decoding fails. + ''' + + def __init__(self): + super(LJDumpString, self).__init__( + 'lj-str', gdb.COMMAND_DATA + ) + + def invoke(self, arg, from_tty): + string = cast('GCstr *', parse_arg(arg)) + gdb.write("String: {body} [{len} bytes] with hash {hash}\n".format( + body = strdata(string), + hash = strx64(string['hash']), + len = string['len'], + )) + + +LJDumpString() + +class LJDumpTable(gdb.Command): + ''' +lj-tab <GCtab *> + +The command recieves a GCtab adress and dumps the table contents: +* Metatable address whether the one is set +* Array part <asize> slots: + <aslot ptr>: [<index>]: <tv> +* Hash part <hsize> nodes: + <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> + ''' + + def __init__(self): + super(LJDumpTable, self).__init__( + 'lj-tab', gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + t = cast('GCtab *', parse_arg(arg)) + array = mref('TValue *', t['array']) + nodes = mref('struct Node *', t['node']) + mt = gcval(t['metatable']) + capacity = { + 'apart': int(t['asize']), + 'hpart': int(t['hmask'] + 1) if t['hmask'] > 0 else 0 + } + + if mt != 0: + gdb.write('Metatable detected: {}\n'.format(strx64(mt))) + + gdb.write('Array part: {} slots\n'.format(capacity['apart'])) + for i in range(capacity['apart']): + slot = array + i + gdb.write('{ptr}: [{index}]: {value}\n'.format( + ptr = slot, + index = i, + value = dump_tvalue(slot) + )) + + gdb.write('Hash part: {} nodes\n'.format(capacity['hpart'])) + # See hmask comment in lj_obj.h + for i in range(capacity['hpart']): + node = nodes + i + gdb.write('{ptr}: {{ {key} }} => {{ {val} }}; next = {n}\n'.format( + ptr = node, + key = dump_tvalue(node['key']), + val= dump_tvalue(node['val']), + n = mref('struct Node *', node['next']) + )) + +LJDumpTable() + +class LJDumpStack(gdb.Command): + ''' +lj-stack [<lua_State *>] + +The command recieves a lua_State address and dumps the given Lua +coroutine guest stack: + +<slot ptr> [<slot attributes>] <VALUE|FRAME> + +* <slot ptr>: guest stack slot address +* <slot attributes>: + - S: Bottom of the stack (the slot L->stack points to) + - B: Base of the current guest frame (the slot L->base points to) + - T: Top of the current guest frame (the slot L->top points to) + - M: Last slot of the stack (the slot L->maxstack points to) +* <VALUE>: see help lj-tv for more info +* <FRAME>: framelink slot differs from the value slot: it contains info + related to the function being executed within this guest frame, its + type and link to the parent guest frame + [<frame type>] delta=<slots in frame>, <lj-tv for LJ_TFUNC slot> + - <frame type>: + + L: VM performs a call as a result of bytecode execution + + C: VM performs a call as a result of lj_vm_call + + M: VM performs a call to a metamethod as a result of bytecode + execution + + V: Variable-length frame for storing arguments of a variadic + function + + CP: Protected C frame + + PP: VM performs a call as a result of executinig pcall or xpcall + +If L is ommited the main coroutine is used. + ''' + + def __init__(self): + super(LJDumpStack, self).__init__( + 'lj-stack', gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) + +LJDumpStack() + +class LJState(gdb.Command): + ''' +lj-state +The command requires no args and dumps current VM and GC states +* VM state: <INTERP|C|GC|EXIT|RECORD|OPT|ASM|TRACE> +* GC state: <PAUSE|PROPAGATE|ATOMIC|SWEEPSTRING|SWEEP|FINALIZE|LAST> +* JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> + ''' + + def __init__(self): + super(LJState, self).__init__( + 'lj-state', gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + g = G(L(None)) + gdb.write('{}\n'.format('\n'.join( + map(lambda t: '{} state: {}'.format(*t), { + 'VM': vm_state(g), + 'GC': gc_state(g), + 'JIT': jit_state(g), + }.items()) + ))) + +LJState() + +class LJGC(gdb.Command): + ''' +lj-gc + +The command requires no args and dumps current GC stats: +* total: <total number of allocated bytes in GC area> +* threshold: <limit when gc step is triggered> +* debt: <how much GC is behind schedule> +* estimate: <estimate of memory actually in use> +* stepmul: <incremental GC step granularity> +* pause: <pause between successive GC cycles> +* sweepstr: <sweep position in string table> +* root: <number of all collectable objects> +* gray: <number of gray objects> +* grayagain: <number of objects for atomic traversal> +* weak: <number of weak tables (to be cleared)> + ''' + + def __init__(self): + super(LJGC, self).__init__( + 'lj-gc', gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + g = G(L(None)) + gdb.write('GC stats: {state}\n{stats}\n'.format( + state = gc_state(g), + stats = dump_gc(g) + )) + +LJGC() -- 2.24.0 ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension Igor Munkin @ 2020-02-13 13:24 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-13 13:24 UTC (permalink / raw) To: tarantool-patches One more bug found while using the gdb built against Python 2.x. VM state field is 32-bit length but was treated as 64-bit one. This led to the following invalid output: | (gdb) source ~/luajit-gdb.py | lj-gc command initialized | lj-tab command initialized | lj-str command initialized | lj-state command initialized | lj-stack command initialized | lj-tv command initialized | lj-arch command initialized | luajit-gdb.py is successfully loaded | (gdb) lj-state | JIT state: IDLE | GC state: PAUSE | VM state: TRACE | (gdb) p ((global_State *)L->glref.ptr32)->vmstate | $1 = -1 # <-- VM state is INTERP and not valid traceno Fixed, squashed, force-pushed to the branch. Diff is below: ================================================================================ diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index cccec1c..b6b3212 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -52,9 +52,6 @@ def tou64(val): def tou32(val): return cast('uint32_t', val) & 0xFFFFFFFF -def i2notu64(val): - return ~int(val) & 0xFFFFFFFFFFFFFFFF - def i2notu32(val): return ~int(val) & 0xFFFFFFFF @@ -205,14 +202,14 @@ def J(g): def vm_state(g): return { - i2notu64(0): 'INTERP', - i2notu64(1): 'C', - i2notu64(2): 'GC', - i2notu64(3): 'EXIT', - i2notu64(4): 'RECORD', - i2notu64(5): 'OPT', - i2notu64(6): 'ASM', - }.get(int(tou64(g['vmstate'])), 'TRACE') + i2notu32(0): 'INTERP', + i2notu32(1): 'C', + i2notu32(2): 'GC', + i2notu32(3): 'EXIT', + i2notu32(4): 'RECORD', + i2notu32(5): 'OPT', + i2notu32(6): 'ASM', + }.get(int(tou32(g['vmstate'])), 'TRACE') def gc_state(g): return { =============================================================================== On 05.02.20, Igor Munkin wrote: > The provided luajit-gdb extenstion contains the following additional commands: > * lj-arch -- dumps values of LJ_64 and LJ_GC64 macro definitions > * lj-tv -- dumps the type and some GCobj info related to the given TValue > * lj-str -- dumps the contents of the given GCstr > * lj-tab -- dumps the contents of the given GCtab > * lj-stack -- dumps Lua stack of the given lua_State > * lj-state -- shows current VM, GC and JIT states > * lj-gc -- shows current GC stats > > Currently extension supports only x64 builds but respects LJ_GC64 value > > Signed-off-by: Igor Munkin <imun@tarantool.org> > --- > src/luajit-gdb.py | 684 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 684 insertions(+) > create mode 100644 src/luajit-gdb.py > > diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py > new file mode 100644 > index 0000000..77da5e6 > --- /dev/null > +++ b/src/luajit-gdb.py > @@ -0,0 +1,684 @@ > +import re > +import gdb > + > +gtype_cache = {} > + > +def gtype(typestr): > + global gtype_cache > + if typestr in gtype_cache: > + return gtype_cache[typestr] > + > + m = re.match(r'((?:(?:struct|union) )?\S*)\s*[*]', typestr) > + > + gtype = gdb.lookup_type(typestr) if m is None \ > + else gdb.lookup_type(m.group(1)).pointer() > + > + gtype_cache[typestr] = gtype > + return gtype > + > +def cast(typestr, val): > + return gdb.Value(val).cast(gtype(typestr)) > + > +def lookup(symbol): > + variable, _ = gdb.lookup_symbol(symbol) > + return variable.value() if variable else None > + > +def parse_arg(arg): > + if not arg: > + return None > + > + ret = gdb.parse_and_eval(arg) > + > + if not ret: > + raise gdb.GdbError('table argument empty') > + > + return ret > + > +def tou64(val): > + return cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF > + > +def tou32(val): > + return cast('uint32_t', val) & 0xFFFFFFFF > + > +def i2notu64(val): > + return ~int(val) & 0xFFFFFFFFFFFFFFFF > + > +def i2notu32(val): > + return ~int(val) & 0xFFFFFFFF > + > +def strx64(val): > + return hex(cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF) > + > +# Types {{{ > + > +LJ_T = { > + 'NIL' : i2notu32(0), > + 'FALSE' : i2notu32(1), > + 'TRUE' : i2notu32(2), > + 'LIGHTUD' : i2notu32(3), > + 'STR' : i2notu32(4), > + 'UPVAL' : i2notu32(5), > + 'THREAD' : i2notu32(6), > + 'PROTO' : i2notu32(7), > + 'FUNC' : i2notu32(8), > + 'TRACE' : i2notu32(9), > + 'CDATA' : i2notu32(10), > + 'TAB' : i2notu32(11), > + 'UDATA' : i2notu32(12), > + 'NUMX' : i2notu32(13), > +} > + > +def typenames(value): > + return { > + LJ_T[k]: 'LJ_T' + k for k in LJ_T.keys() > + }.get(int(value), 'LJ_TINVALID') > + > +# }}} > + > +# Frames {{{ > + > +FRAME_TYPE = 0x3 > +FRAME_P = 0x4 > +FRAME_TYPEP = FRAME_TYPE | FRAME_P > + > +FRAME = { > + 'LUA': 0x0, > + 'C': 0x1, > + 'CONT': 0x2, > + 'VARG': 0x3, > + 'LUAP': 0x4, > + 'CP': 0x5, > + 'PCALL': 0x6, > + 'PCALLH': 0x7, > +} > + > +def frametypes(ft): > + return { > + FRAME['LUA'] : 'L', > + FRAME['C'] : 'C', > + FRAME['CONT'] : 'M', > + FRAME['VARG'] : 'V', > + }.get(ft, '?') > + > +def bc_a(ins): > + return (ins >> 8) & 0xff > + > +def frame_ftsz(framelink): > + return cast('ptrdiff_t', framelink['ftsz'] if LJ_FR2 \ > + else framelink['fr']['tp']['ftsz']) > + > +def frame_pc(framelink): > + return cast('BCIns *', frame_ftsz(framelink)) if LJ_FR2 \ > + else mref('BCIns *', framelink['fr']['tp']['pcr']) > + > +def frame_prevl(framelink): > + return framelink - (1 + LJ_FR2 + bc_a(frame_pc(framelink)[-1])) > + > +def frame_ispcall(framelink): > + return (frame_ftsz(framelink) & FRAME['PCALL']) == FRAME['PCALL'] > + > +def frame_sized(framelink): > + return (frame_ftsz(framelink) & ~FRAME_TYPEP) > + > +def frame_prevd(framelink): > + return cast('TValue *', cast('char *', framelink) - frame_sized(framelink)) > + > +def frame_type(framelink): > + return frame_ftsz(framelink) & FRAME_TYPE > + > +def frame_typep(framelink): > + return frame_ftsz(framelink) & FRAME_TYPEP > + > +def frame_islua(framelink): > + return frametypes(int(frame_type(framelink))) == 'L' \ > + and int(frame_ftsz(framelink)) > 0 > + > +def frame_prev(framelink): > + return frame_prevl(framelink) if frame_islua(framelink) \ > + else frame_prevd(framelink) > + > +# }}} > + > +# Const {{{ > + > +LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' > + > +LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' > + > +LJ_FR2 = LJ_GC64 > + > +LJ_GCVMASK = ((1 << 47) - 1) > + > +PADDING = ' ' * len(':' + hex((1 << (47 if LJ_GC64 else 32)) - 1)) > + > +# }}} > + > +def itype(o): > + return cast('uint32_t', o['it64'] >> 47) if LJ_GC64 else o['it'] > + > +def mref(typename, obj): > + return cast(typename, obj['ptr64'] if LJ_GC64 else obj['ptr32']) > + > +def gcref(obj): > + return cast('GCobj *', obj['gcptr64'] if LJ_GC64 > + else cast('uintptr_t', obj['gcptr32'])) > + > +def gcval(obj): > + return cast('GCobj *', obj['gcptr64'] & LJ_GCVMASK if LJ_GC64 > + else cast('uintptr_t', obj['gcptr32'])) > + > +def L(L=None): > + # lookup a symbol for the main coroutine considering the host app > + for l in (L, *map(lambda l: lookup(l), ( > + # LuaJIT main coro (see luajit/src/luajit.c) > + 'globalL', > + # Tarantool main coro (see tarantool/src/lua/init.h) > + 'tarantool_L', > + # TODO: Add more > + ))): > + if l: > + return cast('lua_State *', l) > + > +def G(L): > + return mref('global_State *', L['glref']) > + > +def J(g): > + typeGG = gtype('GG_State') > + > + return cast('jit_State *', int(cast('char *', g)) > + - int(typeGG['g'].bitpos / 8) > + + int(typeGG['J'].bitpos / 8) > + ) > + > +def vm_state(g): > + return { > + i2notu64(0): 'INTERP', > + i2notu64(1): 'C', > + i2notu64(2): 'GC', > + i2notu64(3): 'EXIT', > + i2notu64(4): 'RECORD', > + i2notu64(5): 'OPT', > + i2notu64(6): 'ASM', > + }.get(int(tou64(g['vmstate'])), 'TRACE') > + > +def gc_state(g): > + return { > + 0: 'PAUSE', > + 1: 'PROPAGATE', > + 2: 'ATOMIC', > + 3: 'SWEEPSTRING', > + 4: 'SWEEP', > + 5: 'FINALIZE', > + 6: 'LAST', > + }.get(int(g['gc']['state']), 'INVALID') > + > +def jit_state(g): > + return { > + 0: 'IDLE', > + 0x10: 'ACTIVE', > + 0x11: 'RECORD', > + 0x12: 'START', > + 0x13: 'END', > + 0x14: 'ASM', > + 0x15: 'ERR', > + }.get(int(J(g)['state']), 'INVALID') > + > +def tvisnumber(o): > + return itype(o) <= (0xfffeffff if LJ_64 and not LJ_GC64 else LJ_T['NUMX']) > + > +def tvislightud(o): > + if LJ_64 and not LJ_GC64: > + return (cast('int32_t', itype(o)) >> 15) == -2 > + else: > + return itype(o) == LJ_T['LIGHTUD'] > + > +def strdata(obj): > + # String is printed with pointer to it, thanks to gdb. Just strip it. > + return str(cast('char *', cast('GCstr *', obj) + 1))[len(PADDING):] > + > +def itypemap(o): > + if LJ_64 and not LJ_GC64: > + return LJ_T['NUMX'] if tvisnumber(o) \ > + else LJ_T['LIGHTUD'] if tvislightud(o) \ > + else itype(o) > + else: > + return LJ_T['NUMX'] if tvisnumber(o) else itype(o) > + > +def funcproto(func): > + assert(func['ffid'] == 0) > + > + return cast('GCproto *', > + mref('char *', func['pc']) - gdb.lookup_type('GCproto').sizeof) > + > +def gclistlen(root): > + count = 0 > + while(gcref(root)): > + count += 1 > + root = gcref(root)['gch']['nextgc'] > + return count > + > +# Dumpers {{{ > + > +def dump_lj_tnil(tv): > + return 'nil' > + > +def dump_lj_tfalse(tv): > + return 'false' > + > +def dump_lj_ttrue(tv): > + return 'true' > + > +def dump_lj_tlightud(tv): > + return 'light userdata @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_tstr(tv): > + return 'string {body} @ {address}'.format( > + body = strdata(gcval(tv['gcr'])), > + address = strx64(gcval(tv['gcr'])) > + ) > + > +def dump_lj_tupval(tv): > + return 'upvalue @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_tthread(tv): > + return 'thread @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_tproto(tv): > + return 'proto @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_tfunc(tv): > + func = cast('struct GCfuncC *', gcval(tv['gcr'])) > + ffid = func['ffid'] > + > + if ffid == 0: > + pt = funcproto(func) > + return 'Lua function @ {addr}, {nupvals} upvalues, {chunk}:{line}'.format( > + addr = strx64(func), > + nupvals = int(func['nupvalues']), > + chunk = strdata(cast('GCstr *', gcval(pt['chunkname']))), > + line = pt['firstline'] > + ) > + elif ffid == 1: > + return 'C function @ {}'.format(strx64(func['f'])) > + else: > + return 'fast function #{}'.format(int(ffid)) > + > +def dump_lj_ttrace(tv): > + trace = cast('struct GCtrace *', gcval(tv['gcr'])) > + return 'trace {traceno} @ {addr}'.format( > + traceno = strx64(trace['traceno']), > + addr = strx64(trace) > + ) > + > +def dump_lj_tcdata(tv): > + return 'cdata @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_ttab(tv): > + table = cast('GCtab *', gcval(tv['gcr'])) > + return 'table @ {gcr} (asize: {asize}, hmask: {hmask})'.format( > + gcr = strx64(table), > + asize = table['asize'], > + hmask = strx64(table['hmask']), > + ) > + > +def dump_lj_tudata(tv): > + return 'userdata @ {}'.format(strx64(gcval(tv['gcr']))) > + > +def dump_lj_tnumx(tv): > + return 'number {}'.format(cast('double', tv['n'])) > + > +def dump_lj_invalid(tv): > + return 'not valid type @ {}'.format(strx64(gcval(tv['gcr']))) > + > +# }}} > + > +dumpers = { > + 'LJ_TNIL': dump_lj_tnil, > + 'LJ_TFALSE': dump_lj_tfalse, > + 'LJ_TTRUE': dump_lj_ttrue, > + 'LJ_TLIGHTUD': dump_lj_tlightud, > + 'LJ_TSTR': dump_lj_tstr, > + 'LJ_TUPVAL': dump_lj_tupval, > + 'LJ_TTHREAD': dump_lj_tthread, > + 'LJ_TPROTO': dump_lj_tproto, > + 'LJ_TFUNC': dump_lj_tfunc, > + 'LJ_TTRACE': dump_lj_ttrace, > + 'LJ_TCDATA': dump_lj_tcdata, > + 'LJ_TTAB': dump_lj_ttab, > + 'LJ_TUDATA': dump_lj_tudata, > + 'LJ_TNUMX': dump_lj_tnumx, > +} > + > +def dump_tvalue(tvalue): > + return dumpers.get(typenames(itypemap(tvalue)), dump_lj_invalid)(tvalue) > + > +def dump_framelink(L, fr): > + fr2 = fr + LJ_FR2 > + > + return '{fr}{padding} [ ] FRAME: [{pp}] delta={d}, {f}\n'.format( > + fr = fr, > + padding = ':{fr2}'.format(fr2 = fr2) if LJ_FR2 else PADDING, > + pp = 'PP' if frame_ispcall(fr2) else '{frname}{p}'.format( > + frname = frametypes(int(frame_type(fr2))), > + p = 'P' if frame_typep(fr2) & FRAME_P else '' > + ), > + d = cast('TValue *', fr2) - cast('TValue *', frame_prev(fr2)), > + f = dump_lj_tfunc(fr), > + ) > + > +def dump_stack_slot(L, slot, base=None, top=None): > + base = base or L['base'] > + top = top or L['top'] > + > + return '{addr}{padding} [ {B}{T}{M}] VALUE: {value}\n'.format( > + addr = strx64(slot), > + padding = PADDING, > + B = 'B' if slot == base else ' ', > + T = 'T' if slot == top 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'] > + maxstack = mref('TValue *', L['maxstack']) > + red = 5 + 2 * LJ_FR2 > + > + dump = '\n'.join([ > + '{start}:{end} [ ] {n} slots: Red zone'.format( > + start = strx64(maxstack + 1), > + end = strx64(maxstack + red), > + n = red, > + ), > + '{maxstack}{padding} [ M]'.format( > + maxstack = strx64(maxstack), > + padding = PADDING, > + ), > + '{start}:{end} [ ] {nfreeslots} slots: Free stack slots'.format( > + start = strx64(top + 1), > + end = strx64(maxstack - 1), > + nfreeslots = int((tou64(maxstack) - tou64(top) - 8) >> 3), > + ), > + '{top}{padding} [ T ]'.format( > + top = strx64(top), > + padding = PADDING, > + ) > + ]) + '\n' > + > + slot = top - 1 > + framelink = base - (1 + LJ_FR2) > + > + while framelink > mref('TValue *', L['stack']): > + while slot > framelink + LJ_FR2: > + dump += dump_stack_slot(L, slot, base, top) > + slot -= 1 > + dump += dump_framelink(L, framelink) > + framelink = frame_prev(framelink + LJ_FR2) - LJ_FR2 > + slot -= 1 + LJ_FR2 > + > + dump += '{fr}{padding} [S ] FRAME: dummy L'.format( > + fr = slot, > + padding = ':{nilslot}'.format(nilslot = slot + 1) if LJ_FR2 else PADDING > + ) > + > + return dump > + > +def dump_gc(g): > + gc = g['gc'] > + stats = [ '{key}: {value}'.format(key = f, value = gc[f]) for f in ( > + 'total', 'threshold', 'debt', 'estimate', 'stepmul', 'pause' > + ) ] > + > + stats += [ 'sweepstr: {sweepstr}/{strmask}'.format( > + sweepstr = gc['sweepstr'], > + # String hash mask (size of hash table - 1). > + strmask = g['strmask'] + 1, > + ) ] > + > + stats += [ '{key}: {number} objects'.format( > + key = f, > + number = gclistlen(gc[f]), > + ) for f in ('root', 'gray', 'grayagain', 'weak') ] > + > + # TODO: mmudata > + > + return '\n'.join(map(lambda s: '\t' + s, stats)) > + > +class LJDumpArch(gdb.Command): > + ''' > +lj-arch > + > +The command requires no args and dumps values of LJ_64 and LJ_GC64 > +compile-time flags. These values define the sizes of host and GC > +pointers respectively. > + ''' > + > + def __init__(self): > + super(LJDumpArch, self).__init__( > + 'lj-arch', gdb.COMMAND_DATA > + ) > + > + def invoke(self, arg, from_tty): > + gdb.write('LJ_64: {LJ_64}, LJ_GC64: {LJ_GC64}\n'.format( > + LJ_64 = LJ_64, > + LJ_GC64 = LJ_GC64 > + )) > + > +LJDumpArch() > + > +class LJDumpTValue(gdb.Command): > + ''' > +lj-tv <TValue *> > + > +The command recieves a pointer to <tv> (TValue address) and dumps > +the type and some info related to it. > + > +* LJ_TNIL: nil > +* LJ_TFALSE: false > +* LJ_TTRUE: true > +* LJ_TLIGHTUD: light userdata @ <gcr> > +* LJ_TSTR: string <string payload> @ <gcr> > +* LJ_TUPVAL: upvalue @ <gcr> > +* LJ_TTHREAD: thread @ <gcr> > +* LJ_TPROTO: proto @ <gcr> > +* LJ_TFUNC: <LFUNC|CFUNC|FFUNC> > + <LFUNC>: Lua function @ <gcr>, <nupvals> upvalues, <chunk:line> > + <CFUNC>: C function <mcode address> > + <FFUNC>: fast function #<ffid> > +* LJ_TTRACE: trace <traceno> @ <gcr> > +* LJ_TCDATA: cdata @ <gcr> > +* LJ_TTAB: table @ <gcr> (asize: <asize>, hmask: <hmask>) > +* LJ_TUDATA: userdata @ <gcr> > +* LJ_TNUMX: number <numeric payload> > + > +Whether the type of the given address differs from the listed above, then > +error message occurs. > + ''' > + > + def __init__(self): > + super(LJDumpTValue, self).__init__( > + 'lj-tv', gdb.COMMAND_DATA > + ) > + > + def invoke(self, arg, from_tty): > + tv = cast('TValue *', parse_arg(arg)) > + gdb.write('{}\n'.format(dump_tvalue(tv))) > + > +LJDumpTValue() > + > +class LJDumpString(gdb.Command): > + ''' > +lj-str <GCstr *> > + > +The command recieves a <gcr> of the corresponding GCstr object and dumps > +the payload, size in bytes and hash. > + > +*Caveat*: Since Python 2 provides no native Unicode support, the payload > +is replaced with the corresponding error when decoding fails. > + ''' > + > + def __init__(self): > + super(LJDumpString, self).__init__( > + 'lj-str', gdb.COMMAND_DATA > + ) > + > + def invoke(self, arg, from_tty): > + string = cast('GCstr *', parse_arg(arg)) > + gdb.write("String: {body} [{len} bytes] with hash {hash}\n".format( > + body = strdata(string), > + hash = strx64(string['hash']), > + len = string['len'], > + )) > + > + > +LJDumpString() > + > +class LJDumpTable(gdb.Command): > + ''' > +lj-tab <GCtab *> > + > +The command recieves a GCtab adress and dumps the table contents: > +* Metatable address whether the one is set > +* Array part <asize> slots: > + <aslot ptr>: [<index>]: <tv> > +* Hash part <hsize> nodes: > + <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> > + ''' > + > + def __init__(self): > + super(LJDumpTable, self).__init__( > + 'lj-tab', gdb.COMMAND_DATA) > + > + def invoke(self, arg, from_tty): > + t = cast('GCtab *', parse_arg(arg)) > + array = mref('TValue *', t['array']) > + nodes = mref('struct Node *', t['node']) > + mt = gcval(t['metatable']) > + capacity = { > + 'apart': int(t['asize']), > + 'hpart': int(t['hmask'] + 1) if t['hmask'] > 0 else 0 > + } > + > + if mt != 0: > + gdb.write('Metatable detected: {}\n'.format(strx64(mt))) > + > + gdb.write('Array part: {} slots\n'.format(capacity['apart'])) > + for i in range(capacity['apart']): > + slot = array + i > + gdb.write('{ptr}: [{index}]: {value}\n'.format( > + ptr = slot, > + index = i, > + value = dump_tvalue(slot) > + )) > + > + gdb.write('Hash part: {} nodes\n'.format(capacity['hpart'])) > + # See hmask comment in lj_obj.h > + for i in range(capacity['hpart']): > + node = nodes + i > + gdb.write('{ptr}: {{ {key} }} => {{ {val} }}; next = {n}\n'.format( > + ptr = node, > + key = dump_tvalue(node['key']), > + val= dump_tvalue(node['val']), > + n = mref('struct Node *', node['next']) > + )) > + > +LJDumpTable() > + > +class LJDumpStack(gdb.Command): > + ''' > +lj-stack [<lua_State *>] > + > +The command recieves a lua_State address and dumps the given Lua > +coroutine guest stack: > + > +<slot ptr> [<slot attributes>] <VALUE|FRAME> > + > +* <slot ptr>: guest stack slot address > +* <slot attributes>: > + - S: Bottom of the stack (the slot L->stack points to) > + - B: Base of the current guest frame (the slot L->base points to) > + - T: Top of the current guest frame (the slot L->top points to) > + - M: Last slot of the stack (the slot L->maxstack points to) > +* <VALUE>: see help lj-tv for more info > +* <FRAME>: framelink slot differs from the value slot: it contains info > + related to the function being executed within this guest frame, its > + type and link to the parent guest frame > + [<frame type>] delta=<slots in frame>, <lj-tv for LJ_TFUNC slot> > + - <frame type>: > + + L: VM performs a call as a result of bytecode execution > + + C: VM performs a call as a result of lj_vm_call > + + M: VM performs a call to a metamethod as a result of bytecode > + execution > + + V: Variable-length frame for storing arguments of a variadic > + function > + + CP: Protected C frame > + + PP: VM performs a call as a result of executinig pcall or xpcall > + > +If L is ommited the main coroutine is used. > + ''' > + > + def __init__(self): > + super(LJDumpStack, self).__init__( > + 'lj-stack', gdb.COMMAND_DATA) > + > + def invoke(self, arg, from_tty): > + gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) > + > +LJDumpStack() > + > +class LJState(gdb.Command): > + ''' > +lj-state > +The command requires no args and dumps current VM and GC states > +* VM state: <INTERP|C|GC|EXIT|RECORD|OPT|ASM|TRACE> > +* GC state: <PAUSE|PROPAGATE|ATOMIC|SWEEPSTRING|SWEEP|FINALIZE|LAST> > +* JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> > + ''' > + > + def __init__(self): > + super(LJState, self).__init__( > + 'lj-state', gdb.COMMAND_DATA) > + > + def invoke(self, arg, from_tty): > + g = G(L(None)) > + gdb.write('{}\n'.format('\n'.join( > + map(lambda t: '{} state: {}'.format(*t), { > + 'VM': vm_state(g), > + 'GC': gc_state(g), > + 'JIT': jit_state(g), > + }.items()) > + ))) > + > +LJState() > + > +class LJGC(gdb.Command): > + ''' > +lj-gc > + > +The command requires no args and dumps current GC stats: > +* total: <total number of allocated bytes in GC area> > +* threshold: <limit when gc step is triggered> > +* debt: <how much GC is behind schedule> > +* estimate: <estimate of memory actually in use> > +* stepmul: <incremental GC step granularity> > +* pause: <pause between successive GC cycles> > +* sweepstr: <sweep position in string table> > +* root: <number of all collectable objects> > +* gray: <number of gray objects> > +* grayagain: <number of objects for atomic traversal> > +* weak: <number of weak tables (to be cleared)> > + ''' > + > + def __init__(self): > + super(LJGC, self).__init__( > + 'lj-gc', gdb.COMMAND_DATA) > + > + def invoke(self, arg, from_tty): > + g = G(L(None)) > + gdb.write('GC stats: {state}\n{stats}\n'.format( > + state = gc_state(g), > + stats = dump_gc(g) > + )) > + > +LJGC() > -- > 2.24.0 > -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* [Tarantool-patches] [PATCH v2 luajit 2/3] gdb: adjust the extension to be used with Python 2 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension Igor Munkin @ 2020-02-05 16:22 ` Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading Igor Munkin ` (5 subsequent siblings) 7 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-05 16:22 UTC (permalink / raw) To: tarantool-patches This patch covers all problems faced while using the extension via gdb compiled with Python 2. Signed-off-by: Igor Munkin <imun@tarantool.org> --- src/luajit-gdb.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 77da5e6..90df101 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -1,5 +1,14 @@ import re import gdb +import sys + +# make script compatible with the ancient Python {{{ + +if re.match(r'^2\.', sys.version): + int = long + range = xrange + +# }}} gtype_cache = {} @@ -47,7 +56,8 @@ def i2notu32(val): return ~int(val) & 0xFFFFFFFF def strx64(val): - return hex(cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF) + return re.sub('L?$', '', + hex(int(cast('uint64_t', val) & 0xFFFFFFFFFFFFFFFF))) # Types {{{ @@ -169,7 +179,9 @@ def gcval(obj): def L(L=None): # lookup a symbol for the main coroutine considering the host app - for l in (L, *map(lambda l: lookup(l), ( + # XXX Fragile: though the loop initialization looks like a crap but it + # respects both Python 2 and Python 3. + for l in [ L ] + list(map(lambda l: lookup(l), ( # LuaJIT main coro (see luajit/src/luajit.c) 'globalL', # Tarantool main coro (see tarantool/src/lua/init.h) @@ -234,7 +246,10 @@ def tvislightud(o): def strdata(obj): # String is printed with pointer to it, thanks to gdb. Just strip it. - return str(cast('char *', cast('GCstr *', obj) + 1))[len(PADDING):] + try: + return str(cast('char *', cast('GCstr *', obj) + 1))[len(PADDING):] + except UnicodeEncodeError: + return "<luajit-gdb: error occured while rendering non-ascii slot>" def itypemap(o): if LJ_64 and not LJ_GC64: -- 2.24.0 ^ permalink raw reply [flat|nested] 23+ messages in thread
* [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 2/3] gdb: adjust the extension to be used with Python 2 Igor Munkin @ 2020-02-05 16:22 ` Igor Munkin 2020-02-13 11:48 ` Igor Munkin 2020-02-26 22:41 ` [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Alexander Turenko ` (4 subsequent siblings) 7 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-02-05 16:22 UTC (permalink / raw) To: tarantool-patches Definition of LJ_64, LJ_GC64 and LJ_FR2 constants in script requires LuaJIT internal enum values to be presented in the executable. Otherwise the extension loading fails. The changes handles exception for the following scenarios: * luajit-gdb.py is loaded for an arbitrary executable, e.g. /bin/echo * luajit-gdb.py is loaded for a luajit executable (or the one linked against libluajit) but debug info is not found Signed-off-by: Igor Munkin <imun@tarantool.org> --- src/luajit-gdb.py | 102 ++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 90df101..bc37674 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -1,3 +1,6 @@ +# GDB extension for LuaJIT post-mortem analysis. +# To use, just put 'source <path-to-repo>/src/luajit-gdb.py' in gdb. + import re import gdb import sys @@ -151,11 +154,9 @@ def frame_prev(framelink): # Const {{{ -LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' - -LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' - -LJ_FR2 = LJ_GC64 +LJ_64 = None +LJ_GC64 = None +LJ_FR2 = None LJ_GCVMASK = ((1 << 47) - 1) @@ -460,7 +461,14 @@ def dump_gc(g): return '\n'.join(map(lambda s: '\t' + s, stats)) -class LJDumpArch(gdb.Command): + +class LJBase(gdb.Command): + + def __init__(self, name): + super(__class__, self).__init__(name, gdb.COMMAND_DATA) + gdb.write('{} command initialized\n'.format(name)) + +class LJDumpArch(LJBase): ''' lj-arch @@ -469,20 +477,13 @@ compile-time flags. These values define the sizes of host and GC pointers respectively. ''' - def __init__(self): - super(LJDumpArch, self).__init__( - 'lj-arch', gdb.COMMAND_DATA - ) - def invoke(self, arg, from_tty): gdb.write('LJ_64: {LJ_64}, LJ_GC64: {LJ_GC64}\n'.format( LJ_64 = LJ_64, LJ_GC64 = LJ_GC64 )) -LJDumpArch() - -class LJDumpTValue(gdb.Command): +class LJDumpTValue(LJBase): ''' lj-tv <TValue *> @@ -511,18 +512,11 @@ Whether the type of the given address differs from the listed above, then error message occurs. ''' - def __init__(self): - super(LJDumpTValue, self).__init__( - 'lj-tv', gdb.COMMAND_DATA - ) - def invoke(self, arg, from_tty): tv = cast('TValue *', parse_arg(arg)) gdb.write('{}\n'.format(dump_tvalue(tv))) -LJDumpTValue() - -class LJDumpString(gdb.Command): +class LJDumpString(LJBase): ''' lj-str <GCstr *> @@ -533,11 +527,6 @@ the payload, size in bytes and hash. is replaced with the corresponding error when decoding fails. ''' - def __init__(self): - super(LJDumpString, self).__init__( - 'lj-str', gdb.COMMAND_DATA - ) - def invoke(self, arg, from_tty): string = cast('GCstr *', parse_arg(arg)) gdb.write("String: {body} [{len} bytes] with hash {hash}\n".format( @@ -546,10 +535,7 @@ is replaced with the corresponding error when decoding fails. len = string['len'], )) - -LJDumpString() - -class LJDumpTable(gdb.Command): +class LJDumpTable(LJBase): ''' lj-tab <GCtab *> @@ -561,10 +547,6 @@ The command recieves a GCtab adress and dumps the table contents: <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> ''' - def __init__(self): - super(LJDumpTable, self).__init__( - 'lj-tab', gdb.COMMAND_DATA) - def invoke(self, arg, from_tty): t = cast('GCtab *', parse_arg(arg)) array = mref('TValue *', t['array']) @@ -598,9 +580,7 @@ The command recieves a GCtab adress and dumps the table contents: n = mref('struct Node *', node['next']) )) -LJDumpTable() - -class LJDumpStack(gdb.Command): +class LJDumpStack(LJBase): ''' lj-stack [<lua_State *>] @@ -633,16 +613,10 @@ coroutine guest stack: If L is ommited the main coroutine is used. ''' - def __init__(self): - super(LJDumpStack, self).__init__( - 'lj-stack', gdb.COMMAND_DATA) - def invoke(self, arg, from_tty): gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) -LJDumpStack() - -class LJState(gdb.Command): +class LJState(LJBase): ''' lj-state The command requires no args and dumps current VM and GC states @@ -651,10 +625,6 @@ The command requires no args and dumps current VM and GC states * JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> ''' - def __init__(self): - super(LJState, self).__init__( - 'lj-state', gdb.COMMAND_DATA) - def invoke(self, arg, from_tty): g = G(L(None)) gdb.write('{}\n'.format('\n'.join( @@ -665,9 +635,7 @@ The command requires no args and dumps current VM and GC states }.items()) ))) -LJState() - -class LJGC(gdb.Command): +class LJGC(LJBase): ''' lj-gc @@ -685,10 +653,6 @@ The command requires no args and dumps current GC stats: * weak: <number of weak tables (to be cleared)> ''' - def __init__(self): - super(LJGC, self).__init__( - 'lj-gc', gdb.COMMAND_DATA) - def invoke(self, arg, from_tty): g = G(L(None)) gdb.write('GC stats: {state}\n{stats}\n'.format( @@ -696,4 +660,28 @@ The command requires no args and dumps current GC stats: stats = dump_gc(g) )) -LJGC() +def load(commands): + global LJ_64, LJ_GC64, LJ_FR2 + + try: + LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' + LJ_FR2 = LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' + except: + gdb.write('luajit-gdb.py failed to load: ' + 'no debugging symbols found for libluajit\n') + return + + for name, command in commands.items(): + command(name) + + gdb.write('luajit-gdb.py is successfully loaded\n') + +load({ + 'lj-arch': LJDumpArch, + 'lj-tv': LJDumpTValue, + 'lj-str': LJDumpString, + 'lj-tab': LJDumpTable, + 'lj-stack': LJDumpStack, + 'lj-state': LJState, + 'lj-gc': LJGC, +}) -- 2.24.0 ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading Igor Munkin @ 2020-02-13 11:48 ` Igor Munkin 2020-02-13 12:24 ` Igor Munkin 0 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-02-13 11:48 UTC (permalink / raw) To: tarantool-patches Damn the old Python: | (gdb) source ~/luajit-gdb.py | Traceback (most recent call last): | File "~/luajit-gdb.py", line 686, in <module> | File "~/luajit-gdb.py", line 675, in load | File "~/luajit-gdb.py", line 468, in __init__ | NameError: global name '__class__' is not defined | (gdb) python | >import sys | >print(sys.version) | >2.7.5 (default, Aug 7 2019, 00:51:29) | [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] I'll fix the issue and send either diff or a new patch for this one in a jiffy. On 05.02.20, Igor Munkin wrote: > Definition of LJ_64, LJ_GC64 and LJ_FR2 constants in script requires > LuaJIT internal enum values to be presented in the executable. Otherwise > the extension loading fails. The changes handles exception for the > following scenarios: > * luajit-gdb.py is loaded for an arbitrary executable, e.g. /bin/echo > * luajit-gdb.py is loaded for a luajit executable (or the one linked > against libluajit) but debug info is not found > > Signed-off-by: Igor Munkin <imun@tarantool.org> > --- > src/luajit-gdb.py | 102 ++++++++++++++++++++-------------------------- > 1 file changed, 45 insertions(+), 57 deletions(-) > > diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py > index 90df101..bc37674 100644 > --- a/src/luajit-gdb.py > +++ b/src/luajit-gdb.py > @@ -1,3 +1,6 @@ > +# GDB extension for LuaJIT post-mortem analysis. > +# To use, just put 'source <path-to-repo>/src/luajit-gdb.py' in gdb. > + > import re > import gdb > import sys > @@ -151,11 +154,9 @@ def frame_prev(framelink): > > # Const {{{ > > -LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' > - > -LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' > - > -LJ_FR2 = LJ_GC64 > +LJ_64 = None > +LJ_GC64 = None > +LJ_FR2 = None > > LJ_GCVMASK = ((1 << 47) - 1) > > @@ -460,7 +461,14 @@ def dump_gc(g): > > return '\n'.join(map(lambda s: '\t' + s, stats)) > > -class LJDumpArch(gdb.Command): > + > +class LJBase(gdb.Command): > + > + def __init__(self, name): > + super(__class__, self).__init__(name, gdb.COMMAND_DATA) > + gdb.write('{} command initialized\n'.format(name)) > + > +class LJDumpArch(LJBase): > ''' > lj-arch > > @@ -469,20 +477,13 @@ compile-time flags. These values define the sizes of host and GC > pointers respectively. > ''' > > - def __init__(self): > - super(LJDumpArch, self).__init__( > - 'lj-arch', gdb.COMMAND_DATA > - ) > - > def invoke(self, arg, from_tty): > gdb.write('LJ_64: {LJ_64}, LJ_GC64: {LJ_GC64}\n'.format( > LJ_64 = LJ_64, > LJ_GC64 = LJ_GC64 > )) > > -LJDumpArch() > - > -class LJDumpTValue(gdb.Command): > +class LJDumpTValue(LJBase): > ''' > lj-tv <TValue *> > > @@ -511,18 +512,11 @@ Whether the type of the given address differs from the listed above, then > error message occurs. > ''' > > - def __init__(self): > - super(LJDumpTValue, self).__init__( > - 'lj-tv', gdb.COMMAND_DATA > - ) > - > def invoke(self, arg, from_tty): > tv = cast('TValue *', parse_arg(arg)) > gdb.write('{}\n'.format(dump_tvalue(tv))) > > -LJDumpTValue() > - > -class LJDumpString(gdb.Command): > +class LJDumpString(LJBase): > ''' > lj-str <GCstr *> > > @@ -533,11 +527,6 @@ the payload, size in bytes and hash. > is replaced with the corresponding error when decoding fails. > ''' > > - def __init__(self): > - super(LJDumpString, self).__init__( > - 'lj-str', gdb.COMMAND_DATA > - ) > - > def invoke(self, arg, from_tty): > string = cast('GCstr *', parse_arg(arg)) > gdb.write("String: {body} [{len} bytes] with hash {hash}\n".format( > @@ -546,10 +535,7 @@ is replaced with the corresponding error when decoding fails. > len = string['len'], > )) > > - > -LJDumpString() > - > -class LJDumpTable(gdb.Command): > +class LJDumpTable(LJBase): > ''' > lj-tab <GCtab *> > > @@ -561,10 +547,6 @@ The command recieves a GCtab adress and dumps the table contents: > <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> > ''' > > - def __init__(self): > - super(LJDumpTable, self).__init__( > - 'lj-tab', gdb.COMMAND_DATA) > - > def invoke(self, arg, from_tty): > t = cast('GCtab *', parse_arg(arg)) > array = mref('TValue *', t['array']) > @@ -598,9 +580,7 @@ The command recieves a GCtab adress and dumps the table contents: > n = mref('struct Node *', node['next']) > )) > > -LJDumpTable() > - > -class LJDumpStack(gdb.Command): > +class LJDumpStack(LJBase): > ''' > lj-stack [<lua_State *>] > > @@ -633,16 +613,10 @@ coroutine guest stack: > If L is ommited the main coroutine is used. > ''' > > - def __init__(self): > - super(LJDumpStack, self).__init__( > - 'lj-stack', gdb.COMMAND_DATA) > - > def invoke(self, arg, from_tty): > gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) > > -LJDumpStack() > - > -class LJState(gdb.Command): > +class LJState(LJBase): > ''' > lj-state > The command requires no args and dumps current VM and GC states > @@ -651,10 +625,6 @@ The command requires no args and dumps current VM and GC states > * JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> > ''' > > - def __init__(self): > - super(LJState, self).__init__( > - 'lj-state', gdb.COMMAND_DATA) > - > def invoke(self, arg, from_tty): > g = G(L(None)) > gdb.write('{}\n'.format('\n'.join( > @@ -665,9 +635,7 @@ The command requires no args and dumps current VM and GC states > }.items()) > ))) > > -LJState() > - > -class LJGC(gdb.Command): > +class LJGC(LJBase): > ''' > lj-gc > > @@ -685,10 +653,6 @@ The command requires no args and dumps current GC stats: > * weak: <number of weak tables (to be cleared)> > ''' > > - def __init__(self): > - super(LJGC, self).__init__( > - 'lj-gc', gdb.COMMAND_DATA) > - > def invoke(self, arg, from_tty): > g = G(L(None)) > gdb.write('GC stats: {state}\n{stats}\n'.format( > @@ -696,4 +660,28 @@ The command requires no args and dumps current GC stats: > stats = dump_gc(g) > )) > > -LJGC() > +def load(commands): > + global LJ_64, LJ_GC64, LJ_FR2 > + > + try: > + LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' > + LJ_FR2 = LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' > + except: > + gdb.write('luajit-gdb.py failed to load: ' > + 'no debugging symbols found for libluajit\n') > + return > + > + for name, command in commands.items(): > + command(name) > + > + gdb.write('luajit-gdb.py is successfully loaded\n') > + > +load({ > + 'lj-arch': LJDumpArch, > + 'lj-tv': LJDumpTValue, > + 'lj-str': LJDumpString, > + 'lj-tab': LJDumpTable, > + 'lj-stack': LJDumpStack, > + 'lj-state': LJState, > + 'lj-gc': LJGC, > +}) > -- > 2.24.0 > -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading 2020-02-13 11:48 ` Igor Munkin @ 2020-02-13 12:24 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-13 12:24 UTC (permalink / raw) To: tarantool-patches Fixed, squashed, force-pushed to the branch. Diff is below: ================================================================================ diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index bc37674..cccec1c 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -465,7 +465,9 @@ def dump_gc(g): class LJBase(gdb.Command): def __init__(self, name): - super(__class__, self).__init__(name, gdb.COMMAND_DATA) + # XXX Fragile: though the command initialization looks like a crap but + # it respects both Python 2 and Python 3. + gdb.Command.__init__(self, name, gdb.COMMAND_DATA) gdb.write('{} command initialized\n'.format(name)) class LJDumpArch(LJBase): ================================================================================ On 13.02.20, Igor Munkin wrote: > Damn the old Python: > > | (gdb) source ~/luajit-gdb.py > | Traceback (most recent call last): > | File "~/luajit-gdb.py", line 686, in <module> > | File "~/luajit-gdb.py", line 675, in load > | File "~/luajit-gdb.py", line 468, in __init__ > | NameError: global name '__class__' is not defined > | (gdb) python > | >import sys > | >print(sys.version) > | >2.7.5 (default, Aug 7 2019, 00:51:29) > | [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] > > I'll fix the issue and send either diff or a new patch for this one in a > jiffy. > > On 05.02.20, Igor Munkin wrote: > > Definition of LJ_64, LJ_GC64 and LJ_FR2 constants in script requires > > LuaJIT internal enum values to be presented in the executable. Otherwise > > the extension loading fails. The changes handles exception for the > > following scenarios: > > * luajit-gdb.py is loaded for an arbitrary executable, e.g. /bin/echo > > * luajit-gdb.py is loaded for a luajit executable (or the one linked > > against libluajit) but debug info is not found > > > > Signed-off-by: Igor Munkin <imun@tarantool.org> > > --- > > src/luajit-gdb.py | 102 ++++++++++++++++++++-------------------------- > > 1 file changed, 45 insertions(+), 57 deletions(-) > > > > diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py > > index 90df101..bc37674 100644 > > --- a/src/luajit-gdb.py > > +++ b/src/luajit-gdb.py > > @@ -1,3 +1,6 @@ > > +# GDB extension for LuaJIT post-mortem analysis. > > +# To use, just put 'source <path-to-repo>/src/luajit-gdb.py' in gdb. > > + > > import re > > import gdb > > import sys > > @@ -151,11 +154,9 @@ def frame_prev(framelink): > > > > # Const {{{ > > > > -LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' > > - > > -LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' > > - > > -LJ_FR2 = LJ_GC64 > > +LJ_64 = None > > +LJ_GC64 = None > > +LJ_FR2 = None > > > > LJ_GCVMASK = ((1 << 47) - 1) > > > > @@ -460,7 +461,14 @@ def dump_gc(g): > > > > return '\n'.join(map(lambda s: '\t' + s, stats)) > > > > -class LJDumpArch(gdb.Command): > > + > > +class LJBase(gdb.Command): > > + > > + def __init__(self, name): > > + super(__class__, self).__init__(name, gdb.COMMAND_DATA) > > + gdb.write('{} command initialized\n'.format(name)) > > + > > +class LJDumpArch(LJBase): > > ''' > > lj-arch > > > > @@ -469,20 +477,13 @@ compile-time flags. These values define the sizes of host and GC > > pointers respectively. > > ''' > > > > - def __init__(self): > > - super(LJDumpArch, self).__init__( > > - 'lj-arch', gdb.COMMAND_DATA > > - ) > > - > > def invoke(self, arg, from_tty): > > gdb.write('LJ_64: {LJ_64}, LJ_GC64: {LJ_GC64}\n'.format( > > LJ_64 = LJ_64, > > LJ_GC64 = LJ_GC64 > > )) > > > > -LJDumpArch() > > - > > -class LJDumpTValue(gdb.Command): > > +class LJDumpTValue(LJBase): > > ''' > > lj-tv <TValue *> > > > > @@ -511,18 +512,11 @@ Whether the type of the given address differs from the listed above, then > > error message occurs. > > ''' > > > > - def __init__(self): > > - super(LJDumpTValue, self).__init__( > > - 'lj-tv', gdb.COMMAND_DATA > > - ) > > - > > def invoke(self, arg, from_tty): > > tv = cast('TValue *', parse_arg(arg)) > > gdb.write('{}\n'.format(dump_tvalue(tv))) > > > > -LJDumpTValue() > > - > > -class LJDumpString(gdb.Command): > > +class LJDumpString(LJBase): > > ''' > > lj-str <GCstr *> > > > > @@ -533,11 +527,6 @@ the payload, size in bytes and hash. > > is replaced with the corresponding error when decoding fails. > > ''' > > > > - def __init__(self): > > - super(LJDumpString, self).__init__( > > - 'lj-str', gdb.COMMAND_DATA > > - ) > > - > > def invoke(self, arg, from_tty): > > string = cast('GCstr *', parse_arg(arg)) > > gdb.write("String: {body} [{len} bytes] with hash {hash}\n".format( > > @@ -546,10 +535,7 @@ is replaced with the corresponding error when decoding fails. > > len = string['len'], > > )) > > > > - > > -LJDumpString() > > - > > -class LJDumpTable(gdb.Command): > > +class LJDumpTable(LJBase): > > ''' > > lj-tab <GCtab *> > > > > @@ -561,10 +547,6 @@ The command recieves a GCtab adress and dumps the table contents: > > <hnode ptr>: { <tv> } => { <tv> }; next = <next hnode ptr> > > ''' > > > > - def __init__(self): > > - super(LJDumpTable, self).__init__( > > - 'lj-tab', gdb.COMMAND_DATA) > > - > > def invoke(self, arg, from_tty): > > t = cast('GCtab *', parse_arg(arg)) > > array = mref('TValue *', t['array']) > > @@ -598,9 +580,7 @@ The command recieves a GCtab adress and dumps the table contents: > > n = mref('struct Node *', node['next']) > > )) > > > > -LJDumpTable() > > - > > -class LJDumpStack(gdb.Command): > > +class LJDumpStack(LJBase): > > ''' > > lj-stack [<lua_State *>] > > > > @@ -633,16 +613,10 @@ coroutine guest stack: > > If L is ommited the main coroutine is used. > > ''' > > > > - def __init__(self): > > - super(LJDumpStack, self).__init__( > > - 'lj-stack', gdb.COMMAND_DATA) > > - > > def invoke(self, arg, from_tty): > > gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) > > > > -LJDumpStack() > > - > > -class LJState(gdb.Command): > > +class LJState(LJBase): > > ''' > > lj-state > > The command requires no args and dumps current VM and GC states > > @@ -651,10 +625,6 @@ The command requires no args and dumps current VM and GC states > > * JIT state: <IDLE|ACTIVE|RECORD|START|END|ASM|ERR> > > ''' > > > > - def __init__(self): > > - super(LJState, self).__init__( > > - 'lj-state', gdb.COMMAND_DATA) > > - > > def invoke(self, arg, from_tty): > > g = G(L(None)) > > gdb.write('{}\n'.format('\n'.join( > > @@ -665,9 +635,7 @@ The command requires no args and dumps current VM and GC states > > }.items()) > > ))) > > > > -LJState() > > - > > -class LJGC(gdb.Command): > > +class LJGC(LJBase): > > ''' > > lj-gc > > > > @@ -685,10 +653,6 @@ The command requires no args and dumps current GC stats: > > * weak: <number of weak tables (to be cleared)> > > ''' > > > > - def __init__(self): > > - super(LJGC, self).__init__( > > - 'lj-gc', gdb.COMMAND_DATA) > > - > > def invoke(self, arg, from_tty): > > g = G(L(None)) > > gdb.write('GC stats: {state}\n{stats}\n'.format( > > @@ -696,4 +660,28 @@ The command requires no args and dumps current GC stats: > > stats = dump_gc(g) > > )) > > > > -LJGC() > > +def load(commands): > > + global LJ_64, LJ_GC64, LJ_FR2 > > + > > + try: > > + LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' > > + LJ_FR2 = LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' > > + except: > > + gdb.write('luajit-gdb.py failed to load: ' > > + 'no debugging symbols found for libluajit\n') > > + return > > + > > + for name, command in commands.items(): > > + command(name) > > + > > + gdb.write('luajit-gdb.py is successfully loaded\n') > > + > > +load({ > > + 'lj-arch': LJDumpArch, > > + 'lj-tv': LJDumpTValue, > > + 'lj-str': LJDumpString, > > + 'lj-tab': LJDumpTable, > > + 'lj-stack': LJDumpStack, > > + 'lj-state': LJState, > > + 'lj-gc': LJGC, > > +}) > > -- > > 2.24.0 > > > > -- > Best regards, > IM -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin ` (2 preceding siblings ...) 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading Igor Munkin @ 2020-02-26 22:41 ` Alexander Turenko 2020-02-28 10:46 ` Igor Munkin 2020-02-26 22:45 ` Alexander Turenko ` (3 subsequent siblings) 7 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-02-26 22:41 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches Bugreport: When I source the extension from .gdbinit and run gdb to inspect a core file like so: gdb tarantool \ -ex "set solib-search-path $(realpath lib64):$(realpath lib64/lua/5.1):$(realpath lib64/tarantool):$(realpath lib64/tarantool/http):$(realpath lib64/tarantool/cron)" \ -ex "add-auto-load-safe-path $(realpath .)" \ -ex "set sysroot $(realpath .)" \ -ex 'set substitute-path /build/usr/src/debug/tarantool-2.2.1.137 src/tarantool' \ -ex 'core core' I got 'luajit-gdb.py failed to load: no debugging symbols found for libluajit' warning and the extesion is not loaded. However if I source it afterwards it loded successfully. I guess it is because `gdb.parse_and_eval('IRT_PTR')` and `gdb.parse_and_eval('IRT_PGC')` needs values from runtime, which is available only after `-ex 'core core'` command. Possible solution: always register lj-* functions, but load those values inside them (if it is not already done). A kind of lazy loading. WBR, Alexander Turenko. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-26 22:41 ` [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Alexander Turenko @ 2020-02-28 10:46 ` Igor Munkin 2020-03-02 16:00 ` Igor Munkin 0 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-02-28 10:46 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, Thanks for the report, I've digged a problem a bit and found a solution for the subj based on the events[1]. On 27.02.20, Alexander Turenko wrote: > Bugreport: > > When I source the extension from .gdbinit and run gdb to inspect a core > file like so: > > gdb tarantool \ > -ex "set solib-search-path $(realpath lib64):$(realpath lib64/lua/5.1):$(realpath lib64/tarantool):$(realpath lib64/tarantool/http):$(realpath lib64/tarantool/cron)" \ > -ex "add-auto-load-safe-path $(realpath .)" \ > -ex "set sysroot $(realpath .)" \ > -ex 'set substitute-path /build/usr/src/debug/tarantool-2.2.1.137 src/tarantool' \ > -ex 'core core' > > I got 'luajit-gdb.py failed to load: no debugging symbols found for > libluajit' warning and the extesion is not loaded. Now you will see the following: | <snipped> | luajit-gdb.py loading postponed until libluajit objfile is loaded | Reading symbols from ./usr/bin/tarantool... | lj-arch command initialized | lj-tv command initialized | lj-str command initialized | lj-tab command initialized | lj-stack command initialized | lj-state command initialized | lj-gc command initialized | luajit-gdb.py is successfully loaded | <snipped> > > However if I source it afterwards it loded successfully. > > I guess it is because `gdb.parse_and_eval('IRT_PTR')` and > `gdb.parse_and_eval('IRT_PGC')` needs values from runtime, which is > available only after `-ex 'core core'` command. > > Possible solution: always register lj-* functions, but load those values > inside them (if it is not already done). A kind of lazy loading. > > WBR, Alexander Turenko. Fixed, squashed, force-pushed to the branch. Diff is below: ================================================================================ diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 0ac0aaa..f23e119 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -659,9 +659,20 @@ The command requires no args and dumps current GC stats: stats = dump_gc(g) )) -def load(commands): +def init(commands): global LJ_64, LJ_GC64, LJ_FR2 + if not gdb.lookup_global_symbol('luaJIT_setmode'): + gdb.write('luajit-gdb.py initialization is postponed ' + 'until libluajit objfile is loaded\n') + gdb.events.new_objfile.connect(load) + return + + try: + gdb.events.new_objfile.disconnect(load) + except: + pass # was not connected + try: LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64' LJ_FR2 = LJ_GC64 = str(gdb.parse_and_eval('IRT_PGC')) == 'IRT_P64' @@ -675,12 +686,15 @@ def load(commands): gdb.write('luajit-gdb.py is successfully loaded\n') -load({ - 'lj-arch': LJDumpArch, - 'lj-tv': LJDumpTValue, - 'lj-str': LJDumpString, - 'lj-tab': LJDumpTable, - 'lj-stack': LJDumpStack, - 'lj-state': LJState, - 'lj-gc': LJGC, -}) +def load(event=None): + init({ + 'lj-arch': LJDumpArch, + 'lj-tv': LJDumpTValue, + 'lj-str': LJDumpString, + 'lj-tab': LJDumpTable, + 'lj-stack': LJDumpStack, + 'lj-state': LJState, + 'lj-gc': LJGC, + }) + +load(None) ================================================================================ [1]: https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html#Events-In-Python -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-28 10:46 ` Igor Munkin @ 2020-03-02 16:00 ` Igor Munkin 2020-03-03 14:16 ` Sergey Ostanevich 0 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-03-02 16:00 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches OK, this fix doesn't work e.g. for luajit w/o debuginfo. Consider the following: | <snipped> | luajit-gdb.py initialization is postponed until libluajit objfile is loaded | Reading symbols from /home/imun/projects/tarantool-luajit/src/luajit... | (No debugging symbols found in /home/imun/projects/tarantool-luajit/src/luajit) | luajit-gdb.py initialization is postponed until libluajit objfile is loaded | <snipped> The extension initialization goes in loops, since it fails to find luaJIT_setmode global symbol. I didn't investigate the problem a lot, just made the extension work. On 28.02.20, Igor Munkin wrote: <snipped> > + if not gdb.lookup_global_symbol('luaJIT_setmode'): > + gdb.write('luajit-gdb.py initialization is postponed ' > + 'until libluajit objfile is loaded\n') > + gdb.events.new_objfile.connect(load) > + return > + Here is the problem, so I rewrote it the following way: ================================================================================ diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 2e20642..f142fc5 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -662,7 +662,9 @@ The command requires no args and dumps current GC stats: def init(commands): global LJ_64, LJ_GC64, LJ_FR2 - if not gdb.lookup_global_symbol('luaJIT_setmode'): + try: + gdb.parse_and_eval('luaJIT_setmode') + except: gdb.write('luajit-gdb.py initialization is postponed ' 'until libluajit objfile is loaded\n') gdb.events.new_objfile.connect(load) ================================================================================ Squashed, force-pushed to the branch. <snipped> > > -- > Best regards, > IM -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-03-02 16:00 ` Igor Munkin @ 2020-03-03 14:16 ` Sergey Ostanevich 0 siblings, 0 replies; 23+ messages in thread From: Sergey Ostanevich @ 2020-03-03 14:16 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches Hi! Thanks for the update, LGTM for me. regards, Sergos On 02 мар 19:00, Igor Munkin wrote: > OK, this fix doesn't work e.g. for luajit w/o debuginfo. Consider the > following: > > | <snipped> > | luajit-gdb.py initialization is postponed until libluajit objfile is loaded > | Reading symbols from /home/imun/projects/tarantool-luajit/src/luajit... > | (No debugging symbols found in /home/imun/projects/tarantool-luajit/src/luajit) > | luajit-gdb.py initialization is postponed until libluajit objfile is loaded > | <snipped> > > The extension initialization goes in loops, since it fails to find > luaJIT_setmode global symbol. I didn't investigate the problem a lot, > just made the extension work. > > On 28.02.20, Igor Munkin wrote: > > <snipped> > > > + if not gdb.lookup_global_symbol('luaJIT_setmode'): > > + gdb.write('luajit-gdb.py initialization is postponed ' > > + 'until libluajit objfile is loaded\n') > > + gdb.events.new_objfile.connect(load) > > + return > > + > > Here is the problem, so I rewrote it the following way: > > ================================================================================ > > diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py > index 2e20642..f142fc5 100644 > --- a/src/luajit-gdb.py > +++ b/src/luajit-gdb.py > @@ -662,7 +662,9 @@ The command requires no args and dumps current GC stats: > def init(commands): > global LJ_64, LJ_GC64, LJ_FR2 > > - if not gdb.lookup_global_symbol('luaJIT_setmode'): > + try: > + gdb.parse_and_eval('luaJIT_setmode') > + except: > gdb.write('luajit-gdb.py initialization is postponed ' > 'until libluajit objfile is loaded\n') > gdb.events.new_objfile.connect(load) > > ================================================================================ > > Squashed, force-pushed to the branch. > > <snipped> > > > > > -- > > Best regards, > > IM > > -- > Best regards, > IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin ` (3 preceding siblings ...) 2020-02-26 22:41 ` [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Alexander Turenko @ 2020-02-26 22:45 ` Alexander Turenko 2020-02-27 10:48 ` Igor Munkin 2020-02-26 23:04 ` Alexander Turenko ` (2 subsequent siblings) 7 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-02-26 22:45 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches Are not it worth to squash those commits? Now the series looks as 'feature + fixup + fixup' and since we don't pushed it yet to a long-term branch is looks okay to squash fixups into the first commit. It is up to you, I just asked. WBR, Alexander Turenko. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-26 22:45 ` Alexander Turenko @ 2020-02-27 10:48 ` Igor Munkin 2020-02-27 11:35 ` Alexander Turenko 0 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-02-27 10:48 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, On 27.02.20, Alexander Turenko wrote: > Are not it worth to squash those commits? Now the series looks as > 'feature + fixup + fixup' and since we don't pushed it yet to a I see it for now as: * the feature * the feature port to Python 2 (I hope it can be dropped in some time) * several enhancments asked by Sergos (yep, it can be squashed prior to be pushed to the long-term branch) > long-term branch is looks okay to squash fixups into the first commit. > > It is up to you, I just asked. I'll think about it, thanks. > > WBR, Alexander Turenko. -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-27 10:48 ` Igor Munkin @ 2020-02-27 11:35 ` Alexander Turenko 2020-03-03 16:17 ` Igor Munkin 0 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-02-27 11:35 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches > * the feature port to Python 2 (I hope it can be dropped in some time) According to [1] not before June, 2024. However I guess that in fact it may be needed even after 2024. Not soon. [1]: https://wiki.centos.org/About/Product ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-27 11:35 ` Alexander Turenko @ 2020-03-03 16:17 ` Igor Munkin 2020-03-03 22:39 ` Alexander Turenko 0 siblings, 1 reply; 23+ messages in thread From: Igor Munkin @ 2020-03-03 16:17 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, I squashed the first patch of the series (gdb: introduce luajit-gdb extension) with the third one (gdb: enhance the extension loading) as you proposed supra. As discussed offline, the second patch is left to separate the changes required for compatibility with Python 2 in git repo history. Rebased, force-pushed to the branch. On 27.02.20, Alexander Turenko wrote: > > * the feature port to Python 2 (I hope it can be dropped in some > > time) > > According to [1] not before June, 2024. However I guess that in fact > it may be needed even after 2024. > > Not soon. > > [1]: https://wiki.centos.org/About/Product -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-03-03 16:17 ` Igor Munkin @ 2020-03-03 22:39 ` Alexander Turenko 2020-03-17 22:46 ` Igor Munkin 0 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-03-03 22:39 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches I don't think that it worth to do in-depth review from my side. But I tried the extension, it works and gives output that I can interpret. It also has the documentation that is available using gdb's built-in help command. It is nice, thanks, Igor! So the patchset LGTM. CCed Kirill. Igor, please, file an issue for the further activity: we should ship the tool in our packages (possibly tarantool-dev/devel) in paths, where gdb will able to catch it automatically. WBR, Alexander Turenko. On Tue, Mar 03, 2020 at 07:17:12PM +0300, Igor Munkin wrote: > Sasha, > > I squashed the first patch of the series (gdb: introduce luajit-gdb > extension) with the third one (gdb: enhance the extension loading) as > you proposed supra. > > As discussed offline, the second patch is left to separate the changes > required for compatibility with Python 2 in git repo history. > > Rebased, force-pushed to the branch. > > On 27.02.20, Alexander Turenko wrote: > > > * the feature port to Python 2 (I hope it can be dropped in some > > > time) > > > > According to [1] not before June, 2024. However I guess that in fact > > it may be needed even after 2024. > > > > Not soon. > > > > [1]: https://wiki.centos.org/About/Product > > -- > Best regards, > IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-03-03 22:39 ` Alexander Turenko @ 2020-03-17 22:46 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-03-17 22:46 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, On 04.03.20, Alexander Turenko wrote: > I don't think that it worth to do in-depth review from my side. But I > tried the extension, it works and gives output that I can interpret. It > also has the documentation that is available using gdb's built-in help > command. It is nice, thanks, Igor! > > So the patchset LGTM. CCed Kirill. > > Igor, please, file an issue for the further activity: we should ship the > tool in our packages (possibly tarantool-dev/devel) in paths, where gdb > will able to catch it automatically. See #4808[1] for the further enhancements I proposed for the extension. > > WBR, Alexander Turenko. > <snipped> [1]: https://github.com/tarantool/tarantool/issues/4808 -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin ` (4 preceding siblings ...) 2020-02-26 22:45 ` Alexander Turenko @ 2020-02-26 23:04 ` Alexander Turenko 2020-02-27 10:13 ` Igor Munkin 2020-02-26 23:10 ` Alexander Turenko 2020-03-05 7:44 ` Kirill Yukhin 7 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-02-26 23:04 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches Typo: recieve -> receive. $ ag recieve tools/tarantool/luajit/luajit-gdb.py 492:The command recieves a pointer to <tv> (TValue address) and dumps 525:The command recieves a <gcr> of the corresponding GCstr object and dumps 544:The command recieves a GCtab adress and dumps the table contents: 589:The command recieves a lua_State address and dumps the given Lua WBR, Alexander Turenko. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-26 23:04 ` Alexander Turenko @ 2020-02-27 10:13 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-27 10:13 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, Thanks, I hope it was a copy-paste. On 27.02.20, Alexander Turenko wrote: > Typo: recieve -> receive. > > $ ag recieve tools/tarantool/luajit/luajit-gdb.py > 492:The command recieves a pointer to <tv> (TValue address) and dumps > 525:The command recieves a <gcr> of the corresponding GCstr object and dumps > 544:The command recieves a GCtab adress and dumps the table contents: > 589:The command recieves a lua_State address and dumps the given Lua > > WBR, Alexander Turenko. Fixed, squashed, force-pushed to the branch. Diff is below: ================================================================================ diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index b6b3212..0ac0aaa 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -486,7 +486,7 @@ class LJDumpTValue(LJBase): ''' lj-tv <TValue *> -The command recieves a pointer to <tv> (TValue address) and dumps +The command receives a pointer to <tv> (TValue address) and dumps the type and some info related to it. * LJ_TNIL: nil @@ -519,7 +519,7 @@ class LJDumpString(LJBase): ''' lj-str <GCstr *> -The command recieves a <gcr> of the corresponding GCstr object and dumps +The command receives a <gcr> of the corresponding GCstr object and dumps the payload, size in bytes and hash. *Caveat*: Since Python 2 provides no native Unicode support, the payload @@ -538,7 +538,7 @@ class LJDumpTable(LJBase): ''' lj-tab <GCtab *> -The command recieves a GCtab adress and dumps the table contents: +The command receives a GCtab adress and dumps the table contents: * Metatable address whether the one is set * Array part <asize> slots: <aslot ptr>: [<index>]: <tv> @@ -583,7 +583,7 @@ class LJDumpStack(LJBase): ''' lj-stack [<lua_State *>] -The command recieves a lua_State address and dumps the given Lua +The command receives a lua_State address and dumps the given Lua coroutine guest stack: <slot ptr> [<slot attributes>] <VALUE|FRAME> ================================================================================ -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin ` (5 preceding siblings ...) 2020-02-26 23:04 ` Alexander Turenko @ 2020-02-26 23:10 ` Alexander Turenko 2020-02-27 10:37 ` Igor Munkin 2020-03-05 7:44 ` Kirill Yukhin 7 siblings, 1 reply; 23+ messages in thread From: Alexander Turenko @ 2020-02-26 23:10 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches NB: It seems logical to install the script with tarantool-dev / tarantool-devel package. Is there some place, from which gdb loads extensions automatically? Maybe `/usr/share/gdb/python/`? ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-26 23:10 ` Alexander Turenko @ 2020-02-27 10:37 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-02-27 10:37 UTC (permalink / raw) To: Alexander Turenko; +Cc: tarantool-patches Sasha, On 27.02.20, Alexander Turenko wrote: > NB: It seems logical to install the script with tarantool-dev / > tarantool-devel package. Is there some place, from which gdb loads > extensions automatically? Maybe `/usr/share/gdb/python/`? Yes, there is a place where extensions are autoloaded (see the link[1] for more info). E.g. uJIT package installs its extension to the default Ubuntu path[2]. Furthemore, one can specify its own path for extension to be autoloaded on gdb startup. [1]: https://sourceware.org/gdb/current/onlinedocs/gdb/objfile_002dgdbdotext-file.html#objfile_002dgdbdotext-file [2]: https://github.com/iponweb/luavela/blob/master/tools/CMakeLists.txt#L133 -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin ` (6 preceding siblings ...) 2020-02-26 23:10 ` Alexander Turenko @ 2020-03-05 7:44 ` Kirill Yukhin 2020-03-05 9:22 ` Igor Munkin 7 siblings, 1 reply; 23+ messages in thread From: Kirill Yukhin @ 2020-03-05 7:44 UTC (permalink / raw) To: Igor Munkin; +Cc: tarantool-patches Hello, On 05 фев 19:22, Igor Munkin wrote: > > Signed-off-by: Igor Munkin <imun@tarantool.org> I've checked the patchset into tarantool/luajit repo and bumped a new version in 1.10, 2.2, 2.3 and master. -- Regards, Kirill Yukhin ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT 2020-03-05 7:44 ` Kirill Yukhin @ 2020-03-05 9:22 ` Igor Munkin 0 siblings, 0 replies; 23+ messages in thread From: Igor Munkin @ 2020-03-05 9:22 UTC (permalink / raw) To: Kirill Yukhin; +Cc: tarantool-patches Kirill, Thanks! As you reminded, here is a ChangeLog entry: @ChangeLog: * Introduced luajit-gdb.py extension with commands for inspecting LuaJIT internals. The extension obliges the one to provide gdbinfo for libluajit, otherwise loading fails. The extension provides the following commands: - `lj-arch` dumps values of LJ_64 and LJ_GC64 macro definitions - `lj-tv` dumps the type and GCobj info related to the given TValue - `lj-str` dumps the contents of the given GCstr - `lj-tab` dumps the contents of the given GCtab - `lj-stack` dumps Lua stack of the given lua_State - `lj-state` shows current VM, GC and JIT states - `lj-gc` shows current GC stats On 05.03.20, Kirill Yukhin wrote: > Hello, > > On 05 фев 19:22, Igor Munkin wrote: > > > > Signed-off-by: Igor Munkin <imun@tarantool.org> > > I've checked the patchset into tarantool/luajit repo > and bumped a new version in 1.10, 2.2, 2.3 and master. > > -- > Regards, Kirill Yukhin Added to the corresponding release notes. -- Best regards, IM ^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2020-03-17 22:52 UTC | newest] Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-02-05 16:22 [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 1/3] gdb: introduce luajit-gdb extension Igor Munkin 2020-02-13 13:24 ` Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 2/3] gdb: adjust the extension to be used with Python 2 Igor Munkin 2020-02-05 16:22 ` [Tarantool-patches] [PATCH v2 luajit 3/3] gdb: enhance the extension loading Igor Munkin 2020-02-13 11:48 ` Igor Munkin 2020-02-13 12:24 ` Igor Munkin 2020-02-26 22:41 ` [Tarantool-patches] [PATCH v2 luajit 0/3] Introduce gdb extension for LuaJIT Alexander Turenko 2020-02-28 10:46 ` Igor Munkin 2020-03-02 16:00 ` Igor Munkin 2020-03-03 14:16 ` Sergey Ostanevich 2020-02-26 22:45 ` Alexander Turenko 2020-02-27 10:48 ` Igor Munkin 2020-02-27 11:35 ` Alexander Turenko 2020-03-03 16:17 ` Igor Munkin 2020-03-03 22:39 ` Alexander Turenko 2020-03-17 22:46 ` Igor Munkin 2020-02-26 23:04 ` Alexander Turenko 2020-02-27 10:13 ` Igor Munkin 2020-02-26 23:10 ` Alexander Turenko 2020-02-27 10:37 ` Igor Munkin 2020-03-05 7:44 ` Kirill Yukhin 2020-03-05 9:22 ` Igor Munkin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox