From 1e213ff3c02609862e517d55ebb58c40823a2e75 Mon Sep 17 00:00:00 2001 From: Luke Gorrie Date: Sat, 15 Oct 2016 23:14:14 +0200 Subject: [PATCH] Added LUAJIT_TRACEPROFILE Trace profiling takes regular samples of the program counter and uses them to maintain three new counters for each trace: nonloop: Samples taken when running the non-loop part of the trace loop: Samples taken when running the loop part of the trace other: Samples taken outside the trace mcode (e.g. FFI calls) Trace profiling can be enabled and disabled with: jit.traceprofile.start(interval) jit.traceprofile.stop() The current counter values for a trace can be queried via an extension to the JIT introspection API: jit.util.tracestats(tr) => nonloop, loop, other The overall intention of these counters is to enable development of trace-oriented profiling tools, including self-profiling programs that can identify suspicious traces to flush (e.g. traces that are biased away from the root trace). --- src/Makefile | 2 +- src/lib_jit.c | 48 +++++++++++++++++++ src/lj_jit.h | 6 +++ src/lj_traceprofile.c | 104 ++++++++++++++++++++++++++++++++++++++++++ src/lj_traceprofile.h | 15 ++++++ src/luajit.h | 5 ++ 6 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/lj_traceprofile.c create mode 100644 src/lj_traceprofile.h diff --git a/src/Makefile b/src/Makefile index 4e479ae5..5870ad24 100644 --- a/src/Makefile +++ b/src/Makefile @@ -478,7 +478,7 @@ LJLIB_C= $(LJLIB_O:.o=.c) LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ - lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o \ + lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o lj_traceprofile.o \ lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ diff --git a/src/lib_jit.c b/src/lib_jit.c index 592538bd..045ce11e 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c @@ -299,6 +299,9 @@ LJLIB_CF(jit_util_traceinfo) setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk); setintfield(L, t, "link", T->link); setintfield(L, t, "nexit", T->nsnap); + setintfield(L, t, "szmcode", T->szmcode); + setintfield(L, t, "mcode", (int32_t)(intptr_t)T->mcode); + setintfield(L, t, "mcloop", T->mcloop); setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype])); lua_setfield(L, -2, "linktype"); /* There are many more fields. Add them only when needed. */ @@ -629,6 +632,47 @@ static int luaopen_jit_profile(lua_State *L) #endif +/* -- Trace profiling ----------------------------------------------------- */ + +#ifdef LUAJIT_TRACEPROFILE + +#define LJLIB_MODULE_jit_traceprofile + +LJLIB_CF(jit_traceprofile_tracestats) +{ + GCtrace *T = jit_checktrace(L); + if (T) { + setint64V(L->top-1, T->prof.nonloop); + setint64V(L->top++, T->prof.loop); + setint64V(L->top++, T->prof.other); + return 3; + } + return 0; +} + +LJLIB_CF(jit_traceprofile_start) +{ + int interval = lj_lib_checkint(L, 1); + luaJIT_traceprofile_start(L, interval); + return 0; +} + +LJLIB_CF(jit_traceprofile_stop) +{ + luaJIT_traceprofile_stop(L); + return 0; +} + +#include "lj_libdef.h" + +static int luaopen_jit_traceprofile(lua_State *L) +{ + LJ_LIB_REG(L, NULL, jit_traceprofile); + return 1; +} + +#endif + /* -- JIT compiler initialization ----------------------------------------- */ #if LJ_HASJIT @@ -764,6 +808,10 @@ LUALIB_API int luaopen_jit(lua_State *L) lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile, tabref(L->env)); #endif +#ifdef LUAJIT_TRACEPROFILE + lj_lib_prereg(L, LUA_JITLIBNAME ".traceprofile", luaopen_jit_traceprofile, + tabref(L->env)); +#endif #ifndef LUAJIT_DISABLE_JITUTIL lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env)); #endif diff --git a/src/lj_jit.h b/src/lj_jit.h index 3505c63f..836a58b2 100644 --- a/src/lj_jit.h +++ b/src/lj_jit.h @@ -8,6 +8,9 @@ #include "lj_obj.h" #include "lj_ir.h" +#ifdef LUAJIT_TRACEPROFILE +#include "lj_traceprofile.h" +#endif /* JIT engine flags. */ #define JIT_F_ON 0x00000001 @@ -255,6 +258,9 @@ typedef struct GCtrace { TraceNo1 nextside; /* Next side trace of same root trace. */ uint8_t sinktags; /* Trace has SINK tags. */ uint8_t unused1; +#ifdef LUAJIT_TRACEPROFILE + TraceProfile prof; /* Samples of where the trace spends execution time */ +#endif #ifdef LUAJIT_USE_GDBJIT void *gdbjit_entry; /* GDB JIT entry. */ #endif diff --git a/src/lj_traceprofile.c b/src/lj_traceprofile.c new file mode 100644 index 00000000..757f68ac --- /dev/null +++ b/src/lj_traceprofile.c @@ -0,0 +1,104 @@ +/* +** Trace profiling. +** Copyright (C) 2016 Luke Gorrie. See Copyright Notice in luajit.h +*/ + +#define lj_traceprofile_c +#define LUA_CORE + +#ifdef LUAJIT_TRACEPROFILE + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#undef _GNU_SOURCE + +#include "lj_obj.h" +#include "lj_dispatch.h" +#include "lj_jit.h" +#include "lj_trace.h" +#include "lj_traceprofile.h" + +/* vmstate extended to represent running in a trace (any one) */ +enum { + LJ_TRACEPROF_VMST_TRACE = LJ_VMST__MAX, + LJ_TRACEPROF_VMST__MAX +}; + +typedef struct TraceProfileState { + global_State *g; /* VM state that started the profiler. */ + struct sigaction oldsa; + int events; /* Number of events (signals) handled. */ + uint64_t vmstate[LJ_TRACEPROF_VMST__MAX]; /* Counter per VM state */ +} TraceProfileState; + +static TraceProfileState state; + +static void traceprofile_signal(int sig, siginfo_t *si, void *data) +{ + global_State *g = state.g; + intptr_t ip = (intptr_t)((ucontext_t*)data)->uc_mcontext.gregs[REG_RIP]; + int st = g->vmstate; + state.events++; + assert((st >= 0) || (~st < LJ_VMST__MAX)); + if (st >= 0) { + lua_State *L = gco2th(gcref(g->cur_L)); + TraceNo tr = (TraceNo)st; + jit_State *J = L2J(L); + GCtrace *T = traceref(J, tr); + ptrdiff_t rel_ip = ip - (intptr_t)T->mcode; + if ((rel_ip >= 0) && (rel_ip < T->szmcode)) { + if (rel_ip < T->mcloop) { + T->prof.nonloop++; /* Sample is in non-loop mcode. */ + } else { + T->prof.loop++; /* Sample is in loop mcode. */ + } + } else { + T->prof.other++; /* Sample is outside the trace mcode. */ + } + state.vmstate[LJ_TRACEPROF_VMST_TRACE]++; + } else { + state.vmstate[~st]++; + } +} + +static void traceprofile_start_timer(int interval) +{ + struct itimerval tm; + struct sigaction sa; + tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000; + tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000; + setitimer(ITIMER_PROF, &tm, NULL); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sa.sa_sigaction = traceprofile_signal; + sigemptyset(&sa.sa_mask); + sigaction(SIGPROF, &sa, &state.oldsa); +} + +static void traceprofile_stop_timer() +{ + struct itimerval tm; + tm.it_value.tv_sec = tm.it_interval.tv_sec = 0; + tm.it_value.tv_usec = tm.it_interval.tv_usec = 0; + setitimer(ITIMER_PROF, &tm, NULL); + sigaction(SIGPROF, NULL, &state.oldsa); +} + +LUA_API void luaJIT_traceprofile_start(lua_State *L, int interval) +{ + memset(&state, sizeof(state), 0); + state.g = G(L); + traceprofile_start_timer(interval); +} + +LUA_API void luaJIT_traceprofile_stop(lua_State *L) +{ + traceprofile_stop_timer(); +} + +#endif + diff --git a/src/lj_traceprofile.h b/src/lj_traceprofile.h new file mode 100644 index 00000000..152f7e32 --- /dev/null +++ b/src/lj_traceprofile.h @@ -0,0 +1,15 @@ +/* +** Trace profiling. +** Copyright (C) 2016 Luke Gorrie. See Copyright Notice in luajit.h +*/ + +#ifndef _LJ_TRACEPROFILE_H +#define _LJ_TRACEPROFILE_H + +typedef struct TraceProfile { + uint64_t nonloop; /* Samples taken in non-loop mcode */ + uint64_t loop; /* Samples taken in loop mcode */ + uint64_t other; /* Samples taken outside trace mcode */ +} TraceProfile; + +#endif diff --git a/src/luajit.h b/src/luajit.h index 1d0a558c..a1ba4b24 100644 --- a/src/luajit.h +++ b/src/luajit.h @@ -73,6 +73,11 @@ LUA_API void luaJIT_profile_stop(lua_State *L); LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, int depth, size_t *len); +/* Trace profiling API. */ +LUA_API void luaJIT_traceprofile_start(lua_State *L, int interval); +LUA_API void luaJIT_traceprofile_stop(lua_State *L); + + /* Enforce (dynamic) linker error for version mismatches. Call from main. */ LUA_API void LUAJIT_VERSION_SYM(void);