From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id DBA334696C4 for ; Mon, 27 Jan 2020 23:43:25 +0300 (MSK) From: Igor Munkin Date: Mon, 27 Jan 2020 23:41:07 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@dev.tarantool.org * Introduce lj-gc command that shows current GC stats * Remove mandatory L argument for lj-state and make it optional for lj-stack (main coroutine is used if argument is ommited) * Adjust doc strings Signed-off-by: Igor Munkin --- src/luajit-gdb.py | 204 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 171 insertions(+), 33 deletions(-) diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 4f69152..77da5e6 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -19,6 +19,21 @@ def gtype(typestr): 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 @@ -152,18 +167,30 @@ 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(L): +def J(g): typeGG = gtype('GG_State') - return cast('jit_State *', int(cast('char *', G(L))) + return cast('jit_State *', int(cast('char *', g)) - int(typeGG['g'].bitpos / 8) + int(typeGG['J'].bitpos / 8) ) -def vm_state(L): +def vm_state(g): return { i2notu64(0): 'INTERP', i2notu64(1): 'C', @@ -174,7 +201,7 @@ def vm_state(L): i2notu64(6): 'ASM', }.get(int(tou64(g['vmstate'])), 'TRACE') -def gc_state(L): +def gc_state(g): return { 0: 'PAUSE', 1: 'PROPAGATE', @@ -183,9 +210,9 @@ def gc_state(L): 4: 'SWEEP', 5: 'FINALIZE', 6: 'LAST', - }.get(int(G(L)['gc']['state']), 'INVALID') + }.get(int(g['gc']['state']), 'INVALID') -def jit_state(L): +def jit_state(g): return { 0: 'IDLE', 0x10: 'ACTIVE', @@ -194,7 +221,7 @@ def jit_state(L): 0x13: 'END', 0x14: 'ASM', 0x15: 'ERR', - }.get(int(J(L)['state']), 'INVALID') + }.get(int(J(g)['state']), 'INVALID') def tvisnumber(o): return itype(o) <= (0xfffeffff if LJ_64 and not LJ_GC64 else LJ_T['NUMX']) @@ -223,6 +250,13 @@ def funcproto(func): 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): @@ -390,24 +424,34 @@ def dump_stack(L, base=None, top=None): return dump -def parse_arg(arg): - argv = gdb.string_to_argv(arg) +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' + ) ] - if len(argv) == 0: - raise gdb.GdbError("Wrong number of arguments." - "Use 'help ' to get more info.") + stats += [ 'sweepstr: {sweepstr}/{strmask}'.format( + sweepstr = gc['sweepstr'], + # String hash mask (size of hash table - 1). + strmask = g['strmask'] + 1, + ) ] - ret = gdb.parse_and_eval(arg) + stats += [ '{key}: {number} objects'.format( + key = f, + number = gclistlen(gc[f]), + ) for f in ('root', 'gray', 'grayagain', 'weak') ] - if not ret: - raise gdb.GdbError('table argument empty') + # TODO: mmudata - return ret + return '\n'.join(map(lambda s: '\t' + s, stats)) class LJDumpArch(gdb.Command): ''' lj-arch -Dumps compile-time information + +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): @@ -425,8 +469,31 @@ LJDumpArch() class LJDumpTValue(gdb.Command): ''' -lj-tv address -Dumps the contents of the TValue at address. +lj-tv + +The command recieves a pointer to (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 @ +* LJ_TSTR: string @ +* LJ_TUPVAL: upvalue @ +* LJ_TTHREAD: thread @ +* LJ_TPROTO: proto @ +* LJ_TFUNC: + : Lua function @ , upvalues, + : C function + : fast function # +* LJ_TTRACE: trace @ +* LJ_TCDATA: cdata @ +* LJ_TTAB: table @ (asize: , hmask: ) +* LJ_TUDATA: userdata @ +* LJ_TNUMX: number + +Whether the type of the given address differs from the listed above, then +error message occurs. ''' def __init__(self): @@ -442,8 +509,13 @@ LJDumpTValue() class LJDumpString(gdb.Command): ''' -lj-str address -Dumps the contents of the GCstr at address. +lj-str + +The command recieves a 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): @@ -464,8 +536,14 @@ LJDumpString() class LJDumpTable(gdb.Command): ''' -lj-tab address -Dumps the contents of the GCtab at address. +lj-tab + +The command recieves a GCtab adress and dumps the table contents: +* Metatable address whether the one is set +* Array part slots: + : []: +* Hash part nodes: + : { } => { }; next = ''' def __init__(self): @@ -509,8 +587,35 @@ LJDumpTable() class LJDumpStack(gdb.Command): ''' -lj-stack L -Dumps Lua stack of the given coroutine L. +lj-stack [] + +The command recieves a lua_State address and dumps the given Lua +coroutine guest stack: + + [] + +* : guest stack slot address +* : + - 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) +* : see help lj-tv for more info +* : 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 + [] delta=, + - : + + 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): @@ -518,15 +623,17 @@ Dumps Lua stack of the given coroutine L. 'lj-stack', gdb.COMMAND_DATA) def invoke(self, arg, from_tty): - L = cast('lua_State *', parse_arg(arg)) - gdb.write('{}\n'.format(dump_stack(L))) + gdb.write('{}\n'.format(dump_stack(L(parse_arg(arg))))) LJDumpStack() class LJState(gdb.Command): ''' -lj-state L -Show current VM and GC state. +lj-state +The command requires no args and dumps current VM and GC states +* VM state: +* GC state: +* JIT state: ''' def __init__(self): @@ -534,13 +641,44 @@ Show current VM and GC state. 'lj-state', gdb.COMMAND_DATA) def invoke(self, arg, from_tty): - L = cast('lua_State *', parse_arg(arg)) + g = G(L(None)) gdb.write('{}\n'.format('\n'.join( map(lambda t: '{} state: {}'.format(*t), { - 'VM': vm_state(L), - 'GC': gc_state(L), - 'JIT': jit_state(L), + '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: +* threshold: +* debt: +* estimate: +* stepmul: +* pause: +* sweepstr: +* root: +* gray: +* grayagain: +* weak: + ''' + + 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