table

--[[
]]
local ffi = require("ffi")
local libbcc = require("bcc.libbcc")
local Posix = require("bcc.vendor.posix")
local BaseTable = class("BaseTable")
BaseTable.static.BPF_MAP_TYPE_HASH = 1
BaseTable.static.BPF_MAP_TYPE_ARRAY = 2
BaseTable.static.BPF_MAP_TYPE_PROG_ARRAY = 3
BaseTable.static.BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4
BaseTable.static.BPF_MAP_TYPE_PERCPU_HASH = 5
BaseTable.static.BPF_MAP_TYPE_PERCPU_ARRAY = 6
BaseTable.static.BPF_MAP_TYPE_STACK_TRACE = 7
BaseTable.static.BPF_MAP_TYPE_CGROUP_ARRAY = 8
BaseTable.static.BPF_MAP_TYPE_LRU_HASH = 9
BaseTable.static.BPF_MAP_TYPE_LRU_PERCPU_HASH = 10
BaseTable.static.BPF_MAP_TYPE_LPM_TRIE = 11
function BaseTable:initialize(t_type, bpf, map_id, map_fd, key_type, leaf_type)
  assert(t_type == libbcc.bpf_table_type_id(bpf.module, map_id))
  self.t_type = t_type
  self.bpf = bpf
  self.map_id = map_id
  self.map_fd = map_fd
  self.c_key = ffi.typeof(key_type.."[1]")
  self.c_leaf = ffi.typeof(leaf_type.."[1]")
end
function BaseTable:key_sprintf(key)
  local pkey = self.c_key(key)
  local buf_len = ffi.sizeof(self.c_key) * 8
  local pbuf = ffi.new("char[?]", buf_len)
  local res = libbcc.bpf_table_key_snprintf(
    self.bpf.module, self.map_id, pbuf, buf_len, pkey)
  assert(res == 0, "could not print key")
  return ffi.string(pbuf)
end
function BaseTable:leaf_sprintf(leaf)
  local pleaf = self.c_leaf(leaf)
  local buf_len = ffi.sizeof(self.c_leaf) * 8
  local pbuf = ffi.new("char[?]", buf_len)
  local res = libbcc.bpf_table_leaf_snprintf(
    self.bpf.module, self.map_id, pbuf, buf_len, pleaf)
  assert(res == 0, "could not print leaf")
  return ffi.string(pbuf)
end
function BaseTable:key_scanf(key_str)
  local pkey = self.c_key()
  local res = libbcc.bpf_table_key_sscanf(
    self.bpf.module, self.map_id, key_str, pkey)
  assert(res == 0, "could not scanf key")
  return pkey[0]
end
function BaseTable:leaf_scanf(leaf_str)
  local pleaf = self.c_leaf()
  local res = libbcc.bpf_table_leaf_sscanf(
    self.bpf.module, self.map_id, leaf_str, pleaf)
  assert(res == 0, "could not scanf leaf")
  return pleaf[0]
end
function BaseTable:get(key)
  local pkey = self.c_key(key)
  local pvalue = self.c_leaf()
  if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) < 0 then
    return nil
  end
  return pvalue[0]
end
function BaseTable:set(key, value)
  local pkey = self.c_key(key)
  local pvalue = self.c_leaf(value)
  assert(libbcc.bpf_update_elem(self.map_fd, pkey, pvalue, 0) == 0, "could not update table")
end
function BaseTable:_empty_key()
  local pkey = self.c_key()
  local pvalue = self.c_leaf()
  for _, v in ipairs({0x0, 0x55, 0xff}) do
    ffi.fill(pkey, ffi.sizeof(pkey[0]), v)
    if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) < 0 then
      return pkey
    end
  end
  error("failed to find an empty key for table iteration")
end
function BaseTable:keys()
  local pkey = self:_empty_key()
  return function()
    local pkey_next = self.c_key()
    if libbcc.bpf_get_next_key(self.map_fd, pkey, pkey_next) < 0 then
      return nil
    end
    pkey = pkey_next
    return pkey[0]
  end
end
function BaseTable:items()
  local pkey = self:_empty_key()
  return function()
    local pkey_next = self.c_key()
    local pvalue = self.c_leaf()
    if libbcc.bpf_get_next_key(self.map_fd, pkey, pkey_next) < 0 then
      return nil
    end
    pkey = pkey_next
    assert(libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) == 0)
    return pkey[0], pvalue[0]
  end
end
local HashTable = class("HashTable", BaseTable)
function HashTable:initialize(bpf, map_id, map_fd, key_type, leaf_type)
  BaseTable.initialize(self, BaseTable.BPF_MAP_TYPE_HASH, bpf, map_id, map_fd, key_type, leaf_type)
end
function HashTable:delete(key)
  local pkey = self.c_key(key)
  return libbcc.bpf_delete_elem(self.map_fd, pkey) == 0
end
function HashTable:size()
  local n = 0
  self:each(function() n = n + 1 end)
  return n
end
local BaseArray = class("BaseArray", BaseTable)
function BaseArray:initialize(t_type, bpf, map_id, map_fd, key_type, leaf_type)
  BaseTable.initialize(self, t_type, bpf, map_id, map_fd, key_type, leaf_type)
  self.max_entries = tonumber(libbcc.bpf_table_max_entries_id(self.bpf.module, self.map_id))
end
function BaseArray:_normalize_key(key)
  assert(type(key) == "number", "invalid key (expected a number")
  if key < 0 then
    key = self.max_entries + key
  end
  assert(key < self.max_entries, string.format("out of range (%d >= %d)", key, self.max_entries))
  return key
end
function BaseArray:get(key)
  return BaseTable.get(self, self:_normalize_key(key))
end
function BaseArray:set(key, value)
  return BaseTable.set(self, self:_normalize_key(key), value)
end
function BaseArray:delete(key)
  assert(nil, "unsupported")
end
function BaseArray:items(with_index)
  local pkey = self.c_key()
  local max = self.max_entries
  local n = 0
  -- TODO
  return function()
    local pvalue = self.c_leaf()
    if n == max then
      return nil
    end
    pkey[0] = n
    n = n + 1
    if libbcc.bpf_lookup_elem(self.map_fd, pkey, pvalue) ~= 0 then
      return nil
    end
    if with_index then
      return n, pvalue[0] -- return 1-based index
    else
      return pvalue[0]
    end
  end
end
local Array = class("Array", BaseArray)
function Array:initialize(bpf, map_id, map_fd, key_type, leaf_type)
  BaseArray.initialize(self, BaseTable.BPF_MAP_TYPE_ARRAY, bpf, map_id, map_fd, key_type, leaf_type)
end
local PerfEventArray = class("PerfEventArray", BaseArray)
function PerfEventArray:initialize(bpf, map_id, map_fd, key_type, leaf_type)
  BaseArray.initialize(self, BaseTable.BPF_MAP_TYPE_PERF_EVENT_ARRAY, bpf, map_id, map_fd, key_type, leaf_type)
  self._callbacks = {}
end
local function _perf_id(id, cpu)
  return string.format("bcc:perf_event_array:%d:%d", tonumber(id), cpu or 0)
end
function PerfEventArray:_open_perf_buffer(cpu, callback, ctype, page_cnt, lost_cb)
  local _cb = ffi.cast("perf_reader_raw_cb",
    function (cookie, data, size)
      callback(cpu, ctype(data)[0])
    end)
  local _lost_cb = nil
  if lost_cb then
    _lost_cb = ffi.cast("perf_reader_lost_cb",
      function (cookie, lost)
        lost_cb(cookie, lost)
      end)
  end
  -- default to 8 pages per buffer
  local reader = libbcc.bpf_open_perf_buffer(_cb, _lost_cb, nil, -1, cpu, page_cnt or 8)
  assert(reader, "failed to open perf buffer")
  local fd = libbcc.perf_reader_fd(reader)
  self:set(cpu, fd)
  self.bpf:perf_buffer_store(_perf_id(self.map_id, cpu), reader)
  self._callbacks[cpu] = _cb
end
function PerfEventArray:open_perf_buffer(callback, data_type, data_params, page_cnt, lost_cb)
  assert(data_type, "a data type is needed for callback conversion")
  local ctype = ffi.typeof(data_type.."*", unpack(data_params or {}))
  for i = 0, Posix.cpu_count() - 1 do
    self:_open_perf_buffer(i, callback, ctype, page_cnt, lost_cb)
  end
end
local StackTrace = class("StackTrace", BaseTable)
StackTrace.static.MAX_STACK = 127
function StackTrace:initialize(bpf, map_id, map_fd, key_type, leaf_type)
  BaseTable.initialize(self, BaseTable.BPF_MAP_TYPE_STACK_TRACE, bpf, map_id, map_fd, key_type, leaf_type)
  self._stackp = self.c_leaf() -- FIXME: not threadsafe
end
function StackTrace:walk(id)
  local pkey = self.c_key(id)
  local pstack = self._stackp
  local i = 0
  if libbcc.bpf_lookup_elem(self.map_fd, pkey, pstack) < 0 then
    return nil
  end
  return function()
    if i >= StackTrace.MAX_STACK then
      return nil
    end
    local addr = pstack[0].ip[i]
    if addr == 0 then
      return nil
    end
    i = i + 1
    return addr
  end
end
function StackTrace:get(id, resolver)
  local stack = {}
  for addr in self:walk(id) do
    table.insert(stack, resolver and resolver(addr) or addr)
  end
  return stack
end
local function _decode_table_type(desc)
  local json = require("bcc.vendor.json")
  local json_desc = ffi.string(desc)
  local function _dec(t)
    if type(t) == "string" then
      return t
    end
    local fields = {}
    local struct = t[3] or "struct"
    for _, value in ipairs(t[2]) do
      local f = nil
      if #value == 2 then
        f = string.format("%s %s;", _dec(value[2]), value[1])
      elseif #value == 3 then
        if type(value[3]) == "table" then
          f = string.format("%s %s[%d];", _dec(value[2]), value[1], value[3][1])
        elseif type(value[3]) == "number" then
          local t = _dec(value[2])
          assert(t == "int" or t == "unsigned int",
            "bitfields can only appear in [unsigned] int types")
          f = string.format("%s %s:%d;", t, value[1], value[3])
        end
      end
      assert(f ~= nil, "failed to decode type "..json_desc)
      table.insert(fields, f)
    end
    assert(struct == "struct" or struct == "struct_packed" or struct == "union",
           "unknown complex type: "..struct)
    if struct == "union" then
      return string.format("union { %s }", table.concat(fields, " "))
    else
      return string.format("struct { %s }", table.concat(fields, " "))
    end
  end
  return _dec(json.parse(json_desc))
end
local function NewTable(bpf, name, key_type, leaf_type)
  local id = libbcc.bpf_table_id(bpf.module, name)
  local fd = libbcc.bpf_table_fd(bpf.module, name)
  if fd < 0 then
    return nil
  end
  local t_type = libbcc.bpf_table_type_id(bpf.module, id)
  local table = nil
  if t_type == BaseTable.BPF_MAP_TYPE_HASH then
    table = HashTable
  elseif t_type == BaseTable.BPF_MAP_TYPE_ARRAY then
    table = Array
  elseif t_type == BaseTable.BPF_MAP_TYPE_PERF_EVENT_ARRAY then
    table = PerfEventArray
  elseif t_type == BaseTable.BPF_MAP_TYPE_STACK_TRACE then
    table = StackTrace
  end
  assert(table, "unsupported table type %d" % t_type)
  if key_type == nil then
    local desc = libbcc.bpf_table_key_desc(bpf.module, name)
    assert(desc, "Failed to load BPF table description for "..name)
    key_type = _decode_table_type(desc)
  end
  if leaf_type == nil then
    local desc = libbcc.bpf_table_leaf_desc(bpf.module, name)
    assert(desc, "Failed to load BPF table description for "..name)
    leaf_type = _decode_table_type(desc)
  end
  log.info("key = %s value = %s", key_type, leaf_type)
  return table:new(bpf, id, fd, key_type, leaf_type)
end
return NewTable


sym

--[[
]]
local ffi = require("ffi")
local libbcc = require("bcc.libbcc")
local SYM = ffi.typeof("struct bcc_symbol[1]")
local function create_cache(pid)
  return {
    _CACHE = libbcc.bcc_symcache_new(pid or -1, nil),
    resolve = function(self, addr)
      local sym = SYM()
      if libbcc.bcc_symcache_resolve(self._CACHE, addr, sym) < 0 then
        return "[unknown]", 0x0
      end
      local name_res = ffi.string(sym[0].demangle_name)
      libbcc.bcc_symbol_free_demangle_name(sym);
      return name_res, sym[0].offset
    end
  }
end
local function check_path_symbol(module, symname, addr, pid, sym_off)
  local sym = SYM()
  local module_path
  local new_addr
  if libbcc.bcc_resolve_symname(module, symname, addr or 0x0, pid or 0, nil, sym) < 0 then
    if sym[0].module == nil then
      error("could not find library '%s' in the library path" % module)
    else
      module_path = ffi.string(sym[0].module)
      libbcc.bcc_procutils_free(sym[0].module)
      error("failed to resolve symbol '%s' in '%s'" % {
        symname, module_path})
    end
  end
  new_addr = sym[0].offset + (sym_off or 0)
  module_path = ffi.string(sym[0].module)
  libbcc.bcc_procutils_free(sym[0].module)
  return module_path, new_addr
end
return { create_cache=create_cache, check_path_symbol=check_path_symbol }


libbcc

--[[
]]
local ffi = require("ffi")
ffi.cdef[[
enum bpf_prog_type {
  BPF_PROG_TYPE_UNSPEC,
  BPF_PROG_TYPE_SOCKET_FILTER,
  BPF_PROG_TYPE_KPROBE,
  BPF_PROG_TYPE_SCHED_CLS,
  BPF_PROG_TYPE_SCHED_ACT,
};
int bcc_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, int map_flags);
int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags);
int bpf_lookup_elem(int fd, void *key, void *value);
int bpf_delete_elem(int fd, void *key);
int bpf_get_next_key(int fd, void *key, void *next_key);
int bcc_prog_load(enum bpf_prog_type prog_type, const char *name,
  const struct bpf_insn *insns, int insn_len,
  int log_level, char *log_buf, unsigned log_buf_size);
int bpf_attach_socket(int sockfd, int progfd);
/* create RAW socket and bind to interface 'name' */
int bpf_open_raw_sock(const char *name);
typedef void (*perf_reader_raw_cb)(void *cb_cookie, void *raw, int raw_size);
typedef void (*perf_reader_lost_cb)(void *cb_cookie, uint64_t lost);
int bpf_attach_kprobe(int progfd, int attach_type, const char *ev_name,
                      const char *fn_name, uint64_t fn_offset, int maxactive);
int bpf_detach_kprobe(const char *ev_name);
int bpf_attach_uprobe(int progfd, int attach_type, const char *ev_name,
                      const char *binary_path, uint64_t offset, int pid);
int bpf_detach_uprobe(const char *ev_name);
void * bpf_open_perf_buffer(perf_reader_raw_cb raw_cb, perf_reader_lost_cb lost_cb, void *cb_cookie, int pid, int cpu, int page_cnt);
int bpf_close_perf_event_fd(int fd);
]]
ffi.cdef[[
void * bpf_module_create_c(const char *filename, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit);
void * bpf_module_create_c_from_string(const char *text, unsigned flags, const char *cflags[], int ncflags, bool allow_rlimit);
void bpf_module_destroy(void *program);
unsigned bpf_module_kern_version(void *program);
size_t bpf_num_functions(void *program);
const char * bpf_function_name(void *program, size_t id);
void * bpf_function_start_id(void *program, size_t id);
void * bpf_function_start(void *program, const char *name);
size_t bpf_function_size_id(void *program, size_t id);
size_t bpf_function_size(void *program, const char *name);
size_t bpf_num_tables(void *program);
size_t bpf_table_id(void *program, const char *table_name);
int bpf_table_fd(void *program, const char *table_name);
int bpf_table_fd_id(void *program, size_t id);
int bpf_table_type(void *program, const char *table_name);
int bpf_table_type_id(void *program, size_t id);
size_t bpf_table_max_entries(void *program, const char *table_name);
size_t bpf_table_max_entries_id(void *program, size_t id);
int bpf_table_flags(void *program, const char *table_name);
int bpf_table_flags_id(void *program, size_t id);
const char * bpf_table_name(void *program, size_t id);
const char * bpf_table_key_desc(void *program, const char *table_name);
const char * bpf_table_key_desc_id(void *program, size_t id);
const char * bpf_table_leaf_desc(void *program, const char *table_name);
const char * bpf_table_leaf_desc_id(void *program, size_t id);
size_t bpf_table_key_size(void *program, const char *table_name);
size_t bpf_table_key_size_id(void *program, size_t id);
size_t bpf_table_leaf_size(void *program, const char *table_name);
size_t bpf_table_leaf_size_id(void *program, size_t id);
int bpf_table_key_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *key);
int bpf_table_leaf_snprintf(void *program, size_t id, char *buf, size_t buflen, const void *leaf);
int bpf_table_key_sscanf(void *program, size_t id, const char *buf, void *key);
int bpf_table_leaf_sscanf(void *program, size_t id, const char *buf, void *leaf);
]]
ffi.cdef[[
struct perf_reader;
void perf_reader_free(void *ptr);
int perf_reader_mmap(struct perf_reader *reader);
int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout);
int perf_reader_fd(struct perf_reader *reader);
void perf_reader_set_fd(struct perf_reader *reader, int fd);
]]
ffi.cdef[[
struct bcc_symbol {
	const char *name;
	const char *demangle_name;
	const char *module;
	uint64_t offset;
};
struct bcc_symbol_option {
  int use_debug_file;
  int check_debug_file_crc;
  int lazy_symbolize;
  uint32_t use_symbol_type;
};
int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr,
                        int pid, struct bcc_symbol_option *option,
                        struct bcc_symbol *sym);
void bcc_procutils_free(const char *ptr);
void *bcc_symcache_new(int pid, struct bcc_symbol_option *option);
void bcc_symbol_free_demangle_name(struct bcc_symbol *sym);
int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym);
void bcc_symcache_refresh(void *resolver);
]]
ffi.cdef[[
void *bcc_usdt_new_frompid(int pid);
void *bcc_usdt_new_frompath(const char *path);
void bcc_usdt_close(void *usdt);
int bcc_usdt_enable_probe(void *, const char *, const char *);
char *bcc_usdt_genargs(void *);
typedef void (*bcc_usdt_uprobe_cb)(const char *, const char *, uint64_t, int);
void bcc_usdt_foreach_uprobe(void *usdt, bcc_usdt_uprobe_cb callback);
]]
if rawget(_G, "BCC_STANDALONE") then
  return ffi.C
else
  return ffi.load(
    os.getenv("LIBBCC_SO_PATH") or
    rawget(_G, "LIBBCC_SO_PATH") or
    "bcc")
end


tracerpipe

--[[
]]
local TracerPipe = class("TracerPipe")
TracerPipe.static.TRACEFS = "/sys/kernel/debug/tracing"
TracerPipe.static.fields = "%s+(.-)%-(%d+)%s+%[(%d+)%]%s+(....)%s+([%d%.]+):.-:%s+(.+)"
function TracerPipe:close()
  if self.pipe ~= nil then
    self.pipe:close()
  end
end
function TracerPipe:open()
  if self.pipe == nil then
    self.pipe = assert(io.open(TracerPipe.TRACEFS .. "/trace_pipe"))
  end
  return self.pipe
end
function TracerPipe:readline()
  return self:open():read()
end
function TracerPipe:trace_fields()
  while true do
    local line = self:readline()
    if not line and self.nonblocking then
      return nil
    end
    if not line:starts("CPU:") then
      local task, pid, cpu, flags, ts, msg = line:match(TracerPipe.fields)
      if task ~= nil then
        return task, tonumber(pid), tonumber(cpu), flags, tonumber(ts), msg
      end
    end
  end
end
function TracerPipe:initialize(nonblocking)
  self.nonblocking = nonblocking
end
return TracerPipe


bpf

--[[
]]
local ffi = require("ffi")
local libbcc = require("bcc.libbcc")
local TracerPipe = require("bcc.tracerpipe")
local Table = require("bcc.table")
local Sym = require("bcc.sym")
local Bpf = class("BPF")
Bpf.static.open_kprobes = {}
Bpf.static.open_uprobes = {}
Bpf.static.perf_buffers = {}
end
function Bpf.static.cleanup()
  local function detach_all(probe_type, all_probes)
    for key, fd in pairs(all_probes) do
      libbcc.bpf_close_perf_event_fd(fd)
      -- skip bcc-specific kprobes
      if not key:starts("bcc:") then
        if probe_type == "kprobes" then
          libbcc.bpf_detach_kprobe(key)
        elseif probe_type == "uprobes" then
          libbcc.bpf_detach_uprobe(key)
        end
      end
      all_probes[key] = nil
    end
  end
  detach_all("kprobes", Bpf.static.open_kprobes)
  detach_all("uprobes", Bpf.static.open_uprobes)
  for key, perf_buffer in pairs(Bpf.static.perf_buffers) do
    libbcc.perf_reader_free(perf_buffer)
    Bpf.static.perf_buffers[key] = nil
  end
  if Bpf.static.tracer_pipe ~= nil then
    Bpf.static.tracer_pipe:close()
  end
end
function Bpf.static.SymbolCache(pid)
  return Sym.create_cache(pid)
end
function Bpf.static.num_open_uprobes()
  return table.count(Bpf.static.open_uprobes)
end
function Bpf.static.num_open_kprobes()
  return table.count(Bpf.static.open_kprobes)
end
Bpf.static.SCRIPT_ROOT = "./"
function Bpf.static.script_root(root)
  local dir, file = root:match'(.*/)(.*)'
  Bpf.static.SCRIPT_ROOT = dir or "./"
  return Bpf
end
local function _find_file(script_root, filename)
  if filename == nil then
    return nil
  end
  if os.exists(filename) then
    return filename
  end
  if not filename:starts("/") then
    filename = script_root .. filename
    if os.exists(filename) then
      return filename
    end
  end
  assert(nil, "failed to find file "..filename.." (root="..script_root..")")
end
function Bpf:initialize(args)
  self.funcs = {}
  self.tables = {}
  if args.usdt and args.text then
    args.text = args.usdt:_get_text() .. args.text
  end
  local cflags = table.join(Bpf.DEFAULT_CFLAGS, args.cflags)
  local cflags_ary = ffi.new("const char *[?]", #cflags, cflags)
  local llvm_debug = rawget(_G, "LIBBCC_LLVM_DEBUG") or args.debug or 0
  assert(type(llvm_debug) == "number")
  if args.text then
    log.info("\n%s\n", args.text)
    self.module = libbcc.bpf_module_create_c_from_string(args.text, llvm_debug, cflags_ary, #cflags, true)
  elseif args.src_file then
    local src = _find_file(Bpf.SCRIPT_ROOT, args.src_file)
    self.module = libbcc.bpf_module_create_c(src, llvm_debug, cflags_ary, #cflags, true)
  end
  assert(self.module ~= nil, "failed to compile BPF module")
  if args.usdt then
    args.usdt:_attach_uprobes(self)
  end
end
function Bpf:load_funcs(prog_type)
  prog_type = prog_type or "BPF_PROG_TYPE_KPROBE"
  local result = {}
  local fn_count = tonumber(libbcc.bpf_num_functions(self.module))
  for i = 0,fn_count-1 do
    local name = ffi.string(libbcc.bpf_function_name(self.module, i))
    table.insert(result, self:load_func(name, prog_type))
  end
  return result
end
function Bpf:load_func(fn_name, prog_type)
  if self.funcs[fn_name] ~= nil then
    return self.funcs[fn_name]
  end
  assert(libbcc.bpf_function_start(self.module, fn_name) ~= nil,
    "unknown program: "..fn_name)
  local fd = libbcc.bcc_prog_load(prog_type,
    fn_name,
    libbcc.bpf_function_start(self.module, fn_name),
    libbcc.bpf_function_size(self.module, fn_name),
    libbcc.bpf_module_kern_version(self.module),
    0, nil, 0)
  assert(fd >= 0, "failed to load BPF program "..fn_name)
  log.info("loaded %s (%d)", fn_name, fd)
  local fn = {bpf=self, name=fn_name, fd=fd}
  self.funcs[fn_name] = fn
  return fn
end
function Bpf:dump_func(fn_name)
  local start = libbcc.bpf_function_start(self.module, fn_name)
  assert(start ~= nil, "unknown program")
  local len = libbcc.bpf_function_size(self.module, fn_name)
  return ffi.string(start, tonumber(len))
end
function Bpf:attach_uprobe(args)
  Bpf.check_probe_quota(1)
  local path, addr = Sym.check_path_symbol(args.name, args.sym, args.addr, args.pid, args.sym_off)
  local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE')
  local ptype = args.retprobe and "r" or "p"
  local ev_name = string.format("%s_%s_0x%p", ptype, path:gsub("[^%a%d]", "_"), addr)
  local retprobe = args.retprobe and 1 or 0
  local res = libbcc.bpf_attach_uprobe(fn.fd, retprobe, ev_name, path, addr,
    args.pid or -1)
  assert(res >= 0, "failed to attach BPF to uprobe")
  self:probe_store("uprobe", ev_name, res)
  return self
end
function Bpf:attach_kprobe(args)
  -- TODO: allow the caller to glob multiple functions together
  Bpf.check_probe_quota(1)
  local fn = self:load_func(args.fn_name, 'BPF_PROG_TYPE_KPROBE')
  local event = args.event or ""
  local ptype = args.retprobe and "r" or "p"
  local ev_name = string.format("%s_%s", ptype, event:gsub("[%+%.]", "_"))
  local offset = args.fn_offset or 0
  local retprobe = args.retprobe and 1 or 0
  local maxactive = args.maxactive or 0
  local res = libbcc.bpf_attach_kprobe(fn.fd, retprobe, ev_name, event, offset, maxactive)
  assert(res >= 0, "failed to attach BPF to kprobe")
  self:probe_store("kprobe", ev_name, res)
  return self
end
function Bpf:pipe()
  if Bpf.tracer_pipe == nil then
    Bpf.tracer_pipe = TracerPipe:new()
  end
  return Bpf.tracer_pipe
end
function Bpf:get_table(name, key_type, leaf_type)
  if self.tables[name] == nil then
    self.tables[name] = Table(self, name, key_type, leaf_type)
  end
  return self.tables[name]
end
function Bpf:probe_store(t, id, fd)
  if t == "kprobe" then
    Bpf.open_kprobes[id] = fd
  elseif t == "uprobe" then
    Bpf.open_uprobes[id] = fd
  else
    error("unknown probe type '%s'" % t)
  end
  log.info("%s -> %s", id, fd)
end
function Bpf:perf_buffer_store(id, reader)
    Bpf.perf_buffers[id] = reader
    log.info("%s -> %s", id, reader)
end
function Bpf:probe_lookup(t, id)
  if t == "kprobe" then
    return Bpf.open_kprobes[id]
  elseif t == "uprobe" then
    return Bpf.open_uprobes[id]
  else
    return nil
  end
end
function Bpf:_perf_buffer_array()
  local perf_buffer_count = table.count(Bpf.perf_buffers)
  local readers = ffi.new("struct perf_reader*[?]", perf_buffer_count)
  local n = 0
  for _, r in pairs(Bpf.perf_buffers) do
    readers[n] = r
    n = n + 1
  end
  assert(n == perf_buffer_count)
  return readers, n
end
function Bpf:perf_buffer_poll_loop()
  local perf_buffers, perf_buffer_count = self:_perf_buffer_array()
  return pcall(function()
    while true do
      libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, -1)
    end
  end)
end
function Bpf:kprobe_poll_loop()
  return self:perf_buffer_poll_loop()
end
function Bpf:perf_buffer_poll(timeout)
  local perf_buffers, perf_buffer_count = self:_perf_buffer_array()
  libbcc.perf_reader_poll(perf_buffer_count, perf_buffers, timeout or -1)
end
function Bpf:kprobe_poll(timeout)
  self:perf_buffer_poll(timeout)
end
return Bpf


usdt

--[[
]]
local ffi = require("ffi")
local libbcc = require("bcc.libbcc")
local Usdt = class("USDT")
Usdt.static.open_contexts = {}
function Usdt.static.cleanup()
  for _, context in ipairs(Usdt.static.open_contexts) do
    context:_cleanup()
  end
end
function Usdt:initialize(args)
  assert(args.pid or args.path)
  if args.pid then
    self.pid = args.pid
    self.context = libbcc.bcc_usdt_new_frompid(args.pid)
  elseif args.path then
    self.path = args.path
    self.context = libbcc.bcc_usdt_new_frompath(args.path)
  end
  assert(self.context ~= nil, "failed to create USDT context")
  table.insert(Usdt.open_contexts, self)
end
function Usdt:enable_probe(args)
  assert(args.probe and args.fn_name)
  assert(libbcc.bcc_usdt_enable_probe(
    self.context, args.probe, args.fn_name) == 0)
end
function Usdt:_cleanup()
  libbcc.bcc_usdt_close(self.context)
  self.context = nil
end
function Usdt:_get_text()
  local argc = libbcc.bcc_usdt_genargs(self.context)
  assert(argc ~= nil)
  return ffi.string(argc)
end
function Usdt:_attach_uprobes(bpf)
  local uprobes = {}
  local cb = ffi.cast("bcc_usdt_uprobe_cb",
    function(binpath, fn_name, addr, pid)
      table.insert(uprobes, {name=ffi.string(binpath),
        addr=addr, fn_name=ffi.string(fn_name), pid=pid})
    end)
  libbcc.bcc_usdt_foreach_uprobe(self.context, cb)
  cb:free()
  for _, args in ipairs(uprobes) do
    bpf:attach_uprobe(args)
  end
end
return Usdt


init

--[[
]]
require("bcc.vendor.helpers")
return { BPF = require("bcc.bpf") }


json

--[[ json.lua

A compact pure-Lua JSON library.

This code is in the public domain:
https://gist.github.com/tylerneylon/59f4bcf316be525b30ab

The main functions are: json.stringify, json.parse.

## json.stringify:

This expects the following to be true of any tables being encoded:
 * They only have string or number keys. Number keys must be represented as
   strings in json; this is part of the json spec.
 * They are not recursive. Such a structure cannot be specified in json.

A Lua table is considered to be an array if and only if its set of keys is a
consecutive sequence of positive integers starting at 1. Arrays are encoded like
so: `[2, 3, false, "hi"]`. Any other type of Lua table is encoded as a json
object, encoded like so: `{"key1": 2, "key2": false}`.

Because the Lua nil value cannot be a key, and as a table value is considered
equivalent to a missing key, there is no way to express the json "null" value in
a Lua table. The only way this will output "null" is if your entire input obj is
nil itself.

An empty Lua table, {}, could be considered either a json object or array -
it's an ambiguous edge case. We choose to treat this as an object as it is the
more general type.

To be clear, none of the above considerations is a limitation of this code.
Rather, it is what we get when we completely observe the json specification for
as arbitrary a Lua object as json is capable of expressing.

## json.parse:

This function parses json, with the exception that it does not pay attention to
\u-escaped unicode code points in strings.

It is difficult for Lua to return null as a value. In order to prevent the loss
of keys with a null value in a json string, this function uses the one-off
table value json.null (which is just an empty table) to indicate null values.
This way you can check if a value is null with the conditional
`val == json.null`.

If you have control over the data and are using Lua, I would recommend just
avoiding null values in your data to begin with.

--]]

local json = {}

-- Internal functions.

local function kind_of(obj)
  if type(obj) ~= 'table' then return type(obj) end
  local i = 1
  for _ in pairs(obj) do
    if obj[i] ~= nil then i = i + 1 else return 'table' end
  end
  if i == 1 then return 'table' else return 'array' end
end

local function escape_str(s)
  local in_char  = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
  local out_char = {'\\', '"', '/',  'b',  'f',  'n',  'r',  't'}
  for i, c in ipairs(in_char) do
    s = s:gsub(c, '\\' .. out_char[i])
  end
  return s
end

-- Returns pos, did_find; there are two cases:
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
-- 2. Delimiter not found: pos = pos after leading space;     did_find = false.
-- This throws an error if err_if_missing is true and the delim is not found.
local function skip_delim(str, pos, delim, err_if_missing)
  pos = pos + #str:match('^%s*', pos)
  if str:sub(pos, pos) ~= delim then
    if err_if_missing then
      error('Expected ' .. delim .. ' near position ' .. pos)
    end
    return pos, false
  end
  return pos + 1, true
end

-- Expects the given pos to be the first character after the opening quote.
-- Returns val, pos; the returned pos is after the closing quote character.
local function parse_str_val(str, pos, val)
  val = val or ''
  local early_end_error = 'End of input found while parsing string.'
  if pos > #str then error(early_end_error) end
  local c = str:sub(pos, pos)
  if c == '"'  then return val, pos + 1 end
  if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
  -- We must have a \ character.
  local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
  local nextc = str:sub(pos + 1, pos + 1)
  if not nextc then error(early_end_error) end
  return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
end

-- Returns val, pos; the returned pos is after the number's final character.
local function parse_num_val(str, pos)
  local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
  local val = tonumber(num_str)
  if not val then error('Error parsing number at position ' .. pos .. '.') end
  return val, pos + #num_str
end

-- Public values and functions.

function json.stringify(obj, as_key)
  local s = {}  -- We'll build the string as an array of strings to be concatenated.
  local kind = kind_of(obj)  -- This is 'array' if it's an array or type(obj) otherwise.
  if kind == 'array' then
    if as_key then error('Can\'t encode array as key.') end
    s[#s + 1] = '['
    for i, val in ipairs(obj) do
      if i > 1 then s[#s + 1] = ', ' end
      s[#s + 1] = json.stringify(val)
    end
    s[#s + 1] = ']'
  elseif kind == 'table' then
    if as_key then error('Can\'t encode table as key.') end
    s[#s + 1] = '{'
    for k, v in pairs(obj) do
      if #s > 1 then s[#s + 1] = ', ' end
      s[#s + 1] = json.stringify(k, true)
      s[#s + 1] = ':'
      s[#s + 1] = json.stringify(v)
    end
    s[#s + 1] = '}'
  elseif kind == 'string' then
    return '"' .. escape_str(obj) .. '"'
  elseif kind == 'number' then
    if as_key then return '"' .. tostring(obj) .. '"' end
    return tostring(obj)
  elseif kind == 'boolean' then
    return tostring(obj)
  elseif kind == 'nil' then
    return 'null'
  else
    error('Unjsonifiable type: ' .. kind .. '.')
  end
  return table.concat(s)
end

json.null = {}  -- This is a one-off table to represent the null value.

function json.parse(str, pos, end_delim)
  pos = pos or 1
  if pos > #str then error('Reached unexpected end of input.') end
  local pos = pos + #str:match('^%s*', pos)  -- Skip whitespace.
  local first = str:sub(pos, pos)
  if first == '{' then  -- Parse an object.
    local obj, key, delim_found = {}, true, true
    pos = pos + 1
    while true do
      key, pos = json.parse(str, pos, '}')
      if key == nil then return obj, pos end
      if not delim_found then error('Comma missing between object items.') end
      pos = skip_delim(str, pos, ':', true)  -- true -> error if missing.
      obj[key], pos = json.parse(str, pos)
      pos, delim_found = skip_delim(str, pos, ',')
    end
  elseif first == '[' then  -- Parse an array.
    local arr, val, delim_found = {}, true, true
    pos = pos + 1
    while true do
      val, pos = json.parse(str, pos, ']')
      if val == nil then return arr, pos end
      if not delim_found then error('Comma missing between array items.') end
      arr[#arr + 1] = val
      pos, delim_found = skip_delim(str, pos, ',')
    end
  elseif first == '"' then  -- Parse a string.
    return parse_str_val(str, pos + 1)
  elseif first == '-' or first:match('%d') then  -- Parse a number.
    return parse_num_val(str, pos)
  elseif first == end_delim then  -- End of an object or array.
    return nil, pos + 1
  else  -- Parse true, false, or null.
    local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
    for lit_str, lit_val in pairs(literals) do
      local lit_end = pos + #lit_str - 1
      if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
    end
    local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
    error('Invalid json syntax starting at ' .. pos_info_str)
  end
end

return json


argparse

local function deep_update(t1, t2)
   for k, v in pairs(t2) do
      if type(v) == "table" then
         v = deep_update({}, v)
      end
      t1[k] = v
   end
   return t1
end
-- A property is a tuple {name, callback}.
-- properties.args is number of properties that can be set as arguments
-- when calling an object.
local function class(prototype, properties, parent)
   -- Class is the metatable of its instances.
   local cl = {}
   cl.__index = cl
   if parent then
      cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype)
   else
      cl.__prototype = prototype
   end
   if properties then
      local names = {}
      -- Create setter methods and fill set of property names.
      for _, property in ipairs(properties) do
         local name, callback = property[1], property[2]
         cl[name] = function(self, value)
            if not callback(self, value) then
               self["_" .. name] = value
            end
            return self
         end
         names[name] = true
      end
      function cl.__call(self, ...)
         -- When calling an object, if the first argument is a table,
         -- interpret keys as property names, else delegate arguments
         -- to corresponding setters in order.
         if type((...)) == "table" then
            for name, value in pairs((...)) do
               if names[name] then
                  self[name](self, value)
               end
            end
         else
            local nargs = select("#", ...)
            for i, property in ipairs(properties) do
               if i > nargs or i > properties.args then
                  break
               end
               local arg = select(i, ...)
               if arg ~= nil then
                  self[property[1]](self, arg)
               end
            end
         end
         return self
      end
   end
   -- If indexing class fails, fallback to its parent.
   local class_metatable = {}
   class_metatable.__index = parent
   function class_metatable.__call(self, ...)
      -- Calling a class returns its instance.
      -- Arguments are delegated to the instance.
      local object = deep_update({}, self.__prototype)
      setmetatable(object, self)
      return object(...)
   end
   return setmetatable(cl, class_metatable)
end
local function typecheck(name, types, value)
   for _, type_ in ipairs(types) do
      if type(value) == type_ then
         return true
      end
   end
   error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value)))
end
local function typechecked(name, ...)
   local types = {...}
   return {name, function(_, value) typecheck(name, types, value) end}
end
local multiname = {"name", function(self, value)
   typecheck("name", {"string"}, value)
   for alias in value:gmatch("%S+") do
      self._name = self._name or alias
      table.insert(self._aliases, alias)
   end
   -- Do not set _name as with other properties.
   return true
end}
local function parse_boundaries(str)
   if tonumber(str) then
      return tonumber(str), tonumber(str)
   end
   if str == "*" then
      return 0, math.huge
   end
   if str == "+" then
      return 1, math.huge
   end
   if str == "?" then
      return 0, 1
   end
   if str:match "^%d+%-%d+$" then
      local min, max = str:match "^(%d+)%-(%d+)$"
      return tonumber(min), tonumber(max)
   end
   if str:match "^%d+%+$" then
      local min = str:match "^(%d+)%+$"
      return tonumber(min), math.huge
   end
end
local function boundaries(name)
   return {name, function(self, value)
      typecheck(name, {"number", "string"}, value)
      local min, max = parse_boundaries(value)
      if not min then
         error(("bad property '%s'"):format(name))
      end
      self["_min" .. name], self["_max" .. name] = min, max
   end}
end
local actions = {}
local option_action = {"action", function(_, value)
   typecheck("action", {"function", "string"}, value)
   if type(value) == "string" and not actions[value] then
      error(("unknown action '%s'"):format(value))
   end
end}
local option_init = {"init", function(self)
   self._has_init = true
end}
local option_default = {"default", function(self, value)
   if type(value) ~= "string" then
      self._init = value
      self._has_init = true
      return true
   end
end}
local add_help = {"add_help", function(self, value)
   typecheck("add_help", {"boolean", "string", "table"}, value)
   if self._has_help then
      table.remove(self._options)
      self._has_help = false
   end
   if value then
      local help = self:flag()
         :description "Show this help message and exit."
         :action(function()
            print(self:get_help())
            os.exit(0)
         end)
      if value ~= true then
         help = help(value)
      end
      if not help._name then
         help "-h" "--help"
      end
      self._has_help = true
   end
end}
local Parser = class({
   _arguments = {},
   _options = {},
   _commands = {},
   _mutexes = {},
   _require_command = true,
   _handle_options = true
}, {
   args = 3,
   typechecked("name", "string"),
   typechecked("description", "string"),
   typechecked("epilog", "string"),
   typechecked("usage", "string"),
   typechecked("help", "string"),
   typechecked("require_command", "boolean"),
   typechecked("handle_options", "boolean"),
   typechecked("action", "function"),
   typechecked("command_target", "string"),
   add_help
})
local Command = class({
   _aliases = {}
}, {
   args = 3,
   multiname,
   typechecked("description", "string"),
   typechecked("epilog", "string"),
   typechecked("target", "string"),
   typechecked("usage", "string"),
   typechecked("help", "string"),
   typechecked("require_command", "boolean"),
   typechecked("handle_options", "boolean"),
   typechecked("action", "function"),
   typechecked("command_target", "string"),
   add_help
}, Parser)
local Argument = class({
   _minargs = 1,
   _maxargs = 1,
   _mincount = 1,
   _maxcount = 1,
   _defmode = "unused",
   _show_default = true
}, {
   args = 5,
   typechecked("name", "string"),
   typechecked("description", "string"),
   option_default,
   typechecked("convert", "function", "table"),
   boundaries("args"),
   typechecked("target", "string"),
   typechecked("defmode", "string"),
   typechecked("show_default", "boolean"),
   typechecked("argname", "string", "table"),
   option_action,
   option_init
})
local Option = class({
   _aliases = {},
   _mincount = 0,
   _overwrite = true
}, {
   args = 6,
   multiname,
   typechecked("description", "string"),
   option_default,
   typechecked("convert", "function", "table"),
   boundaries("args"),
   boundaries("count"),
   typechecked("target", "string"),
   typechecked("defmode", "string"),
   typechecked("show_default", "boolean"),
   typechecked("overwrite", "boolean"),
   typechecked("argname", "string", "table"),
   option_action,
   option_init
}, Argument)
function Argument:_get_argument_list()
   local buf = {}
   local i = 1
   while i <= math.min(self._minargs, 3) do
      local argname = self:_get_argname(i)
      if self._default and self._defmode:find "a" then
         argname = "[" .. argname .. "]"
      end
      table.insert(buf, argname)
      i = i+1
   end
   while i <= math.min(self._maxargs, 3) do
      table.insert(buf, "[" .. self:_get_argname(i) .. "]")
      i = i+1
      if self._maxargs == math.huge then
         break
      end
   end
   if i < self._maxargs then
      table.insert(buf, "...")
   end
   return buf
end
function Argument:_get_usage()
   local usage = table.concat(self:_get_argument_list(), " ")
   if self._default and self._defmode:find "u" then
      if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then
         usage = "[" .. usage .. "]"
      end
   end
   return usage
end
function actions.store_true(result, target)
   result[target] = true
end
function actions.store_false(result, target)
   result[target] = false
end
function actions.store(result, target, argument)
   result[target] = argument
end
function actions.count(result, target, _, overwrite)
   if not overwrite then
      result[target] = result[target] + 1
   end
end
function actions.append(result, target, argument, overwrite)
   result[target] = result[target] or {}
   table.insert(result[target], argument)
   if overwrite then
      table.remove(result[target], 1)
   end
end
function actions.concat(result, target, arguments, overwrite)
   if overwrite then
      error("'concat' action can't handle too many invocations")
   end
   result[target] = result[target] or {}
   for _, argument in ipairs(arguments) do
      table.insert(result[target], argument)
   end
end
function Argument:_get_action()
   local action, init
   if self._maxcount == 1 then
      if self._maxargs == 0 then
         action, init = "store_true", nil
      else
         action, init = "store", nil
      end
   else
      if self._maxargs == 0 then
         action, init = "count", 0
      else
         action, init = "append", {}
      end
   end
   if self._action then
      action = self._action
   end
   if self._has_init then
      init = self._init
   end
   if type(action) == "string" then
      action = actions[action]
   end
   return action, init
end
-- Returns placeholder for `narg`-th argument.
function Argument:_get_argname(narg)
   local argname = self._argname or self:_get_default_argname()
   if type(argname) == "table" then
      return argname[narg]
   else
      return argname
   end
end
function Argument:_get_default_argname()
   return "<" .. self._name .. ">"
end
function Option:_get_default_argname()
   return "<" .. self:_get_default_target() .. ">"
end
-- Returns label to be shown in the help message.
function Argument:_get_label()
   return self._name
end
function Option:_get_label()
   local variants = {}
   local argument_list = self:_get_argument_list()
   table.insert(argument_list, 1, nil)
   for _, alias in ipairs(self._aliases) do
      argument_list[1] = alias
      table.insert(variants, table.concat(argument_list, " "))
   end
   return table.concat(variants, ", ")
end
function Command:_get_label()
   return table.concat(self._aliases, ", ")
end
function Argument:_get_description()
   if self._default and self._show_default then
      if self._description then
         return ("%s (default: %s)"):format(self._description, self._default)
      else
         return ("default: %s"):format(self._default)
      end
   else
      return self._description or ""
   end
end
function Command:_get_description()
   return self._description or ""
end
function Option:_get_usage()
   local usage = self:_get_argument_list()
   table.insert(usage, 1, self._name)
   usage = table.concat(usage, " ")
   if self._mincount == 0 or self._default then
      usage = "[" .. usage .. "]"
   end
   return usage
end
function Argument:_get_default_target()
   return self._name
end
function Option:_get_default_target()
   local res
   for _, alias in ipairs(self._aliases) do
      if alias:sub(1, 1) == alias:sub(2, 2) then
         res = alias:sub(3)
         break
      end
   end
   res = res or self._name:sub(2)
   return (res:gsub("-", "_"))
end
function Option:_is_vararg()
   return self._maxargs ~= self._minargs
end
function Parser:_get_fullname()
   local parent = self._parent
   local buf = {self._name}
   while parent do
      table.insert(buf, 1, parent._name)
      parent = parent._parent
   end
   return table.concat(buf, " ")
end
function Parser:_update_charset(charset)
   charset = charset or {}
   for _, command in ipairs(self._commands) do
      command:_update_charset(charset)
   end
   for _, option in ipairs(self._options) do
      for _, alias in ipairs(option._aliases) do
         charset[alias:sub(1, 1)] = true
      end
   end
   return charset
end
function Parser:argument(...)
   local argument = Argument(...)
   table.insert(self._arguments, argument)
   return argument
end
function Parser:option(...)
   local option = Option(...)
   if self._has_help then
      table.insert(self._options, #self._options, option)
   else
      table.insert(self._options, option)
   end
   return option
end
function Parser:flag(...)
   return self:option():args(0)(...)
end
function Parser:command(...)
   local command = Command():add_help(true)(...)
   command._parent = self
   table.insert(self._commands, command)
   return command
end
function Parser:mutex(...)
   local options = {...}
   for i, option in ipairs(options) do
      assert(getmetatable(option) == Option, ("bad argument #%d to 'mutex' (Option expected)"):format(i))
   end
   table.insert(self._mutexes, options)
   return self
end
local max_usage_width = 70
local usage_welcome = "Usage: "
function Parser:get_usage()
   if self._usage then
      return self._usage
   end
   local lines = {usage_welcome .. self:_get_fullname()}
   local function add(s)
      if #lines[#lines]+1+#s <= max_usage_width then
         lines[#lines] = lines[#lines] .. " " .. s
      else
         lines[#lines+1] = (" "):rep(#usage_welcome) .. s
      end
   end
   -- This can definitely be refactored into something cleaner
   local mutex_options = {}
   local vararg_mutexes = {}
   -- First, put mutexes which do not contain vararg options and remember those which do
   for _, mutex in ipairs(self._mutexes) do
      local buf = {}
      local is_vararg = false
      for _, option in ipairs(mutex) do
         if option:_is_vararg() then
            is_vararg = true
         end
         table.insert(buf, option:_get_usage())
         mutex_options[option] = true
      end
      local repr = "(" .. table.concat(buf, " | ") .. ")"
      if is_vararg then
         table.insert(vararg_mutexes, repr)
      else
         add(repr)
      end
   end
   -- Second, put regular options
   for _, option in ipairs(self._options) do
      if not mutex_options[option] and not option:_is_vararg() then
         add(option:_get_usage())
      end
   end
   -- Put positional arguments
   for _, argument in ipairs(self._arguments) do
      add(argument:_get_usage())
   end
   -- Put mutexes containing vararg options
   for _, mutex_repr in ipairs(vararg_mutexes) do
      add(mutex_repr)
   end
   for _, option in ipairs(self._options) do
      if not mutex_options[option] and option:_is_vararg() then
         add(option:_get_usage())
      end
   end
   if #self._commands > 0 then
      if self._require_command then
         add("<command>")
      else
         add("[<command>]")
      end
      add("...")
   end
   return table.concat(lines, "\n")
end
local margin_len = 3
local margin_len2 = 25
local margin = (" "):rep(margin_len)
local margin2 = (" "):rep(margin_len2)
local function make_two_columns(s1, s2)
   if s2 == "" then
      return margin .. s1
   end
   s2 = s2:gsub("\n", "\n" .. margin2)
   if #s1 < (margin_len2-margin_len) then
      return margin .. s1 .. (" "):rep(margin_len2-margin_len-#s1) .. s2
   else
      return margin .. s1 .. "\n" .. margin2 .. s2
   end
end
function Parser:get_help()
   if self._help then
      return self._help
   end
   local blocks = {self:get_usage()}
   if self._description then
      table.insert(blocks, self._description)
   end
   local labels = {"Arguments:", "Options:", "Commands:"}
   for i, elements in ipairs{self._arguments, self._options, self._commands} do
      if #elements > 0 then
         local buf = {labels[i]}
         for _, element in ipairs(elements) do
            table.insert(buf, make_two_columns(element:_get_label(), element:_get_description()))
         end
         table.insert(blocks, table.concat(buf, "\n"))
      end
   end
   if self._epilog then
      table.insert(blocks, self._epilog)
   end
   return table.concat(blocks, "\n\n")
end
local function get_tip(context, wrong_name)
   local context_pool = {}
   local possible_name
   local possible_names = {}
   for name in pairs(context) do
      if type(name) == "string" then
         for i = 1, #name do
            possible_name = name:sub(1, i - 1) .. name:sub(i + 1)
            if not context_pool[possible_name] then
               context_pool[possible_name] = {}
            end
            table.insert(context_pool[possible_name], name)
         end
      end
   end
   for i = 1, #wrong_name + 1 do
      possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1)
      if context[possible_name] then
         possible_names[possible_name] = true
      elseif context_pool[possible_name] then
         for _, name in ipairs(context_pool[possible_name]) do
            possible_names[name] = true
         end
      end
   end
   local first = next(possible_names)
   if first then
      if next(possible_names, first) then
         local possible_names_arr = {}
         for name in pairs(possible_names) do
            table.insert(possible_names_arr, "'" .. name .. "'")
         end
         table.sort(possible_names_arr)
         return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?"
      else
         return "\nDid you mean '" .. first .. "'?"
      end
   else
      return ""
   end
end
local ElementState = class({
   invocations = 0
})
function ElementState:__call(state, element)
   self.state = state
   self.result = state.result
   self.element = element
   self.target = element._target or element:_get_default_target()
   self.action, self.result[self.target] = element:_get_action()
   return self
end
function ElementState:error(fmt, ...)
   self.state:error(fmt, ...)
end
function ElementState:convert(argument)
   local converter = self.element._convert
   if converter then
      local ok, err
      if type(converter) == "function" then
         ok, err = converter(argument)
      else
         ok = converter[argument]
      end
      if ok == nil then
         self:error(err and "%s" or "malformed argument '%s'", err or argument)
      end
      argument = ok
   end
   return argument
end
function ElementState:default(mode)
   return self.element._defmode:find(mode) and self.element._default
end
local function bound(noun, min, max, is_max)
   local res = ""
   if min ~= max then
      res = "at " .. (is_max and "most" or "least") .. " "
   end
   local number = is_max and max or min
   return res .. tostring(number) .. " " .. noun ..  (number == 1 and "" or "s")
end
function ElementState:invoke(alias)
   self.open = true
   self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name)
   self.overwrite = false
   if self.invocations >= self.element._maxcount then
      if self.element._overwrite then
         self.overwrite = true
      else
         self:error("%s must be used %s", self.name, bound("time", self.element._mincount, self.element._maxcount, true))
      end
   else
      self.invocations = self.invocations + 1
   end
   self.args = {}
   if self.element._maxargs <= 0 then
      self:close()
   end
   return self.open
end
function ElementState:pass(argument)
   argument = self:convert(argument)
   table.insert(self.args, argument)
   if #self.args >= self.element._maxargs then
      self:close()
   end
   return self.open
end
function ElementState:complete_invocation()
   while #self.args < self.element._minargs do
      self:pass(self.element._default)
   end
end
function ElementState:close()
   if self.open then
      self.open = false
      if #self.args < self.element._minargs then
         if self:default("a") then
            self:complete_invocation()
         else
            if #self.args == 0 then
               if getmetatable(self.element) == Argument then
                  self:error("missing %s", self.name)
               elseif self.element._maxargs == 1 then
                  self:error("%s requires an argument", self.name)
               end
            end
            self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs))
         end
      end
      local args = self.args
      if self.element._maxargs <= 1 then
         args = args[1]
      end
      if self.element._maxargs == 1 and self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then
         args = self.args
      end
      self.action(self.result, self.target, args, self.overwrite)
   end
end
local ParseState = class({
   result = {},
   options = {},
   arguments = {},
   argument_i = 1,
   element_to_mutexes = {},
   mutex_to_used_option = {},
   command_actions = {}
})
function ParseState:__call(parser, error_handler)
   self.parser = parser
   self.error_handler = error_handler
   self.charset = parser:_update_charset()
   self:switch(parser)
   return self
end
function ParseState:error(fmt, ...)
   self.error_handler(self.parser, fmt:format(...))
end
function ParseState:switch(parser)
   self.parser = parser
   if parser._action then
      table.insert(self.command_actions, {action = parser._action, name = parser._name})
   end
   for _, option in ipairs(parser._options) do
      option = ElementState(self, option)
      table.insert(self.options, option)
      for _, alias in ipairs(option.element._aliases) do
         self.options[alias] = option
      end
   end
   for _, mutex in ipairs(parser._mutexes) do
      for _, option in ipairs(mutex) do
         if not self.element_to_mutexes[option] then
            self.element_to_mutexes[option] = {}
         end
         table.insert(self.element_to_mutexes[option], mutex)
      end
   end
   for _, argument in ipairs(parser._arguments) do
      argument = ElementState(self, argument)
      table.insert(self.arguments, argument)
      argument:invoke()
   end
   self.handle_options = parser._handle_options
   self.argument = self.arguments[self.argument_i]
   self.commands = parser._commands
   for _, command in ipairs(self.commands) do
      for _, alias in ipairs(command._aliases) do
         self.commands[alias] = command
      end
   end
end
function ParseState:get_option(name)
   local option = self.options[name]
   if not option then
      self:error("unknown option '%s'%s", name, get_tip(self.options, name))
   else
      return option
   end
end
function ParseState:get_command(name)
   local command = self.commands[name]
   if not command then
      if #self.commands > 0 then
         self:error("unknown command '%s'%s", name, get_tip(self.commands, name))
      else
         self:error("too many arguments")
      end
   else
      return command
   end
end
function ParseState:invoke(option, name)
   self:close()
   if self.element_to_mutexes[option.element] then
      for _, mutex in ipairs(self.element_to_mutexes[option.element]) do
         local used_option = self.mutex_to_used_option[mutex]
         if used_option and used_option ~= option then
            self:error("option '%s' can not be used together with %s", name, used_option.name)
         else
            self.mutex_to_used_option[mutex] = option
         end
      end
   end
   if option:invoke(name) then
      self.option = option
   end
end
function ParseState:pass(arg)
   if self.option then
      if not self.option:pass(arg) then
         self.option = nil
      end
   elseif self.argument then
      if not self.argument:pass(arg) then
         self.argument_i = self.argument_i + 1
         self.argument = self.arguments[self.argument_i]
      end
   else
      local command = self:get_command(arg)
      self.result[command._target or command._name] = true
      if self.parser._command_target then
         self.result[self.parser._command_target] = command._name
      end
      self:switch(command)
   end
end
function ParseState:close()
   if self.option then
      self.option:close()
      self.option = nil
   end
end
function ParseState:finalize()
   self:close()
   for i = self.argument_i, #self.arguments do
      local argument = self.arguments[i]
      if #argument.args == 0 and argument:default("u") then
         argument:complete_invocation()
      else
         argument:close()
      end
   end
   if self.parser._require_command and #self.commands > 0 then
      self:error("a command is required")
   end
   for _, option in ipairs(self.options) do
      local name = option.name or ("option '%s'"):format(option.element._name)
      if option.invocations == 0 then
         if option:default("u") then
            option:invoke(name)
            option:complete_invocation()
            option:close()
         end
      end
      local mincount = option.element._mincount
      if option.invocations < mincount then
         if option:default("a") then
            while option.invocations < mincount do
               option:invoke(name)
               option:close()
            end
         elseif option.invocations == 0 then
            self:error("missing %s", name)
         else
            self:error("%s must be used %s", name, bound("time", mincount, option.element._maxcount))
         end
      end
   end
   for i = #self.command_actions, 1, -1 do
      self.command_actions[i].action(self.result, self.command_actions[i].name)
   end
end
function ParseState:parse(args)
   for _, arg in ipairs(args) do
      local plain = true
      if self.handle_options then
         local first = arg:sub(1, 1)
         if self.charset[first] then
            if #arg > 1 then
               plain = false
               if arg:sub(2, 2) == first then
                  if #arg == 2 then
                     self:close()
                     self.handle_options = false
                  else
                     local equals = arg:find "="
                     if equals then
                        local name = arg:sub(1, equals - 1)
                        local option = self:get_option(name)
                        if option.element._maxargs <= 0 then
                           self:error("option '%s' does not take arguments", name)
                        end
                        self:invoke(option, name)
                        self:pass(arg:sub(equals + 1))
                     else
                        local option = self:get_option(arg)
                        self:invoke(option, arg)
                     end
                  end
               else
                  for i = 2, #arg do
                     local name = first .. arg:sub(i, i)
                     local option = self:get_option(name)
                     self:invoke(option, name)
                     if i ~= #arg and option.element._maxargs > 0 then
                        self:pass(arg:sub(i + 1))
                        break
                     end
                  end
               end
            end
         end
      end
      if plain then
         self:pass(arg)
      end
   end
   self:finalize()
   return self.result
end
function Parser:error(msg)
   io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg))
   os.exit(1)
end
-- Compatibility with strict.lua and other checkers:
local default_cmdline = rawget(_G, "arg") or {}
function Parser:_parse(args, error_handler)
   return ParseState(self, error_handler):parse(args or default_cmdline)
end
function Parser:parse(args)
   return self:_parse(args, self.error)
end
local function xpcall_error_handler(err)
   return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2)
end
function Parser:pparse(args)
   local parse_error
   local ok, result = xpcall(function()
      return self:_parse(args, function(_, err)
         parse_error = err
         error(err, 0)
      end)
   end, xpcall_error_handler)
   if ok then
      return true, result
   elseif not parse_error then
      error(result, 0)
   else
      return false, parse_error
   end
end
return function(...)
   return Parser(default_cmdline[0]):add_help(true)(...)
end


helpers

do
  local ffi = require("ffi")
  local ptrtype = ffi.typeof("uint64_t")
  local strformat = string.format
  function string.format(format, ...)
    local args = {...}
    local match_no = 1
    local newfmt, count = string.gsub(format, "()%%(.-)(%a)",
      function(_, mods, t)
        local n = match_no
        match_no = match_no + 1
        if t == 'p' and ffi.istype(ptrtype, args[n]) then
          local lo = tonumber(args[n] % 4294967296ULL)
          local hi = tonumber(args[n] / 4294967296ULL)
          args[n] = (hi == 0) and strformat("%x", lo) or strformat("%x%08x", hi, lo)
          return "%"..mods.."s"
        end
      end)
    if count == 0 then
      return strformat(format, ...)
    else
      return strformat(newfmt, unpack(args,1,select('#',...)))
    end
  end
end
function string.starts(s, p)
  return string.sub(s, 1, string.len(p)) == p
end
function string.lstrip(s, p)
  return string.sub(s, string.len(p) + 1)
end
function string.ends(s, e)
  return e == '' or string.sub(s, -string.len(e))==e
end
function string.escape(s)
  return s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')
end
--- split a string into a list of strings separated by a delimiter.
function string.split(s,re,plain,n)
  local find,sub,append = string.find, string.sub, table.insert
  local i1,ls = 1,{}
  if not re then re = '%s+' end
  if re == '' then return {s} end
  while true do
    local i2,i3 = find(s,re,i1,plain)
    if not i2 then
      local last = sub(s,i1)
      if last ~= '' then append(ls,last) end
      if #ls == 1 and ls[1] == '' then
        return {}
      else
        return ls
      end
    end
    append(ls,sub(s,i1,i2-1))
    if n and #ls == n then
      ls[#ls] = sub(s,i1)
      return ls
    end
    i1 = i3+1
  end
end
function table.count(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end
function table.bsearch(list, value, mkval)
  local low = 1
  local high = #list
  while low <= high do
    local mid = math.floor((low+high)/2)
    local this = mkval and mkval(list[mid]) or list[mid]
    if this > value then
      high = mid - 1
    elseif this < value then
      low = mid + 1
    else
      return mid
    end
  end
  return low - 1
end
function table.join(a, b)
  assert(a)
  if b == nil or #b == 0 then
    return a
  end
  local res = {}
  for _, v in ipairs(a) do
    table.insert(res, v)
  end
  for _, v in ipairs(b) do
    table.insert(res, v)
  end
  return res
end
function table.build(iterator_fn, build_fn)
  build_fn = (build_fn or function(arg) return arg end)
  local res = {}
  while true do
    local vars = {iterator_fn()}
    if vars[1] == nil then break end
    table.insert(res, build_fn(vars))
  end
  return res
end
function table.values(T)
  local V = {}
  for k, v in pairs(T) do
    table.insert(V, v)
  end
  return V
end
function table.tuples(T)
  local i = 0
  local n = table.getn(t)
  return function ()
    i = i + 1
    if i <= n then return t[i][1], t[i][2] end
  end
end
getmetatable("").__mod = function(a, b)
  if not b then
    return a
  elseif type(b) == "table" then
    return string.format(a, unpack(b))
  else
    return string.format(a, b)
  end
end
function os.exists(path)
  local f=io.open(path,"r")
  if f~=nil then
    io.close(f)
    return true
  else
    return false
  end
end
function os.spawn(...)
  local cmd = string.format(...)
  local proc = assert(io.popen(cmd))
  local out = proc:read("*a")
  proc:close()
  return out
end
local function logline(...)
  if not log.enabled then
    return
  end
  local c_green = "\27[32m"
  local c_grey = "\27[1;30m"
  local c_clear = "\27[0m"
  local msg = string.format(...)
  local info = debug.getinfo(2, "Sln")
  local line = string.format("%s[%s:%s]%s %s", c_grey,
    info.short_src:match("^.+/(.+)$"), info.currentline, c_clear, info.name)
  io.stderr:write(
    string.format("%s[%s]%s %s: %s\n", c_green,
      os.date("%H:%M:%S"), c_clear, line, msg))
end
setmetatable(_G, {
  __newindex = function (_, n)
    error("attempt to write to undeclared variable "..n, 2)
  end,
  __index = function (_, n)
    error("attempt to read undeclared variable "..n, 2)
  end,
})
rawset(_G, "log", { info = logline, enabled = false })
rawset(_G, "class", require("bcc.vendor.middleclass"))


middleclass

local middleclass = {
  _VERSION     = 'middleclass v4.0.0',
  _DESCRIPTION = 'Object Orientation for Lua',
  _URL         = 'https://github.com/kikito/middleclass',
  ]]
}
local function _createIndexWrapper(aClass, f)
  if f == nil then
    return aClass.__instanceDict
  else
    return function(self, name)
      local value = aClass.__instanceDict[name]
      if value ~= nil then
        return value
      elseif type(f) == "function" then
        return (f(self, name))
      else
        return f[name]
      end
    end
  end
end
local function _propagateInstanceMethod(aClass, name, f)
  f = name == "__index" and _createIndexWrapper(aClass, f) or f
  aClass.__instanceDict[name] = f
  for subclass in pairs(aClass.subclasses) do
    if rawget(subclass.__declaredMethods, name) == nil then
      _propagateInstanceMethod(subclass, name, f)
    end
  end
end
local function _declareInstanceMethod(aClass, name, f)
  aClass.__declaredMethods[name] = f
  if f == nil and aClass.super then
    f = aClass.super.__instanceDict[name]
  end
  _propagateInstanceMethod(aClass, name, f)
end
local function _tostring(self) return "class " .. self.name end
local function _call(self, ...) return self:new(...) end
local function _createClass(name, super)
  local dict = {}
  dict.__index = dict
  local aClass = { name = name, super = super, static = {},
                   __instanceDict = dict, __declaredMethods = {},
                   subclasses = setmetatable({}, {__mode='k'})  }
  if super then
    setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or super.static[k] end })
  else
    setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })
  end
  setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,
                         __call = _call, __newindex = _declareInstanceMethod })
  return aClass
end
local function _includeMixin(aClass, mixin)
  assert(type(mixin) == 'table', "mixin must be a table")
  for name,method in pairs(mixin) do
    if name ~= "included" and name ~= "static" then aClass[name] = method end
  end
  for name,method in pairs(mixin.static or {}) do
    aClass.static[name] = method
  end
  if type(mixin.included)=="function" then mixin:included(aClass) end
  return aClass
end
local DefaultMixin = {
  __tostring   = function(self) return "instance of " .. tostring(self.class) end,
  initialize   = function(self, ...) end,
  isInstanceOf = function(self, aClass)
    return type(self)       == 'table' and
           type(self.class) == 'table' and
           type(aClass)     == 'table' and
           ( aClass == self.class or
             type(aClass.isSubclassOf) == 'function' and
             self.class:isSubclassOf(aClass) )
  end,
  static = {
    allocate = function(self)
      assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
      return setmetatable({ class = self }, self.__instanceDict)
    end,
    new = function(self, ...)
      assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'")
      local instance = self:allocate()
      instance:initialize(...)
      return instance
    end,
    subclass = function(self, name)
      assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
      assert(type(name) == "string", "You must provide a name(string) for your class")
      local subclass = _createClass(name, self)
      for methodName, f in pairs(self.__instanceDict) do
        _propagateInstanceMethod(subclass, methodName, f)
      end
      subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
      self.subclasses[subclass] = true
      self:subclassed(subclass)
      return subclass
    end,
    subclassed = function(self, other) end,
    isSubclassOf = function(self, other)
      return type(other)      == 'table' and
             type(self)       == 'table' and
             type(self.super) == 'table' and
             ( self.super == other or
               type(self.super.isSubclassOf) == 'function' and
               self.super:isSubclassOf(other) )
    end,
    include = function(self, ...)
      assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
      for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
      return self
    end
  }
}
function middleclass.class(name, super)
  assert(type(name) == 'string', "A name (string) is needed for the new class")
  return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin)
end
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
return middleclass


posix

--[[
]]
local ffi = require("ffi")
-- Avoid duplicate declarations if syscall library is present
local has_syscall, _ = pcall(require, "syscall")
if not has_syscall then
  ffi.cdef [[
  typedef int clockid_t;
  typedef long time_t;
  struct timespec {
    time_t tv_sec;
    long tv_nsec;
  };
  int clock_gettime(clockid_t clk_id, struct timespec *tp);
  int clock_nanosleep(clockid_t clock_id, int flags,
    const struct timespec *request, struct timespec *remain);
  ]]
end
ffi.cdef [[
int get_nprocs(void);
uint64_t strtoull(const char *nptr, char **endptr, int base);
]]
local CLOCK = {
  REALTIME                  = 0,
  MONOTONIC                 = 1,
  PROCESS_CPUTIME_ID        = 2,
  THREAD_CPUTIME_ID         = 3,
  MONOTONIC_RAW             = 4,
  REALTIME_COARSE           = 5,
  MONOTONIC_COARSE          = 6,
}
local function time_ns(clock)
  local ts = ffi.new("struct timespec[1]")
  assert(ffi.C.clock_gettime(clock or CLOCK.MONOTONIC_RAW, ts) == 0,
    "clock_gettime() failed: "..ffi.errno())
  return tonumber(ts[0].tv_sec * 1e9 + ts[0].tv_nsec)
end
local function sleep(seconds, clock)
  local s, ns = math.modf(seconds)
  local ts = ffi.new("struct timespec[1]")
  ts[0].tv_sec = s
  ts[0].tv_nsec = ns / 1e9
  ffi.C.clock_nanosleep(clock or CLOCK.MONOTONIC, 0, ts, nil)
end
local function cpu_count()
  return tonumber(ffi.C.get_nprocs())
end
local function tonumber64(n, base)
  assert(type(n) == "string")
  return ffi.C.strtoull(n, nil, base or 10)
end
return {
  time_ns=time_ns,
  sleep=sleep,
  CLOCK=CLOCK,
  cpu_count=cpu_count,
  tonumber64=tonumber64,
}


ljbytecode

--[[
]]
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local bit = require('bit')
local shr, band = bit.rshift, bit.band
-- Decode LuaJIT 2.0 Byte Format
-- Reference: http://wiki.luajit.org/Bytecode-2.0
-- Thanks to LJ, we get code in portable bytecode with constants folded, basic
-- virtual registers allocated etc.
-- No SSA IR, type inference or advanced optimizations because the code wasn't traced yet.
local function decode_ins(func, pc)
	local ins, m = jutil.funcbc(func, pc)
	if not ins then return nil end
	local op, ma, mb, mc = band(ins, 0xff), band(m, 7), band(m, 15*8), band(m, 15*128)
	local a, b, c, d = band(shr(ins, 8), 0xff), nil, nil, shr(ins, 16)
	if mb ~= 0 then
		d = band(d, 0xff)
		b = shr(ins, 24)
	end
	if ma == 5 then          -- BCMuv
	    a = jutil.funcuvname(func, a)
	end
	if mc == 13*128 then     -- BCMjump
		c = pc+d-0x7fff
	elseif mc == 14*128 then -- BCMcdata
		c = jutil.funck(func, -d-1)
	elseif mc == 9*128 then  -- BCMint
		c = jutil.funck(func, d)
	elseif mc == 10*128 then -- BCMstr
		c = jutil.funck(func, -d-1)
	elseif mc == 5*128 then  -- BCMuv
	    c = jutil.funcuvname(func, d)
	end
	-- Convert version-specific opcode to string
	op = 6*op
	op = string.sub(vmdef.bcnames, op+1, op+6):match('[^%s]+')
	return pc, op, a, b, c, d
end
-- Decoder closure
local function decoder(func)
	local pc = 0
	return function ()
		pc = pc + 1
		return decode_ins(func, pc)
	end
end
-- Hexdump generated code
local function dump(func)
	return require('jit.bc').dump(func)
end
return {
	decode = decode_ins,
	decoder = decoder,
	dump = dump,
	funcinfo = function (...) return jutil.funcinfo(...) end,
}


proto

--[[
]]
local ffi = require('ffi')
local BPF = ffi.typeof('struct bpf')
ffi.cdef [[
struct sk_buff {
	uint32_t len;
	uint32_t pkt_type;
	uint32_t mark;
	uint32_t queue_mapping;
	uint32_t protocol;
	uint32_t vlan_present;
	uint32_t vlan_tci;
	uint32_t vlan_proto;
	uint32_t priority;
	uint32_t ingress_ifindex;
	uint32_t ifindex;
	uint32_t tc_index;
	uint32_t cb[5];
	uint32_t hash;
	uint32_t tc_classid;
	uint32_t data;
	uint32_t data_end;
	uint32_t napi_id;
	/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
	uint32_t family;
	uint32_t remote_ip4;	/* Stored in network byte order */
	uint32_t local_ip4;	/* Stored in network byte order */
	uint32_t remote_ip6[4];	/* Stored in network byte order */
	uint32_t local_ip6[4];	/* Stored in network byte order */
	uint32_t remote_port;	/* Stored in network byte order */
	uint32_t local_port;	/* stored in host byte order */
	/* ... here. */
	uint32_t data_meta;
};
struct net_off_t {
	uint8_t  ver:4;
} __attribute__((packed));
struct eth_t {
	uint8_t  dst[6];
	uint8_t  src[6];
	uint16_t type;
} __attribute__((packed));
struct dot1q_t {
	uint16_t pri:3;
	uint16_t cfi:1;
	uint16_t vlanid:12;
	uint16_t type;
} __attribute__((packed));
struct arp_t {
	uint16_t htype;
	uint16_t ptype;
	uint8_t  hlen;
	uint8_t  plen;
	uint16_t oper;
	uint8_t  sha[6];
	uint32_t spa;
	uint8_t  tha[6];
	uint32_t tpa;
} __attribute__((packed));
struct ip_t {
	uint8_t  ver:4;
	uint8_t  hlen:4;
	uint8_t  tos;
	uint16_t tlen;
	uint16_t identification;
	uint16_t ffo_unused:1;
	uint16_t df:1;
	uint16_t mf:1;
	uint16_t foffset:13;
	uint8_t  ttl;
	uint8_t  proto;
	uint16_t hchecksum;
	uint32_t src;
	uint32_t dst;
} __attribute__((packed));
struct icmp_t {
	uint8_t  type;
	uint8_t  code;
	uint16_t checksum;
} __attribute__((packed));
struct ip6_t {
	uint32_t ver:4;
	uint32_t priority:8;
	uint32_t flow_label:20;
	uint16_t payload_len;
	uint8_t  next_header;
	uint8_t  hop_limit;
	uint64_t src_hi;
	uint64_t src_lo;
	uint64_t dst_hi;
	uint64_t dst_lo;
} __attribute__((packed));
struct ip6_opt_t {
	uint8_t  next_header;
	uint8_t  ext_len;
	uint8_t  pad[6];
} __attribute__((packed));
struct icmp6_t {
	uint8_t  type;
	uint8_t  code;
	uint16_t checksum;
} __attribute__((packed));
struct udp_t {
	uint16_t src_port;
	uint16_t dst_port;
	uint16_t length;
	uint16_t crc;
} __attribute__((packed));
struct tcp_t {
	uint16_t src_port;
	uint16_t dst_port;
	uint32_t seq_num;
	uint32_t ack_num;
	uint8_t  offset:4;
	uint8_t  reserved:4;
	uint8_t  flag_cwr:1;
	uint8_t  flag_ece:1;
	uint8_t  flag_urg:1;
	uint8_t  flag_ack:1;
	uint8_t  flag_psh:1;
	uint8_t  flag_rst:1;
	uint8_t  flag_syn:1;
	uint8_t  flag_fin:1;
	uint16_t rcv_wnd;
	uint16_t cksum;
	uint16_t urg_ptr;
} __attribute__((packed));
struct vxlan_t {
	uint32_t rsv1:4;
	uint32_t iflag:1;
	uint32_t rsv2:3;
	uint32_t rsv3:24;
	uint32_t key:24;
	uint32_t rsv4:8;
} __attribute__((packed));
]]
-- Architecture-specific ptrace register layout
local S = require('syscall')
local arch = S.abi.arch
local parm_to_reg = {}
if arch == 'x64' then
	ffi.cdef [[
	struct pt_regs {
		unsigned long r15;
		unsigned long r14;
		unsigned long r13;
		unsigned long r12;
		unsigned long bp;
		unsigned long bx;
		unsigned long r11;
		unsigned long r10;
		unsigned long r9;
		unsigned long r8;
		unsigned long ax;
		unsigned long cx;
		unsigned long dx;
		unsigned long si;
		unsigned long di;
		unsigned long orig_ax;
		unsigned long ip;
		unsigned long cs;
		unsigned long flags;
		unsigned long sp;
		unsigned long ss;
	};]]
	parm_to_reg = {parm1='di', parm2='si', parm3='dx', parm4='cx', parm5='r8', ret='sp', fp='bp'}
else
	ffi.cdef 'struct pt_regs {};'
end
-- Map symbolic registers to architecture ABI
ffi.metatype('struct pt_regs', {
		__index = function (_ --[[t]],k)
			return assert(parm_to_reg[k], 'no such register: '..k)
		end,
})
local M = {}
-- Dissector interface
local function dissector(type, e, dst, src, field)
	local parent = e.V[src].const
	-- Create new dissector variable
	e.vcopy(dst, src)
	-- Compute and materialize new dissector offset from parent
	e.V[dst].const = {off=e.V[src].const.off, __dissector=e.V[src].const.__dissector}
	parent.__dissector[field](e, dst)
	e.V[dst].const.__dissector = type
end
M.dissector = dissector
-- Get current effective offset, load field value at an offset relative to it and
-- add its value to compute next effective offset (e.g. udp_off = ip_off + pkt[ip_off].hlen)
local function next_offset(e, var, type, off, mask, shift)
	local d = e.V[var].const
	-- Materialize relative offset value in R0
	local dst_reg, tmp_reg
	if d.off then
		dst_reg = e.vreg(var, 0, true)
		tmp_reg = dst_reg -- Use target register to avoid copy
		e.emit(BPF.LD + BPF.ABS + e.const_width[ffi.sizeof(type)], tmp_reg, 0, 0, d.off + off or 0)
	else
		tmp_reg = e.vreg(e.tmpvar, 0, true, type) -- Reserve R0 for temporary relative offset
		dst_reg = e.vreg(var) -- Must rematerialize (if it was spilled by tmp var)
		e.emit(BPF.LD + BPF.IND + e.const_width[ffi.sizeof(type)], tmp_reg, dst_reg, 0, off or 0)
	end
	-- Finalize relative offset
	if mask then
		e.emit(BPF.ALU + BPF.AND + BPF.K, tmp_reg, 0, 0, mask)
	end
	if shift and shift ~= 0 then
		local op = BPF.LSH
		if shift < 0 then
			op = BPF.RSH
			shift = -shift
		end
		e.emit(BPF.ALU + op + BPF.K, tmp_reg, 0, 0, shift)
	end
	-- Add to base offset to turn it into effective address
	if dst_reg ~= tmp_reg then
		e.emit(BPF.ALU + BPF.ADD + BPF.X, dst_reg, tmp_reg, 0, 0)
	else
		e.emit(BPF.ALU + BPF.ADD + BPF.K, dst_reg, 0, 0, d.off)
	end
	-- Discard temporary allocations
	d.off = nil
	e.V[e.tmpvar].reg = nil
end
local function next_skip(e, var, off)
	local d = e.V[var].const
	if not d.off then
		local dst_reg = e.vreg(var)
		e.emit(BPF.ALU64 + BPF.ADD + BPF.K, dst_reg, 0, 0, off)
	else
		d.off = d.off + off
	end
end
local function skip_eth(e, dst)
	-- IP starts right after ETH header (fixed size)
	local d = e.V[dst].const
	d.off = d.off + ffi.sizeof('struct eth_t')
end
-- Export types
M.type = function(typestr, t)
	t = t or {}
	t.__dissector=ffi.typeof(typestr)
	return t
end
M.skb     = M.type('struct sk_buff', {source='ptr_to_ctx'})
M.pt_regs = M.type('struct pt_regs', {source='ptr_to_probe'})
M.pkt     = M.type('struct eth_t',   {off=0, source='ptr_to_pkt'}) -- skb needs special accessors
-- M.eth     = function (...) return dissector(ffi.typeof('struct eth_t'), ...) end
M.dot1q   = function (...) return dissector(ffi.typeof('struct dot1q_t'), ...) end
M.arp     = function (...) return dissector(ffi.typeof('struct arp_t'), ...) end
M.icmp    = function (...) return dissector(ffi.typeof('struct icmp_t'), ...) end
M.ip      = function (...) return dissector(ffi.typeof('struct ip_t'), ...) end
M.icmp6   = function (...) return dissector(ffi.typeof('struct icmp6_t'), ...) end
M.ip6     = function (...) return dissector(ffi.typeof('struct ip6_t'), ...) end
M.ip6_opt = function (...) return dissector(ffi.typeof('struct ip6_opt_t'), ...) end
M.udp     = function (...) return dissector(ffi.typeof('struct udp_t'), ...) end
M.tcp     = function (...) return dissector(ffi.typeof('struct tcp_t'), ...) end
M.vxlan   = function (...) return dissector(ffi.typeof('struct vxlan_t'), ...) end
M.data    = function (...) return dissector(ffi.typeof('uint8_t'), ...) end
M.net_off = function (...) return dissector(ffi.typeof('struct net_off_t'), ...) end
-- Metatables
ffi.metatype(ffi.typeof('struct eth_t'), {
	__index = {
		ip = skip_eth,
		ip6 = skip_eth,
		net_off = function (e, dst)
			next_skip(e, dst, BPF.NET_OFF)
		end,
	}
})
ffi.metatype(ffi.typeof('struct net_off_t'), {
	__index = {
		ip = function () end,
		ip6 = function () end,
	}
})
ffi.metatype(ffi.typeof('struct ip_t'), {
	__index = {
		-- Skip IP header length (stored as number of words)
		-- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets
		-- Mask first nibble and shift by 2 (multiplication by 4)
		icmp = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end,
		udp  = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end,
		tcp  = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), 0, 0x0f, 2) end,
	}
})
ffi.metatype(ffi.typeof('struct ip6_t'), {
	__index = {
		-- Skip fixed IPv6 header length (40 bytes)
		-- The caller must check the value of `next_header` to skip any extension headers
		icmp6 = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end,
		udp  = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end,
		tcp  = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end,
		ip6_opt = function(e, dst) next_skip(e, dst, ffi.sizeof('struct ip6_t'), 0) end,
	}
})
local ip6_opt_ext_len_off = ffi.offsetof('struct ip6_opt_t', 'ext_len')
ffi.metatype(ffi.typeof('struct ip6_opt_t'), {
	__index = {
		-- Skip IPv6 extension header length (field `ext_len`)
		icmp6 = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end,
		udp  = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end,
		tcp  = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end,
		ip6_opt = function(e, dst) next_offset(e, dst, ffi.typeof('uint8_t'), ip6_opt_ext_len_off) end,
	}
})
ffi.metatype(ffi.typeof('struct tcp_t'), {
	__index = {
		-- Skip TCP header length (stored as number of words)
		-- e.g. hlen = 5, Header Length = 5 x sizeof(u32) = 20 octets
		data = function(e, dst)
			next_offset(e, dst, ffi.typeof('uint8_t'), ffi.offsetof('struct tcp_t', 'offset'), 0xf0, -2)
		end,
	}
})
ffi.metatype(ffi.typeof('struct udp_t'), {
	__index = {
		-- Skip UDP header length (8 octets)
		data = function(e, dst)
			next_skip(e, dst, ffi.sizeof('struct udp_t'))
		end,
	}
})
-- Constants
M.c = {
	eth = { -- Constants http://standards.ieee.org/regauth/ethertype
		ip     = 0x0800, -- IP (v4) protocol
		ip6    = 0x86dd, -- IP (v6) protocol
		arp    = 0x0806, -- Address resolution protocol
		revarp = 0x8035, -- Reverse addr resolution protocol
		vlan   = 0x8100, -- IEEE 802.1Q VLAN tagging
	},
	ip = {
		-- Reserved Addresses
		addr_any         = 0x00000000, -- 0.0.0.0
		addr_broadcast   = 0xffffffff, -- 255.255.255.255
		addr_loopback    = 0x7f000001, -- 127.0.0.1
		addr_mcast_all   = 0xe0000001, -- 224.0.0.1
		addr_mcast_local = 0xe00000ff, -- 224.0.0.255
		-- Type of service (ip_tos), RFC 1349 ("obsoleted by RFC 2474")
		tos_default      = 0x00, -- default
		tos_lowdelay     = 0x10, -- low delay
		tos_throughput   = 0x08, -- high throughput
		tos_reliability  = 0x04, -- high reliability
		tos_lowcost      = 0x02, -- low monetary cost - XXX
		tos_ect          = 0x02, -- ECN-capable transport
		tos_ce           = 0x01, -- congestion experienced
		-- Fragmentation flags (ip_off)
		rf = 0x8000, -- reserved
		df = 0x4000, -- don't fragment
		mf = 0x2000, -- more fragments (not last frag)
		offmask  = 0x1fff, -- mask for fragment offset
		-- Time-to-live (ip_ttl), seconds
		ttl_default = 64,  -- default ttl, RFC 1122, RFC 1340
		ttl_max     = 255, -- maximum ttl
		-- Protocol (ip_p) - http://www.iana.org/assignments/protocol-numbers
		proto_ip      = 0,  -- dummy for IP
		proto_hopopts = 0,  -- IPv6 hop-by-hop options
		proto_icmp    = 1,  -- ICMP
		proto_igmp    = 2,  -- IGMP
		proto_ggp     = 3,  -- gateway-gateway protocol
		proto_ipip    = 4,  -- IP in IP
		proto_st      = 5,  -- ST datagram mode
		proto_tcp     = 6,  -- TCP
		proto_cbt     = 7,  -- CBT
		proto_egp     = 8,  -- exterior gateway protocol
		proto_igp     = 9,  -- interior gateway protocol
		proto_bbnrcc  = 10,  -- BBN RCC monitoring
		proto_nvp     = 11,  -- Network Voice Protocol
		proto_pup     = 12,  -- PARC universal packet
		proto_argus   = 13,  -- ARGUS
		proto_emcon   = 14,  -- EMCON
		proto_xnet    = 15,  -- Cross Net Debugger
		proto_chaos   = 16,  -- Chaos
		proto_udp     = 17,  -- UDP
		proto_mux     = 18,  -- multiplexing
		proto_dcnmeas = 19,  -- DCN measurement
		proto_hmp     = 20,  -- Host Monitoring Protocol
		proto_prm     = 21,  -- Packet Radio Measurement
		proto_idp     = 22,  -- Xerox NS IDP
		proto_trunk1  = 23,  -- Trunk-1
		proto_trunk2  = 24,  -- Trunk-2
		proto_leaf1   = 25,  -- Leaf-1
		proto_leaf2   = 26,  -- Leaf-2
		proto_rdp     = 27,  -- "Reliable Datagram" proto
		proto_irtp    = 28,  -- Inet Reliable Transaction
		proto_tp      = 29,  -- ISO TP class 4
		proto_netblt  = 30,  -- Bulk Data Transfer
		proto_mfpnsp  = 31,  -- MFE Network Services
		proto_meritinp= 32,  -- Merit Internodal Protocol
		proto_sep     = 33,  -- Sequential Exchange proto
		proto_3pc     = 34,  -- Third Party Connect proto
		proto_idpr    = 35,  -- Interdomain Policy Route
		proto_xtp     = 36,  -- Xpress Transfer Protocol
		proto_ddp     = 37,  -- Datagram Delivery Proto
		proto_cmtp    = 38,  -- IDPR Ctrl Message Trans
		proto_tppp    = 39,  -- TP++ Transport Protocol
		proto_il      = 40,  -- IL Transport Protocol
		proto_ip6     = 41,  -- IPv6
		proto_sdrp    = 42,  -- Source Demand Routing
		proto_routing = 43,  -- IPv6 routing header
		proto_fragment= 44,  -- IPv6 fragmentation header
		proto_rsvp    = 46,  -- Reservation protocol
		proto_gre     = 47,  -- General Routing Encap
		proto_mhrp    = 48,  -- Mobile Host Routing
		proto_ena     = 49,  -- ENA
		proto_esp     = 50,  -- Encap Security Payload
		proto_ah      = 51,  -- Authentication Header
		proto_inlsp   = 52,  -- Integated Net Layer Sec
		proto_swipe   = 53,  -- SWIPE
		proto_narp    = 54,  -- NBMA Address Resolution
		proto_mobile  = 55,  -- Mobile IP, RFC 2004
		proto_tlsp    = 56,  -- Transport Layer Security
		proto_skip    = 57,  -- SKIP
		proto_icmp6   = 58,  -- ICMP for IPv6
		proto_none    = 59,  -- IPv6 no next header
		proto_dstopts = 60,  -- IPv6 destination options
		proto_anyhost = 61,  -- any host internal proto
		proto_cftp    = 62,  -- CFTP
		proto_anynet  = 63,  -- any local network
		proto_expak   = 64,  -- SATNET and Backroom EXPAK
		proto_kryptolan = 65,  -- Kryptolan
		proto_ippc    = 67,  -- Inet Pluribus Packet Core
		proto_distfs  = 68,  -- any distributed fs
		proto_satmon  = 69,  -- SATNET Monitoring
		proto_visa    = 70,  -- VISA Protocol
		proto_ipcv    = 71,  -- Inet Packet Core Utility
		proto_cpnx    = 72,  -- Comp Proto Net Executive
		proto_cphb    = 73,  -- Comp Protocol Heart Beat
		proto_wsn     = 74,  -- Wang Span Network
		proto_pvp     = 75,  -- Packet Video Protocol
		proto_brsatmon= 76,  -- Backroom SATNET Monitor
		proto_sunnd   = 77,  -- SUN ND Protocol
		proto_wbmon   = 78,  -- WIDEBAND Monitoring
		proto_wbexpak = 79,  -- WIDEBAND EXPAK
		proto_eon     = 80,  -- ISO CNLP
		proto_vmtp    = 81,  -- Versatile Msg Transport
		proto_svmtp   = 82,  -- Secure VMTP
		proto_vines   = 83,  -- VINES
		proto_ttp     = 84,  -- TTP
		proto_nsfigp  = 85,  -- NSFNET-IGP
		proto_dgp     = 86,  -- Dissimilar Gateway Proto
		proto_tcf     = 87,  -- TCF
		proto_eigrp   = 88,  -- EIGRP
		proto_ospf    = 89,  -- Open Shortest Path First
		proto_spriterpc= 90,  -- Sprite RPC Protocol
		proto_larp    = 91,  -- Locus Address Resolution
		proto_mtp     = 92,  -- Multicast Transport Proto
		proto_ax25    = 93,  -- AX.25 Frames
		proto_ipipencap= 94,  -- yet-another IP encap
		proto_micp    = 95,  -- Mobile Internet Ctrl
		proto_sccsp   = 96,  -- Semaphore Comm Sec Proto
		proto_etherip = 97,  -- Ethernet in IPv4
		proto_encap   = 98,  -- encapsulation header
		proto_anyenc  = 99,  -- private encryption scheme
		proto_gmtp    = 100,  -- GMTP
		proto_ifmp    = 101,  -- Ipsilon Flow Mgmt Proto
		proto_pnni    = 102,  -- PNNI over IP
		proto_pim     = 103,  -- Protocol Indep Multicast
		proto_aris    = 104,  -- ARIS
		proto_scps    = 105,  -- SCPS
		proto_qnx     = 106,  -- QNX
		proto_an      = 107,  -- Active Networks
		proto_ipcomp  = 108,  -- IP Payload Compression
		proto_snp     = 109,  -- Sitara Networks Protocol
		proto_compaqpeer= 110,  -- Compaq Peer Protocol
		proto_ipxip   = 111,  -- IPX in IP
		proto_vrrp    = 112,  -- Virtual Router Redundancy
		proto_pgm     = 113,  -- PGM Reliable Transport
		proto_any0hop = 114,  -- 0-hop protocol
		proto_l2tp    = 115,  -- Layer 2 Tunneling Proto
		proto_ddx     = 116,  -- D-II Data Exchange (DDX)
		proto_iatp    = 117,  -- Interactive Agent Xfer
		proto_stp     = 118,  -- Schedule Transfer Proto
		proto_srp     = 119,  -- SpectraLink Radio Proto
		proto_uti     = 120,  -- UTI
		proto_smp     = 121,  -- Simple Message Protocol
		proto_sm      = 122,  -- SM
		proto_ptp     = 123,  -- Performance Transparency
		proto_isis    = 124,  -- ISIS over IPv4
		proto_fire    = 125,  -- FIRE
		proto_crtp    = 126,  -- Combat Radio Transport
		proto_crudp   = 127,  -- Combat Radio UDP
		proto_sscopmce= 128,  -- SSCOPMCE
		proto_iplt    = 129,  -- IPLT
		proto_sps     = 130,  -- Secure Packet Shield
		proto_pipe    = 131,  -- Private IP Encap in IP
		proto_sctp    = 132,  -- Stream Ctrl Transmission
		proto_fc      = 133,  -- Fibre Channel
		proto_rsvpign = 134,  -- RSVP-E2E-IGNORE
		proto_raw     = 255,  -- Raw IP packets
		proto_reserved= 255,  -- Reserved
	},
}
return M