[PATCH RFC v2 1/1] Initial support for Lua scripting

Victor Krapivensky krapivenskiy.va at phystech.edu
Fri Jun 16 14:50:37 UTC 2017


This change also defines currpers field of struct tcb if LuaJIT is
enabled, even if SUPPORTED_PERSONALITIES == 1.

* Makefile.am: Build with LuaJIT if configured so.
(strace_SOURCES): Add defs_tcb.h, syscall_class.h.
* configure.ac: Add new --with-luajit configure option.
* defs.h (QUAL_HOOK_ENTRY, QUAL_HOOK_EXIT): new qual flags.
Move definition of struct tcb that needs to be fed to LuaJIT's
FFI to...
* defs_tcb.h: ...new file.
* luajit.h: New file.
* luajit_funcs.h: Likewise.
* luajit_lib.h: Likewise.
* mpers_type.h (STRINGIFY): Rename to MPERS_STRINGIFY so that it does
not pollute the global namespace.
* qualify.c (syscall_classes): move syscall classes list to the global
scope, terminate it with a null entry.
(hook_entry_set, hook_exit_set): New sets (if built with LuaJIT
support).
(lookup_class): Use global, null entry-terminated list of syscall
classes.
(qual_flags): If built with LuaJIT, return
QUAL_HOOK_ENTRY/QUAL_HOOK_EXIT flags.
(set_hook_qual): New function.
* strace.c (alloctcb): update the condition of presence of currpers
field.
(init): New -l option (if built with LuaJIT support).
(main): run Lua script, if built with Lua scripting support and a script
was provided.
* syscall.c (syscall_exiting_decode): don't bail out if QUAL_HOOK_EXIT
flag for this syscall it set.
* syscall_class.h: New file.
* sysent.h: Modify to support inclusion with FFI_CDEF.
---
 Makefile.am     |   8 +++
 configure.ac    |  36 ++++++++++
 defs.h          |  39 ++--------
 defs_tcb.h      |  56 +++++++++++++++
 luajit.h        | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 luajit_funcs.h  |  30 ++++++++
 luajit_lib.h    |  70 ++++++++++++++++++
 mpers_type.h    |   4 +-
 qualify.c       |  85 +++++++++++++---------
 strace.c        |  35 ++++++++-
 syscall.c       |   2 +-
 syscall_class.h |  27 +++++++
 sysent.h        |  31 +++++++-
 13 files changed, 567 insertions(+), 72 deletions(-)
 create mode 100644 defs_tcb.h
 create mode 100644 luajit.h
 create mode 100644 luajit_funcs.h
 create mode 100644 luajit_lib.h
 create mode 100644 syscall_class.h

diff --git a/Makefile.am b/Makefile.am
index 80f8a341..7054477e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -100,6 +100,7 @@ strace_SOURCES =	\
 	copy_file_range.c \
 	count.c		\
 	defs.h		\
+	defs_tcb.h	\
 	desc.c		\
 	dirent.c	\
 	dirent64.c	\
@@ -242,6 +243,7 @@ strace_SOURCES =	\
 	strace.c	\
 	swapon.c	\
 	syscall.c	\
+	syscall_class.h	\
 	sysctl.c	\
 	sysent.h	\
 	sysinfo.c	\
@@ -277,6 +279,12 @@ strace_LDFLAGS += $(libunwind_LDFLAGS)
 strace_LDADD += $(libunwind_LIBS)
 endif
 
+if USE_LUAJIT
+strace_SOURCES += luajit.h luajit_lib.h luajit_funcs.h
+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..afa1b2f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -715,6 +715,42 @@ 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) ;;
+             *)     force_luajit=yes; luajit_lib="${withval}" ;;
+             esac],
+            [:]
+)
+AS_IF([test "x$luajit_lib" != xno],
+      [PKG_CHECK_MODULES([LUAJIT],
+                         [$luajit_lib],
+                         [use_luajit=yes],
+                         [AS_IF([test "x$force_luajit" = xyes],
+                                [AC_MSG_ERROR([cannot find luajit library: $luajit_lib])]
+                               )]
+                        )]
+)
+
+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..8641929b 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_tcb.h"
 
 /* TCB flags */
 /* We have attached to this process, but did not see it stopping yet */
@@ -274,6 +242,8 @@ struct tcb {
 #define QUAL_SIGNAL	0x100	/* report events with this signal */
 #define QUAL_READ	0x200	/* dump data read from this file descriptor */
 #define QUAL_WRITE	0x400	/* dump data written to this file descriptor */
+#define QUAL_HOOK_ENTRY	0x800	/* return this syscall on entry from next_sc() */
+#define QUAL_HOOK_EXIT	0x1000	/* return this syscall on exit from next_sc() */
 
 #define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
 
@@ -658,6 +628,9 @@ extern struct number_set signal_set;
 extern bool is_number_in_set(unsigned int number, const struct number_set *);
 extern void qualify(const char *);
 extern unsigned int qual_flags(const unsigned int);
+#ifdef USE_LUAJIT
+extern void set_hook_qual(unsigned int scno, unsigned int pers, bool entry_hook, bool exit_hook);
+#endif
 
 #define DECL_IOCTL(name)						\
 extern int								\
diff --git a/defs_tcb.h b/defs_tcb.h
new file mode 100644
index 00000000..f15f2864
--- /dev/null
+++ b/defs_tcb.h
@@ -0,0 +1,56 @@
+/*
+ * Should only be included without FFI_CDEF from defs.h, so no include guards.
+ */
+
+#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 defined(USE_LUAJIT) || 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(
+};
+)
+
+#undef STRINGIFY
+#undef CONTENT
diff --git a/luajit.h b/luajit.h
new file mode 100644
index 00000000..62865673
--- /dev/null
+++ b/luajit.h
@@ -0,0 +1,216 @@
+/*
+ * Should only be included from strace.c, so no include guards.
+ */
+
+#include "luajit_funcs.h"
+
+#define L script_L
+
+#define LUAJIT_FUNC(name) luajit_func_ ## name
+
+static struct tcb *
+LUAJIT_FUNC(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;								\
+		return NULL;								\
+	}
+
+	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:
+					if (current_tcp->qual_flg & QUAL_HOOK_ENTRY)
+						return current_tcp;
+					res = syscall_entering_trace(current_tcp, &sig);
+					/* fall through */
+				default:
+					syscall_entering_finish(current_tcp, res);
+				}
+			} else {
+				res = syscall_exiting_decode(current_tcp, &tv);
+				switch (res) {
+				case 0:
+					break;
+				case 1:
+					if (current_tcp->qual_flg & QUAL_HOOK_EXIT)
+						return current_tcp;
+					/* fall through */
+				default:
+					res = syscall_exiting_trace(current_tcp, tv, res);
+				}
+				syscall_exiting_finish(current_tcp);
+			}
+			MBRESTART(res, sig);
+		} else {
+			if (!dispatch_event(ret, &status, &si)) {
+				return NULL;
+			}
+		}
+	}
+#undef MBRESTART
+}
+
+static bool
+LUAJIT_FUNC(monitor)(unsigned int scno, unsigned int pers, bool entry_hook, bool exit_hook)
+{
+	if (pers >= SUPPORTED_PERSONALITIES || scno >= nsyscall_vec[pers])
+		return false;
+	set_hook_qual(scno, pers, entry_hook, exit_hook);
+	return true;
+}
+
+static const char *
+get_lua_msg(void)
+{
+	const char *msg = lua_tostring(L, -1);
+	return msg ? msg : "(error object can't be converted to string)";
+}
+
+static void
+assert_lua_impl(int ret, const char *expr, const char *file, int line)
+{
+	if (ret == 0)
+		return;
+	error_msg_and_die("assert_lua(%s) failed at %s:%d: %s", expr, file,
+		line, get_lua_msg());
+}
+
+#define assert_lua(expr) assert_lua_impl(expr, #expr, __FILE__, __LINE__)
+
+static void
+check_lua(int ret)
+{
+	if (ret == 0)
+		return;
+	error_msg_and_die("lua: %s", get_lua_msg());
+}
+
+static void
+init_luajit(const char *scriptfile)
+{
+	static struct strace_luajit_funcs funcs = {
+#define X(rettype, name, ...) LUAJIT_FUNC(name),
+LUAJIT_FUNCS_XPAND()
+#undef X
+	};
+
+	if (L)
+		/* already initialized? */
+		error_msg_and_help("multiple -l arguments");
+
+	if (!(L = luaL_newstate()))
+		die_out_of_memory();
+
+	luaL_openlibs(L);
+	/* L: - */
+	assert_lua(luaL_loadfile(L, scriptfile)); /* L: chunk */
+	lua_getglobal(L, "require"); /* L: chunk require */
+	lua_pushstring(L, "ffi"); /* L: chunk require "ffi" */
+	assert_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 "sysent.h"
+#include "defs_tcb.h"
+#include "luajit_funcs.h"
+#include "syscall_class.h"
+#undef FFI_CDEF
+	); /* L: chunk ffi cdef ? */
+	luaL_pushresult(&b); /* L: chunk ffi cdef str */
+	assert_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 ptr */
+
+	assert_lua(lua_pcall(L, 2, 1, 0)); /* L: chunk ffi table funcs */
+
+#define X(rettype, name, ...)						\
+	lua_getfield(L, -1, #name); /* L: chunk ffi table funcs func */	\
+	lua_setfield(L, -3, #name); /* L: chunk ffi table funcs */
+LUAJIT_FUNCS_XPAND()
+#undef X
+
+	lua_pop(L, 1); /* L: chunk ffi table */
+
+	lua_pushinteger(L, SUPPORTED_PERSONALITIES); /* L: chunk ffi table number */
+	lua_setfield(L, -2, "npersonalities"); /* L: chunk ffi table */
+
+#define EXPOSE(typestr, var)								\
+	lua_getfield(L, -2, "cast"); /* L: chunk ffi table cast */			\
+	lua_pushstring(L, typestr); /* L: chunk ffi table cast str */			\
+	lua_pushlightuserdata(L, (void *) var); /* L: chunk ffi table cast str ptr */	\
+	assert_lua(lua_pcall(L, 2, 1, 0)); /* L: chunk ffi table var */			\
+	lua_setfield(L, -2, #var); /* L: chunk ffi table */
+
+	EXPOSE("const struct_sysent **", sysent_vec)
+	EXPOSE("const unsigned int *", nsyscall_vec)
+	EXPOSE("const struct syscall_class *", syscall_classes)
+
+#undef EXPOSE
+
+	lua_setglobal(L, "strace"); /* L: chunk ffi */
+	lua_pop(L, 1); /* L: chunk */
+
+	const char *code =
+#include "luajit_lib.h"
+	;
+	assert_lua(luaL_dostring(L, code));
+}
+
+static void ATTRIBUTE_NORETURN
+run_luajit(void)
+{
+	/* L: chunk */
+	check_lua(lua_pcall(L, 0, 0, 0)); /* L: - */
+
+	lua_getglobal(L, "strace"); /* L: strace */
+	lua_getfield(L, -1, "_run"); /* L: strace _run */
+	check_lua(lua_pcall(L, 0, 0, 0)); /* L: strace */
+
+	terminate();
+}
+
+#undef assert_lua
+#undef LUAJIT_FUNC
+#undef L
diff --git a/luajit_funcs.h b/luajit_funcs.h
new file mode 100644
index 00000000..4ed16560
--- /dev/null
+++ b/luajit_funcs.h
@@ -0,0 +1,30 @@
+#if !defined(STRACE_LUAJIT_FUNCS_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_LUAJIT_FUNCS_H
+#endif
+
+#define STRINGIFY(...) #__VA_ARGS__
+#ifdef FFI_CDEF
+# define CONTENT(...)  STRINGIFY(__VA_ARGS__)
+#else
+# define CONTENT(...)  __VA_ARGS__
+#endif
+
+#define LUAJIT_FUNCS_XPAND()							\
+	X(struct tcb *,	next_sc,	void)					\
+	X(bool,		monitor,	unsigned int scno, unsigned int pers,	\
+					bool entry_hook, bool exit_hook)	\
+	/* end */
+
+#define X(rettype, name, ...) rettype (*name)(__VA_ARGS__);
+CONTENT(
+struct strace_luajit_funcs {
+	LUAJIT_FUNCS_XPAND()
+};
+)
+#undef X
+
+#undef STRINGIFY
+#undef CONTENT
+
+#endif /* !defined(STRACE_LUAJIT_FUNCS_H) || defined(FFI_CDEF) */
diff --git a/luajit_lib.h b/luajit_lib.h
new file mode 100644
index 00000000..5f97b0ea
--- /dev/null
+++ b/luajit_lib.h
@@ -0,0 +1,70 @@
+#define STRINGIFY(...) #__VA_ARGS__
+#define STRVAL(...)    STRINGIFY(__VA_ARGS__)
+
+"do\n\
+	local ffi = require 'ffi'\n\
+	local bit = require 'bit'\n\
+	strace._en, strace._ex = {}, {}\n\
+	for p = 0, strace.npersonalities - 1 do\n\
+		strace._en[p] = {}\n\
+		strace._ex[p] = {}\n\
+	end\n\
+	local function chain(f, g)\n\
+		if not f then return g end\n\
+		return function(tcp)\n\
+			f(tcp)\n\
+			g(tcp)\n\
+		end\n\
+	end\n\
+	function strace.hook(scname, how, cb)\n\
+		local en, ex = false, false\n\
+		if how == 'entering' then\n\
+			en = true\n\
+		elseif how == 'exiting' then\n\
+			ex = true\n\
+		elseif how == 'both' then\n\
+			en, ex = true, true\n\
+		else\n\
+			error('unknown \"how\" value')\n\
+		end\n\
+		local found = false\n\
+		for p = 0, strace.npersonalities - 1 do\n\
+			for i = 0, tonumber(strace.nsyscall_vec[p]) - 1 do\n\
+				local s = strace.sysent_vec[p][i].sys_name\n\
+				if s ~= nil and ffi.string(s) == scname then\n\
+					assert(strace.monitor(i, p, en, ex))\n\
+					if en then strace._en[p][i] = chain(strace._en[p][i], cb) end\n\
+					if ex then strace._ex[p][i] = chain(strace._ex[p][i], cb) end\n\
+					found = true\n\
+				end\n\
+			end\n\
+		end\n\
+		if not found then\n\
+			error('syscall not found')\n\
+		end\n\
+	end\n\
+	function strace.entering(tcp)\n\
+		return bit.band(tcp.flags, " STRVAL(TCB_INSYSCALL) ") == 0\n\
+	end\n\
+	function strace.exiting(tcp)\n\
+		return bit.band(tcp.flags, " STRVAL(TCB_INSYSCALL) ") ~= 0\n\
+	end\n\
+	function strace.at_exit(f)\n\
+		strace._at_exit = f\n\
+	end\n\
+	function strace._run()\n\
+		while true do\n\
+			local tcp = strace.next_sc()\n\
+			if tcp == nil then\n\
+				if strace._at_exit then strace._at_exit() end\n\
+				break\n\
+			end\n\
+			local cb = (strace.entering(tcp) and strace._en or strace._ex)[tonumber(tcp.currpers)][tonumber(tcp.scno)]\n\
+			cb(tcp)\n\
+		end\n\
+	end\n\
+end\n\
+"
+
+#undef STRINGIFY
+#undef STRVAL
diff --git a/mpers_type.h b/mpers_type.h
index ecb1efa8..26c00767 100644
--- a/mpers_type.h
+++ b/mpers_type.h
@@ -31,8 +31,8 @@
 #define STRACE_MPERS_TYPE_H
 
 #ifdef IN_MPERS
-# define STRINGIFY(a) #a
-# define DEF_MPERS_TYPE(args) STRINGIFY(args.h)
+# define MPERS_STRINGIFY(a) #a
+# define DEF_MPERS_TYPE(args) MPERS_STRINGIFY(args.h)
 # ifdef MPERS_IS_m32
 #  define MPERS_PREFIX m32_
 #  define MPERS_DEFS "m32_type_defs.h"
diff --git a/qualify.c b/qualify.c
index de1141d6..b44af1c0 100644
--- a/qualify.c
+++ b/qualify.c
@@ -28,8 +28,34 @@
 
 #include "defs.h"
 #include "nsig.h"
+#include "syscall_class.h"
 #include <regex.h>
 
+const struct syscall_class syscall_classes[] = {
+	{ "desc",	TRACE_DESC	},
+	{ "file",	TRACE_FILE	},
+	{ "memory",	TRACE_MEMORY	},
+	{ "process",	TRACE_PROCESS	},
+	{ "signal",	TRACE_SIGNAL	},
+	{ "ipc",	TRACE_IPC	},
+	{ "network",	TRACE_NETWORK	},
+	{ "%desc",	TRACE_DESC	},
+	{ "%file",	TRACE_FILE	},
+	{ "%memory",	TRACE_MEMORY	},
+	{ "%process",	TRACE_PROCESS	},
+	{ "%signal",	TRACE_SIGNAL	},
+	{ "%ipc",	TRACE_IPC	},
+	{ "%network",	TRACE_NETWORK	},
+	{ "%stat",	TRACE_STAT	},
+	{ "%lstat",	TRACE_LSTAT	},
+	{ "%fstat",	TRACE_FSTAT	},
+	{ "%%stat",	TRACE_STAT_LIKE	},
+	{ "%statfs",	TRACE_STATFS	},
+	{ "%fstatfs",	TRACE_FSTATFS	},
+	{ "%%statfs",	TRACE_STATFS_LIKE	},
+	{}
+};
+
 typedef unsigned int number_slot_t;
 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
 
@@ -48,6 +74,10 @@ static struct number_set inject_set[SUPPORTED_PERSONALITIES];
 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
+#ifdef USE_LUAJIT
+static struct number_set hook_entry_set[SUPPORTED_PERSONALITIES];
+static struct number_set hook_exit_set[SUPPORTED_PERSONALITIES];
+#endif
 
 static void
 number_setbit(const unsigned int i, number_slot_t *const vec)
@@ -245,37 +275,10 @@ qualify_syscall_regex(const char *s, struct number_set *set)
 static unsigned int
 lookup_class(const char *s)
 {
-	static const struct {
-		const char *name;
-		unsigned int value;
-	} syscall_class[] = {
-		{ "desc",	TRACE_DESC	},
-		{ "file",	TRACE_FILE	},
-		{ "memory",	TRACE_MEMORY	},
-		{ "process",	TRACE_PROCESS	},
-		{ "signal",	TRACE_SIGNAL	},
-		{ "ipc",	TRACE_IPC	},
-		{ "network",	TRACE_NETWORK	},
-		{ "%desc",	TRACE_DESC	},
-		{ "%file",	TRACE_FILE	},
-		{ "%memory",	TRACE_MEMORY	},
-		{ "%process",	TRACE_PROCESS	},
-		{ "%signal",	TRACE_SIGNAL	},
-		{ "%ipc",	TRACE_IPC	},
-		{ "%network",	TRACE_NETWORK	},
-		{ "%stat",	TRACE_STAT	},
-		{ "%lstat",	TRACE_LSTAT	},
-		{ "%fstat",	TRACE_FSTAT	},
-		{ "%%stat",	TRACE_STAT_LIKE	},
-		{ "%statfs",	TRACE_STATFS	},
-		{ "%fstatfs",	TRACE_FSTATFS	},
-		{ "%%statfs",	TRACE_STATFS_LIKE	},
-	};
-
-	unsigned int i;
-	for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
-		if (strcmp(s, syscall_class[i].name) == 0) {
-			return syscall_class[i].value;
+	const struct syscall_class *c;
+	for (c = syscall_classes; c->name; ++c) {
+		if (strcmp(s, c->name) == 0) {
+			return c->value;
 		}
 	}
 
@@ -693,5 +696,23 @@ qual_flags(const unsigned int scno)
 		| (is_number_in_set(scno, &raw_set[current_personality])
 		   ? QUAL_RAW : 0)
 		| (is_number_in_set(scno, &inject_set[current_personality])
-		   ? QUAL_INJECT : 0);
+		   ? QUAL_INJECT : 0)
+#ifdef USE_LUAJIT
+		| (is_number_in_set(scno, &hook_entry_set[current_personality])
+		   ? QUAL_HOOK_ENTRY : 0)
+		| (is_number_in_set(scno, &hook_exit_set[current_personality])
+		   ? QUAL_HOOK_EXIT : 0)
+#endif
+		;
+}
+
+#ifdef USE_LUAJIT
+void
+set_hook_qual(unsigned int scno, unsigned int pers, bool entry_hook, bool exit_hook)
+{
+	if (entry_hook)
+		add_number_to_set(scno, &hook_entry_set[pers]);
+	if (exit_hook)
+		add_number_to_set(scno, &hook_exit_set[pers]);
 }
+#endif
diff --git a/strace.c b/strace.c
index 5a1bcff3..5d6ffdf4 100644
--- a/strace.c
+++ b/strace.c
@@ -45,10 +45,16 @@
 # 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"
 #include "printsiginfo.h"
+#include "syscall_class.h"
 
 /* In some libc, these aren't declared. Do it ourself: */
 extern char **environ;
@@ -166,6 +172,11 @@ static volatile sig_atomic_t interrupted;
 static volatile int interrupted;
 #endif
 
+#ifdef USE_LUAJIT
+static lua_State *script_L = NULL;
+static void init_luajit(const char *scriptfile);
+#endif
+
 #ifndef HAVE_STRERROR
 
 #if !HAVE_DECL_SYS_ERRLIST
@@ -216,6 +227,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\
@@ -768,7 +784,7 @@ alloctcb(int pid)
 		if (!tcp->pid) {
 			memset(tcp, 0, sizeof(*tcp));
 			tcp->pid = pid;
-#if SUPPORTED_PERSONALITIES > 1
+#if defined(USE_LUAJIT) || SUPPORTED_PERSONALITIES > 1
 			tcp->currpers = current_personality;
 #endif
 
@@ -1645,6 +1661,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 +1774,11 @@ init(int argc, char *argv[])
 			stack_trace_enabled = true;
 			break;
 #endif
+#ifdef USE_LUAJIT
+		case 'l':
+			init_luajit(optarg);
+			break;
+#endif
 		case 'E':
 			if (putenv(optarg) < 0)
 				die_out_of_memory();
@@ -2637,6 +2661,10 @@ terminate(void)
 	exit(exit_code);
 }
 
+#ifdef USE_LUAJIT
+# include "luajit.h"
+#endif
+
 int
 main(int argc, char *argv[])
 {
@@ -2644,6 +2672,11 @@ main(int argc, char *argv[])
 
 	exit_code = !nprocs;
 
+#ifdef USE_LUAJIT
+	if (script_L)
+		run_luajit();
+#endif
+
 	int status;
 	siginfo_t si;
 	while (dispatch_event(next_event(&status, &si), &status, &si))
diff --git a/syscall.c b/syscall.c
index 0250540c..896119a9 100644
--- a/syscall.c
+++ b/syscall.c
@@ -790,7 +790,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timeval *ptv)
 	}
 #endif
 
-	if (filtered(tcp) || hide_log(tcp))
+	if ((filtered(tcp) || hide_log(tcp)) && !(tcp->qual_flg & QUAL_HOOK_EXIT))
 		return 0;
 
 	get_regs(tcp->pid);
diff --git a/syscall_class.h b/syscall_class.h
new file mode 100644
index 00000000..a39d5bdf
--- /dev/null
+++ b/syscall_class.h
@@ -0,0 +1,27 @@
+#if !defined(STRACE_SYSCALL_CLASS_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_SYSCALL_CLASS_H
+#endif
+
+#define STRINGIFY(...) #__VA_ARGS__
+#ifdef FFI_CDEF
+# define CONTENT(...)  STRINGIFY(__VA_ARGS__)
+#else
+# define CONTENT(...)  __VA_ARGS__
+#endif
+
+CONTENT(
+struct syscall_class {
+	const char *name;
+	unsigned int value;
+};
+)
+
+#undef STRINGIFY
+#undef CONTENT
+
+#ifndef FFI_CDEF
+extern const struct syscall_class syscall_classes[];
+#endif
+
+#endif /* !defined(STRACE_SYSCALL_CLASS_H) || defined(FFI_CDEF) */
diff --git a/sysent.h b/sysent.h
index 92de7468..94c81da9 100644
--- a/sysent.h
+++ b/sysent.h
@@ -1,13 +1,38 @@
-#ifndef STRACE_SYSENT_H
-#define STRACE_SYSENT_H
+#if !defined(STRACE_SYSENT_H) || defined(FFI_CDEF)
+#ifndef FFI_CDEF
+# define STRACE_SYSENT_H
+#endif
 
+#define STRINGIFY(...) #__VA_ARGS__
+#ifdef FFI_CDEF
+# define CONTENT(...)  STRINGIFY(__VA_ARGS__)
+#else
+# define CONTENT(...)  __VA_ARGS__
+#endif
+
+CONTENT(
 typedef struct sysent {
 	unsigned nargs;
 	int	sys_flags;
 	int	sen;
+)
+/* We don't want to expose sys_func to LuaJIT */
+#ifdef FFI_CDEF
+CONTENT(
+	char	priv[sizeof(int(*)())];
+)
+#else
+CONTENT(
 	int	(*sys_func)();
+)
+#endif
+CONTENT(
 	const char *sys_name;
 } struct_sysent;
+)
+
+#undef STRINGIFY
+#undef CONTENT
 
 #define TRACE_FILE			00000001	/* Trace file-related syscalls. */
 #define TRACE_IPC			00000002	/* Trace IPC-related syscalls. */
@@ -29,4 +54,4 @@ typedef struct sysent {
 #define TRACE_FSTAT			00400000	/* Trace *fstat{,at}{,64} syscalls. */
 #define TRACE_STAT_LIKE			01000000	/* Trace *{,l,f}stat{,x,at}{,64} syscalls. */
 
-#endif /* !STRACE_SYSENT_H */
+#endif /* !defined(STRACE_SYSENT_H) || defined(FFI_CDEF) */
-- 
2.11.0





More information about the Strace-devel mailing list