Skip to content

Instantly share code, notes, and snippets.

@annulen
Last active August 29, 2015 14:02
Show Gist options
  • Save annulen/8e487a489ba77a6c7d56 to your computer and use it in GitHub Desktop.
Save annulen/8e487a489ba77a6c7d56 to your computer and use it in GitHub Desktop.
Proof of concept for sysdig code generation
flags {
poll = {
"POLLIN",
"POLLPRI",
"POLLOUT",
"POLLRDHUP",
"POLLERR",
"POLLHUP",
"POLLNVAL",
"POLLRDNORM",
"POLLRDBAND",
"POLLWRNORM",
"POLLWRBAND"
},
prot = {
[0] = "PROT_NONE",
"PROT_READ",
"PROT_WRITE",
"PROT_EXEC",
"PROT_SEM",
"PROT_GROWSDOWN",
"PROT_GROWSUP",
"PROT_SAO"
},
mmap = {
"MAP_SHARED",
"MAP_PRIVATE",
"MAP_FIXED",
"MAP_ANONYMOUS",
"MAP_32BIT",
"MAP_RENAME",
"MAP_NORESERVE",
"MAP_POPULATE",
"MAP_NONBLOCK",
"MAP_GROWSDOWN",
"MAP_DENYWRITE",
"MAP_EXECUTABLE",
"MAP_INHERIT",
"MAP_FILE",
"MAP_LOCKED"
},
splice = {
"SPLICE_F_MOVE",
"SPLICE_F_NONBLOCK",
"SPLICE_F_MORE",
"SPLICE_F_GIFT"
}
}
syscalls {
{
syscall = "brk",
category = "EC_MEMORY",
enter_params = { UINT64("addr", HEX) },
exit_params = { UINT64("res", HEX),
UINT32("vm_size"), UINT32("vm_rss"), UINT32("vm_swap") },
fillers = { enter = AUTOFILL(0), exit = "f_sys_brk_munmap_mmap_x" }
},
{
syscall = "mmap",
category = "EC_MEMORY",
enter_params = { UINT64("addr", HEX), UINT64("length"),
FLAGS32("prot", flags.prot), FLAGS32("flags", flags.mmap),
FD("fd"), UINT64("offset") },
exit_params = { ERRNO("res"),
UINT32("vm_size"), UINT32("vm_rss"), UINT32("vm_swap") },
fillers = { enter = "f_sys_mmap_e", exit = "f_sys_brk_munmap_mmap_x" }
},
{
syscall = "mmap2",
category = "EC_MEMORY",
enter_params = { UINT64("addr", HEX), UINT64("length"),
FLAGS32("prot", flags.prot), FLAGS32("flags", flags.mmap),
FD("fd"), UINT64("pgoffset") },
exit_params = { ERRNO("res"),
UINT32("vm_size"), UINT32("vm_rss"), UINT32("vm_swap") },
fillers = { enter = "f_sys_mmap_e", exit = "f_sys_brk_munmap_mmap_x" }
},
{
syscall = "munmap",
category = "EC_MEMORY",
enter_params = { UINT64("addr", HEX), UINT64("length") },
exit_params = { ERRNO("res"),
UINT32("vm_size"), UINT32("vm_rss"), UINT32("vm_swap") },
fillers = { enter = AUTOFILL(0, 1), exit = "f_sys_brk_munmap_mmap_x" }
},
{
syscall = "splice",
category = "EC_IO_OTHER",
never_drop = true,
flags = { "EF_USES_FD", "EF_NONE" },
enter_params = { FD("fd_in"), FD("fd_out"), UINT64("size"),
FLAGS32("flags", flags.splice) },
exit_params = { ERRNO("res") },
fillers = { enter = AUTOFILL(0, 2, 4, 5), exit = AUTOFILL(RETVAL) }
}
}
PPME_SYSCALL_BRK_E = 158,
PPME_SYSCALL_BRK_X = 159,
PPME_SYSCALL_MMAP_E = 160,
PPME_SYSCALL_MMAP_X = 161,
PPME_SYSCALL_MMAP2_E = 162,
PPME_SYSCALL_MMAP2_X = 163,
PPME_SYSCALL_MUNMAP_E = 164,
PPME_SYSCALL_MUNMAP_X = 165,
PPME_SYSCALL_SPLICE_E = 166,
PPME_SYSCALL_SPLICE_X = 167,
PPM_EVENT_MAX = 168
/* PPME_SYSCALL_BRK_E */{ "brk",
EC_MEMORY,
EF_NONE,
1,
{ { "addr", PT_UINT64, PF_HEX } } },
/* PPME_SYSCALL_BRK_X */{ "brk",
EC_MEMORY,
EF_NONE,
4,
{ { "res", PT_UINT64, PF_HEX }, { "vm_size", PT_UINT32, PF_DEC }, { "vm_rss", PT_UINT32, PF_DEC }, { "vm_swap", PT_UINT32, PF_DEC } } },
/* PPME_SYSCALL_MMAP_E */{ "mmap",
EC_MEMORY,
EF_NONE,
6,
{ { "addr", PT_UINT64, PF_HEX }, { "length", PT_UINT64, PF_DEC }, { "prot", PT_FLAGS32, PF_HEX, prot_flags }, { "flags", PT_FLAGS32, PF_HEX, mmap_flags }, { "fd", PT_FD, PF_DEC }, { "offset", PT_UINT64, PF_DEC } } },
/* PPME_SYSCALL_MMAP_X */{ "mmap",
EC_MEMORY,
EF_NONE,
4,
{ { "res", PT_ERRNO, PF_DEC }, { "vm_size", PT_UINT32, PF_DEC }, { "vm_rss", PT_UINT32, PF_DEC }, { "vm_swap", PT_UINT32, PF_DEC } } },
/* PPME_SYSCALL_MMAP2_E */{ "mmap2",
EC_MEMORY,
EF_NONE,
6,
{ { "addr", PT_UINT64, PF_HEX }, { "length", PT_UINT64, PF_DEC }, { "prot", PT_FLAGS32, PF_HEX, prot_flags }, { "flags", PT_FLAGS32, PF_HEX, mmap_flags }, { "fd", PT_FD, PF_DEC }, { "pgoffset", PT_UINT64, PF_DEC } } },
/* PPME_SYSCALL_MMAP2_X */{ "mmap2",
EC_MEMORY,
EF_NONE,
4,
{ { "res", PT_ERRNO, PF_DEC }, { "vm_size", PT_UINT32, PF_DEC }, { "vm_rss", PT_UINT32, PF_DEC }, { "vm_swap", PT_UINT32, PF_DEC } } },
/* PPME_SYSCALL_MUNMAP_E */{ "munmap",
EC_MEMORY,
EF_NONE,
2,
{ { "addr", PT_UINT64, PF_HEX }, { "length", PT_UINT64, PF_DEC } } },
/* PPME_SYSCALL_MUNMAP_X */{ "munmap",
EC_MEMORY,
EF_NONE,
4,
{ { "res", PT_ERRNO, PF_DEC }, { "vm_size", PT_UINT32, PF_DEC }, { "vm_rss", PT_UINT32, PF_DEC }, { "vm_swap", PT_UINT32, PF_DEC } } },
/* PPME_SYSCALL_SPLICE_E */{ "splice",
EC_IO_OTHER,
(enum ppm_event_flags)(EF_USES_FD | EF_NONE),
4,
{ { "fd_in", PT_FD, PF_DEC }, { "fd_out", PT_FD, PF_DEC }, { "size", PT_UINT64, PF_DEC }, { "flags", PT_FLAGS32, PF_HEX, splice_flags } } },
/* PPME_SYSCALL_SPLICE_X */{ "splice",
EC_IO_OTHER,
(enum ppm_event_flags)(EF_USES_FD | EF_NONE),
1,
{ { "res", PT_ERRNO, PF_DEC } } },
#ifdef __NR_brk
[__NR_brk - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_BRK_E, PPME_SYSCALL_BRK_X },
#endif
#ifdef __NR_mmap
[__NR_mmap - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_MMAP_E, PPME_SYSCALL_MMAP_X },
#endif
#ifdef __NR_mmap2
[__NR_mmap2 - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_MMAP2_E, PPME_SYSCALL_MMAP2_X },
#endif
#ifdef __NR_munmap
[__NR_munmap - SYSCALL_TABLE_ID0] = { UF_USED, PPME_SYSCALL_MUNMAP_E, PPME_SYSCALL_MUNMAP_X },
#endif
#ifdef __NR_splice
[__NR_splice - SYSCALL_TABLE_ID0] = { UF_USED | UF_NEVER_DROP, PPME_SYSCALL_SPLICE_E, PPME_SYSCALL_SPLICE_X },
#endif
/* poll_flags*/
#define PPM_POLLIN (1 << 0)
#define PPM_POLLPRI (1 << 1)
#define PPM_POLLOUT (1 << 2)
#define PPM_POLLRDHUP (1 << 3)
#define PPM_POLLERR (1 << 4)
#define PPM_POLLHUP (1 << 5)
#define PPM_POLLNVAL (1 << 6)
#define PPM_POLLRDNORM (1 << 7)
#define PPM_POLLRDBAND (1 << 8)
#define PPM_POLLWRNORM (1 << 9)
#define PPM_POLLWRBAND (1 << 10)
extern const struct ppm_name_value poll_flags[];
/* splice_flags*/
#define PPM_SPLICE_F_MOVE (1 << 0)
#define PPM_SPLICE_F_NONBLOCK (1 << 1)
#define PPM_SPLICE_F_MORE (1 << 2)
#define PPM_SPLICE_F_GIFT (1 << 3)
extern const struct ppm_name_value splice_flags[];
/* mmap_flags*/
#define PPM_MAP_SHARED (1 << 0)
#define PPM_MAP_PRIVATE (1 << 1)
#define PPM_MAP_FIXED (1 << 2)
#define PPM_MAP_ANONYMOUS (1 << 3)
#define PPM_MAP_32BIT (1 << 4)
#define PPM_MAP_RENAME (1 << 5)
#define PPM_MAP_NORESERVE (1 << 6)
#define PPM_MAP_POPULATE (1 << 7)
#define PPM_MAP_NONBLOCK (1 << 8)
#define PPM_MAP_GROWSDOWN (1 << 9)
#define PPM_MAP_DENYWRITE (1 << 10)
#define PPM_MAP_EXECUTABLE (1 << 11)
#define PPM_MAP_INHERIT (1 << 12)
#define PPM_MAP_FILE (1 << 13)
#define PPM_MAP_LOCKED (1 << 14)
extern const struct ppm_name_value mmap_flags[];
/* prot_flags*/
#define PPM_PROT_NONE 0
#define PPM_PROT_READ (1 << 0)
#define PPM_PROT_WRITE (1 << 1)
#define PPM_PROT_EXEC (1 << 2)
#define PPM_PROT_SEM (1 << 3)
#define PPM_PROT_GROWSDOWN (1 << 4)
#define PPM_PROT_GROWSUP (1 << 5)
#define PPM_PROT_SAO (1 << 6)
extern const struct ppm_name_value prot_flags[];
const struct ppm_name_value poll_flags[] = {
{ "POLLIN", PPM_POLLIN },
{ "POLLPRI", PPM_POLLPRI },
{ "POLLOUT", PPM_POLLOUT },
{ "POLLRDHUP", PPM_POLLRDHUP },
{ "POLLERR", PPM_POLLERR },
{ "POLLHUP", PPM_POLLHUP },
{ "POLLNVAL", PPM_POLLNVAL },
{ "POLLRDNORM", PPM_POLLRDNORM },
{ "POLLRDBAND", PPM_POLLRDBAND },
{ "POLLWRNORM", PPM_POLLWRNORM },
{ "POLLWRBAND", PPM_POLLWRBAND },
{ }
}
const struct ppm_name_value splice_flags[] = {
{ "SPLICE_F_MOVE", PPM_SPLICE_F_MOVE },
{ "SPLICE_F_NONBLOCK", PPM_SPLICE_F_NONBLOCK },
{ "SPLICE_F_MORE", PPM_SPLICE_F_MORE },
{ "SPLICE_F_GIFT", PPM_SPLICE_F_GIFT },
{ }
}
const struct ppm_name_value mmap_flags[] = {
{ "MAP_SHARED", PPM_MAP_SHARED },
{ "MAP_PRIVATE", PPM_MAP_PRIVATE },
{ "MAP_FIXED", PPM_MAP_FIXED },
{ "MAP_ANONYMOUS", PPM_MAP_ANONYMOUS },
{ "MAP_32BIT", PPM_MAP_32BIT },
{ "MAP_RENAME", PPM_MAP_RENAME },
{ "MAP_NORESERVE", PPM_MAP_NORESERVE },
{ "MAP_POPULATE", PPM_MAP_POPULATE },
{ "MAP_NONBLOCK", PPM_MAP_NONBLOCK },
{ "MAP_GROWSDOWN", PPM_MAP_GROWSDOWN },
{ "MAP_DENYWRITE", PPM_MAP_DENYWRITE },
{ "MAP_EXECUTABLE", PPM_MAP_EXECUTABLE },
{ "MAP_INHERIT", PPM_MAP_INHERIT },
{ "MAP_FILE", PPM_MAP_FILE },
{ "MAP_LOCKED", PPM_MAP_LOCKED },
{ }
}
const struct ppm_name_value prot_flags[] = {
{ "PROT_READ", PPM_PROT_READ },
{ "PROT_WRITE", PPM_PROT_WRITE },
{ "PROT_EXEC", PPM_PROT_EXEC },
{ "PROT_SEM", PPM_PROT_SEM },
{ "PROT_GROWSDOWN", PPM_PROT_GROWSDOWN },
{ "PROT_GROWSUP", PPM_PROT_GROWSUP },
{ "PROT_SAO", PPM_PROT_SAO },
{ }
}
[PPME_SYSCALL_BRK_E] = {PPM_AUTOFILL, 1, APT_REG, { {0} }},
[PPME_SYSCALL_BRK_X] = {f_sys_brk_munmap_mmap_x},
[PPME_SYSCALL_MMAP_E] = {f_sys_mmap_e},
[PPME_SYSCALL_MMAP_X] = {f_sys_brk_munmap_mmap_x},
[PPME_SYSCALL_MMAP2_E] = {f_sys_mmap_e},
[PPME_SYSCALL_MMAP2_X] = {f_sys_brk_munmap_mmap_x},
[PPME_SYSCALL_MUNMAP_E] = {PPM_AUTOFILL, 2, APT_REG, { {0}, {1} }},
[PPME_SYSCALL_MUNMAP_X] = {f_sys_brk_munmap_mmap_x},
[PPME_SYSCALL_SPLICE_E] = {PPM_AUTOFILL, 4, APT_SOCK, { {0}, {2}, {4}, {5} }},
[PPME_SYSCALL_SPLICE_X] = {PPM_AUTOFILL, 1, APT_REG, { {AF_ID_RETVAL} }},
-- Generate syscall defintions for sysdig
-- (c) 2014 Konstantin Tokarev
require "strict"
-- Makes table immutable and forbids indexing undeclared keys
-- Inspired by strict.lua
local function protect_table(table, tablename)
local mt = {}
mt.tn = tablename
mt.__newindex = function(t, n, v)
local varname = mt.tn .. '.' .. n
error("cannot assign variable '" .. varname .. '" - table is protected', 2)
end
mt.__index = function(t, n)
local value = rawget(t, n)
if not value then
local varname = mt.tn .. '.' .. n
error("variable '" .. varname .."' is not defined", 2)
end
return value
end
setmetatable(table, mt)
end
-- Opens file for write
local function openw(file)
return io.open(file, "w")
end
-----------------------------------
-- Serialization of data structures
-----------------------------------
local function quoted(str)
return '"' .. (str or '') .. '"'
end
local function c_array(array, sep)
sep = sep or ' '
return '{ ' .. table.concat(array, ',' .. sep) .. ' }'
end
local function serialize_syscall_params(syscall_params)
local res = {}
for _, p in ipairs(syscall_params) do
-- Insert defaults
if not p.type then error("type is missing") end
if not p.fmt then error("fmt is missing") end
res[#res + 1] = c_array { quoted(p.name), p.type, p.fmt, p.flags }
end
return res
end
local function serialize_flags(flags)
if #flags == 1 then
return flags[1]
else
return '(enum ppm_event_flags)(' .. table.concat(flags, ' | ') .. ')'
end
end
local function serialize_autofill(params)
local paramtype = "APT_REG"
if type(params[1]) == "string" then
-- Assert on unknown type?
if params[1]:find("^APT_") then
paramtype = table.remove(params, 1)
end
end
local nums = {}
for _, v in ipairs(params) do
nums[#nums + 1] = '{' .. v .. '}'
end
return table.concat({"PPM_AUTOFILL", #nums, paramtype, c_array(nums)}, ', ')
end
-----------------------------------
-- Code writers
-----------------------------------
local event_counter = 158
local event_type = openw("event_type.inc")
local event_info = openw("event_info.inc")
local syscall_table = openw("syscall_table.inc")
local flags_h = openw("flags.h")
local flags_c = openw("flags.c")
local fillers = openw("ppm_events.inc")
local function write_event_type(id)
event_type:write(id, " = ", event_counter, ",\n")
end
local function finalize_event_type()
event_type:write("PPM_EVENT_MAX = ", event_counter, "\n")
end
local function write_event_info(id, props, params)
local comment = '/* ' .. id .. ' */'
local param_array = serialize_syscall_params(params)
if not props.flags then
props.flags = { "EF_NONE" }
end
local info = {
quoted(props.syscall),
props.category,
serialize_flags(props.flags),
#param_array,
c_array(param_array)
}
event_info:write(comment, c_array(info, '\n\t'), ',\n')
end
local function write_event(id, props, params)
write_event_type(id)
write_event_info(id, props, params)
event_counter = event_counter + 1
end
local function write_filler(id, filler)
fillers:write('[', id, '] = {', filler, '},\n')
end
local function write_syscall_table(props, evt_e, evt_x)
local NR_syscall = '__NR_' .. props.syscall
local uf_flags = props.never_drop and 'UF_USED | UF_NEVER_DROP' or 'UF_USED'
local value = c_array{uf_flags, evt_e, evt_x}
syscall_table:write(
'#ifdef ', NR_syscall, '\n',
'\t[', NR_syscall, ' - SYSCALL_TABLE_ID0] =\t\t', value, ',\n',
'#endif\n'
)
end
local function validateSyscallEntry(props)
end
local function write_syscalls(table)
for _, props in ipairs(table) do
validateSyscallEntry(props)
local evt_id = "PPME_SYSCALL_" .. props.syscall:upper()
local enter_evt_id = evt_id .. "_E"
local exit_evt_id = evt_id .. "_X"
write_event(enter_evt_id, props, props.enter_params)
write_event(exit_evt_id, props, props.exit_params)
if props.fillers then
write_filler(enter_evt_id, props.fillers.enter)
write_filler(exit_evt_id, props.fillers.exit)
end
write_syscall_table(props, enter_evt_id, exit_evt_id)
end
finalize_event_type()
end
local function write_flags_h(name, flags)
flags_h:write('/* ', name, '*/\n')
if flags[0] then
flags_h:write('#define PPM_', flags[0], '\t\t0\n')
end
for i, flag in ipairs(flags) do
flags_h:write('#define PPM_', flag, '\t\t(1 << ', i-1, ')\n')
end
flags_h:write('\nextern const struct ppm_name_value ',
name, '[];\n\n')
end
local function write_flags_c(name, flags)
flags_c:write('const struct ppm_name_value ', name, '[] = {\n')
for _, flag in ipairs(flags) do
flags_c:write('\t{ "', flag, '", PPM_', flag, ' },\n')
end
flags_c:write('\t{ }\n', '}\n\n')
end
local function write_flags(table)
for k, v in pairs(table) do
local array_name = k .. "_flags" -- e.g. "file_flags"
write_flags_h(array_name, v)
write_flags_c(array_name, v)
table[k] = array_name
end
end
-----------------------------------
-- API generators
-----------------------------------
-- API function with 1 paramter (name), uses predefined type and fmt, no flags
local function api_name(_type, _fmt)
assert(_type and _fmt, "Invalid api declaration")
return function(pname)
return { name = pname, type = _type, fmt = _fmt }
end
end
-- API function with 2 parameters (name, fmt), uses predefined type,
-- fmt is optional (predefined used if left unspecifed)
-- no flags
local function api_name_fmt(_type, _fmt)
assert(_type, "Invalid api declaration")
return function(pname, pfmt)
return { name = pname, type = _type, fmt = pfmt or _fmt }
end
end
-- API function with 2 parameters (name, flags), uses predefined type and fmt
local function api_name_flags(_type, _fmt)
assert(_type and _fmt, "Invalid api declaration")
return function(pname, pflags)
return { name = pname, type = _type, fmt = _fmt, flags = pflags }
end
end
---------------------------------------
-- Global declarations for syscalls.lua
---------------------------------------
flags = function(t)
write_flags(t)
protect_table(t, "flags")
_G.flags = t -- Use flags.xxx in code below
end
syscalls = function(t)
write_syscalls(t)
_G.syscalls = nil -- Only one syscalls{} block is allowed
end
AUTOFILL = function(...)
return serialize_autofill({...})
end
REG = "APT_REG"
SOCK = "APT_SOCK"
DEFAULT = "AF_ID_DEFAULT"
RETVAL = "AF_ID_RETVAL"
DEC = "PF_DEC"
HEX = "PF_HEX"
INT8 = api_name_fmt("PT_INT8", "PF_DEC")
INT16 = api_name_fmt("PT_INT16", "PF_DEC")
INT32 = api_name_fmt("PT_INT32", "PF_DEC")
INT64 = api_name_fmt("PT_INT64", "PF_DEC")
UINT8 = api_name_fmt("PT_UINT8", "PF_DEC")
UINT16 = api_name_fmt("PT_UINT16", "PF_DEC")
UINT32 = api_name_fmt("PT_UINT32", "PF_DEC")
UINT64 = api_name_fmt("PT_UINT64", "PF_DEC")
ERRNO = api_name("PT_ERRNO", "PF_DEC")
FD = api_name("PT_FD", "PF_DEC")
FLAGS8 = api_name_flags("PT_FLAGS8", "PF_DEC")
FLAGS16 = api_name_flags("PT_FLAGS16", "PF_HEX")
FLAGS32 = api_name_flags("PT_FLAGS32", "PF_HEX")
-----------------------------------
-- Do it!
-----------------------------------
assert(loadfile("flags.lua"))()
assert(loadfile("syscalls.lua"))()
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
-- distributed under the Lua license: http://www.lua.org/license.html
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment