[Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements
Igor Munkin
imun at tarantool.org
Mon Jan 27 23:41:07 MSK 2020
* 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 <imun at tarantool.org>
---
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 <command>' 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 <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):
@@ -442,8 +509,13 @@ LJDumpTValue()
class LJDumpString(gdb.Command):
'''
-lj-str address
-Dumps the contents of the GCstr at address.
+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):
@@ -464,8 +536,14 @@ LJDumpString()
class LJDumpTable(gdb.Command):
'''
-lj-tab address
-Dumps the contents of the GCtab at address.
+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):
@@ -509,8 +587,35 @@ LJDumpTable()
class LJDumpStack(gdb.Command):
'''
-lj-stack L
-Dumps Lua stack of the given coroutine L.
+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):
@@ -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: <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):
@@ -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: <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
More information about the Tarantool-patches
mailing list