* [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
* [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 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
* 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-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-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-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: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-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-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-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-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-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
* 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
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