* [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT
@ 2020-01-27 20:41 Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 1/4] gdb: introduce luajit-gdb extension Igor Munkin
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Igor Munkin @ 2020-01-27 20:41 UTC (permalink / raw)
To: tarantool-patches
The series provides a gdb extension with commands for inspecting LuaJIT
internals. The following set of commands is implemented now:
### 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>
---
Branch: https://github.com/tarantool/luajit/tree/imun/luajit-gdb
Igor Munkin (4):
gdb: introduce luajit-gdb extension
gdb: make existing commands more verbose
gdb: make several enhancements
gdb: adjust extension to be used with Python 2
src/luajit-gdb.py | 699 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 699 insertions(+)
create mode 100644 src/luajit-gdb.py
--
2.24.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Tarantool-patches] [PATCH luajit 1/4] gdb: introduce luajit-gdb extension
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
@ 2020-01-27 20:41 ` Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 2/4] gdb: make existing commands more verbose Igor Munkin
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Igor Munkin @ 2020-01-27 20:41 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 -- show current VM and GC states
Currently extension supports only x64 builds but respects LJ_GC64 value
Signed-off-by: Igor Munkin <imun@tarantool.org>
---
src/luajit-gdb.py | 511 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 511 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..633c56a
--- /dev/null
+++ b/src/luajit-gdb.py
@@ -0,0 +1,511 @@
+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 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 G(L):
+ return mref('global_State *', L['glref'])
+
+def vm_state(L):
+ 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(L)['vmstate'])), 'TRACE')
+
+def gc_state(L):
+ return {
+ 0: 'PAUSE',
+ 1: 'PROPAGATE',
+ 2: 'ATOMIC',
+ 3: 'SWEEPSTRING',
+ 4: 'SWEEP',
+ 5: 'FINALIZE',
+ 6: 'LAST',
+ }.get(int(G(L)['gc']['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)
+
+# 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)
+ # String will be printed with pointer to that string, thanks to gdb.
+ 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):
+ return 'table @ {}'.format(strx64(gcval(tv['gcr'])))
+
+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 parse_arg(arg):
+ argv = gdb.string_to_argv(arg)
+
+ if len(argv) == 0:
+ raise gdb.GdbError("Wrong number of arguments."
+ "Use 'help <command>' to get more info.")
+
+ ret = gdb.parse_and_eval(arg)
+
+ if not ret:
+ raise gdb.GdbError('table argument empty')
+
+ return ret
+
+class LJDumpArch(gdb.Command):
+ '''
+lj-arch
+Dumps compile-time information
+ '''
+
+ 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 address
+Dumps the contents of the TValue at address.
+ '''
+
+ 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 address
+Dumps the contents of the GCstr at address.
+ '''
+
+ 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 address
+Dumps the contents of the GCtab at address.
+ '''
+
+ 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'])
+
+ if mt != 0:
+ gdb.write('Metatable detected: {}\n'.format(strx64(mt)))
+
+ gdb.write('Array part:\n')
+ for i in range(int(t['asize'])):
+ gdb.write('[{index}] {value}\n'.format(
+ index = i,
+ value = dump_tvalue(array + i)
+ ))
+
+ gdb.write('Hash part:\n')
+ # See hmask comment in lj_obj.h
+ for i in range(int(t['hmask'] + 1)):
+ node = nodes + i
+ gdb.write('{{ {key} }} => {{ {value} }} next = {next}\n'.format(
+ key = dump_tvalue(node['key']),
+ value = dump_tvalue(node['val']),
+ next = mref('struct Node *', node['next'])
+ ))
+
+LJDumpTable()
+
+class LJDumpStack(gdb.Command):
+ '''
+lj-stack L
+Dumps Lua stack of the given coroutine L.
+ '''
+
+ def __init__(self):
+ super(LJDumpStack, self).__init__(
+ '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)))
+
+LJDumpStack()
+
+class LJState(gdb.Command):
+ '''
+lj-state L
+Show current VM and GC state.
+ '''
+
+ def __init__(self):
+ super(LJState, self).__init__(
+ 'lj-state', gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ L = cast('lua_State *', parse_arg(arg))
+ gdb.write('VM state: {}\n'.format(vm_state(L)))
+ gdb.write('GC state: {}\n'.format(gc_state(L)))
+
+LJState()
--
2.24.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Tarantool-patches] [PATCH luajit 2/4] gdb: make existing commands more verbose
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 1/4] gdb: introduce luajit-gdb extension Igor Munkin
@ 2020-01-27 20:41 ` Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements Igor Munkin
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Igor Munkin @ 2020-01-27 20:41 UTC (permalink / raw)
To: tarantool-patches
* Enrich GCtab object dumpers with array and hash parts sizes
* Make lj-state reporting the current JIT state
Signed-off-by: Igor Munkin <imun@tarantool.org>
---
src/luajit-gdb.py | 63 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 49 insertions(+), 14 deletions(-)
diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py
index 633c56a..4f69152 100644
--- a/src/luajit-gdb.py
+++ b/src/luajit-gdb.py
@@ -155,6 +155,14 @@ def gcval(obj):
def G(L):
return mref('global_State *', L['glref'])
+def J(L):
+ typeGG = gtype('GG_State')
+
+ return cast('jit_State *', int(cast('char *', G(L)))
+ - int(typeGG['g'].bitpos / 8)
+ + int(typeGG['J'].bitpos / 8)
+ )
+
def vm_state(L):
return {
i2notu64(0): 'INTERP',
@@ -164,7 +172,7 @@ def vm_state(L):
i2notu64(4): 'RECORD',
i2notu64(5): 'OPT',
i2notu64(6): 'ASM',
- }.get(int(tou64(G(L)['vmstate'])), 'TRACE')
+ }.get(int(tou64(g['vmstate'])), 'TRACE')
def gc_state(L):
return {
@@ -177,6 +185,17 @@ def gc_state(L):
6: 'LAST',
}.get(int(G(L)['gc']['state']), 'INVALID')
+def jit_state(L):
+ return {
+ 0: 'IDLE',
+ 0x10: 'ACTIVE',
+ 0x11: 'RECORD',
+ 0x12: 'START',
+ 0x13: 'END',
+ 0x14: 'ASM',
+ 0x15: 'ERR',
+ }.get(int(J(L)['state']), 'INVALID')
+
def tvisnumber(o):
return itype(o) <= (0xfffeffff if LJ_64 and not LJ_GC64 else LJ_T['NUMX'])
@@ -239,7 +258,6 @@ def dump_lj_tfunc(tv):
if ffid == 0:
pt = funcproto(func)
- # String will be printed with pointer to that string, thanks to gdb.
return 'Lua function @ {addr}, {nupvals} upvalues, {chunk}:{line}'.format(
addr = strx64(func),
nupvals = int(func['nupvalues']),
@@ -262,7 +280,12 @@ def dump_lj_tcdata(tv):
return 'cdata @ {}'.format(strx64(gcval(tv['gcr'])))
def dump_lj_ttab(tv):
- return 'table @ {}'.format(strx64(gcval(tv['gcr'])))
+ table = cast('GCtab *', gcval(tv['gcr']))
+ return 'table @ {gcr} (asize: {asize}, hmask: {hmask})'.format(
+ gcr = strx64(table),
+ asize = table['asize'],
+ hmask = strx64(table['hmask']),
+ )
def dump_lj_tudata(tv):
return 'userdata @ {}'.format(strx64(gcval(tv['gcr'])))
@@ -454,25 +477,32 @@ Dumps the contents of the GCtab at address.
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:\n')
- for i in range(int(t['asize'])):
- gdb.write('[{index}] {value}\n'.format(
+ 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(array + i)
+ value = dump_tvalue(slot)
))
- gdb.write('Hash part:\n')
+ gdb.write('Hash part: {} nodes\n'.format(capacity['hpart']))
# See hmask comment in lj_obj.h
- for i in range(int(t['hmask'] + 1)):
+ for i in range(capacity['hpart']):
node = nodes + i
- gdb.write('{{ {key} }} => {{ {value} }} next = {next}\n'.format(
+ gdb.write('{ptr}: {{ {key} }} => {{ {val} }}; next = {n}\n'.format(
+ ptr = node,
key = dump_tvalue(node['key']),
- value = dump_tvalue(node['val']),
- next = mref('struct Node *', node['next'])
+ val= dump_tvalue(node['val']),
+ n = mref('struct Node *', node['next'])
))
LJDumpTable()
@@ -505,7 +535,12 @@ Show current VM and GC state.
def invoke(self, arg, from_tty):
L = cast('lua_State *', parse_arg(arg))
- gdb.write('VM state: {}\n'.format(vm_state(L)))
- gdb.write('GC state: {}\n'.format(gc_state(L)))
+ gdb.write('{}\n'.format('\n'.join(
+ map(lambda t: '{} state: {}'.format(*t), {
+ 'VM': vm_state(L),
+ 'GC': gc_state(L),
+ 'JIT': jit_state(L),
+ }.items())
+ )))
LJState()
--
2.24.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 1/4] gdb: introduce luajit-gdb extension Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 2/4] gdb: make existing commands more verbose Igor Munkin
@ 2020-01-27 20:41 ` Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 4/4] gdb: adjust extension to be used with Python 2 Igor Munkin
2020-02-03 13:48 ` [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Sergey Ostanevich
4 siblings, 0 replies; 6+ messages in thread
From: Igor Munkin @ 2020-01-27 20:41 UTC (permalink / raw)
To: tarantool-patches
* 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@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
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Tarantool-patches] [PATCH luajit 4/4] gdb: adjust extension to be used with Python 2
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
` (2 preceding siblings ...)
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements Igor Munkin
@ 2020-01-27 20:41 ` Igor Munkin
2020-02-03 13:48 ` [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Sergey Ostanevich
4 siblings, 0 replies; 6+ messages in thread
From: Igor Munkin @ 2020-01-27 20:41 UTC (permalink / raw)
To: tarantool-patches
This patch covers all problems faced while using 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] 6+ messages in thread
* Re: [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
` (3 preceding siblings ...)
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 4/4] gdb: adjust extension to be used with Python 2 Igor Munkin
@ 2020-02-03 13:48 ` Sergey Ostanevich
4 siblings, 0 replies; 6+ messages in thread
From: Sergey Ostanevich @ 2020-02-03 13:48 UTC (permalink / raw)
To: Igor Munkin; +Cc: tarantool-patches
Hi!
Thanks for the patch!
1. It would be nice to include at least short description on how to hook up
the script into the gdb. I used "source <fullpath>" - is it intended?
2. If there's no tarantool running - then most of commands fail with error:
(gdb) source /workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py
(gdb) lj-arch
LJ_64: True, LJ_GC64: False
(gdb) lj-gc
Traceback (most recent call last):
File "/workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py", line 615, in invoke
g = G(L(None))
File "/workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py", line 188, in L
'tarantool_L',
File "/workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py", line 184, in <lambda>
for l in [ L ] + list(map(lambda l: lookup(l), (
File "/workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py", line 32, in lookup
variable, _ = gdb.lookup_symbol(symbol)
gdb.error: No frame selected.
Error occurred in Python command: No frame selected.
3. You didn't mention it anywhere but you rely on internal symbols of tarantool,
so it will fail when debugging stripped executable or a non-tarantool one:
(gdb) source /workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py
Traceback (most recent call last):
File "/workspaces/tarantool.review/third_party/luajit/src/luajit-gdb.py", line 154, in <module>
LJ_64 = str(gdb.parse_and_eval('IRT_PTR')) == 'IRT_P64'
gdb.error: No symbol "IRT_PTR" in current context.
I believe those should be augmented with grace failures with some meaningful explanation.
Otherwise I have no objections neither in code, nor in split patchset.
Regards,
Sergos
On 27 Jan 23:41, Igor Munkin wrote:
> The series provides a gdb extension with commands for inspecting LuaJIT
> internals. The following set of commands is implemented now:
>
> ### 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>
>
> ---
>
> Branch: https://github.com/tarantool/luajit/tree/imun/luajit-gdb
>
> Igor Munkin (4):
> gdb: introduce luajit-gdb extension
> gdb: make existing commands more verbose
> gdb: make several enhancements
> gdb: adjust extension to be used with Python 2
>
> src/luajit-gdb.py | 699 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 699 insertions(+)
> create mode 100644 src/luajit-gdb.py
>
> --
> 2.24.0
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2020-02-03 13:48 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-27 20:41 [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 1/4] gdb: introduce luajit-gdb extension Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 2/4] gdb: make existing commands more verbose Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 3/4] gdb: make several enhancements Igor Munkin
2020-01-27 20:41 ` [Tarantool-patches] [PATCH luajit 4/4] gdb: adjust extension to be used with Python 2 Igor Munkin
2020-02-03 13:48 ` [Tarantool-patches] [PATCH luajit 0/4] Introduce gdb extension for LuaJIT Sergey Ostanevich
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox