* 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], [:])] + ) + ] +) + +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; +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 +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"); + + 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 int status; siginfo_t si; while (dispatch_event(next_event(&status, &si), &status, &si)) -- 2.11.0 ------------------------------------------------------------------------------ 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