From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id E48256ECE3; Thu, 9 Jun 2022 13:14:42 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org E48256ECE3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1654769683; bh=2nSR/qyw5el/J6mm6faemuazML1UIeqxlB3Lwdxs9BI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=G0IE6mNMW3STT7kwbKkujA3W+H24uYSDHgph747y23H8/9T4QSikGOznzRAEo7QUN yoqBA9HuFKR+vqqY2peplismUJk8L9EmLO03WHmXSdhQsi63zAeVDm4fDZYfobyAp/ Jt40NaQCS4NaawY8hESOIa0FMaBouSR5QQbEBX5c= Received: from smtpng3.i.mail.ru (smtpng3.i.mail.ru [94.100.177.149]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 316346EC41 for ; Thu, 9 Jun 2022 13:13:42 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 316346EC41 Received: by smtpng3.m.smailru.net with esmtpa (envelope-from ) id 1nzFAb-0007Ak-64; Thu, 09 Jun 2022 13:13:41 +0300 To: Maxim Kokryashkin , Igor Munkin Date: Thu, 9 Jun 2022 13:11:14 +0300 Message-Id: <6ef8aa43d1a571fef477550885e24faf1e4b962b.1654767443.git.skaplun@tarantool.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: 78E4E2B564C1792B X-77F55803: 4F1203BC0FB41BD97D44297578DBEB86DB1F38E855E458E5207BA634EADC6428182A05F5380850405063BF9EDC7D471BD829C7527769396EBACBCD97A3E3BFB3DF4BC831FAE4808E X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE782A779A89F7D69B2C2099A533E45F2D0395957E7521B51C2CFCAF695D4D8E9FCEA1F7E6F0F101C6778DA827A17800CE7D4EF26BAC91EEE7FEA1F7E6F0F101C6723150C8DA25C47586E58E00D9D99D84E1BDDB23E98D2D38BEBC5CAB6D411FFA6029531C4D8111E62C89154E449E991A2CC7F00164DA146DAFE8445B8C89999728AA50765F790063793270F7220657A0A389733CBF5DBD5E9C8A9BA7A39EFB766F5D81C698A659EA7CC7F00164DA146DA9985D098DBDEAEC8C2B5EEE3591E0D35F6B57BC7E6449061A352F6E88A58FB86F5D81C698A659EA7E827F84554CEF5019E625A9149C048EE9ECD01F8117BC8BEE2021AF6380DFAD18AA50765F790063735872C767BF85DA227C277FBC8AE2E8B2303E78B907142AC75ECD9A6C639B01B4E70A05D1297E1BBCB5012B2E24CD356 X-8FC586DF: 6EFBBC1D9D64D975 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C83BF3A71D03FAA4ADBFCF79D4C1C77D4FC3F94E46D0E38E29C2B6934AE262D3EE7EAB7254005DCED1C39E39C5FB3188C4EAF44D9B582CE87C8A4C02DF684249C42578FD4EBC74EEF699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34F0A5F58274334C952968721CDECBEE53ACC147FC981C44F12F98418452AF85EFD1591C2C390AECE31D7E09C32AA3244CFF0767564EF021CFC9526B9B0D0A2EE77101BF96129E4011927AC6DF5659F194 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojgs1behNStknGaNavjdcekA== X-Mailru-Sender: 689FA8AB762F739339CABD9B3CA9A7D68530776C849732A07D4CF458496E2D4E0FBE9A32752B8C9C2AA642CC12EC09F1FB559BB5D741EB962F61BD320559CF1EFD657A8799238ED55FEEDEB644C299C0ED14614B50AE0675 X-Mras: Ok Subject: [Tarantool-patches] [PATCH luajit 2/2] gdb: introduce lj-bc, lj-func and lj-proto dumpers X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This patch adds dumpers as for single bytecode instruction (`lj-bc`), as for all bytecodes inside one function (`lj-func`) or prototype (`lj-proto`). Its dump is quite similar with -bl flag, but also reports types of registers operands (`jmp`, `dst`, `str`, etc.). --- src/luajit-gdb.py | 324 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 323 insertions(+), 1 deletion(-) diff --git a/src/luajit-gdb.py b/src/luajit-gdb.py index 779a25f8..339b57ed 100644 --- a/src/luajit-gdb.py +++ b/src/luajit-gdb.py @@ -113,8 +113,162 @@ def frametypes(ft): FRAME['VARG'] : 'V', }.get(ft, '?') +def bc_op(ins): + return int(ins) & 0xff + def bc_a(ins): - return (ins >> 8) & 0xff + return (int(ins) >> 8) & 0xff + +def bc_b(ins): + return int(ins) >> 24 + +def bc_c(ins): + return (int(ins) >> 16) & 0xff + +def bc_d(ins): + return int(ins) >> 16 + +___ = 0 +BC_NAME = 0 +BC_A = 1 +BC_B = 2 +BC_CD = 3 +BC_MM = 4 + +BYTECODES = [ + # Comparison ops. ORDER OPR. + ['ISLT', 'var', ___, 'var', 'lt'], + ['ISGE', 'var', ___, 'var', 'lt'], + ['ISLE', 'var', ___, 'var', 'le'], + ['ISGT', 'var', ___, 'var', 'le'], + + ['ISEQV', 'var', ___, 'var', 'eq'], + ['ISNEV', 'var', ___, 'var', 'eq'], + ['ISEQS', 'var', ___, 'str', 'eq'], + ['ISNES', 'var', ___, 'str', 'eq'], + ['ISEQN', 'var', ___, 'num', 'eq'], + ['ISNEN', 'var', ___, 'num', 'eq'], + ['ISEQP', 'var', ___, 'pri', 'eq'], + ['ISNEP', 'var', ___, 'pri', 'eq'], + + # Unary test and copy ops. + ['ISTC', 'dst', ___, 'var', ___], + ['ISFC', 'dst', ___, 'var', ___], + ['IST', ___, ___, 'var', ___], + ['ISF', ___, ___, 'var', ___], + ['ISTYPE', 'var', ___, 'lit', ___], + ['ISNUM', 'var', ___, 'lit', ___], + ['MOV', 'dst', ___, 'var', ___], + ['NOT', 'dst', ___, 'var', ___], + ['UNM', 'dst', ___, 'var', 'unm'], + ['LEN', 'dst', ___, 'var', 'len'], + ['ADDVN', 'dst', 'var', 'num', 'add'], + ['SUBVN', 'dst', 'var', 'num', 'sub'], + ['MULVN', 'dst', 'var', 'num', 'mul'], + ['DIVVN', 'dst', 'var', 'num', 'div'], + ['MODVN', 'dst', 'var', 'num', 'mod'], + + # Binary ops. ORDER OPR. + ['ADDNV', 'dst', 'var', 'num', 'add'], + ['SUBNV', 'dst', 'var', 'num', 'sub'], + ['MULNV', 'dst', 'var', 'num', 'mul'], + ['DIVNV', 'dst', 'var', 'num', 'div'], + ['MODNV', 'dst', 'var', 'num', 'mod'], + + ['ADDVV', 'dst', 'var', 'var', 'add'], + ['SUBVV', 'dst', 'var', 'var', 'sub'], + ['MULVV', 'dst', 'var', 'var', 'mul'], + ['DIVVV', 'dst', 'var', 'var', 'div'], + ['MODVV', 'dst', 'var', 'var', 'mod'], + + ['POW', 'dst', 'var', 'var', 'pow'], + ['CAT', 'dst', 'rbase', 'rbase', 'concat'], + + # Constant ops. + ['KSTR', 'dst', ___, 'str', ___], + ['KCDATA', 'dst', ___, 'cdata', ___], + ['KSHORT', 'dst', ___, 'lits', ___], + ['KNUM', 'dst', ___, 'num', ___], + ['KPRI', 'dst', ___, 'pri', ___], + ['KNIL', 'base', ___, 'base', ___], + + # Upvalue and function ops. + ['UGET', 'dst', ___, 'uv', ___], + ['USETV', 'uv', ___, 'var', ___], + ['USETS', 'uv', ___, 'str', ___], + ['USETN', 'uv', ___, 'num', ___], + ['USETP', 'uv', ___, 'pri', ___], + ['UCLO', 'rbase', ___, 'jump', ___], + ['FNEW', 'dst', ___, 'func', ___], + + # Table ops. + ['TNEW', 'dst', ___, 'lit', ___], + ['TDUP', 'dst', ___, 'tab', ___], + ['GGET', 'dst', ___, 'str', 'index'], + ['GSET', 'var', ___, 'str', 'newindex'], + ['TGETV', 'dst', 'var', 'var', 'index'], + ['TGETS', 'dst', 'var', 'str', 'index'], + ['TGETB', 'dst', 'var', 'lit', 'index'], + ['TGETR', 'dst', 'var', 'var', 'index'], + ['TSETV', 'var', 'var', 'var', 'newindex'], + ['TSETS', 'var', 'var', 'str', 'newindex'], + ['TSETB', 'var', 'var', 'lit', 'newindex'], + ['TSETM', 'base', ___, 'num', 'newindex'], + ['TSETR', 'var', 'var', 'var', 'newindex'], + + # Calls and vararg handling. T = tail call. + ['CALLM', 'base', 'lit', 'lit', 'call'], + ['CALL', 'base', 'lit', 'lit', 'call'], + ['CALLMT', 'base', ___, 'lit', 'call'], + ['CALLT', 'base', ___, 'lit', 'call'], + ['ITERC', 'base', 'lit', 'lit', 'call'], + ['ITERN', 'base', 'lit', 'lit', 'call'], + ['VARG', 'base', 'lit', 'lit', ___], + ['ISNEXT', 'base', ___, 'jump', ___], + + # Returns. + ['RETM', 'base', ___, 'lit', ___], + ['RET', 'rbase', ___, 'lit', ___], + ['RET0', 'rbase', ___, 'lit', ___], + ['RET1', 'rbase', ___, 'lit', ___], + + # Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop. + ['FORI', 'base', ___, 'jump', ___], + ['JFORI', 'base', ___, 'jump', ___], + + ['FORL', 'base', ___, 'jump', ___], + ['IFORL', 'base', ___, 'jump', ___], + ['JFORL', 'base', ___, 'lit', ___], + + ['ITERL', 'base', ___, 'jump', ___], + ['IITERL', 'base', ___, 'jump', ___], + ['JITERL', 'base', ___, 'lit', ___], + + ['LOOP', 'rbase', ___, 'jump', ___], + ['ILOOP', 'rbase', ___, 'jump', ___], + ['JLOOP', 'rbase', ___, 'lit', ___], + + ['JMP', 'rbase', ___, 'jump', ___], + + # Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. + ['FUNCF', 'rbase', ___, ___, ___], + ['IFUNCF', 'rbase', ___, ___, ___], + ['JFUNCF', 'rbase', ___, 'lit', ___], + ['FUNCV', 'rbase', ___, ___, ___], + ['IFUNCV', 'rbase', ___, ___, ___], + ['JFUNCV', 'rbase', ___, 'lit', ___], + ['FUNCC', 'rbase', ___, ___, ___], + ['FUNCCW', 'rbase', ___, ___, ___], +] + +def proto_bc(proto): + return cast('BCIns *', cast('char *', proto) + gdb.lookup_type('GCproto').sizeof) + +def proto_kgc(pt, idx): + return gcref(mref('GCRef *', pt['k'])[idx]) + +def proto_knumtv(pt, idx): + return mref('TValue *', pt['k'])[idx] def frame_ftsz(framelink): return cast('ptrdiff_t', framelink['ftsz'] if LJ_FR2 \ @@ -561,6 +715,132 @@ def dump_gc(g): return '\n'.join(map(lambda s: '\t' + s, stats)) +def proto_loc(proto): + return '{chunk}:{firstline}'.format( + chunk = strdata(cast('GCstr *', gcval(proto['chunkname']))), + firstline = proto['firstline'], + ) + +def funck(pt, idx): + if idx >= 0: + assert idx < pt['sizekn'], 'invalid idx for numeric constant in proto' + tv = proto_knumtv(pt, idx) + return dump_tvalue(tv) + else: + assert ~idx < pt['sizekgc'], 'invalid idx for GC constant in proto' + gcobj = proto_kgc(pt, idx) + if typenames(i2notu32(gcobj['gch']['gct'])) == 'LJ_TPROTO': + return proto_loc(cast('GCproto *', gcobj)) + return dump_gcobj(gcobj) + +def funcuvname(pt, idx): + assert idx < pt['sizeuv'], 'invalid idx for upvalue in proto' + uvinfo = mref('uint8_t *', pt['uvinfo']) + if not uvinfo: + return '' + + # if (idx) while (*uvinfo++ || --idx); + while idx > 0: + while uvinfo[0]: + uvinfo += 1 + uvinfo += 1 + idx -= 1 + + return str(cast('char *', uvinfo)) + +def dump_reg(bc, reg, value, jmp_format=None, jmp_ctx=None): + rtype = bc[reg] + is_jmp = rtype == 'jump' + padding = ':' + ' ' * (5 - len(rtype)) + + if rtype == 'jump': + # Destination of jump instruction encoded as offset from BCBIAS_J. + delta = value - 0x7fff + if jmp_format: + value = jmp_format(jmp_ctx, delta) + else: + prefix = '+' if delta >= 0 else '' + value = prefix + str(delta) + else: + value = '{:3d}'.format(value) + + return '{rtype}{padding} {value}'.format( + rtype = rtype, + padding = padding, + value = value, + ) + +def dump_kc(bc, reg, value, proto): + rtype = bc[reg] + kc = '' + if proto: + if rtype == 'str' or rtype == 'func': + kc = funck(proto, ~value) + elif rtype == 'num': + kc = funck(proto, value) + elif rtype == 'uv': + kc = funcuvname(proto, value) + + if kc != '': + kc = ' ; ' + kc + return kc + +def dump_bc(ins, jmp_format=None, jmp_ctx=None, proto=None): + op = bc_op(ins) + if op >= len(BYTECODES): + return 'INVALID' + + bc = BYTECODES[op] + bc_name = bc[BC_NAME] + name_padding = ' ' * (6 - len(bc_name)) + + bc_hasa = bc[BC_A] + bc_hasb = bc[BC_B] + + kca = dump_kc(bc, BC_A, bc_a(ins), proto) if bc_hasa else '' + kcc = dump_kc(bc, BC_CD, bc_c(ins) if bc_hasb else bc_d(ins), proto) if bc[BC_CD] else '' + + return '{name}{npad} {ra}{rb}{rcd}{kc}'.format( + name = bc_name, + npad = name_padding, + ra = dump_reg(bc, BC_A, bc_a(ins)) + ' ' if bc_hasa else '', + rb = dump_reg(bc, BC_B, bc_b(ins)) + ' ' if bc_hasb else '', + rcd = dump_reg( + bc, BC_CD, bc_c(ins) if bc_hasb else bc_d(ins), + jmp_format=jmp_format, jmp_ctx=jmp_ctx + ) if bc[BC_CD] else '', + kc=kca+kcc + ) + +def dump_proto(proto): + startbc = proto_bc(proto) + func_loc = proto_loc(proto) + # Location has the following format: '{chunk}:{firstline}'. + dump = '{func_loc}-{lastline}\n'.format( + func_loc = func_loc, + lastline = proto['firstline'] + proto['numline'], + ) + + def jmp_format(npc_from, delta): + return '=> ' + str(npc_from + delta).zfill(4) + + for bcnum in range(0, proto['sizebc']): + dump += (str(bcnum).zfill(4) + ' ' + dump_bc( + startbc[bcnum], jmp_format=jmp_format, jmp_ctx=bcnum, + proto = proto, + ) + '\n') + return dump + +def dump_func(func): + ffid = func['ffid'] + + if ffid == 0: + pt = funcproto(func) + return dump_proto(pt) + elif ffid == 1: + return 'C function @ {}'.format(strx64(func['f'])) + else: + return 'fast function #{}'.format(int(ffid)) class LJBase(gdb.Command): @@ -767,6 +1047,45 @@ The command requires no args and dumps current GC stats: stats = dump_gc(g) )) +class LJDumpBC(LJBase): + ''' +lj-bc + +The command receives a pointer to bytecode instruction and dumps +type of an instruction, the values of RA, RB and RC (or RD) registers. + ''' + + def invoke(self, arg, from_tty): + gdb.write('{}\n'.format(dump_bc(cast("BCIns *", parse_arg(arg))[0]))) + +class LJDumpProto(LJBase): + ''' +lj-proto + +The command receives a of the corresponding GCproto object and dumps +the chunk name, where the corresponding function is defined, corresponding +range of lines and a list of bytecodes related to this function. + +The constants or upvalues of the prototype are decoded after ';'. + ''' + + def invoke(self, arg, from_tty): + gdb.write('{}'.format(dump_proto(cast("GCproto *", parse_arg(arg))))) + +class LJDumpFunc(LJBase): + ''' +lj-funcl + +The command receives a of the corresponding GCfunc object and dumps +the chunk name, where the corresponding function is defined, corresponding +range of lines and a list of bytecodes related to this function. + +The constants or upvalues of the function are decoded after ';'. + ''' + + def invoke(self, arg, from_tty): + gdb.write('{}'.format(dump_func(cast("GCfuncC *", parse_arg(arg))))) + def init(commands): global LJ_64, LJ_GC64, LJ_FR2, LJ_DUALNUM, LJ_TISNUM, PADDING @@ -832,6 +1151,9 @@ def load(event=None): 'lj-stack': LJDumpStack, 'lj-state': LJState, 'lj-gc': LJGC, + 'lj-bc': LJDumpBC, + 'lj-proto': LJDumpProto, + 'lj-func': LJDumpFunc, }) load(None) -- 2.34.1