[PATCH v5 1/1] Initial support for LuaJIT scripting

Dmitry V. Levin ldv at altlinux.org
Tue Jul 4 20:31:49 UTC 2017


On Tue, Jul 04, 2017 at 01:25:23PM +0300, Victor Krapivensky wrote:
[...]
> diff --git a/luajit_lib.h b/luajit_lib.h
> new file mode 100644
> index 00000000..16e49610
> --- /dev/null
> +++ b/luajit_lib.h
> @@ -0,0 +1,260 @@
> +"do\n\
> +	local ffi = require(strace._ffilibname)\n\
> +	local bit = require(strace._bitlibname)\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(...)\n\
> +			f(...)\n\
> +			g(...)\n\
> +		end\n\
> +	end\n\
> +	function strace.entering(tcp)\n\
> +		return bit.band(tcp.flags, strace._tcbflg.insyscall) == 0\n\
> +	end\n\
> +	function strace.exiting(tcp)\n\
> +		return bit.band(tcp.flags, strace._tcbflg.insyscall) ~= 0\n\
> +	end\n\
> +\n\
> +	-- `arg' defaults to true.\n\
> +	local function alter_trace_opt(flagbit, tcp, arg)\n\
> +		if strace.exiting(tcp) then\n\
> +			error('altering tracing options must be done on syscall entering')\n\
> +		end\n\
> +		-- we want to support custom boolean-like objects, so check for nil explicitly\n\
> +		if type(arg) == 'nil' or arg then\n\
> +			tcp.qual_flg = bit.bor(tcp.qual_flg, flagbit)\n\
> +		else\n\
> +			tcp.qual_flg = bit.band(tcp.qual_flg, bit.bnot(flagbit))\n\
> +		end\n\
> +	end\n\
> +	function strace.trace  (tcp, arg) alter_trace_opt(strace._qualflg.trace,   tcp, arg) end\n\
> +	function strace.abbrev (tcp, arg) alter_trace_opt(strace._qualflg.abbrev,  tcp, arg) end\n\
> +	function strace.verbose(tcp, arg) alter_trace_opt(strace._qualflg.verbose, tcp, arg) end\n\
> +	function strace.raw    (tcp, arg) alter_trace_opt(strace._qualflg.raw,     tcp, arg) end\n\
> +\n\
> +	function strace.ptr_to_kulong(ptr)\n\
> +		return ffi.cast('kernel_ulong_t', ffi.cast('unsigned long', ptr))\n\
> +	end\n\
> +	function strace.at_exit(f)\n\
> +		strace._at_exit = chain(strace._at_exit, f)\n\
> +	end\n\
> +	function strace.get_err_name(err, pers)\n\
> +		pers = pers or 0\n\
> +		if err < 0 or err > strace.nerrnoent_vec[pers] then\n\
> +			return nil\n\
> +		end\n\
> +		local s = strace.errnoent_vec[pers][err]\n\
> +		if s ~= nil then\n\
> +			return ffi.string(s)\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.get_sc_name(scno, pers)\n\
> +		pers = pers or 0\n\
> +		if scno < 0 or scno >= strace.nsysent_vec[pers] then\n\
> +			return nil\n\
> +		end\n\
> +		local s = strace.sysent_vec[pers][scno].sys_name\n\
> +		if s ~= nil then\n\
> +			return ffi.string(s)\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.get_ioctl_name(code, pers)\n\
> +		pers = pers or 0\n\
> +		-- we could have provided a definition for stdlib's bsearch() and used it, but LuaJIT's FFI manual\n\
> +		-- says generated callbacks are a limited resource and also slow. So implement binary search ourselves.\n\
> +		local lb, rb = ffi.cast('unsigned int', 0), strace.nioctlent_vec[pers]\n\
> +		if rb == 0 then\n\
> +			return nil\n\
> +		end\n\
> +		local arr = strace.ioctlent_vec[pers]\n\
> +		while rb - lb > 1 do\n\
> +			local mid = lb + (rb - lb) / 2\n\
> +			if arr[mid].code <= code then\n\
> +				lb = mid\n\
> +			else\n\
> +				rb = mid\n\
> +			end\n\
> +		end\n\
> +		if arr[lb].code == code then\n\
> +			return ffi.string(arr[lb].symbol)\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.get_scno(scname, pers)\n\
> +		pers = pers or 0\n\
> +		for i = 0, tonumber(strace.nsysent_vec[pers]) - 1 do\n\
> +			local s = strace.sysent_vec[pers][i].sys_name\n\
> +			if s ~= nil and ffi.string(s) == scname then\n\
> +				return i\n\
> +			end\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.get_signo(signame, pers)\n\
> +		pers = pers or 0\n\
> +		for i = 0, tonumber(strace.nsignalent_vec[pers]) - 1 do\n\
> +			local s = strace.signalent_vec[pers][i]\n\
> +			if s ~= nil and ffi.string(s) == signame then\n\
> +				return i\n\
> +			end\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.get_errno(errname, pers)\n\
> +		pers = pers or 0\n\
> +		for i = 0, tonumber(strace.nerrnoent_vec[pers]) - 1 do\n\
> +			local s = strace.errnoent_vec[pers][i]\n\
> +			if s ~= nil and ffi.string(s) == errname then\n\
> +				return i\n\
> +			end\n\
> +		end\n\
> +		return nil\n\
> +	end\n\
> +	function strace.inject_signal(tcp, sig)\n\
> +		if type(sig) == 'string' then\n\
> +			sig = strace.get_signo(sig, tcp.currpers)\n\
> +			if not sig then\n\
> +				error('signal not found')\n\
> +			end\n\
> +		end\n\
> +		if not strace.inject_signo(tcp, sig) then\n\
> +			error('cannot inject signal')\n\
> +		end\n\
> +	end\n\
> +	function strace.inject_error(tcp, err)\n\
> +		if type(err) == 'string' then\n\
> +			err = strace.get_errno(err, tcp.currpers)\n\
> +			if not err then\n\
> +				error('error not found')\n\
> +			end\n\
> +		end\n\
> +		if err <= 0 then\n\
> +			error('err must be positive')\n\
> +		end\n\
> +		if not strace.inject_retval(tcp, -err) then\n\
> +			error('cannot inject error')\n\
> +		end\n\
> +	end\n\
> +	function strace.read_obj(tcp, addr, ct, ...)\n\
> +		local obj = ffi.new(ct, ...)\n\
> +		return strace.umoven(tcp, addr, ffi.sizeof(obj, ...), obj) == 0 and obj or nil\n\
> +	end\n\
> +	function strace.read_str(tcp, addr, maxsz, bufsz)\n\
> +		maxsz = maxsz or 4 * 1024 * 1024\n\
> +		bufsz = bufsz or 1024\n\
> +		local t = {}\n\
> +		local buf = ffi.new('char[?]', bufsz)\n\
> +		while true do\n\
> +			local r = strace.umovestr(tcp, addr, bufsz, buf)\n\
> +			if r < 0 then\n\
> +				return nil\n\
> +			elseif r == 0 then\n\
> +				maxsz = maxsz - bufsz\n\
> +				if maxsz < 0 then return nil end\n\
> +				t[#t + 1] = ffi.string(buf, bufsz)\n\
> +				addr = addr + bufsz\n\
> +			else\n\
> +				local s = ffi.string(buf)\n\
> +				if #s > maxsz then return nil end\n\
> +				return table.concat(t) .. s\n\
> +			end\n\
> +		end\n\
> +	end\n\
> +	function strace.read_path(tcp, addr)\n\
> +		return strace.read_str(tcp, addr, strace.path_max, strace.path_max)\n\
> +	end\n\
> +	local function register_hook(scno, pers, en, ex, cb)\n\
> +		assert(not not strace.monitor(scno, pers, en, ex))\n\
> +		pers = tonumber(pers)\n\
> +		scno = tonumber(scno)\n\
> +		if en then\n\
> +			strace._en[pers][scno] = chain(strace._en[pers][scno], cb)\n\
> +		end\n\
> +		if ex then\n\
> +			strace._ex[pers][scno] = chain(strace._ex[pers][scno], cb)\n\
> +		end\n\
> +	end\n\
> +	local function parse_when(when)\n\
> +		if when == 'entering' then\n\
> +			return true, false\n\
> +		elseif when == 'exiting' then\n\
> +			return false, true\n\
> +		elseif when == 'both' then\n\
> +			return true, true\n\
> +		else\n\
> +			error('unknown \"when\" value')\n\
> +		end\n\
> +	end\n\
> +	function strace.monitor_all()\n\
> +		for p = 0, strace.npersonalities - 1 do\n\
> +			for i = 0, tonumber(strace.nsysent_vec[p]) - 1 do\n\
> +				strace.monitor(i, p, true, true)\n\
> +			end\n\
> +		end\n\
> +	end\n\
> +	function strace.hook(scname, when, cb)\n\
> +		local en, ex = parse_when(when)\n\
> +		local found = false\n\
> +		for p = 0, strace.npersonalities - 1 do\n\
> +			local scno = strace.get_scno(scname, p)\n\
> +			if scno then\n\
> +				register_hook(scno, p, en, ex, cb)\n\
> +				found = true\n\
> +			end\n\
> +		end\n\
> +		if not found then\n\
> +			error('syscall not found')\n\
> +		end\n\
> +	end\n\
> +	function strace.hook_class(clsname, when, cb)\n\
> +		local en, ex = parse_when(when)\n\
> +		local flag = nil\n\
> +		local ptr = strace.syscall_classes\n\
> +		while ptr.name ~= nil do\n\
> +			if ffi.string(ptr.name) == clsname then\n\
> +				flag = ptr.value\n\
> +				break\n\
> +			end\n\
> +			ptr = ptr + 1\n\
> +		end\n\
> +		if not flag then\n\
> +			error('syscall class not found')\n\
> +		end\n\
> +		for p = 0, strace.npersonalities - 1 do\n\
> +			for i = 0, tonumber(strace.nsysent_vec[p]) - 1 do\n\
> +				if bit.band(strace.sysent_vec[p][i].sys_flags, flag) ~= 0 then\n\
> +					register_hook(i, p, en, ex, cb)\n\
> +				end\n\
> +			end\n\
> +		end\n\
> +	end\n\
> +	function strace.hook_scno(scno, when, cb, pers)\n\
> +		pers = pers or 0\n\
> +		local en, ex = parse_when(when)\n\
> +		reigster_hook(scno, pers, en, ex, cb)\n\
> +	end\n\
> +	function strace._run()\n\
> +		while true do\n\
> +			local tcp = strace.next_sc()\n\
> +			if tcp == nil then break end\n\
> +			local cb = (strace.entering(tcp) and strace._en or strace._ex)[tonumber(tcp.currpers)][tonumber(tcp.scno)]\n\
> +			if cb then cb(tcp) end\n\
> +		end\n\
> +		if strace._at_exit then strace._at_exit() end\n\
> +	end\n\
> +	function print(...)\n\
> +		local sep = ''\n\
> +		for i = 1, select('#', ...) do\n\
> +			io.stderr:write(sep .. tostring(select(i, ...)))\n\
> +			sep = '\\t'\n\
> +		end\n\
> +		io.stderr:write('\\n')\n\
> +	end\n\
> +end"

This longish C string is actually a lua script, and it doesn't look nice
in this form.  Wouldn't it be better if this script was translated into
a C string automatically by some Makefile rule?


-- 
ldv
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.strace.io/pipermail/strace-devel/attachments/20170704/f9c5d88d/attachment.bin>


More information about the Strace-devel mailing list