Skip to content

Commit

Permalink
Merge pull request snabbco#66 from lukego/vmprofile
Browse files Browse the repository at this point in the history
Port VMProfile sampling profiler from Snabb
  • Loading branch information
lukego authored Jul 9, 2017
2 parents f8bd4d8 + 65675f5 commit 8ca5612
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,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_vmmath.o lj_strscan.o \
lj_strfmt.o lj_strfmt_num.o lj_api.o \
lj_strfmt.o lj_strfmt_num.o lj_api.o lj_vmprofile.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 \
Expand Down
25 changes: 25 additions & 0 deletions src/lib_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,29 @@ LJLIB_CF(jit_opt_start)

#include "lj_libdef.h"

/* -- jit.vmprofile module ----------------------------------------------- */

#define LJLIB_MODULE_jit_vmprofile

LJLIB_CF(jit_vmprofile_start)
{
luaJIT_vmprofile_start(L);
return 0;
}

LJLIB_CF(jit_vmprofile_stop)
{
luaJIT_vmprofile_stop(L);
return 0;
}

#include "lj_libdef.h"

static int luaopen_jit_vmprofile(lua_State *L)
{
LJ_LIB_REG(L, NULL, jit_vmprofile);
return 1;
}

/* -- JIT compiler initialization ----------------------------------------- */

Expand Down Expand Up @@ -259,6 +282,8 @@ LUALIB_API int luaopen_jit(lua_State *L)
lua_pushinteger(L, LUAJIT_VERSION_NUM);
lua_pushliteral(L, LUAJIT_VERSION);
LJ_LIB_REG(L, LUA_JITLIBNAME, jit);
lj_lib_prereg(L, LUA_JITLIBNAME ".vmprofile", luaopen_jit_vmprofile,
tabref(L->env));
LJ_LIB_REG(L, "jit.opt", jit_opt);
L->top -= 2;
return 1;
Expand Down
2 changes: 2 additions & 0 deletions src/lj_gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ int lj_gc_step(lua_State *L)
global_State *g = G(L);
GCSize lim;
int32_t ostate = g->vmstate;
g->gcvmstate = ostate;
setvmstate(g, GC);
lim = (GCSTEPSIZE/100) * g->gc.stepmul;
if (lim == 0)
Expand Down Expand Up @@ -698,6 +699,7 @@ void lj_gc_fullgc(lua_State *L)
{
global_State *g = G(L);
int32_t ostate = g->vmstate;
g->gcvmstate = ostate;
setvmstate(g, GC);
if (g->gc.state <= GCSatomic) { /* Caught somewhere in the middle. */
setmref(g->gc.sweep, &g->gc.root); /* Sweep everything (preserving it). */
Expand Down
3 changes: 2 additions & 1 deletion src/lj_obj.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,8 @@ typedef struct global_State {
lua_Alloc allocf; /* Memory allocator. */
void *allocd; /* Memory allocator data. */
GCState gc; /* Garbage collector. */
volatile int32_t vmstate; /* VM state or current JIT code trace number. */
volatile int32_t vmstate; /* VM state or current JIT code trace number. */
volatile int32_t gcvmstate; /* Previous VM state (only when state is GC). */
SBuf tmpbuf; /* Temporary string buffer. */
GCstr strempty; /* Empty string. */
uint8_t stremptyz; /* Zero terminator of empty string. */
Expand Down
113 changes: 113 additions & 0 deletions src/lj_vmprofile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
** VM profiling.
** Copyright (C) 2016 Luke Gorrie. See Copyright Notice in luajit.h
*/

#define lj_vmprofile_c
#define LUA_CORE

#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_vmprofile.h"

static struct {
global_State *g;
struct sigaction oldsa;
} state;

/* -- State that the application can manage via FFI ----------------------- */

static VMProfile *profile; /* Current counters */

/* How much memory to allocate for profiler counters. */
int vmprofile_get_profile_size() {
return sizeof(VMProfile);
}

/* Set the memory where the next samples will be counted.
Size of the memory must match vmprofile_get_profile_size(). */
void vmprofile_set_profile(void *counters) {
profile = (VMProfile*)counters;
profile->magic = 0x1d50f007;
profile->major = 2;
profile->minor = 0;
}

/* -- Signal handler ------------------------------------------------------ */

/* Signal handler: bumps one counter. */
static void vmprofile_signal(int sig, siginfo_t *si, void *data)
{
if (profile != NULL) {
lua_State *L = gco2th(gcref(state.g->cur_L));
int vmstate = state.g->vmstate;
int trace = ~vmstate == LJ_VMST_GC ? state.g->gcvmstate : vmstate;
/* Not in a trace */
if (trace < 0) {
profile->vm[~vmstate]++;
} else {
int bucket = trace > LJ_VMPROFILE_TRACE_MAX ? 0 : trace;
VMProfileTraceCount *count = &profile->trace[bucket];
GCtrace *T = traceref(L2J(L), (TraceNo)trace);
intptr_t ip = (intptr_t)((ucontext_t*)data)->uc_mcontext.gregs[REG_RIP];
ptrdiff_t mcposition = ip - (intptr_t)T->mcode;
if (~vmstate == LJ_VMST_GC) {
count->gc++;
} else if ((mcposition < 0) || (mcposition >= T->szmcode)) {
count->other++;
} else if ((T->mcloop != 0) && (mcposition >= T->mcloop)) {
count->loop++;
} else {
count->head++;
}
}
}
}

static void 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 = vmprofile_signal;
sigemptyset(&sa.sa_mask);
sigaction(SIGPROF, &sa, &state.oldsa);
}

static void 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 ------------------------------------------------------------- */

LUA_API void luaJIT_vmprofile_start(lua_State *L)
{
memset(&state, 0, sizeof(state));
state.g = G(L);
start_timer(1); /* Sample every 1ms */
}

LUA_API void luaJIT_vmprofile_stop(lua_State *L)
{
stop_timer();
}

38 changes: 38 additions & 0 deletions src/lj_vmprofile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
** Virtual machine profiling.
** Copyright (C) 2017 Luke Gorrie. See Copyright Notice in luajit.h
*/

#ifndef _LJ_VMPROFILE_H
#define _LJ_VMPROFILE_H


/* Counters are 64-bit to avoid overflow even in long running processes. */
typedef uint64_t VMProfileCount;

/* Maximum trace number for distinct counter buckets. Traces with
higher numbers will be counted together in bucket zero. */
#define LJ_VMPROFILE_TRACE_MAX 4096

/* Traces have separate counters for different machine code regions. */
typedef struct VMProfileTraceCount {
VMProfileCount head; /* Head of the trace (non-looping part) */
VMProfileCount loop; /* Loop of the trace */
VMProfileCount other; /* Outside the trace mcode (unidentified) */
VMProfileCount gc; /* Garbage collection from this trace. */
} VMProfileTraceCount;

/* Complete set of counters for VM and traces. */
typedef struct VMProfile {
uint32_t magic; /* 0x1d50f007 */
uint16_t major, minor; /* 2, 0 */
VMProfileCount vm[LJ_VMST__MAX];
VMProfileTraceCount trace[LJ_VMPROFILE_TRACE_MAX+1];
} VMProfile;

/* Functions that should be accessed via FFI. */

void vmprofile_set_profile(void *counters);
int vmprofile_get_profile_size();

#endif
4 changes: 4 additions & 0 deletions src/luajit.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ enum {
/* Control the JIT engine. */
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode);

/* VM profiling API. */
LUA_API void luaJIT_vmprofile_start(lua_State *L);
LUA_API void luaJIT_vmprofile_stop(lua_State *L);

/* Enforce (dynamic) linker error for version mismatches. Call from main. */
LUA_API void LUAJIT_VERSION_SYM(void);

Expand Down

0 comments on commit 8ca5612

Please sign in to comment.