diff --git a/src/lua/init.c b/src/lua/init.c index b6ea368eca7b6becd4ef382aa463c12fa3aae248..456560592c37c1fb54cdbfed11fd98b22af6493c 100644 --- a/src/lua/init.c +++ b/src/lua/init.c @@ -35,6 +35,7 @@ #include <libgen.h> #endif +#include <assert.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> @@ -89,6 +90,8 @@ LUALIB_API int luaopen_zip(lua_State *L); #endif +#define MAX_MODNAME 64 + /** * The single Lua state of the transaction processor (tx) thread. */ @@ -424,6 +427,50 @@ static const char *lua_modules_preload[] = { * {{{ box Lua library: common functions */ +/* + * Retrieve builtin module sources, if available. + */ +static const char * +tarantool_debug_getsources(const char *modname) +{ + for (size_t i = 0; lua_modules[i] != NULL; i += 2) { + const char *shortname = lua_modules[i]; + const char *lua_code = lua_modules[i + 1]; + assert(lua_code != NULL); + assert(strlen(shortname) + sizeof("@builtin/.lua") <= + MAX_MODNAME); + char fullname[MAX_MODNAME]; + snprintf(fullname, sizeof(fullname), "@builtin/%s.lua", + shortname); + if (!strcmp(shortname, modname) || !strcmp(fullname, modname)) + return lua_code; + } + return NULL; +} + +/* + * LuaC implementation of a function to retrieve builtin module sources. + */ +static int +lbox_tarantool_debug_getsources(struct lua_State *L) +{ + int index = lua_gettop(L); + if (index != 1) + luaL_error(L, "getsources() function expects one argument"); + size_t len = 0; + const char *modname = luaL_checklstring(L, index, &len); + if (len <= 0) + goto ret_nil; + const char *code = tarantool_debug_getsources(modname); + if (code == NULL) + goto ret_nil; + lua_pushstring(L, code); + return 1; +ret_nil: + lua_pushnil(L); + return 1; +} + /** * Convert lua number or string to lua cdata 64bit number. */ @@ -707,6 +754,13 @@ luaopen_tarantool(lua_State *L) lua_settable(L, -3); lua_settable(L, -3); /* box.info.build */ + + /* debug */ + lua_newtable(L); + lua_pushcfunction(L, lbox_tarantool_debug_getsources); + lua_setfield(L, -2, "getsources"); + lua_setfield(L, -2, "debug"); + lua_pop(L, 1); return 1; } @@ -806,7 +860,6 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv) lua_pop(L, 1); /* _PRELOAD */ luaopen_tarantool(L); - lua_pop(L, 1); lua_newtable(L); lua_pushinteger(L, -1); diff --git a/test/app-luatest/tnt_debug_getsources_test.lua b/test/app-luatest/tnt_debug_getsources_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..3658a6f89c3a77da6d59b0e56806c88e0810eada --- /dev/null +++ b/test/app-luatest/tnt_debug_getsources_test.lua @@ -0,0 +1,50 @@ +local t = require('luatest') +local fio = require('fio') +local g = t.group() + + +local function readfile(filename) + local f = assert(io.open(filename, "rb")) + return f:read('*a') +end + +local files = { + 'strict', + 'debug', + 'errno', + 'fiber', + 'env', + 'datetime', +} + +-- calculate reporsitory root using directory of a current +-- script, but get 2 directories above +local function repo_root() + local myself = fio.abspath(debug.getinfo(1,'S').source:gsub("^@", "")) + local root = fio.abspath(fio.dirname(myself) .. '/../..') + return root +end + +g.test_tarantool_debug_getsources = function() + local git_root = repo_root() + t.assert_is_not(git_root, nil) + t.assert(fio.stat(git_root):is_dir()) + local lua_src_dir = fio.pathjoin(git_root, '/src/lua') + t.assert_is_not(lua_src_dir, nil) + t.assert(fio.stat(lua_src_dir):is_dir()) + + local tnt = require('tarantool') + t.assert_is_not(tnt, nil) + local luadebug = tnt.debug + t.assert_is_not(luadebug, nil) + + for _, file in pairs(files) do + local path = ('%s/%s.lua'):format(lua_src_dir, file) + local text = readfile(path) + t.assert_is_not(text, nil) + local source = luadebug.getsources(file) + t.assert_equals(source, text) + source = luadebug.getsources(('@builtin/%s.lua'):format(file)) + t.assert_equals(source, text) + end +end diff --git a/third_party/lua/luadebug.lua b/third_party/lua/luadebug.lua index d024d025fb47ca0247299770680a3005a14357a2..2ed213a56eeb73bd9becf7941e1c94abf1435c7b 100644 --- a/third_party/lua/luadebug.lua +++ b/third_party/lua/luadebug.lua @@ -248,14 +248,34 @@ end local SOURCE_CACHE = {} local function where(info, context_lines) + local filesource = info.source local source = SOURCE_CACHE[info.source] if not source then source = {} - local filename = info.source:match("@(.*)") - if filename then - pcall(function() for line in io.lines(filename) do table.insert(source, line) end end) - elseif info.source then - for line in info.source:gmatch("(.-)\n") do table.insert(source, line) end + -- Tarantool builtin module + if filesource:match("@builtin/.*.lua") then + pcall(function() + local tnt_debug = require('tarantool').debug + local lua_code = tnt_debug.getsources(filesource) + + for line in string.gmatch(lua_code, "([^\n]*)\n?") do + table.insert(source, line) + end + end) + else + -- external module - load file + local filename = filesource:match("@(.*)") + if filename then + pcall(function() + for line in io.lines(filename) do + table.insert(source, line) + end + end) + elseif filesource then + for line in info.source:gmatch("(.-)\n") do + table.insert(filesource, line) + end + end end SOURCE_CACHE[info.source] = source end