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

Reply via email to