diff --git a/src/lj_debug.c b/src/lj_debug.c index bb77ad7..8061e1a 100644 --- a/src/lj_debug.c +++ b/src/lj_debug.c @@ -18,6 +18,8 @@ #if LJ_HASJIT #include "lj_jit.h" #endif +#include + /* -- Frames -------------------------------------------------------------- */ @@ -697,3 +699,145 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, lua_concat(L, (int)(L->top - L->base) - top); } + +#define LOG_MSG_LEN 256 +#define RING_BUFFER_SIZE 200 + +#define FRAME_BUFSIZE 128 +#define FRAME_BUFFERS 128 + +static char frame_bufs[FRAME_BUFFERS][FRAME_BUFSIZE]; +static int frame_buf_idx = 0; + +static BCLine debug_frameline2(lua_State *L, GCfunc *fn, cTValue *nextframe, + const BCIns *ins) +{ + BCPos pc; + GCproto *pt; + + if (ins) { + pt = funcproto(fn); + pc = proto_bcpos(pt, ins) - 1; +#if LJ_HASJIT + if (pc > pt->sizebc) { /* Undo the effects of lj_trace_exit for JLOOP. */ + GCtrace *T = (GCtrace *)((char *)(ins-1) - offsetof(GCtrace, startins)); + if (!bc_isret(bc_op(ins[-1]))) { + lj_rb_log("Assertion failed: bc_isret(bc_op(ins[-1])) " + "pt=%p pc=%p", pt, ins); + return -1; + } + pc = proto_bcpos(pt, mref(T->startpc, const BCIns)); + } +#endif + + } else { + pc = debug_framepc(L, fn, nextframe); + } + + if (pc != NO_BCPOS) { + GCproto *pt = funcproto(fn); + if (!(pc <= pt->sizebc)) { + lj_rb_log("Assertion failed: pc <= pt->sizebc pt=%p pc=%p", + pt, ins); + return -1; + } + return lj_debug_line(pt, pc); + } + return -1; +} + + +char * +lj_dump_frame(lua_State *L, int level, const BCIns *pc) +{ + char *frame_buf; + int size; + cTValue *frame = lj_debug_frame(L, level, &size); + if (frame == NULL) { + return NULL; + } + + frame_buf = frame_bufs[frame_buf_idx]; + if (++frame_buf_idx == FRAME_BUFFERS) { + frame_buf_idx = 0; + } + + cTValue *nextframe = size ? frame+size : NULL; + GCfunc *fn = frame_func(frame); + + if (isluafunc(fn)) { + /* Regular Lua function. */ + + GCproto *pt = funcproto(fn); + GCstr *name = proto_chunkname(pt); + const char *p = strdata(name); + MSize len; + + if (*p == '=' || *p == '@') { + len = name->len-1; + p++; + if (1) { + int i; + for (i = len-1; i >= 0; i--) + if (p[i] == '/' || p[i] == '\\') { + len -= i+1; + p = p+i+1; + break; + } + } + } else { + p = "[string]"; + len = 8; + } + + BCLine line = debug_frameline2(L, fn, nextframe, pc); + if (line < 0) { + line = pt->firstline; + } + + snprintf(frame_buf, FRAME_BUFSIZE, "%.*s:%d", (int) len, p, line); + + } else if (isffunc(fn)) { /* Dump numbered builtins. */ + snprintf(frame_buf, FRAME_BUFSIZE, "[builtin#%d]", (int) fn->c.ffid); + } else { /* Dump C function address. */ + snprintf(frame_buf, FRAME_BUFSIZE, "@%p", fn->c.f); + } + + return frame_buf; +} + +static char ringbuffer[RING_BUFFER_SIZE][LOG_MSG_LEN]; +static int rb_start = 0; +static int rb_end = 0; +static int rb_full = 0; + +void +lj_rb_log(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(ringbuffer[rb_end], LOG_MSG_LEN, fmt, args); + va_end(args); + + if (rb_full) { + rb_end++; + if (rb_end == RING_BUFFER_SIZE) { + rb_end = 0; + } + rb_start = rb_end; + + } else { + rb_end++; + if (rb_end == RING_BUFFER_SIZE) { + rb_end = 0; + rb_full = RING_BUFFER_SIZE; + } + } +} + +void +lj_rb_log_str(const char *msg) +{ + lj_rb_log("%s", msg); +} diff --git a/src/lj_debug.h b/src/lj_debug.h index f6e5217..3c32b12 100644 --- a/src/lj_debug.h +++ b/src/lj_debug.h @@ -62,4 +62,7 @@ enum { VARNAME__MAX }; +void lj_rb_log(const char *fmt, ...); +char *lj_dump_frame(lua_State *L, int level, const BCIns *ins); + #endif diff --git a/src/lj_trace.c b/src/lj_trace.c index 7bb6c8a..ceafa25 100644 --- a/src/lj_trace.c +++ b/src/lj_trace.c @@ -358,6 +358,7 @@ static void trace_start(jit_State *J) { lua_State *L; TraceNo traceno; + const BCIns *pc = J->pc; if ((J->pt->flags & PROTO_NOJIT)) { /* JIT disabled for this proto? */ if (J->parent == 0 && J->exitno == 0) { @@ -408,6 +409,8 @@ static void trace_start(jit_State *J) } ); lj_record_setup(J); + + lj_rb_log("trace_start at %p for #%u", pc, (unsigned) J->cur.traceno); } /* Stop tracing. */ @@ -762,6 +765,45 @@ static TraceNo trace_exit_find(jit_State *J, MCode *pc) } #endif +void LJ_FASTCALL lj_trace_entry_hook(lua_State *L, unsigned traceno, const BCIns *pc) +{ + char *p0 = NULL; + char *p1; + /* lj_rb_log("funcisL: %d", curr_funcisL(L) ? 1 : 0); */ + p0 = lj_dump_frame(L, 0, pc); + p1 = lj_dump_frame(L, 1, NULL); + lj_rb_log("->%u L=%p %s < %s", traceno, L, p0 ? p0 : "(nil)", + p1 ? p1 : "(nil)"); +} + +void LJ_FASTCALL lj_trace_exit_hook(lua_State *L, int vmstate, + const BCIns *pc) +{ + if (vmstate >= 0) { + jit_State *J = L2J(L); + char *p0 = NULL, *p1; + /* lj_rb_log("funcisL: %d", curr_funcisL(L) ? 1 : 0); */ + p0 = lj_dump_frame(L, 0, pc); + p1 = lj_dump_frame(L, 1, NULL); + lj_rb_log("<-%d L=%p %s < %s (exitno=%u)", vmstate, L, p0 ? p0 : "(nil)", + p1 ? p1 : "(nil)", (unsigned) J->exitno); + } +} + +void LJ_FASTCALL lj_trace_direct_exit_hook(lua_State *L, int vmstate, + const BCIns *pc) +{ + if (vmstate >= 0) { + jit_State *J = L2J(L); + char *p0 = NULL, *p1; + /* lj_rb_log("funcisL: %d", curr_funcisL(L) ? 1 : 0); */ + p0 = lj_dump_frame(L, 0, pc); + p1 = lj_dump_frame(L, 1, NULL); + lj_rb_log("<-%d L=%p %s < %s (direct exit, exitno=%u)", vmstate, L, p0 ? p0 : "(nil)", + p1 ? p1 : "(nil)", (unsigned) J->exitno); + } +} + /* A trace exited. Restore interpreter state. */ int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr) { @@ -825,6 +867,9 @@ int LJ_FASTCALL lj_trace_exit(jit_State *J, void *exptr) } } } + + lj_trace_exit_hook(L, (int) T->traceno, pc); + /* Return MULTRES or 0. */ ERRNO_RESTORE switch (bc_op(*pc)) { diff --git a/src/lj_trace.h b/src/lj_trace.h index 74c5431..56099b8 100644 --- a/src/lj_trace.h +++ b/src/lj_trace.h @@ -22,6 +22,13 @@ typedef enum { LJ_FUNC_NORET void lj_trace_err(jit_State *J, TraceError e); LJ_FUNC_NORET void lj_trace_err_info(jit_State *J, TraceError e); +LJ_FUNC void LJ_FASTCALL lj_trace_entry_hook(lua_State *L, unsigned traceno, + const BCIns *pc); +LJ_FUNC void LJ_FASTCALL lj_trace_exit_hook(lua_State *L, int traceno, + const BCIns *pc); +LJ_FUNC void LJ_FASTCALL lj_trace_direct_exit_hook(lua_State *L, int traceno, + const BCIns *pc); + /* Trace management. */ LJ_FUNC void LJ_FASTCALL lj_trace_free(global_State *g, GCtrace *T); LJ_FUNC void lj_trace_reenableproto(GCproto *pt); diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc index bbe341f..aa82acf 100644 --- a/src/vm_x86.dasc +++ b/src/vm_x86.dasc @@ -2846,6 +2846,19 @@ static void build_subroutines(BuildCtx *ctx) | mov r13, TMPa | mov r12, TMPQ |.endif + | + | mov FCARG1, SAVE_L + | mov L:FCARG1->base, BASE + | mov RB, RD // Save RD + | mov TMP1, PC // Save PC + | mov CARG3d, PC // CARG3d == BASE + | mov FCARG2, dword [DISPATCH+DISPATCH_GL(vmstate)] + | call extern lj_trace_direct_exit_hook@8 + | mov PC, TMP1 + | mov RD, RB + | mov RB, SAVE_L + | mov BASE, L:RB->base + | | test RD, RD; js >9 // Check for error from exit. | mov L:RB, SAVE_L | mov MULTRES, RD @@ -5474,6 +5487,17 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) case BC_JLOOP: |.if JIT | ins_AD // RA = base (ignored), RD = traceno + | + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Save BASE + | mov TMP1, RD // Save RD + | mov CARG3d, PC // CARG3d == BASE + | mov FCARG2, RD + | mov FCARG1, RB + | call extern lj_trace_entry_hook@8 + | mov RD, TMP1 + | mov BASE, L:RB->base + | | mov RA, [DISPATCH+DISPATCH_J(trace)] | mov TRACE:RD, [RA+RD*4] | mov RDa, TRACE:RD->mcode