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).
This commit is contained in:
Luke Gorrie 2016-10-15 23:14:14 +02:00
parent 3f43f09413
commit 1e213ff3c0
6 changed files with 179 additions and 1 deletions

View File

@ -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 \ 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_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_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_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_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 \ lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \

View File

@ -299,6 +299,9 @@ LJLIB_CF(jit_util_traceinfo)
setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk); setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk);
setintfield(L, t, "link", T->link); setintfield(L, t, "link", T->link);
setintfield(L, t, "nexit", T->nsnap); 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])); setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype]));
lua_setfield(L, -2, "linktype"); lua_setfield(L, -2, "linktype");
/* There are many more fields. Add them only when needed. */ /* There are many more fields. Add them only when needed. */
@ -629,6 +632,47 @@ static int luaopen_jit_profile(lua_State *L)
#endif #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 ----------------------------------------- */ /* -- JIT compiler initialization ----------------------------------------- */
#if LJ_HASJIT #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, lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile,
tabref(L->env)); tabref(L->env));
#endif #endif
#ifdef LUAJIT_TRACEPROFILE
lj_lib_prereg(L, LUA_JITLIBNAME ".traceprofile", luaopen_jit_traceprofile,
tabref(L->env));
#endif
#ifndef LUAJIT_DISABLE_JITUTIL #ifndef LUAJIT_DISABLE_JITUTIL
lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env)); lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env));
#endif #endif

View File

@ -8,6 +8,9 @@
#include "lj_obj.h" #include "lj_obj.h"
#include "lj_ir.h" #include "lj_ir.h"
#ifdef LUAJIT_TRACEPROFILE
#include "lj_traceprofile.h"
#endif
/* JIT engine flags. */ /* JIT engine flags. */
#define JIT_F_ON 0x00000001 #define JIT_F_ON 0x00000001
@ -255,6 +258,9 @@ typedef struct GCtrace {
TraceNo1 nextside; /* Next side trace of same root trace. */ TraceNo1 nextside; /* Next side trace of same root trace. */
uint8_t sinktags; /* Trace has SINK tags. */ uint8_t sinktags; /* Trace has SINK tags. */
uint8_t unused1; uint8_t unused1;
#ifdef LUAJIT_TRACEPROFILE
TraceProfile prof; /* Samples of where the trace spends execution time */
#endif
#ifdef LUAJIT_USE_GDBJIT #ifdef LUAJIT_USE_GDBJIT
void *gdbjit_entry; /* GDB JIT entry. */ void *gdbjit_entry; /* GDB JIT entry. */
#endif #endif

104
src/lj_traceprofile.c Normal file
View File

@ -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 <stdio.h>
#include <assert.h>
#include <sys/time.h>
#include <stdint.h>
#include <signal.h>
#include <ucontext.h>
#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

15
src/lj_traceprofile.h Normal file
View File

@ -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

View File

@ -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, LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
int depth, size_t *len); 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. */ /* Enforce (dynamic) linker error for version mismatches. Call from main. */
LUA_API void LUAJIT_VERSION_SYM(void); LUA_API void LUAJIT_VERSION_SYM(void);