On Sat, Jun 10, 2017 at 11:17:29PM +0300, Victor Krapivensky wrote: > * Makefile.am: Build with LuaJIT if configured so. > (strace_SOURCES): Add defs_reuse.h. > * configure.ac: Add new --with-luajit configure option. > * defs.h: Move code that needs to be fed to LuaJIT's FFI to... > * defs_reuse.h: ...new file. > * strace.c: Initial support for Lua scripting. > (init): New -l option (if built with Lua scripting support). > (luajit_next_sc, check_lua, luajit_init): New functions. > (main): run Lua script, if built with Lua scripting support and a script > was provided. > --- > Makefile.am | 6 +++ > configure.ac | 34 +++++++++++++ > defs.h | 34 +------------ > defs_reuse.h | 58 ++++++++++++++++++++++ > strace.c | 160 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 259 insertions(+), 33 deletions(-) > create mode 100644 defs_reuse.h > > diff --git a/Makefile.am b/Makefile.am > index 3d6eba1e..a7c4bce6 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -100,6 +100,7 @@ strace_SOURCES = \ > copy_file_range.c \ > count.c \ > defs.h \ > + defs_reuse.h \ > desc.c \ > dirent.c \ > dirent64.c \ > @@ -276,6 +277,11 @@ strace_LDFLAGS += $(libunwind_LDFLAGS) > strace_LDADD += $(libunwind_LIBS) > endif > > +if USE_LUAJIT > +strace_CPPFLAGS += $(luajit_CFLAGS) > +strace_LDADD += $(luajit_LIBS) > +endif > + > @CODE_COVERAGE_RULES@ > CODE_COVERAGE_BRANCH_COVERAGE = 1 > CODE_COVERAGE_GENHTML_OPTIONS = $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \ > diff --git a/configure.ac b/configure.ac > index dc49d397..b853c294 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -715,6 +715,40 @@ AC_SUBST(dl_LIBS) > > AC_PATH_PROG([PERL], [perl]) > > +dnl LuaJIT scripting support > +use_luajit=no > +force_luajit=no > +luajit_lib=luajit > +luajit_LIBS= > +luajit_CFLAGS= > +AC_ARG_WITH([luajit], > + [AS_HELP_STRING([--with-luajit], > + [build with LuaJIT scripting support])], > + [case "${withval}" in > + yes) force_luajit=yes ;; > + check) ;; > + *) luajit_lib="${withval}" ;; > + esac], > + [:] > +) > +AS_IF([test "x$luajit_lib" != xno], > + [AS_IF([test "x$force_luajit" = xyes], > + [PKG_CHECK_MODULES([luajit], [$luajit_lib], [use_luajit=yes])], > + [PKG_CHECK_MODULES([luajit], [$luajit_lib], [use_luajit=yes], > [:])] I'd rather move $force_lua check inside the failure branch of PKG_CHECK_MODULES and use "LUAJIT" as a prefix.
> + ) > + ] > +) > + > +dnl enable LuaJIT > +AC_MSG_CHECKING([whether to enable Lua scripting]) > +if test "x$use_luajit" = xyes; then > + AC_DEFINE([USE_LUAJIT], 1, [Enable Lua scripting support]) > + AC_SUBST(luajit_LIBS) > + AC_SUBST(luajit_CFLAGS) > +fi > +AM_CONDITIONAL([USE_LUAJIT], [test "x$use_luajit" = xyes]) > +AC_MSG_RESULT([$use_luajit]) > + > dnl stack trace with libunwind > libunwind_CPPFLAGS= > libunwind_LDFLAGS= > diff --git a/defs.h b/defs.h > index 6449bce9..01a8c125 100644 > --- a/defs.h > +++ b/defs.h > @@ -208,39 +208,7 @@ struct inject_opts { > #define MAX_ERRNO_VALUE 4095 > #define INJECT_OPTS_RVAL_DEFAULT (-(MAX_ERRNO_VALUE + 1)) > > -/* Trace Control Block */ > -struct tcb { > - int flags; /* See below for TCB_ values */ > - int pid; /* If 0, this tcb is free */ > - int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW > */ > - unsigned long u_error; /* Error code */ > - kernel_ulong_t scno; /* System call number */ > - kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */ > - kernel_long_t u_rval; /* Return value */ > -#if SUPPORTED_PERSONALITIES > 1 > - unsigned int currpers; /* Personality at the time of scno update */ > -#endif > - int sys_func_rval; /* Syscall entry parser's return value */ > - int curcol; /* Output column for this process */ > - FILE *outf; /* Output file for this process */ > - const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */ > - void *_priv_data; /* Private data for syscall decoding functions > */ > - void (*_free_priv_data)(void *); /* Callback for freeing priv_data */ > - const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad > scno */ > - const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" > msg */ > - struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; > - struct timeval stime; /* System time usage as of last process wait */ > - struct timeval dtime; /* Delta for system time usage */ > - struct timeval etime; /* Syscall entry time */ > - > -#ifdef USE_LIBUNWIND > - struct UPT_info* libunwind_ui; > - struct mmap_cache_t* mmap_cache; > - unsigned int mmap_cache_size; > - unsigned int mmap_cache_generation; > - struct queue_t* queue; > -#endif > -}; > +#include "defs_reuse.h" > > /* TCB flags */ > /* We have attached to this process, but did not see it stopping yet */ > diff --git a/defs_reuse.h b/defs_reuse.h > new file mode 100644 > index 00000000..a6a8f8ca > --- /dev/null > +++ b/defs_reuse.h > @@ -0,0 +1,58 @@ > +#define STRINGIFY(...) #__VA_ARGS__ > +#ifdef FFI_CDEF > +# define CONTENT(...) STRINGIFY(__VA_ARGS__) > +#else > +# define CONTENT(...) __VA_ARGS__ > +#endif > + > +CONTENT( > +struct tcb { > + int flags; /* See below for TCB_ values */ > + int pid; /* If 0, this tcb is free */ > + int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW > */ > + unsigned long u_error; /* Error code */ > + kernel_ulong_t scno; /* System call number */ > + kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */ > + kernel_long_t u_rval; /* Return value */ > +) > + > +#if SUPPORTED_PERSONALITIES > 1 > +CONTENT( > + unsigned int currpers; /* Personality at the time of scno update */ > +) > +#endif > + > +#ifndef FFI_CDEF > + int sys_func_rval; /* Syscall entry parser's return value */ > + int curcol; /* Output column for this process */ > + FILE *outf; /* Output file for this process */ > + const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */ > + void *_priv_data; /* Private data for syscall decoding functions > */ > + void (*_free_priv_data)(void *); /* Callback for freeing priv_data */ > + const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad > scno */ > + const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" > msg */ > + struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; > + struct timeval stime; /* System time usage as of last process wait */ > + struct timeval dtime; /* Delta for system time usage */ > + struct timeval etime; /* Syscall entry time */ > +# ifdef USE_LIBUNWIND > + struct UPT_info* libunwind_ui; > + struct mmap_cache_t* mmap_cache; > + unsigned int mmap_cache_size; > + unsigned int mmap_cache_generation; > + struct queue_t* queue; > +# endif > +#endif /* FFI_CDEF */ > + > +CONTENT(};) > + > +#ifdef USE_LUAJIT > +CONTENT( > +struct strace_luajit_funcs { > + struct tcb * (*next_sc)(void); > +}; > +) > +#endif > + > +#undef STRINGIFY > +#undef CONTENT > diff --git a/strace.c b/strace.c > index 5a1bcff3..30004444 100644 > --- a/strace.c > +++ b/strace.c > @@ -45,6 +45,11 @@ > # include <sys/prctl.h> > #endif > #include <asm/unistd.h> > +#ifdef USE_LUAJIT > +# include <lua.h> > +# include <lualib.h> > +# include <lauxlib.h> > +#endif > > #include "scno.h" > #include "ptrace.h" > @@ -166,6 +171,11 @@ static volatile sig_atomic_t interrupted; > static volatile int interrupted; > #endif > > +#ifdef USE_LUAJIT > +static lua_State *L = NULL; I'd prefer to have a bit more verbose variable name. > +static void luajit_init(const char *scriptfile); > +#endif > + > #ifndef HAVE_STRERROR > > #if !HAVE_DECL_SYS_ERRLIST > @@ -216,6 +226,11 @@ Output format:\n\ > -k obtain stack trace between each syscall (experimental)\n\ > " > #endif > +#ifdef USE_LUAJIT > +"\ > + -l file run a Lua script from FILE\n\ > +" > +#endif > "\ > -o file send trace output to FILE instead of stderr\n\ > -q suppress messages about attaching, detaching, etc.\n\ > @@ -1645,6 +1660,9 @@ init(int argc, char *argv[]) > #ifdef USE_LIBUNWIND > "k" > #endif > +#ifdef USE_LUAJIT > + "l:" > +#endif > "D" > "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) { > switch (c) { > @@ -1755,6 +1773,11 @@ init(int argc, char *argv[]) > stack_trace_enabled = true; > break; > #endif > +#ifdef USE_LUAJIT > + case 'l': > + luajit_init(optarg); > + break; > +#endif > case 'E': > if (putenv(optarg) < 0) > die_out_of_memory(); > @@ -2637,6 +2660,137 @@ terminate(void) > exit(exit_code); > } > > +#ifdef USE_LUAJIT I'd probably prefer to have the following functions being defined in a separate file. It may be even included in-place if it is too burdensome to fix all the references to the static symbols. > +static struct tcb * > +luajit_next_sc(void) > +{ > + static struct timeval tv = {}; > + static bool first = true; > + > +# define MBRESTART(res, sig) > \ > + if ((res) >= 0 && ptrace_restart(PTRACE_SYSCALL, current_tcp, sig) < 0) > { \ > + /* Note: ptrace_restart emitted error message */ > \ > + exit_code = 1; > \ > + terminate(); > \ > + } > + > + if (!first) { > + unsigned int sig = 0; > + int res; > + if (entering(current_tcp)) { > + res = syscall_entering_trace(current_tcp, &sig); > + syscall_entering_finish(current_tcp, res); > + } else { > + res = syscall_exiting_trace(current_tcp, tv, 1); > + syscall_exiting_finish(current_tcp); > + } > + MBRESTART(res, sig); > + } > + first = false; > + > + while (1) { > + int status; > + siginfo_t si; > + enum trace_event ret = next_event(&status, &si); > + if (ret == TE_SYSCALL_STOP) { > + unsigned int sig = 0; > + int res; > + if (entering(current_tcp)) { > + res = syscall_entering_decode(current_tcp); > + switch (res) { > + case 0: > + break; > + case 1: > + return current_tcp; > + default: > + res = > syscall_entering_trace(current_tcp, &sig); > + syscall_entering_finish(current_tcp, > res); > + } > + } else { > + res = syscall_exiting_decode(current_tcp, &tv); > + switch (res) { > + case 0: > + break; > + case 1: > + return current_tcp; > + default: > + res = > syscall_exiting_trace(current_tcp, tv, res); > + syscall_exiting_finish(current_tcp); > + } > + } > + MBRESTART(res, sig); > + } else { > + if (!dispatch_event(ret, &status, &si)) { > + terminate(); > + } > + } > + } > +# undef MBRESTART > +} > + > +static void > +check_lua(int ret) > +{ > + if (ret == 0) > + return; > + const char *msg = lua_tostring(L, -1); > + if (!msg) > + msg = "(error object can't be converted to string)"; > + error_msg_and_die("lua: %s", msg); > +} > + > +static void > +luajit_init(const char *scriptfile) > +{ > + static struct strace_luajit_funcs funcs = { > + .next_sc = luajit_next_sc, > + }; > + > + if (L) > + /* already initialized? */ > + error_msg_and_help("multiple -l arguments"); Do you really consider multiple -l arguments in a command line an error incompatible with continuation of strace execution? > + > + if (!(L = luaL_newstate())) > + die_out_of_memory(); > + > + luaL_openlibs(L); > + /* L: - */ > + check_lua(luaL_loadfile(L, scriptfile)); /* L: chunk */ > + lua_getglobal(L, "require"); /* L: chunk require */ > + lua_pushstring(L, "ffi"); /* L: chunk require "ffi" */ > + check_lua(lua_pcall(L, 1, 1, 0)); /* L: chunk ffi */ > + lua_getfield(L, -1, "cdef"); /* L: chunk ffi cdef */ > + luaL_Buffer b; > + luaL_buffinit(L, &b); /* L: chunk ffi cdef ? */ > + { > + char buf[128]; > + snprintf(buf, sizeof(buf), > + "typedef int%zu_t kernel_long_t;" > + "typedef uint%zu_t kernel_ulong_t;", > + sizeof(kernel_long_t) * 8, > + sizeof(kernel_ulong_t) * 8); > + luaL_addstring(&b, buf); /* L: chunk ffi cdef ? */ > + } > + luaL_addstring(&b, > +# define FFI_CDEF > +# include "defs_reuse.h" > +# undef FFI_CDEF > + ); /* L: chunk ffi cdef ? */ > + luaL_pushresult(&b); /* L: chunk ffi cdef str */ > + check_lua(lua_pcall(L, 1, 0, 0)); /* L: chunk ffi */ > + lua_newtable(L); /* L: chunk ffi table */ > + lua_getfield(L, -2, "cast"); /* L: chunk ffi table cast */ > + lua_pushstring(L, "struct strace_luajit_funcs *"); /* L: chunk ffi > table cast str */ > + lua_pushlightuserdata(L, &funcs); /* L: chunk ffi table cast str > lightuserdata */ > + check_lua(lua_pcall(L, 2, 1, 0)); /* L: chunk ffi table funcs */ > + lua_getfield(L, -1, "next_sc"); /* L: chunk ffi table funcs next_sc */ > + lua_setfield(L, -3, "next_sc"); /* L: chunk ffi table funcs */ > + lua_pop(L, 1); /* L: chunk ffi table */ > + lua_setglobal(L, "strace"); /* L: chunk ffi */ > + lua_pop(L, 1); /* L: chunk */ > +} > +#endif /* USE_LUAJIT */ > + > int > main(int argc, char *argv[]) > { > @@ -2644,6 +2798,12 @@ main(int argc, char *argv[]) > > exit_code = !nprocs; > > +#ifdef USE_LUAJIT > + if (L) { > + /* L: chunk */ > + check_lua(lua_pcall(L, 0, 0, 0)); /* L: - */ > + } > +#endif Wouldn't this way non-lua tracing loop being exectuted after Lua one? > int status; > siginfo_t si; > while (dispatch_event(next_event(&status, &si), &status, &si)) > -- > 2.11.0 Overall looks good. ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel