Commit ee84e093 authored by Brenden Blanco's avatar Brenden Blanco

Merge pull request #470 from vmg/vmg/lua-standalone

Standalone Lua tracing tool
parents 31571b4f e57e1c96
......@@ -34,6 +34,9 @@ pushd build
make install/strip DESTDIR=%{buildroot}
%changelog
* Mon Apr 04 2016 Vicent Marti <vicent@github.com> - 0.1.4-1
- Add bcc-lua package
* Sun Nov 29 2015 Brenden Blanco <bblanco@plumgrid.com> - 0.1.3-1
- Add bcc-tools package
......@@ -66,6 +69,12 @@ Requires: python-bcc
%description -n bcc-tools
Command line tools for BPF Compiler Collection (BCC)
%package -n bcc-lua
Summary: Standalone tool to run BCC tracers written in Lua
Requires: libbcc
%description -n bcc-lua
Standalone tool to run BCC tracers written in Lua
%files -n python-bcc
%{python_sitelib}/bcc*
......@@ -85,3 +94,6 @@ Command line tools for BPF Compiler Collection (BCC)
%files -n bcc-tools
/usr/share/bcc/tools/*
/usr/share/bcc/man/*
%files -n bcc-lua
/usr/bin/bcc-lua
......@@ -27,3 +27,8 @@ Package: bcc-tools
Architecture: all
Depends: python-bcc
Description: Command line tools for BPF Compiler Collection (BCC)
Package: bcc-lua
Architecture: all
Depends: libbcc
Description: Standalone tool to run BCC tracers written in Lua
......@@ -11,3 +11,4 @@ if(NOT PYTHON_ONLY)
add_subdirectory(cc)
endif()
add_subdirectory(python)
add_subdirectory(lua)
include(cmake/FindLuaJIT.cmake)
if (LUAJIT_LIBRARIES)
find_program(LUAJIT luajit)
FILE(GLOB_RECURSE SRC_LUA ${CMAKE_CURRENT_SOURCE_DIR}/bcc/*/*.lua)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/bcc.lua
COMMAND ${LUAJIT} src/squish.lua
DEPENDS ${SRC_LUA} ${CMAKE_CURRENT_SOURCE_DIR}/squishy
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/bcc.o
COMMAND ${LUAJIT} -bg src/bcc.lua src/bcc.o
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/bcc.lua
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
include_directories(${LUAJIT_INCLUDE_DIR})
add_executable(bcc-lua src/main.c src/bcc.o)
target_link_libraries(bcc-lua ${LUAJIT_LIBRARIES})
install(TARGETS bcc-lua RUNTIME DESTINATION bin)
endif()
......@@ -14,30 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
function setup_path()
local str = require("debug").getinfo(2, "S").source:sub(2)
local script_path = str:match("(.*/)").."/?.lua;"
package.path = script_path..package.path
end
setup_path()
local BCC = require("bcc.init")
local BPF = BCC.BPF
BPF.script_root(arg[1])
local utils = {
argparse = require("bcc.vendor.argparse"),
posix = require("bcc.vendor.posix"),
sym = BCC.sym
}
local tracefile = table.remove(arg, 1)
local command = dofile(tracefile)
local res, err = pcall(command, BPF, utils)
if not res then
io.stderr:write("[ERROR] "..err.."\n")
end
BPF.cleanup_probes()
local str = require("debug").getinfo(1, "S").source:sub(2)
local script_path = str:match("(.*/)").."/?.lua;"
package.path = script_path..package.path
rawset(_G, "BCC_STANDALONE_NAME", "bcc-probe")
require("bcc.run")()
......@@ -118,7 +118,7 @@ function Bpf:initialize(args)
local cflags = table.join(Bpf.DEFAULT_CFLAGS, args.cflags)
local cflags_ary = ffi.new("const char *[?]", #cflags, cflags)
local llvm_debug = args.debug or 0
local llvm_debug = rawget(_G, "LIBBCC_LLVM_DEBUG") or args.debug or 0
assert(type(llvm_debug) == "number")
if args.text then
......
......@@ -13,10 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
require("bcc.vendor.strict")
require("bcc.vendor.helpers")
class = require("bcc.vendor.middleclass")
return {
BPF = require("bcc.bpf"),
sym = require("bcc.sym"),
......
......@@ -98,5 +98,4 @@ int perf_reader_fd(struct perf_reader *reader);
void perf_reader_set_fd(struct perf_reader *reader, int fd);
]]
local libbcc = ffi.load("bcc")
return libbcc
return ffi.load(os.getenv("LIBBCC_SO_PATH") or rawget(_G, "LIBBCC_SO_PATH") or "bcc")
--[[
Copyright 2016 GitHub, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
return function()
require("bcc.vendor.helpers")
local progname = rawget(_G, "BCC_STANDALONE_NAME") or "bcc-lua"
local function print_usage()
io.stderr:write(string.format(
"usage: %s [[--so-path=PATH|--version|--quiet] --] path_to_script.lua [...]\n",
progname))
os.exit(1)
end
local function print_version()
local jit = require("jit")
print(string.format("%s %s -- Running on %s (%s/%s)",
progname, rawget(_G, "BCC_VERSION") or "HEAD",
jit.version, jit.os, jit.arch))
os.exit(0)
end
while arg[1] and string.starts(arg[1], "-") do
local k = table.remove(arg, 1)
if k == "--" then
break
elseif string.starts(k, "--so-path=") then
rawset(_G, "LIBBCC_SO_PATH", string.lstrip(k, "--so-path="))
elseif k == "--llvm-debug" then
rawset(_G, "LIBBCC_LLVM_DEBUG", 1)
elseif k == "-q" or k == "--quiet" then
log.enabled = false
elseif k == "-v" or k == "--version" then
print_version()
else
print_usage()
end
end
local tracefile = table.remove(arg, 1)
if not tracefile then print_usage() end
local BPF = require("bcc.bpf")
BPF.script_root(tracefile)
local utils = {
argparse = require("bcc.vendor.argparse"),
posix = require("bcc.vendor.posix"),
sym = require("bcc.sym"),
}
local command = dofile(tracefile)
local res, err = xpcall(command, debug.traceback, BPF, utils)
if not res then
io.stderr:write("[ERROR] "..err.."\n")
end
BPF.cleanup_probes()
return res, err
end
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
function string.starts(s, p)
return string.sub(s, 1, string.len(p)) == p
end
function string.ends(String,End)
return End=='' or string.sub(String,-string.len(End))==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)
......@@ -156,4 +160,14 @@ local function logline(...)
os.date("%H:%M:%S"), c_clear, line, msg))
end
log = { info = logline, enabled = true }
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 = true })
rawset(_G, "class", require("bcc.vendor.middleclass"))
--[[
Copyright (C) 2009 Steve Donovan, David Manura.
This code is licensed under the MIT License
https://github.com/stevedonovan/Penlight
--]]
--- Checks uses of undeclared global variables.
-- All global variables must be 'declared' through a regular assignment
-- (even assigning `nil` will do) in a main chunk before being used
-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index`
-- metamethods are respected.
--
-- You can set any table to have strict behaviour using `strict.module`. Creating a new
-- module with `strict.closed_module` makes the module immune to monkey-patching, if
-- you don't wish to encourage monkey business.
--
-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the
-- global environment strict - if you just want to explicitly set table strictness.
--
-- @module pl.strict
require 'debug' -- for Lua 5.2
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local strict = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
--- make an existing table strict.
-- @string name name of table (optional)
-- @tab[opt] mod table - if `nil` then we'll return a new table
-- @tab[opt] predeclared - table of variables that are to be considered predeclared.
-- @return the given table, or a new table
function strict.module (name,mod,predeclared)
local mt, old_newindex, old_index, old_index_type, global, closed
if predeclared then
global = predeclared.__global
closed = predeclared.__closed
end
if type(mod) == 'table' then
mt = getmetatable(mod)
if mt and rawget(mt,'__declared') then return end -- already patched...
else
mod = {}
end
if mt == nil then
mt = {}
setmetatable(mod, mt)
else
old_newindex = mt.__newindex
old_index = mt.__index
old_index_type = type(old_index)
end
mt.__declared = predeclared or {}
mt.__newindex = function(t, n, v)
if old_newindex then
old_newindex(t, n, v)
if rawget(t,n)~=nil then return end
end
if not mt.__declared[n] then
if global then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared global '"..n.."'", 2)
end
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function(t,n)
if not mt.__declared[n] and what() ~= "C" then
if old_index then
if old_index_type == "table" then
local fallback = old_index[n]
if fallback ~= nil then
return fallback
end
else
local res = old_index(t, n)
if res then return res end
end
end
local msg = "variable '"..n.."' is not declared"
if name then
msg = msg .. " in '"..name.."'"
end
error(msg, 2)
end
return rawget(t, n)
end
return mod
end
--- make all tables in a table strict.
-- So `strict.make_all_strict(_G)` prevents monkey-patching
-- of any global table
-- @tab T
function strict.make_all_strict (T)
for k,v in pairs(T) do
if type(v) == 'table' and v ~= T then
strict.module(k,v)
end
end
end
--- make a new module table which is closed to further changes.
function strict.closed_module (mod,name)
local M = {}
mod = mod or {}
local mt = getmetatable(mod)
if not mt then
mt = {}
setmetatable(mod,mt)
end
mt.__newindex = function(t,k,v)
M[k] = v
end
return strict.module(name,M)
end
if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
strict.module(nil,_G,{_PROMPT=true,__global=true})
end
return strict
# Locate Lua library
# This module defines
# LUAJIT_FOUND, if false, do not try to link to Lua
# LUAJIT_LIBRARIES
# LUAJIT_INCLUDE_DIR, where to find lua.h
#
# Note that the expected include convention is
# #include "lua.h"
# and not
# #include <lua/lua.h>
# This is because, the lua location is not standardized and may exist
# in locations other than lua/
#=============================================================================
# Copyright 2007-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distributed this file outside of CMake, substitute the full
# License text for the above reference.)
#
# ################
# 2010 - modified for cronkite to find luajit instead of lua, as it was before.
#
FIND_PATH(LUAJIT_INCLUDE_DIR lua.h
HINTS
$ENV{LUAJIT_DIR}
PATH_SUFFIXES include/luajit-2.0 include/luajit2.0 include/luajit include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_LIBRARY(LUAJIT_LIBRARY
NAMES libluajit-51.a libluajit-5.1.a libluajit.a
HINTS
$ENV{LUAJIT_DIR}
PATH_SUFFIXES lib64 lib
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt
)
IF(LUAJIT_LIBRARY)
IF(UNIX AND NOT APPLE)
FIND_LIBRARY(LUAJIT_MATH_LIBRARY m)
FIND_LIBRARY(LUAJIT_DL_LIBRARY dl)
SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY};${LUAJIT_DL_LIBRARY};${LUAJIT_MATH_LIBRARY}" CACHE STRING "Lua Libraries")
ELSE(UNIX AND NOT APPLE)
SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY}" CACHE STRING "Lua Libraries")
ENDIF(UNIX AND NOT APPLE)
ENDIF(LUAJIT_LIBRARY)
INCLUDE(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if
# all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT DEFAULT_MSG LUAJIT_LIBRARIES LUAJIT_INCLUDE_DIR)
MARK_AS_ADVANCED(LUAJIT_INCLUDE_DIR LUAJIT_LIBRARIES LUAJIT_LIBRARY LUAJIT_MATH_LIBRARY)
Module "bcc.vendor.argparse" "bcc/vendor/argparse.lua"
Module "bcc.vendor.posix" "bcc/vendor/posix.lua"
Module "bcc.vendor.middleclass" "bcc/vendor/middleclass.lua"
Module "bcc.vendor.json" "bcc/vendor/json.lua"
Module "bcc.vendor.helpers" "bcc/vendor/helpers.lua"
Module "bcc.init" "bcc/init.lua"
Module "bcc.run" "bcc/run.lua"
Module "bcc.bpf" "bcc/bpf.lua"
Module "bcc.sym" "bcc/sym.lua"
Module "bcc.libbcc" "bcc/libbcc.lua"
Module "bcc.tracerpipe" "bcc/tracerpipe.lua"
Module "bcc.table" "bcc/table.lua"
Module "bcc.ld" "bcc/ld.lua"
Main "bcc/run.lua"
Output "src/bcc.lua"
/*
* Copyright 2016 GitHub, Inc
*
* Based on lua.c, the Lua C Interpreter
* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static lua_State *globalL = NULL;
static const char *progname = NULL;
static void lstop(lua_State *L, lua_Debug *ar)
{
(void)ar; /* unused arg. */
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "interrupted!");
}
static void laction(int i)
{
signal(i, SIG_DFL);
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
static void l_message(const char *pname, const char *msg)
{
if (pname) fprintf(stderr, "%s: ", pname);
fprintf(stderr, "%s\n", msg);
fflush(stderr);
}
static int report(lua_State *L, int status)
{
if (status && !lua_isnil(L, -1)) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL) msg = "(error object is not a string)";
l_message(progname, msg);
lua_pop(L, 1);
}
return status;
}
static int traceback(lua_State *L)
{
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
static int docall(lua_State *L, int narg, int clear)
{
int status;
int base = lua_gettop(L) - narg; /* function index */
lua_pushcfunction(L, traceback); /* push traceback function */
lua_insert(L, base); /* put it under chunk and args */
signal(SIGINT, laction);
status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
signal(SIGINT, SIG_DFL);
lua_remove(L, base); /* remove traceback function */
/* force a complete garbage collection in case of errors */
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
return status;
}
static int dolibrary(lua_State *L, const char *name, int clear)
{
lua_getglobal(L, "require");
lua_pushstring(L, name);
return report(L, docall(L, 1, clear));
}
struct Smain {
int argc;
char **argv;
int status;
};
static void pushargv(lua_State *L, char **argv, int argc, int offset)
{
int i, j;
lua_createtable(L, argc, 0);
for (i = offset, j = 1; i < argc; i++, j++) {
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, j);
}
}
static void find_local_libbcc(lua_State *L)
{
char buffer[4096];
char *dirname;
if (readlink("/proc/self/exe", buffer, sizeof(buffer)) < 0)
return;
dirname = strrchr(buffer, '/');
if (dirname == NULL)
return;
strcpy(dirname + 1, "libbcc.so");
if (access(buffer, F_OK|R_OK|X_OK) != 0)
return;
lua_pushstring(L, buffer);
lua_setglobal(L, "LIBBCC_SO_PATH");
}
static int pmain(lua_State *L)
{
struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
globalL = L;
lua_gc(L, LUA_GCSTOP, 0);
luaL_openlibs(L);
lua_gc(L, LUA_GCRESTART, 0);
find_local_libbcc(L);
s->status = dolibrary(L, "bcc", 0);
if (s->status)
return 0;
lua_pushstring(L, progname);
lua_setglobal(L, "BCC_STANDALONE_NAME");
pushargv(L, s->argv, s->argc, 1);
lua_setglobal(L, "arg");
s->status = report(L, docall(L, 0, 1));
return 0;
}
int main(int argc, char **argv)
{
int status;
struct Smain s;
lua_State *L = lua_open(); /* create state */
if (L == NULL) {
l_message(argv[0], "cannot create state: not enough memory");
return EXIT_FAILURE;
}
progname = argv[0];
s.argc = argc;
s.argv = argv;
s.status = 0;
status = lua_cpcall(L, &pmain, &s);
report(L, status);
lua_close(L);
return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
}
#!/usr/bin/env lua
local short_opts = { v = "verbose", vv = "very_verbose", o = "output", q = "quiet", qq = "very_quiet", g = "debug" }
local opts = { use_http = false };
for _, opt in ipairs(arg) do
if opt:match("^%-") then
local name = opt:match("^%-%-?([^%s=]+)()")
name = (short_opts[name] or name):gsub("%-+", "_");
if name:match("^no_") then
name = name:sub(4, -1);
opts[name] = false;
else
opts[name] = opt:match("=(.*)$") or true;
end
else
base_path = opt;
end
end
if opts.very_verbose then opts.verbose = true; end
if opts.very_quiet then opts.quiet = true; end
local noprint = function () end
local print_err, print_info, print_verbose, print_debug = noprint, noprint, noprint, noprint;
if not opts.very_quiet then print_err = print; end
if not opts.quiet then print_info = print; end
if opts.verbose or opts.very_verbose then print_verbose = print; end
if opts.very_verbose then print_debug = print; end
print = print_verbose;
local modules, main_files, resources = {}, {}, {};
-- Functions to be called from squishy file --
function Module(name)
if modules[name] then
print_verbose("Ignoring duplicate module definition for "..name);
return function () end
end
local i = #modules+1;
modules[i] = { name = name, url = ___fetch_url };
modules[name] = modules[i];
return function (path)
modules[i].path = path;
end
end
function Resource(name, path)
local i = #resources+1;
resources[i] = { name = name, path = path or name };
return function (path)
resources[i].path = path;
end
end
function AutoFetchURL(url)
___fetch_url = url;
end
function Main(fn)
table.insert(main_files, fn);
end
function Output(fn)
if opts.output == nil then
out_fn = fn;
end
end
function Option(name)
name = name:gsub("%-", "_");
if opts[name] == nil then
opts[name] = true;
return function (value)
opts[name] = value;
end
else
return function () end;
end
end
function GetOption(name)
return opts[name:gsub('%-', '_')];
end
function Message(message)
if not opts.quiet then
print_info(message);
end
end
function Error(message)
if not opts.very_quiet then
print_err(message);
end
end
function Exit()
os.exit(1);
end
-- -- -- -- -- -- -- --- -- -- -- -- -- -- -- --
base_path = (base_path or "."):gsub("/$", "").."/"
squishy_file = base_path .. "squishy";
out_fn = opts.output;
local ok, err = pcall(dofile, squishy_file);
if not ok then
print_err("Couldn't read squishy file: "..err);
os.exit(1);
end
if not out_fn then
print_err("No output file specified by user or squishy file");
os.exit(1);
elseif #main_files == 0 and #modules == 0 and #resources == 0 then
print_err("No files, modules or resources. Not going to generate an empty file.");
os.exit(1);
end
local fetch = {};
function fetch.filesystem(path)
local f, err = io.open(path);
if not f then return false, err; end
local data = f:read("*a");
f:close();
return data;
end
if opts.use_http then
function fetch.http(url)
local http = require "socket.http";
local body, status = http.request(url);
if status == 200 then
return body;
end
return false, "HTTP status code: "..tostring(status);
end
else
function fetch.http(url)
return false, "Module not found. Re-squish with --use-http option to fetch it from "..url;
end
end
print_info("Writing "..out_fn.."...");
local f, err = io.open(out_fn, "w+");
if not f then
print_err("Couldn't open output file: "..tostring(err));
os.exit(1);
end
if opts.executable then
if opts.executable == true then
f:write("#!/usr/bin/env lua\n");
else
f:write(opts.executable, "\n");
end
end
if opts.debug then
f:write(require_resource("squish.debug"));
end
print_verbose("Resolving modules...");
do
local LUA_DIRSEP = package.config:sub(1,1);
local LUA_PATH_MARK = package.config:sub(5,5);
local package_path = package.path:gsub("[^;]+", function (path)
if not path:match("^%"..LUA_DIRSEP) then
return base_path..path;
end
end):gsub("/%./", "/");
local package_cpath = package.cpath:gsub("[^;]+", function (path)
if not path:match("^%"..LUA_DIRSEP) then
return base_path..path;
end
end):gsub("/%./", "/");
function resolve_module(name, path)
name = name:gsub("%.", LUA_DIRSEP);
for c in path:gmatch("[^;]+") do
c = c:gsub("%"..LUA_PATH_MARK, name);
print_debug("Looking for "..c)
local f = io.open(c);
if f then
print_debug("Found!");
f:close();
return c;
end
end
return nil; -- not found
end
for i, module in ipairs(modules) do
if not module.path then
module.path = resolve_module(module.name, package_path);
if not module.path then
print_err("Couldn't resolve module: "..module.name);
else
-- Strip base_path from resolved path
module.path = module.path:gsub("^"..base_path:gsub("%p", "%%%1"), "");
end
end
end
end
print_verbose("Packing modules...");
for _, module in ipairs(modules) do
local modulename, path = module.name, module.path;
if module.path:sub(1,1) ~= "/" then
path = base_path..module.path;
end
print_debug("Packing "..modulename.." ("..path..")...");
local data, err = fetch.filesystem(path);
if (not data) and module.url then
print_debug("Fetching: ".. module.url:gsub("%?", module.path))
data, err = fetch.http(module.url:gsub("%?", module.path));
end
if data then
f:write("package.preload['", modulename, "'] = (function (...)\n");
f:write(data);
f:write(" end)\n");
if opts.debug then
f:write(string.format("package.preload[%q] = ___adjust_chunk(package.preload[%q], %q);\n\n",
modulename, modulename, "@"..path));
end
else
print_err("Couldn't pack module '"..modulename.."': "..(err or "unknown error... path to module file correct?"));
os.exit(1);
end
end
if #resources > 0 then
print_verbose("Packing resources...")
f:write("do local resources = {};\n");
for _, resource in ipairs(resources) do
local name, path = resource.name, resource.path;
local res_file, err = io.open(base_path..path, "rb");
if not res_file then
print_err("Couldn't load resource: "..tostring(err));
os.exit(1);
end
local data = res_file:read("*a");
local maxequals = 0;
data:gsub("(=+)", function (equals_string) maxequals = math.max(maxequals, #equals_string); end);
f:write(("resources[%q] = %q"):format(name, data));
--[[ f:write(("resources[%q] = ["):format(name), string.rep("=", maxequals+1), "[");
f:write(data);
f:write("]", string.rep("=", maxequals+1), "];"); ]]
end
if opts.virtual_io then
local vio = require_resource("vio");
if not vio then
print_err("Virtual IO requested but is not enabled in this build of squish");
else
-- Insert vio library
f:write(vio, "\n")
-- Override standard functions to use vio if opening a resource
f:write[[local io_open, io_lines = io.open, io.lines; function io.open(fn, mode)
if not resources[fn] then
return io_open(fn, mode);
else
return vio.open(resources[fn]);
end end
function io.lines(fn)
if not resources[fn] then
return io_lines(fn);
else
return vio.open(resources[fn]):lines()
end end
local _dofile = dofile;
function dofile(fn)
if not resources[fn] then
return _dofile(fn);
else
return assert(loadstring(resources[fn]))();
end end
local _loadfile = loadfile;
function loadfile(fn)
if not resources[fn] then
return _loadfile(fn);
else
return loadstring(resources[fn], "@"..fn);
end end ]]
end
end
f:write[[function require_resource(name) return resources[name] or error("resource '"..tostring(name).."' not found"); end end ]]
end
print_debug("Finalising...")
for _, fn in pairs(main_files) do
local fin, err = io.open(base_path..fn);
if not fin then
print_err("Failed to open "..fn..": "..err);
os.exit(1);
else
f:write((fin:read("*a"):gsub("^#.-\n", "")));
fin:close();
end
end
f:close();
print_info("OK!");
......@@ -9,4 +9,7 @@ if(LUAJIT)
add_test(NAME lua_test_dump WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${TEST_WRAPPER} lua_test_dump sudo ${LUAJIT} test_dump.lua)
add_test(NAME lua_test_standalone WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_standalone.sh)
endif()
require("test_helper")
TestClang = {}
local suite = require("test_helper")
local TestClang = {}
function TestClang:test_probe_read1()
local text = [[
......@@ -329,4 +328,4 @@ function TestClang:test_syntax_error()
BPF, {text=[[int failure(void *ctx) { if (); return 0; }]]})
end
os.exit(LuaUnit.run())
suite("TestClang", TestClang)
require("test_helper")
local suite = require("test_helper")
local TestDump = {}
function test_dump_func()
function TestDump:test_dump_func()
local raw = "\xb7\x00\x00\x00\x01\x00\x00\x00\x95\x00\x00\x00\x00\x00\x00\x00"
local b = BPF:new{text=[[int entry(void) { return 1; }]]}
assert_equals(b:dump_func("entry"), raw)
end
os.exit(LuaUnit.run())
suite("TestDump", TestDump)
......@@ -12,6 +12,12 @@ USE_EXPECTED_ACTUAL_IN_ASSERT_EQUALS = false
EXPORT_ASSERT_TO_GLOBALS = true
require("luaunit")
BCC = require("bcc.init")
BPF = BCC.BPF
rawset(_G, "BCC", require("bcc.init"))
rawset(_G, "BPF", BCC.BPF)
log.enabled = false
return function (name, f)
rawset(_G, name, f)
os.exit(LuaUnit.run())
end
#!/bin/bash
# Copyright (c) GitHub, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")
set -xe
cd "src/lua"
function fail {
echo "test failed: $1" >&2
exit 1
}
if [[ ! -x bcc-lua ]]; then
echo "bcc-lua not built --- skipping"
exit 0
fi
if ldd bcc-lua | grep -q luajit; then
fail "bcc-lua depends on libluajit"
fi
rm -f libbcc.so probe.lua
echo "return function(BPF) print(\"Hello world\") end" > probe.lua
if ./bcc-lua "probe.lua"; then
fail "bcc-lua runs without libbcc.so"
fi
if ! env LIBBCC_SO_PATH=../cc/libbcc.so ./bcc-lua "probe.lua"; then
fail "bcc-lua cannot load libbcc.so through the environment"
fi
ln -s ../cc/libbcc.so
if ! ./bcc-lua "probe.lua"; then
fail "bcc-lua cannot find local libbcc.so"
fi
PROBE="../../../examples/lua/offcputime.lua"
if ! sudo ./bcc-lua "$PROBE" -d 1 >/dev/null 2>/dev/null; then
fail "bcc-lua cannot run complex probes"
fi
rm -f libbcc.so probe.lua
require("test_helper")
local suite = require("test_helper")
local ffi = require("ffi")
local TestUprobes = {}
ffi.cdef[[
int getpid(void);
void malloc_stats(void);
]]
TestUprobes = {}
function TestUprobes:test_simple_library()
local text = [[
#include <uapi/linux/ptrace.h>
......@@ -68,4 +67,4 @@ function TestUprobes:teardown()
BPF.cleanup_probes()
end
os.exit(LuaUnit.run())
suite("TestUprobes", TestUprobes)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment