Shark代码分析笔记(4)——bpf.lua(未完待续)

bpf.lua位于bpf文件夹下,其代码如下(省去版权信息):

local ffi = require("ffi")
local C = ffi.C

local bpf = {}

bpf.cargs_extra = ""

---------------------------------------------------------------

bpf.cargs_add = function(s)
  bpf.cargs_extra = s
end

---------------------------------------------------------------

ffi.cdef[[
int load_bpf_file(const char *path);
]]

local function load_bpf_file(path)
  local ret = C.load_bpf_file(path)
  if tonumber(ret) ~= 0 then
    os.exit(-1)
  end
end

local function run_llc_bpf(bc_file, bpfobj_file)
  os.execute("llc-bpf -march=bpf -filetype=obj -o " ..
              bpfobj_file .. " " .. bc_file)
end

---------------------------------------------------------------
local function split(s, delimiter)
  local result = {};
  for match in (s..delimiter):gmatch("(.-)"..delimiter) do
    table.insert(result, match);
  end
  return result;
end

local basic_type_tbl = {
  "char", "u8", "short", "u16", "int", "u32", "long", "long long", "u64"
}

local function get_real_type(typestr)
  for _, v in pairs(basic_type_tbl) do
    if v == typestr then
      return v
    end
  end

  return "cdata"
end

local function process_type(typestr0)
  local typestr, size, size

  typestr, size = string.match(typestr0, "(.*) %[(.*)%]")
  if typestr ~= nil and size ~= nil then
    sizestr = "sizeof("..typestr .. ") * "..size
    typestr = typestr
    if typestr == "char" then
      typestr = "string"
    end
  else
    typestr, size = string.match(typestr0, "(.*)%[(.*)%]")
    if typestr ~= nil and size ~= nil then
      sizestr = "sizeof("..typestr .. ") * "..size
      typestr = typestr
      if typestr == "char" then
        typestr = "string"
      end
    else
      sizestr = "sizeof("..typestr0..")"
      typestr = get_real_type(typestr0)
    end
  end

  return typestr, sizestr
end

local function gen_map_decl(map_type, key_type, val_type, entries, name)
  local key_typestr, key_sizestr = process_type(key_type)
  local val_typestr, val_sizestr = process_type(val_type)

  if not entries then
    entries = 1024;
  end

  local str = string.format("struct bpf_map_def SEC(\"maps\") %s = {\n" ..
                            "\t.name = \"%s\",                 \n" ..
                            "\t.key_type = \"%s\",             \n" ..
                            "\t.val_type = \"%s\",             \n" ..
                            "\t.type = BPF_MAP_TYPE_%s,        \n" ..
                            "\t.key_size = %s,                 \n" ..
                            "\t.value_size = %s,               \n" ..
                            "\t.max_entries = %d,              \n};\n",
        name, name, key_typestr, val_typestr, map_type, key_sizestr,
        val_sizestr, entries)
  io.write(str)
end

local function translate_cdef(s)
  local n = split(s, '\n')
  for _, line in pairs(n) do
    local key_type, val_type, entries, name
    local match = false

    key_type, val_type, name = string.match(line,
             "bpf_map_hash<([^,]*), ([^,]*)> (.*);")
    if key_type and val_type and name then
      gen_map_decl("HASH", key_type, val_type, nil, name)
      match = true
    end

    key_type, val_type, entries, name = string.match(line,
             "bpf_map_hash<([^,]*), ([^,]*), (%d+)> (.*);")
    if key_type and val_type and entries and name then
      gen_map_decl("HASH", key_type, val_type, entries, name)
      match = true
    end

    key_type, val_type, name = string.match(line,
             "bpf_map_array<([^,]*), ([^,]*)> (.*);")
    if key_type and val_type and name then
      gen_map_decl("ARRAY", key_type, val_type, nil, name)
      match = true
    end

    key_type, val_type, entries, name = string.match(line,
             "bpf_map_array<([^,]*), ([^,]*), (%d+)> (.*);")
    if key_type and val_type and entries and name then
      gen_map_decl("ARRAY", key_type, val_type, entries, name)
      match = true
    end

    if not match then
      io.write(line, "\n")
    end
  end

  io.write("\nchar _license[] SEC(\"license\") = \"GPL\";\n")
  io.write("\n#include <linux/version.h>\n")
  io.write("\nu32 _version SEC(\"version\") = LINUX_VERSION_CODE;\n")
end

local function compile(srcfile, objfile)
   local f = io.popen("uname -r", "r")
   local release = f:read()
   local linuxinc = string.format("/lib/modules/%s/build/", release)
   local bpfinc = "bpf/libbpf"

   local clang_cmd = string.format("clang -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -I%s -I%s/arch/x86/include -I%s/arch/x86/include/generated/uapi -I%s/arch/x86/include/generated  -I%s/include -I%s/arch/x86/include/uapi -I%s/arch/x86/include/generated/uapi -I%s/include/uapi -I%s/include/generated/uapi -include %s/include/linux/kconfig.h %s -D__KERNEL__ -Wno-unused-value -Wno-pointer-sign -O2 -emit-llvm -x c -c %s -o %s", bpfinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, linuxinc, bpf.cargs_extra, srcfile, objfile)

  os.execute(clang_cmd)
end

-- builtin cdef function
bpf.cdef = function(s)
  local file

  local srctmp = os.tmpname()
  file = io.open(srctmp, "w")
  io.output(file)
  translate_cdef(s)

  io.close(file)
  io.output(io.stdout)

  -- dump source
  local f = io.open(srctmp, "rb")
  local content = f:read("*all")
  print(content)
  f:close()

  local bctmp = os.tmpname()
  compile(srctmp, bctmp)

  local bpftmp = os.tmpname()
  run_llc_bpf(bctmp, bpftmp)

  -- pass bpf table for bpf object loading, need clear it after load done.
  _G["bpf"] = bpf
  load_bpf_file(bpftmp)

  os.remove(srctmp)
  os.remove(bctmp)
  os.remove(bpftmp)
end

---------------------------------------------------------------

bpf.print_map = function(map)
  local map = map
  for k in pairs(map) do
    print(k, map[k])
  end
end

---------------------------------------------------------------

bpf.copy_map = function(map)
  local new = {}
  local map = map
  for k in pairs(map) do
    new[k] = map[k]
  end
  return new
end

---------------------------------------------------------------

local function fill_line(n, max)
  for i = 1, max do
    if i < n then
      io.write("*")
    else
      io.write(" ")
    end
  end
end

-- print histogram for bpf.var.map
-- Only support number key now.
bpf.print_hist_map = function(t)
  local histo = {}
  local stdSum, max = 0, 0

  for k in pairs(t) do
    if t[k] ~= 0 then
      local k1 = math.pow(2, math.floor(math.log(k, 2)))
      if histo[k1] == nil then histo[k1] = 0 end
      histo[k1] = histo[k1] + t[k]

      stdSum = stdSum + t[k]
      if k1 > max then max = k1 end
    end
  end

  print("        value  ------------- Distribution -------------  count")
  local k = 0
  while k <= max do
    local v = histo[k]
    if v == nil then v = 0 end

    io.write(string.format("%13d |", k))
    fill_line(v * 40 / stdSum, 40)
    io.write(string.format("| %d", v))

    print()

    if k == 0 then k = 1 else k = k * 2 end
  end
end

return bpf

(1)

local ffi = require("ffi")
local C = ffi.C

local bpf = {}

bpf.cargs_extra = ""

以上代码导入ffi模块,并且定义bpf这个tablebpf table有一个cargs_extrakey,所对应的值是一个空字符串。

 

Shark代码分析笔记(3)——shark_init.lua

看一下shark_init.lua这个文件(省去版权信息):

local uv = require("uv")
local ffi = require("ffi")

package.path = package.path .. ";./deps/?.lua"
package.cpath = package.cpath .. ";./deps/?.so"

-- microsecond precision
ffi.cdef[[
typedef long time_t;

typedef struct timeval {
    time_t tv_sec;
    time_t tv_usec;
} timeval;

int gettimeofday(struct timeval *t, void *tzp);
]]

local gettimeofday_struct = ffi.new("timeval")

shark.gettimeofday = function()
  ffi.C.gettimeofday(gettimeofday_struct, nil)
  return tonumber(gettimeofday_struct.tv_sec) * 1000000 +
         tonumber(gettimeofday_struct.tv_usec)
end

set_interval = function(callback, interval)
  local timer = uv.new_timer()
  local function ontimeout()
    callback(timer)
  end
  uv.timer_start(timer, interval, interval, ontimeout)
  return timer
end

set_timeout = function(callback, timeout)
  local timer = uv.new_timer()
  local function ontimeout()
    uv.timer_stop(timer)
    uv.close(timer)
    callback(timer)
  end
  uv.timer_start(timer, timeout, 0, ontimeout)
  return timer
end

local shark_end_notify_list = {}

shark.add_end_notify = function(callback)
  table.insert(shark_end_notify_list, callback)
end

shark.on_end = function(callback)
  local function call_end()
    --notify registered on_end function
    for _, cb in pairs(shark_end_notify_list) do
      cb()
    end

    callback()
    os.exit(0)
  end
  local sigint = uv.new_signal()
  uv.signal_start(sigint, "sigint", function()
    call_end()
  end)

  local sigterm = uv.new_signal()
  uv.signal_start(sigterm, "sigterm", function()
    call_end()
  end)
end


---------------------------------------------------------------

local function fill_line(n, max)
  for i = 1, max do
    if i < n then
      io.write("*")
    else
      io.write(" ")
    end
  end
end

-- standard histogram print function
-- all type keys and number value
local __print_hist = function(t, cmp_func, mode)
  local stdSum = 0
  local array = {}

  for k, v in pairs(t) do
    stdSum = stdSum + v
    if tostring(k) ~= "" then
      array[#array + 1] = {k = k, v = v}
    end
  end

  table.sort(array, function(v1, v2)
    if cmp_func ~= nil then
      return cmp_func(v1.v, v2.v)
    else
      if v1.v > v2.v then return true end
    end
  end)

  if mode == "default" then
    io.write("                          value  ---------- Distribution ----------  count\n")
  end

  for k, v in pairs(array) do
    if mode == "default" then
      io.write(string.format("%33s |", tostring(v.k)))
      fill_line(v.v * 34 / stdSum, 34)
      io.write(string.format("| %d\n", v.v))
    else
      io.write(string.format("%s\n%d\n", tostring(v.k), v.v))
    end
  end
end

function print_hist(t, cmp_func)
  __print_hist(t, cmp_func, "default")
end


function print_hist_raw(t, cmp_func)
  __print_hist(t, cmp_func, "raw")
end

shark.print_hist = print_hist
shark.print_hist_raw = print_hist_raw

(1)

local uv = require("uv")
local ffi = require("ffi")

package.path = package.path .. ";./deps/?.lua"
package.cpath = package.cpath .. ";./deps/?.so"

require("uv")加载luv模块(在main函数中已经将luaopen_luv注册进了package.preload table),require("ffi")加载luajit中的ffi模块。
然后修改package.pathpackage.cpath,这样可以找到shark依赖的lua文件和库。

(2)

-- microsecond precision
ffi.cdef[[
typedef long time_t;

typedef struct timeval {
    time_t tv_sec;
    time_t tv_usec;
} timeval;

int gettimeofday(struct timeval *t, void *tzp);
]]

local gettimeofday_struct = ffi.new("timeval")

shark.gettimeofday = function()
  ffi.C.gettimeofday(gettimeofday_struct, nil)
  return tonumber(gettimeofday_struct.tv_sec) * 1000000 +
         tonumber(gettimeofday_struct.tv_usec)
end

ffi.new()返回一个cdata类型的值。shark.gettimeofday返回的是当前时间的微秒(us)值。

(3)

set_interval = function(callback, interval)
  local timer = uv.new_timer()
  local function ontimeout()
    callback(timer)
  end
  uv.timer_start(timer, interval, interval, ontimeout)
  return timer
end

set_timeout = function(callback, timeout)
  local timer = uv.new_timer()
  local function ontimeout()
    uv.timer_stop(timer)
    uv.close(timer)
    callback(timer)
  end
  uv.timer_start(timer, timeout, 0, ontimeout)
  return timer
end

set_intervalset_timeout利用了luv模块中定时器相关函数。set_interval函数会让callback函数间隔interval时间执行。而set_timeout函数会让callback函数在timeout后执行,且执行一次。要注意这两个函数的时间单位是毫秒(ms)。

(4)

local shark_end_notify_list = {}

shark.add_end_notify = function(callback)
  table.insert(shark_end_notify_list, callback)
end

创建一个shark_end_notify_listtable,并且定义一个shark.add_end_notify的函数。这个函数的作用是向shark_end_notify_list添加回调函数(callback),这个table中的函数会在shark.on_end这个函数中调用,也就是在脚本退出时执行收尾工作。

(5)

shark.on_end = function(callback)
  local function call_end()
    --notify registered on_end function
    for _, cb in pairs(shark_end_notify_list) do
      cb()
    end

    callback()
    os.exit(0)
  end
  local sigint = uv.new_signal()
  uv.signal_start(sigint, "sigint", function()
    call_end()
  end)

  local sigterm = uv.new_signal()
  uv.signal_start(sigterm, "sigterm", function()
    call_end()
  end)
end

shark.on_end函数的参数是一个回调函数(callback)。在shark.on_end里定义了一个local函数:call_endcall_end首先会遍历shark_end_notify_list,并把其中的函数都执行一遍,最后调用shark.on_end函数传入的回调函数(callback)。 call_end函数就是shark.on_end函数为sigintsigterm信号注册的信号处理函数。

(6)

local function fill_line(n, max)
  for i = 1, max do
    if i < n then
      io.write("*")
    else
      io.write(" ")
    end
  end
end

fill_line函数用来输出*,用在接下来的__print_hist函数中。

(7)

-- standard histogram print function
-- all type keys and number value
local __print_hist = function(t, cmp_func, mode)
  local stdSum = 0
  local array = {}

  for k, v in pairs(t) do
    stdSum = stdSum + v
    if tostring(k) ~= "" then
      array[#array + 1] = {k = k, v = v}
    end
  end

  table.sort(array, function(v1, v2)
    if cmp_func ~= nil then
      return cmp_func(v1.v, v2.v)
    else
      if v1.v > v2.v then return true end
    end
  end)

  if mode == "default" then
    io.write("                          value  ---------- Distribution ----------  count\n")
  end

  for k, v in pairs(array) do
    if mode == "default" then
      io.write(string.format("%33s |", tostring(v.k)))
      fill_line(v.v * 34 / stdSum, 34)
      io.write(string.format("| %d\n", v.v))
    else
      io.write(string.format("%s\n%d\n", tostring(v.k), v.v))
    end
  end
end

__print_hist函数用来打印柱状图。

  local stdSum = 0
  local array = {}

  for k, v in pairs(t) do
    stdSum = stdSum + v
    if tostring(k) ~= "" then
      array[#array + 1] = {k = k, v = v}
    end
  end

输入参数t是一个table。以上代码是遍历这个table,并且生成一个新的“array table”。这个array的值又是一个table:包含输入tablekeyvalue

table.sort(array, function(v1, v2)
    if cmp_func ~= nil then
      return cmp_func(v1.v, v2.v)
    else
      if v1.v > v2.v then return true end
    end
  end)

以上代码是对array进行排序。如果没有输入cmp_func函数参数,就用默认的比较方式。

if mode == "default" then
    io.write("                          value  ---------- Distribution ----------  count\n")
  end

  for k, v in pairs(array) do
    if mode == "default" then
      io.write(string.format("%33s |", tostring(v.k)))
      fill_line(v.v * 34 / stdSum, 34)
      io.write(string.format("| %d\n", v.v))
    else
      io.write(string.format("%s\n%d\n", tostring(v.k), v.v))
    end
  end

以上代码就是打印最后的柱状图了。输出结果类似:

                           value  ---------- Distribution ----------  count
  syscalls:sys_enter_gettimeofday |********                          | 25940
   syscalls:sys_exit_gettimeofday |********                          | 25940
    syscalls:sys_enter_epoll_wait |****                              | 12977
     syscalls:sys_exit_epoll_wait |****                              | 12977
          syscalls:sys_exit_alarm |*                                 | 3917

 

(8)

function print_hist(t, cmp_func)
  __print_hist(t, cmp_func, "default")
end


function print_hist_raw(t, cmp_func)
  __print_hist(t, cmp_func, "raw")
end

shark.print_hist = print_hist
shark.print_hist_raw = print_hist_raw

最后就是把__print_hist封装出shark.print_histshark.print_hist_raw两个函数供其它程序调用。

 

 

 

 

Shark代码分析笔记(2)——main函数

看一下main函数:

int main(int argc, char **argv)
{
    int ret = EXIT_FAILURE;
    int flags = 0, script;
    int base;

    if (argv[0] && argv[0][0]) progname = argv[0];

    lua_State *ls = lua_open();
    if (!ls) {
        l_message(progname, "cannot create state: not enough memory");
        return ret;
    }

    g_ls = ls;

    script = collectargs(argv, &flags);
    if (script <= 0) {  /* invalid args? */
        print_usage();
        return 0;
    }

    if (flags & FLAGS_VERSION)
        print_version();

    luaL_openlibs(ls);  /* open libraries */

    // Get package.preload so we can store builtins in it.
    lua_getglobal(ls, "package");
    lua_getfield(ls, -1, "preload");
    lua_remove(ls, -2); // Remove package

    // Store uv module definition at preload.uv
    lua_pushcfunction(ls, luaopen_luv);
    lua_setfield(ls, -2, "uv");

    luaL_openlib(ls, "shark", ll_shark, 0);

    lua_getglobal(ls, "shark");
    lua_pushboolean(ls, shark_verbose);
    lua_setfield(ls, -2, "verbose");
    lua_pop(ls, 1);

    int narg = getargs(ls, argv, script);  /* collect arguments */
    lua_setglobal(ls, "arg");

#include "shark_init.h"
    luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE,
            NULL);
    if((ret = lua_pcall(ls, 0, 0, 0))) {
        ret = lua_report(ls, ret);
        goto out;
    }

    g_event_loop = luv_loop(ls);

    if((ret = luaL_loadfile(ls, argv[script]))) {
        ret = lua_report(ls, ret);
        goto out;
    }

    base = lua_gettop(ls) - 1;
    lua_pushcfunction(ls, lua_traceback);
    lua_insert(ls, base);

    if (lua_pcall(ls, 0, 0, base)) {
        fprintf(stderr, "%s\n", lua_tostring(ls, -1));
        exit(EXIT_FAILURE);
    }

    lua_pop(ls, 1);

    //TODO: move to lua init code
    uv_run(g_event_loop, UV_RUN_DEFAULT);

    ret = 0;
 out:
    lua_close(ls);
    return ret;
}  

(1)

if (argv[0] && argv[0][0]) progname = argv[0];

progname存的是运行程序名字:/path/to/shark

(2)

    lua_State *ls = lua_open();
    if (!ls) {
        l_message(progname, "cannot create state: not enough memory");
        return ret;
    }

    g_ls = ls;

创建一个新的Lua运行环境,为了后续使用。

(3)

script = collectargs(argv, &flags);
if (script <= 0) {  /* invalid args? */
    print_usage();
    return 0;
}

if (flags & FLAGS_VERSION)
    print_version();

先看一下print_version

static void print_version(void)
{
    fputs(SHARK_VERSION " -- " SHARK_COPYRIGHT ". " SHARK_URL "\n", stdout);
    exit(0);
}

比较简单,打印出版本就退出了。

再看一下print_usage

static void print_usage(void)
{
    fprintf(stderr,
    "usage: shark [options]... [script [args]...].\n"
    "Available options are:\n"
    "  -v        Show version information.\n");
    fflush(stderr);
}

可以看到shark的使用方法:

shark [options]... [script [args]...]

shark程序后面跟着可选参数,还有执行脚本。

(4)

luaL_openlibs(ls);  /* open libraries */

加载Luajit提供的函数库。

(5)

// Get package.preload so we can store builtins in it.
lua_getglobal(ls, "package");
lua_getfield(ls, -1, "preload");
lua_remove(ls, -2); // Remove package

lua_getglobal(ls, "package");用来把package这个table压入堆栈:

 ___________________________
|____ package(类型:table)___|  (-1)

lua_getfield(ls, -1, "preload");用来从index-1处取出keypreload值,也就是:package.preload这个table,并压入堆栈:

 ___________________________
|package.preload(类型:table)|  (-1)
|____ package(类型:table)___|  (-2)

lua_remove(ls, -2);package从堆栈中移除:

 ___________________________
|package.preload(类型:table)|  (-1)

(6)

// Store uv module definition at preload.uv
lua_pushcfunction(ls, luaopen_luv);
lua_setfield(ls, -2, "uv");

lua_pushcfunction(ls, luaopen_luv);luaopen_luv函数压入堆栈:

 ___________________________
|__luaopen_luv(类型:函数)___|  (-1)
|package.preload(类型:table)|  (-2)

lua_setfield(ls, -2, "uv");执行效果是:package.preload.uv = luaopen_luv,然后把luaopen_luv弹出堆栈:

 ____________________________________________
|package.preload(类型:table,uv = luaopen_luv)|  (-1)

(7)

luaL_openlib(ls, "shark", ll_shark, 0);

ll_shark数组定义如下:

static const struct luaL_reg ll_shark[] = {
        {"debuginfo_set", &shark_api_debuginfo_set},
        {"lua_ref", &shark_api_lua_ref},
        {"get_ref", &shark_api_get_ref},
        {"stats", &shark_api_stats},
        {"set_binary", &shark_api_set_binary},
        {"exec", &shark_api_exec},
//TODO: move to sock library
#ifndef BPF_DISABLE
        {"open_raw_sock", &shark_api_open_raw_sock},
        {"sock_attach_bpf", &shark_api_sock_attach_bpf},
        {"iptos", &shark_api_iptos},
#endif
    {NULL, NULL}
};

luaL_openlib(ls, "shark", ll_shark, 0);函数的作用是创建一个table然后将这个table赋给package.loaded[shark]和全局变量shark,并将ll_shark数组中的函数注册到这个table,最后把table压入堆栈:

 ————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-1)
|package.preload(类型:table,uv = luaopen_luv)|  (-2)

(8)

    lua_getglobal(ls, "shark");
    lua_pushboolean(ls, shark_verbose);
    lua_setfield(ls, -2, "verbose");
    lua_pop(ls, 1);

lua_getglobal(ls, "shark");shark table压入堆栈:

 ____________________________________________
|       shark(类型:table)                    |  (-1)
 ————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-2)
|package.preload(类型:table,uv = luaopen_luv)|  (-3)

lua_pushboolean(ls, shark_verbose);shark_verbose这个布尔值压入堆栈:

_____________________________________________
| ______shark_verbose(类型:bool)_____________|  (-1)
|       shark(类型:table)                    |  (-2)
 ————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-3)
|package.preload(类型:table,uv = luaopen_luv)|  (-4)

lua_setfield(ls, -2, "verbose");执行效果是:shark.verbose = shark_verbose,然后把shark_verbose弹出堆栈:

 ____________________________________________
| shark(类型:table, verbose = shark_verbose) |  (-1)
 ————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-2)
|package.preload(类型:table,uv = luaopen_luv)|  (-3)

lua_pop(ls, 1);把栈顶元素弹出堆栈:

 ————————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-1)
|package.preload(类型:table,uv = luaopen_luv)|  (-2)

(9)

int narg = getargs(ls, argv, script);  /* collect arguments */
lua_setglobal(ls, "arg");

getargs函数实现如下:

static int getargs(lua_State *ls, char **argv, int n)
{
    int narg;
    int i;
    int argc = 0;

    while (argv[argc])
        argc++;  /* count total number of arguments */

    narg = argc - (n + 1);  /* number of arguments to the script */
    luaL_checkstack(ls, narg + 3, "too many arguments to script");

    for (i = n + 1; i < argc; i++)
        lua_pushstring(ls, argv[i]);

    lua_createtable(ls, narg, n + 1);

    for (i = 0; i < argc; i++) {
        lua_pushstring(ls, argv[i]);
        lua_rawseti(ls, -2, i - n);
    }

    return narg;
}

getargs函数中,传入参数nLua脚本在命令行参数的索引,而narg则是脚本的参数。举个例子:

/root/shark/shark trace.lua 1 2

argc4n1narg2

luaL_checkstack(ls, narg + 3, "too many arguments to script");检查堆栈是否有足够的空间。

    for (i = n + 1; i < argc; i++)
        lua_pushstring(ls, argv[i]);

把参数12压入堆栈:

 ___________________________________________
|__________参数: 2__________________________|  (-1)
|          参数: 1                          |  (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-3)
|package.preload(类型:table,uv = luaopen_luv)|  (-4)  

lua_createtable(ls, narg, n + 1);创建一个table(包含narg数组元素,n + 1非数组元素)并压入堆栈:

 ———————————————————————————————————————————
|______table________________________________|  (-1)
|__________参数: 2__________________________|  (-2)
|          参数: 1                          |  (-3)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-4)
|package.preload(类型:table,uv = luaopen_luv)|  (-5)  

看最后一个循环:

    for (i = 0; i < argc; i++) {
        lua_pushstring(ls, argv[i]);
        lua_rawseti(ls, -2, i - n);
    }

lua_pushstring(ls, argv[i]);依次把参数压入堆栈,lua_rawseti(ls, -2, i - n);则是把参数传入table

 —————————————————————————————————————————————————
|_table(index:value->1:shark;0:trace.lua;1:1;2:2_|  (-1)
|__________参数: 2_______________________________|  (-2)
|          参数: 1                               |  (-3)
|————————————————————————————————————————|
|___一个注册ll_shark函数数组的table________________|  (-4)
|package.preload(类型:table,uv = luaopen_luv)     |  (-5)  

lua_setglobal(ls, "arg");作用是把栈顶table弹出,并赋值给arg。所以arg就指向了这个tableindex:value->1:shark;0:trace.lua;1:1;2:2)。堆栈变为:

____________________________________________
|__________参数: 2__________________________|  (-1)
|          参数: 1                          |  (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-3)
|package.preload(类型:table,uv = luaopen_luv)|  (-4) 

(10)

#include "shark_init.h"
luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE,
        NULL);

shark_init.h是由shark_init.lua生成的(以后再详细介绍shark_init.lua),luaJIT_BC_shark_initluaJIT_BC_shark_init_SIZE也定义在shark_init.h文件中。

luaL_loadbuffer(ls, luaJIT_BC_shark_init, luaJIT_BC_shark_init_SIZE, NULL);luaJIT_BC_shark_init这个chunk压入堆栈:

____________________________________________
|_luaJIT_BC_shark_init chunk(类型:函数)______| (-1)
|__________参数: 2__________________________|  (-2)
|          参数: 1                          |  (-3)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-4)
|package.preload(类型:table,uv = luaopen_luv)|  (-5)  

(11)

if((ret = lua_pcall(ls, 0, 0, 0))) {
        ret = lua_report(ls, ret);
        goto out;
}

接下来lua_pcall(ls, 0, 0, 0)会运行luaJIT_BC_shark_init这个chunk。运行完后,把chunk弹出堆栈:

————————————————————————————————————————————
|__________参数: 2__________________________|  (-1)
|          参数: 1                          |  (-2)
|———————————————————————————————————————————
|___一个注册ll_shark函数数组的table___________|  (-3)
|package.preload(类型:table,uv = luaopen_luv)|  (-4)  

lua_report比较简单,就是如果出错的话,就从栈顶取出错误信息,打印完以后再弹栈:

int lua_report(lua_State *ls, int status)
{
    if (status && !lua_isnil(ls, -1)) {
        const char *msg = lua_tostring(ls, -1);
        if (msg == NULL)
            msg = "(error object is not a string)";
        l_message(progname, msg);
        lua_pop(ls, 1);
    }
    return status;
}  

(12)

    g_event_loop = luv_loop(ls);

    if((ret = luaL_loadfile(ls, argv[script]))) {
        ret = lua_report(ls, ret);
        goto out;
    }

    base = lua_gettop(ls) - 1;
    lua_pushcfunction(ls, lua_traceback);
    lua_insert(ls, base);

    if (lua_pcall(ls, 0, 0, base)) {
        fprintf(stderr, "%s\n", lua_tostring(ls, -1));
        exit(EXIT_FAILURE);
    }

    lua_pop(ls, 1);

    //TODO: move to lua init code
    uv_run(g_event_loop, UV_RUN_DEFAULT);

    ret = 0;
 out:
    lua_close(ls);
    return ret;

剩下这段代码就是运行脚本,其中lua_traceback是脚本出错时的处理函数。个人觉得细节上还有些问题,需要和作者沟通一下,这块代码暂时留个小尾巴。

Shark代码分析笔记(1)——Makefile

Shark项目的Makefile

#Define BPF_ENABLE if Linux kernel is 4.0+
#BPF_DISABLE=1

CFLAGS=-I. -I core/ -I core/libuv/include -I core/luajit/src/ -I bpf/libbpf/

CORE_LIB=core/luajit/src/libluajit.a core/libuv/.libs/libuv.a

PERF_LIBS= perf/libperf.a perf/libtraceevent.a perf/libapikfs.a
LIB=$(CORE_LIB) $(PERF_LIBS) -lm -ldl -lelf -lc -lpthread

OBJS=core/shark.o core/luv/luv.o perf/perf.o

BUILTIN_LUA_OBJS = perf/perf_builtin_lua.o
OBJS += $(BUILTIN_LUA_OBJS)

ifndef BPF_DISABLE
OBJS += bpf/bpf.o bpf/libbpf/bpf_load.o bpf/libbpf/libbpf.o
BUILTIN_LUA_OBJS += bpf/bpf_builtin_lua.o
else
CFLAGS += -DBPF_DISABLE
endif

TARGET=shark

#ffi need to call some functions in library, so add -rdynamic option
$(TARGET) : core/luajit/src/libluajit.a core/libuv/.libs/libuv.a core/shark_init.h $(OBJS) force
    $(CC) -o $(TARGET) -rdynamic $(OBJS) $(LIB)

core/luajit/src/libluajit.a:
    @cd core/luajit; make

core/libuv/.libs/libuv.a:
    @cd core/libuv; ./autogen.sh; ./configure; make


DEPS := $(OBJS:.o=.d)
-include $(DEPS)

%.o : %.c
    $(CC) -MD -g -c $(CFLAGS) $< -o $@

LUAJIT_BIN=core/luajit/src/luajit

core/shark_init.h : core/shark_init.lua
    cd core/luajit/src; ./luajit -b ../../shark_init.lua ../../shark_init.h

bpf/bpf_builtin_lua.o : bpf/bpf.lua
    cd core/luajit/src; ./luajit -b ../../../bpf/bpf.lua ../../../bpf/bpf_builtin_lua.o

perf/perf_builtin_lua.o : perf/perf.lua
    cd core/luajit/src; ./luajit -b ../../../perf/perf.lua ../../../perf/perf_builtin_lua.o

force:
    true

clean:
    @rm -rf $(TARGET) *.d *.o core/*.d core/*.o bpf/*.d bpf/*.o perf/*.d perf/*.o core/shark_builtin.h bpf/bpf_builtin_lua.h perf/perf_builtin_lua.h

(1)因为有些BPF的选项只在比较高版本的Linux kernel上才支持,所以加了一个编译开关BPF_DISABLE,可以用来关闭BPF功能( 关闭BPF编译:make BPF_DISABLE=1 )。

(2)

PERF_LIBS= perf/libperf.a perf/libtraceevent.a perf/libapikfs.a

core/luajit/src/libluajit.a:
    @cd core/luajit; make

core/libuv/.libs/libuv.a:
    @cd core/libuv; ./autogen.sh; ./configure; make

使用了三个从Linux kernel生成的perf相关的库:perf/libperf.aperf/libtraceevent.aperf/libapikfs.a

还有三个第三方库:luajit(core/luajit)libuv(core/libuv))luv(core/luv)

(3)

core/shark_init.h : core/shark_init.lua
    cd core/luajit/src; ./luajit -b ../../shark_init.lua ../../shark_init.h

bpf/bpf_builtin_lua.o : bpf/bpf.lua
    cd core/luajit/src; ./luajit -b ../../../bpf/bpf.lua ../../../bpf/bpf_builtin_lua.o

perf/perf_builtin_lua.o : perf/perf.lua
    cd core/luajit/src; ./luajit -b ../../../perf/perf.lua ../../../perf/perf_builtin_lua.o

core/shark_init.lua用来生成core/shark_init.hbpf/bpf.luaperf/perf.lua分别用来生成bpf/bpf_builtin_lua.operf/perf_builtin_lua.o

(4)

DEPS := $(OBJS:.o=.d)
    -include $(DEPS)

%.o : %.c
    $(CC) -MD -g -c $(CFLAGS) $< -o $@

编译生成object文件,并会生成依赖文件。

(5)

TARGET=shark

#ffi need to call some functions in library, so add -rdynamic option
$(TARGET) : core/luajit/src/libluajit.a core/libuv/.libs/libuv.a core/shark_init.h $(OBJS) force
    $(CC) -o $(TARGET) -rdynamic $(OBJS) $(LIB)

最终编译生成一个可执行文件:shark