From 7bc5fe2e69f65a910b4a9fc009dc466d803680c4 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Tue, 9 Jul 2024 17:57:27 +0600 Subject: [PATCH 01/10] Adaption of pallene-tracer into Pallene --- pallene-dev-1.rockspec | 6 + src/pallene/c_compiler.lua | 3 + src/pallene/pallenelib.lua | 375 +------------------------------------ 3 files changed, 14 insertions(+), 370 deletions(-) diff --git a/pallene-dev-1.rockspec b/pallene-dev-1.rockspec index cbcd2475..5cacff15 100644 --- a/pallene-dev-1.rockspec +++ b/pallene-dev-1.rockspec @@ -15,6 +15,12 @@ dependencies = { "lua == 5.4", "lpeg >= 1.0", "argparse >= 0.7.0", + "pallene-tracer >= 0.5.0a" +} +external_dependencies = { + PTRACER = { + header = "ptracer.h", + } } build = { type = "builtin", diff --git a/src/pallene/c_compiler.lua b/src/pallene/c_compiler.lua index ca204b66..e884fdb6 100644 --- a/src/pallene/c_compiler.lua +++ b/src/pallene/c_compiler.lua @@ -18,6 +18,7 @@ local c_compiler = {} local CC = os.getenv("CC") or "cc" local CFLAGS = os.getenv("CFLAGS") or "-O2" +local SHLIBLOC = os.getenv("SHLIBLOC") or "/usr/local/lib" local function get_uname() local ok, err, uname = util.outputs_of_execute("uname -s") @@ -61,6 +62,8 @@ function c_compiler.compile_o_to_so(in_filename, out_filename) CFLAGS_SHARED, "-o", util.shell_quote(out_filename), util.shell_quote(in_filename), + "-lptracer", + "-Wl,-rpath="..SHLIBLOC, }) end diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index 7f69ada0..bb2129da 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -46,6 +46,10 @@ return [==[ #include #include +/* Pallene Tracer for tracebacks (dynamically linked). */ +/* Look at https://github.com/pallene-lang/pallene-tracer for more info. */ +#include + #define PALLENE_UNREACHABLE __builtin_unreachable() /* PALLENE TRACER MACROS */ @@ -59,7 +63,7 @@ return [==[ pt_cont_t *cont = pvalue(&K->uv[0].uv); \ static pt_fn_details_t _details = { \ .fn_name = name, \ - .mod_name = PALLENE_SOURCE_FILE \ + .filename = PALLENE_SOURCE_FILE \ }; \ pt_frame_t _frame = { \ .type = PALLENE_TRACER_FRAME_TYPE_C, \ @@ -83,64 +87,6 @@ return [==[ #define PALLENE_SETLINE(line) pallene_tracer_setline(cont, line) #define PALLENE_FRAMEEXIT() pallene_tracer_frameexit(cont) -/* Pallene stack reference entry for the registry. */ -#define PALLENE_TRACER_CONTAINER_ENTRY "__PALLENE_TRACER_CONTAINER" - -/* Finalizer metatable key. */ -#define PALLENE_TRACER_METATABLE_ENTRY "__PALLENE_TRACER_METATABLE" - -/* The size of the Pallene call stack. */ -#define PALLENE_MAX_CALLSTACK 2000000 - -/* Traceback elipsis threshold. */ -#define PALLENE_TRACEBACK_TOP_THRESHOLD 10 -/* This should always be 2 fewer than top threshold, for symmetry. - Becuase we will always have 2 tail frames lingering around at - at the end which is not captured by '_countlevels'. */ -#define PALLENE_TRACEBACK_BOTTOM_THRESHOLD 8 - -/* PALLENE TRACER RELATED DATA-STRUCTURES. */ - -/* Whether the frame is a Pallene->Pallene or Lua->Pallene call. */ -typedef enum frame_type { - PALLENE_TRACER_FRAME_TYPE_C, - PALLENE_TRACER_FRAME_TYPE_LUA -} frame_type_t; - -/* Details of a single function such as what is the name - and where it is from. */ -typedef struct pt_fn_details { - const char *const fn_name; - const char *const mod_name; -} pt_fn_details_t; - -/* A single frame representation. */ -typedef struct pt_frame { - frame_type_t type; - int line; - - union { - pt_fn_details_t *details; - lua_CFunction frame_sig; - } shared; -} pt_frame_t; - -/* For Full Userdata allocation. That userdata is the module singular - point for Pallene stack. */ -/* 'cont' stands for 'container'. */ -typedef struct pt_cont { - pt_frame_t *stack; - int count; -} pt_cont_t; - -/* Pallene Tracer. */ -static void pallene_tracer_frameenter(lua_State *L, pt_cont_t *cont, pt_frame_t *restrict frame); -static void pallene_tracer_setline(pt_cont_t *cont, int line); -static void pallene_tracer_frameexit(pt_cont_t *cont); -static int pallene_tracer_debug_traceback(lua_State *L); -static pt_cont_t *pallene_tracer_init(lua_State *L); -static int pallene_tracer_finalizer(lua_State *L); - /* Type tags */ static const char *pallene_type_name(lua_State *L, const TValue *v); static int pallene_is_truthy(const TValue *v); @@ -192,317 +138,6 @@ static TString *pallene_type_builtin(lua_State *L, TValue v); static TString *pallene_tostring(lua_State *L, const char* file, int line, TValue v); static void pallene_io_write(lua_State *L, TString *str); -/* PALLENE TRACER IMPLEMENTATION */ - -/* Private routines. */ - -static bool _findfield(lua_State *L, int fn_idx, int level) { - if(level == 0 || !lua_istable(L, -1)) - return false; - - lua_pushnil(L); /* Initial key. */ - - while(lua_next(L, -2)) { - /* We are only interested in String keys. */ - if(lua_type(L, -2) == LUA_TSTRING) { - /* Avoid "_G" recursion in global table. The global table is also part of - global table. */ - if(!strcmp(lua_tostring(L, -2), "_G")) { - /* Remove value and continue. */ - lua_pop(L, 1); - continue; - } - - /* Is it the function we are looking for? */ - if(lua_rawequal(L, fn_idx, -1)) { - /* Remove value and keep name. */ - lua_pop(L, 1); - - return true; - } - /* If not go one level deeper and get the value recursively. */ - else if(_findfield(L, fn_idx, level - 1)) { - /* Remove the table but keep name. */ - lua_remove(L, -2); - - /* Add a "." in between. */ - lua_pushliteral(L, "."); - lua_insert(L, -2); - - /* Concatenate last 3 values, resulting "table.some_func". */ - lua_concat(L, 3); - - return true; - } - } - - /* Pop the value. */ - lua_pop(L, 1); - } - - return false; -} - -/* Pushes a function name if found in the global table and returns true. - Returns false otherwise. */ -/* Expects the function to be pushed in the stack. */ -static bool _pgf_name(lua_State *L) { - int top = lua_gettop(L); - - lua_pushglobaltable(L); - - if(_findfield(L, top, 2)) { - lua_remove(L, -2); - - return true; - } - - lua_pop(L, 1); - return false; -} - -/* Returns the maximum number of levels in Lua stack. */ -static int _countlevels (lua_State *L) { - lua_Debug ar; - int li = 1, le = 1; - - /* Find an upper bound */ - while (lua_getstack(L, le, &ar)) { - li = le, le *= 2; - } - - /* Do a binary search */ - while (li < le) { - int m = (li + le)/2; - - if (lua_getstack(L, m, &ar)) li = m + 1; - else le = m; - } - - return le - 1; -} - -/* Counts the number of white and black frames in the Pallene call stack. */ -static void _countframes(pt_cont_t *cont, int *mwhite, int *mblack) { - *mwhite = *mblack = 0; - - for(int i = 0; i < cont->count; i++) { - *mwhite += (cont->stack[i].type == PALLENE_TRACER_FRAME_TYPE_C); - *mblack += (cont->stack[i].type == PALLENE_TRACER_FRAME_TYPE_LUA); - } -} - -/* Responsible for printing and controlling some of the traceback fn parameters. */ -static void _dbg_print(const char *buf, bool *elipsis, int *pframes, int nframes) { - /* We have printed the frame, even tho it might not be visible ;). */ - (*pframes)++; - - /* Should we print? Are we in the point in top or bottom printing threshold? */ - bool should_print = (*pframes <= PALLENE_TRACEBACK_TOP_THRESHOLD) - || ((nframes - *pframes) <= PALLENE_TRACEBACK_BOTTOM_THRESHOLD); - - if(should_print) - fprintf(stderr, buf); - else if(*elipsis) { - fprintf(stderr, "\n ... (Skipped %d frames) ...\n\n", - nframes - (PALLENE_TRACEBACK_TOP_THRESHOLD - + PALLENE_TRACEBACK_BOTTOM_THRESHOLD)); - - *elipsis = false; - } -} - -/* Frees the heap-allocated resources. */ -static int _free_resources(lua_State *L) { - pt_cont_t *cont = (pt_cont_t *) lua_touserdata(L, 1); - - free(cont->stack); - - return 0; -} - -/* Private routines end. */ - -static void pallene_tracer_frameenter(lua_State *L, pt_cont_t *cont, pt_frame_t *restrict frame) { - /* If we ran out of Pallene frames. */ - if(l_unlikely(cont->count + 1 >= PALLENE_MAX_CALLSTACK)) { - pallene_runtime_callstack_overflow_error(L); - } - - cont->stack[cont->count++] = *frame; -} - -static void pallene_tracer_setline(pt_cont_t *cont, int line) { - if(cont->count != 0) - cont->stack[cont->count - 1].line = line; -} - -static void pallene_tracer_frameexit(pt_cont_t *cont) { - cont->count -= (cont->count > 0); -} - -/* Helper macro specific to this function only :). */ -#define DBG_PRINT() _dbg_print(buf, &elipsis, &pframes, nframes) -static int pallene_tracer_debug_traceback(lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_CONTAINER_ENTRY); - pt_cont_t *cont = (pt_cont_t *) lua_touserdata(L, -1); - pt_frame_t *stack = cont->stack; - /* The point where we are in the Pallene stack. */ - int index = cont->count - 1; - lua_pop(L, 1); - - /* Max number of white and black frames. */ - int mwhite, mblack; - _countframes(cont, &mwhite, &mblack); - /* Max levels of Lua stack. */ - int mlevel = _countlevels(L); - - /* Total frames we are going to print. */ - /* Black frames are used for switching and we will start from - Lua stack level 1. */ - int nframes = mlevel + mwhite - mblack - 1; - /* Amount of frames printed. */ - int pframes = 0; - /* Should we print elipsis? */ - bool elipsis = nframes > (PALLENE_TRACEBACK_TOP_THRESHOLD - + PALLENE_TRACEBACK_BOTTOM_THRESHOLD); - - /* Buffer to store a single frame line to be printed. */ - char buf[1024]; - - const char *message = lua_tostring(L, 1); - fprintf(stderr, "Runtime error: %s\nStack traceback:\n", message); - - lua_Debug ar; - int top = lua_gettop(L); - int level = 1; - - while(lua_getstack(L, level++, &ar)) { - /* Get additional information regarding the frame. */ - lua_getinfo(L, "Slntf", &ar); - - /* If the frame is a C frame. */ - if(lua_iscfunction(L, -1)) { - if(index >= 0) { - /* Check whether this frame is tracked (Pallene C frames). */ - int check = index; - while(stack[check].type != PALLENE_TRACER_FRAME_TYPE_LUA) - check--; - - /* If the frame signature matches, we switch to printing Pallene frames. */ - if(lua_tocfunction(L, -1) == stack[check].shared.frame_sig) { - /* Now print all the frames in Pallene stack. */ - for(; index > check; index--) { - sprintf(buf, " %s:%d: in function '%s'\n", - stack[index].shared.details->mod_name, - stack[index].line, stack[index].shared.details->fn_name); - DBG_PRINT(); - } - - /* 'check' idx is guaranteed to be a Lua interface frame. - Which is basically our 'stack' index at this point. So, - we simply ignore the Lua interface frame. */ - index--; - - /* We are done. */ - lua_settop(L, top); - continue; - } - } - - /* Then it's an untracked C frame. */ - if(_pgf_name(L)) - lua_pushfstring(L, "%s", lua_tostring(L, -1)); - else lua_pushliteral(L, ""); - - sprintf(buf, " C: in function '%s'\n", lua_tostring(L, -1)); - DBG_PRINT(); - } else { - /* It's a Lua frame. */ - - /* Do we have a name? */ - if(*ar.namewhat != '\0') - lua_pushfstring(L, "function '%s'", ar.name); - /* Is it the main chunk? */ - else if(*ar.what == 'm') - lua_pushliteral(L, "
"); - /* Can we deduce the name from the global table? */ - else if(_pgf_name(L)) - lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); - else lua_pushliteral(L, "function ''"); - - sprintf(buf, " %s:%d: in %s\n", ar.short_src, - ar.currentline, lua_tostring(L, -1)); - DBG_PRINT(); - } - - lua_settop(L, top); - } - - return 0; -} -#undef DBG_PRINT - -/* This function is expected to push the finalizer metatable in the stack. */ -static pt_cont_t *pallene_tracer_init(lua_State *L) { - pt_cont_t *cont = NULL; - - /* Try getting the userdata. */ - lua_getfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_CONTAINER_ENTRY); - - /* If we don't find any userdata, create one. */ - if(l_unlikely(lua_isnil(L, -1) == 1)) { - cont = (pt_cont_t *) lua_newuserdata(L, sizeof(pt_cont_t)); - cont->stack = malloc(PALLENE_MAX_CALLSTACK * sizeof(pt_frame_t)); - cont->count = 0; - - lua_newtable(L); - lua_pushcfunction(L, _free_resources); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - - /* This is our finalizer which will reside in the value stack. */ - lua_newtable(L); - lua_newtable(L); - lua_pushvalue(L, -3); - - /* Our finalizer fn. */ - lua_pushcclosure(L, pallene_tracer_finalizer, 1); - lua_setfield(L, -2, "__close"); - lua_setmetatable(L, -2); - - /* Metatable registry. */ - lua_setfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_METATABLE_ENTRY); - - /* Stack container registry .*/ - lua_setfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_CONTAINER_ENTRY); - - /* The debug traceback fn. */ - lua_register(L, "pallene_tracer_debug_traceback", pallene_tracer_debug_traceback); - - /* Push the metatable in the stack. */ - lua_getfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_METATABLE_ENTRY); - } else { - cont = lua_touserdata(L, -1); - lua_getfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_METATABLE_ENTRY); - } - - return cont; -} - -static int pallene_tracer_finalizer(lua_State *L) { - /* Get the userdata. */ - pt_cont_t *cont = (pt_cont_t *) lua_touserdata(L, lua_upvalueindex(1)); - - int idx = cont->count - 1; - while(cont->stack[idx].type != PALLENE_TRACER_FRAME_TYPE_LUA) - idx--; - - cont->count = idx; - - return 0; -} - static const char *pallene_type_name(lua_State *L, const TValue *v) { if (rawtt(v) == LUA_VNUMINT) { From 8d99ce35dc68f4a70c7f8b7637b5ab9fccb64920 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Tue, 9 Jul 2024 18:06:07 +0600 Subject: [PATCH 02/10] Remove -Wpedantic flag from run-tests script We do not need -Wpedantic as we are specifying C99 standard. According to GCC, -Wpedantic will generate warnings based on strict ISO C/ANSI C, which is basically C89. If we really need to use some features from C89 (ISO C), we may use flags specific to the feature we want to use e.g. -Wdeclaratoin-after-statement to lift all the variable declarations at the top of the function definition etc. --- run-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-tests b/run-tests index e804cbe1..dd3bfcfe 100755 --- a/run-tests +++ b/run-tests @@ -19,7 +19,7 @@ FLAGS=(--verbose --no-keep-going) # To speed things up, we tell the C compiler to skip optimizations. (It's OK, the CI still uses -O2) # Also, add some compiler flags to verify standard compliance. -export CFLAGS='-O0 -std=c99 -Wall -Werror -Wundef -Wpedantic -Wno-unused' +export CFLAGS='-O0 -std=c99 -Wall -Werror -Wundef -Wno-unused' if [ "$#" -eq 0 ]; then if command -v parallel >/dev/null; then From bdfd1eb62feece8e048482d98c2818900fd069c7 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sat, 3 Aug 2024 20:58:27 +0600 Subject: [PATCH 03/10] Pallene Tracer adoption and CI script update --- .github/workflows/ci.yml | 14 ++++++++++++ src/pallene/c_compiler.lua | 3 +-- src/pallene/coder.lua | 4 ++++ src/pallene/pallenelib.lua | 46 +++++++++++++++----------------------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 816b2e5b..234256ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,13 @@ jobs: make sudo make install + - name: Install Pallene Tracer + run: | + git clone - https://github.com/pallene-lang/pallene-tracer + cd pallene-tracer + sudo make install + sudo luarocks make pallene-tracer-0.5.0a-1.rockspec + - name: Install Luacheck run: luarocks install --local luacheck @@ -77,6 +84,13 @@ jobs: make sudo make install + - name: Install Pallene Tracer + run: | + git clone - https://github.com/pallene-lang/pallene-tracer + cd pallene-tracer + sudo make install + sudo luarocks make pallene-tracer-0.5.0a-1.rockspec + - name: Build run: luarocks --local make diff --git a/src/pallene/c_compiler.lua b/src/pallene/c_compiler.lua index e884fdb6..3c8d0fd0 100644 --- a/src/pallene/c_compiler.lua +++ b/src/pallene/c_compiler.lua @@ -18,7 +18,6 @@ local c_compiler = {} local CC = os.getenv("CC") or "cc" local CFLAGS = os.getenv("CFLAGS") or "-O2" -local SHLIBLOC = os.getenv("SHLIBLOC") or "/usr/local/lib" local function get_uname() local ok, err, uname = util.outputs_of_execute("uname -s") @@ -63,7 +62,7 @@ function c_compiler.compile_o_to_so(in_filename, out_filename) "-o", util.shell_quote(out_filename), util.shell_quote(in_filename), "-lptracer", - "-Wl,-rpath="..SHLIBLOC, + "-Wl,-rpath=/usr/local/lib", }) end diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 19ae5c75..860357e0 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -1802,6 +1802,10 @@ function Coder:generate_module_header() table.insert(out, "/* This file was generated by the Pallene compiler. Do not edit by hand */") table.insert(out, "") table.insert(out, string.format("#define PALLENE_SOURCE_FILE %s", C.string(self.filename))) + if self.flags.use_traceback then + table.insert(out, "/* Enable Pallene Tracer debugging. */") + table.insert(out, "#define PT_DEBUG") + end table.insert(out, "") table.insert(out, "/* ------------------------ */") diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index bb2129da..09199567 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -47,7 +47,7 @@ return [==[ #include /* Pallene Tracer for tracebacks (dynamically linked). */ -/* Look at https://github.com/pallene-lang/pallene-tracer for more info. */ +/* Look at `https://github.com/pallene-lang/pallene-tracer` for more info. */ #include #define PALLENE_UNREACHABLE __builtin_unreachable() @@ -55,37 +55,27 @@ return [==[ /* PALLENE TRACER MACROS */ /* Prepares finalizer function for Lua interface calls. */ -#define PALLENE_PREPARE_FINALIZER() \ - setobj(L, s2v(L->top.p++), &K->uv[1].uv); \ +#define PALLENE_PREPARE_FINALIZER() \ + setobj(L, s2v(L->top.p++), &K->uv[1].uv); \ lua_toclose(L, -1) -#define PALLENE_C_FRAMEENTER(L, name) \ - pt_cont_t *cont = pvalue(&K->uv[0].uv); \ - static pt_fn_details_t _details = { \ - .fn_name = name, \ - .filename = PALLENE_SOURCE_FILE \ - }; \ - pt_frame_t _frame = { \ - .type = PALLENE_TRACER_FRAME_TYPE_C, \ - .shared = { \ - .details = &_details \ - } \ - }; \ - pallene_tracer_frameenter(L, cont, &_frame) - -#define PALLENE_LUA_FRAMEENTER(L, sig) \ - pt_cont_t *cont = pvalue(&K->uv[0].uv); \ - pt_frame_t _frame = { \ - .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ - .shared = { \ - .frame_sig = sig \ - } \ - }; \ - pallene_tracer_frameenter(L, cont, &_frame); \ +#define PALLENE_C_FRAMEENTER(L, name) \ + pt_fnstack_t *fnstack = pvalue(&K->uv[0].uv); \ + static pt_fn_details_t _details = \ + PALLENE_TRACER_FN_DETAILS(name, PALLENE_SOURCE_FILE); \ + pt_frame_t _frame = \ + PALLENE_TRACER_C_FRAME(_details); \ + PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); + +#define PALLENE_LUA_FRAMEENTER(L, fnptr) \ + pt_fnstack_t *fnstack = pvalue(&K->uv[0].uv); \ + pt_frame_t _frame = \ + PALLENE_TRACER_LUA_FRAME(fnptr); \ + PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); \ PALLENE_PREPARE_FINALIZER() -#define PALLENE_SETLINE(line) pallene_tracer_setline(cont, line) -#define PALLENE_FRAMEEXIT() pallene_tracer_frameexit(cont) +#define PALLENE_SETLINE(line) PALLENE_TRACER_SETLINE(fnstack, line) +#define PALLENE_FRAMEEXIT() PALLENE_TRACER_FRAMEEXIT(fnstack) /* Type tags */ static const char *pallene_type_name(lua_State *L, const TValue *v); From 890999bf12216b887800f3fdfbbe7cb8139744ac Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sat, 3 Aug 2024 21:04:53 +0600 Subject: [PATCH 04/10] CI script typo fix --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 234256ea..c82feeed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,10 +86,10 @@ jobs: - name: Install Pallene Tracer run: | - git clone - https://github.com/pallene-lang/pallene-tracer + git clone https://github.com/pallene-lang/pallene-tracer cd pallene-tracer sudo make install - sudo luarocks make pallene-tracer-0.5.0a-1.rockspec + sudo luarocks --local make - name: Build run: luarocks --local make From a1b85e1ab22f16cc89fe3c97e727abbce651ca08 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sat, 3 Aug 2024 21:22:15 +0600 Subject: [PATCH 05/10] CI script fix, fingers crossed --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c82feeed..b1dfd72c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,10 +50,10 @@ jobs: - name: Install Pallene Tracer run: | - git clone - https://github.com/pallene-lang/pallene-tracer + git clone https://github.com/pallene-lang/pallene-tracer cd pallene-tracer sudo make install - sudo luarocks make pallene-tracer-0.5.0a-1.rockspec + luarocks --local make - name: Install Luacheck run: luarocks install --local luacheck @@ -89,7 +89,7 @@ jobs: git clone https://github.com/pallene-lang/pallene-tracer cd pallene-tracer sudo make install - sudo luarocks --local make + luarocks --local make - name: Build run: luarocks --local make @@ -101,3 +101,4 @@ jobs: run: | eval "$(luarocks path)" busted -o gtest -v ./spec + From 906f8b375031c3c0a1ba3f63fd5bd6ae45b87bc2 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sat, 3 Aug 2024 21:24:26 +0600 Subject: [PATCH 06/10] Remove Pallene Tracer installation for linting check in CI --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1dfd72c..2f413b8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,13 +48,6 @@ jobs: make sudo make install - - name: Install Pallene Tracer - run: | - git clone https://github.com/pallene-lang/pallene-tracer - cd pallene-tracer - sudo make install - luarocks --local make - - name: Install Luacheck run: luarocks install --local luacheck From ec66bf2690b0f3de2f76d67c5309185e7429495b Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Sun, 4 Aug 2024 21:27:26 +0600 Subject: [PATCH 07/10] Pallene Tracer API usage improvements --- src/pallene/c_compiler.lua | 16 +++++--- src/pallene/coder.lua | 81 ++++++++++++++------------------------ src/pallene/pallenec.lua | 6 ++- src/pallene/pallenelib.lua | 62 +++++++++++++++++------------ 4 files changed, 81 insertions(+), 84 deletions(-) diff --git a/src/pallene/c_compiler.lua b/src/pallene/c_compiler.lua index 3c8d0fd0..e5272db8 100644 --- a/src/pallene/c_compiler.lua +++ b/src/pallene/c_compiler.lua @@ -18,6 +18,7 @@ local c_compiler = {} local CC = os.getenv("CC") or "cc" local CFLAGS = os.getenv("CFLAGS") or "-O2" +local PTLIBDIR = os.getenv("PTLIBDIR") or "/usr/local/lib" local function get_uname() local ok, err, uname = util.outputs_of_execute("uname -s") @@ -54,16 +55,21 @@ function c_compiler.compile_c_to_o(in_filename, out_filename) }) end -function c_compiler.compile_o_to_so(in_filename, out_filename) +function c_compiler.compile_o_to_so(in_filename, out_filename, _, _, flags) -- There is no need to add the '-x' flag when compiling an object file without a '.o' extension. -- According to GCC, any file name with no recognized suffix is treated as an object file. - return run_cc({ + local command = { CFLAGS_SHARED, "-o", util.shell_quote(out_filename), util.shell_quote(in_filename), - "-lptracer", - "-Wl,-rpath=/usr/local/lib", - }) + } + + if flags.pt_dynamic or not flags.use_traceback then + table.insert(command, "-lptracer") + table.insert(command, "-Wl,-rpath="..PTLIBDIR) + end + + return run_cc(command) end return c_compiler diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 860357e0..406d77a2 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -214,10 +214,7 @@ end -- these cases instead of invoking the metatable operations, which may impair program optimization -- even if they are never called. local function check_no_metatable(self, src, loc) - local setline = "" - if self.flags.use_traceback then - setline = string.format("PALLENE_SETLINE(%d);", loc.line) - end + local setline = string.format("PALLENE_SETLINE(%d);", loc.line) return (util.render([[ if ($src->metatable) { @@ -254,10 +251,7 @@ function Coder:get_stack_slot(typ, dst, slot, loc, description_fmt, ...) assert(not typ.is_upvalue_box) local extra_args = table.pack(...) - local setline = "" - if self.flags.use_traceback then - setline = string.format("PALLENE_SETLINE(%d);", loc.line) - end + local setline = string.format("PALLENE_SETLINE(%d);", loc.line) check_tag = util.render([[ if (l_unlikely(!$test)) { @@ -461,18 +455,12 @@ function Coder:pallene_entry_point_definition(f_id) local max_frame_size = self.gc[func].max_frame_size local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func] - local setline = "" - local frameexit = "" - if self.flags.use_traceback then - table.insert(prologue, util.render([[ - PALLENE_C_FRAMEENTER(L, "$name"); - ]], { - name = func.name - })); - - setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) - frameexit = "PALLENE_FRAMEEXIT();" - end + table.insert(prologue, util.render([[ + PALLENE_C_FRAMEENTER(L, "$name"); + ]], { + name = func.name + })); + local setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) if slots_needed > 0 then table.insert(prologue, util.render([[ @@ -527,7 +515,7 @@ function Coder:pallene_entry_point_definition(f_id) ${prologue} /**/ ${body} - ${frameexit} + PALLENE_FRAMEEXIT(); ${ret_mult} ${ret_stat} } @@ -537,7 +525,6 @@ function Coder:pallene_entry_point_definition(f_id) prologue = table.concat(prologue, "\n"), body = body, ret_mult = ret_mult, - frameexit = frameexit, ret_stat = ret_stat, })) end @@ -615,22 +602,18 @@ function Coder:lua_entry_point_definition(f_id) Udata *K = uvalue(&func->upvalue[0]); ]] - local frameenter = "" - local frameexit = "" local nargs = #arg_types + -- We will be having our call-stack finalizer object on top of our stack when debugging mode is + -- enabled local cargs = nargs if self.flags.use_traceback then - frameenter = util.render([[ - PALLENE_LUA_FRAMEENTER(L, $fun_name); - ]], { - fun_name = self:lua_entry_point_name(f_id), - }) - -- It's as simple as popping the finalizer. - frameexit = "lua_pop(L, 1);" - - -- We will be having our finalizer on top of our stack. cargs = cargs + 1 end + local frameenter = util.render([[ + PALLENE_LUA_FRAMEENTER(L, $fun_name); + ]], { + fun_name = self:lua_entry_point_name(f_id), + }) local arity_check = util.render([[ int nargs = lua_gettop(L); @@ -694,7 +677,6 @@ function Coder:lua_entry_point_definition(f_id) /**/ ${ret_decls} ${call_pallene} - ${lua_fexit} ${push_results} return $nresults; } @@ -708,7 +690,6 @@ function Coder:lua_entry_point_definition(f_id) ret_decls = table.concat(ret_decls, "\n"), call_pallene = call_pallene, push_results = table.concat(push_results, "\n"), - lua_fexit = frameexit, nresults = C.integer(#ret_types) })) end @@ -728,11 +709,9 @@ define_union("Constant", { function Coder:init_upvalues() - -- If we are using tracebacks - if self.flags.use_traceback then - table.insert(self.constants, coder.Constant.DebugUserdata()) - table.insert(self.constants, coder.Constant.DebugMetatable()) - end + -- Debug traceback constants + table.insert(self.constants, coder.Constant.DebugUserdata()) + table.insert(self.constants, coder.Constant.DebugMetatable()) -- Metatables for _, typ in ipairs(self.module.record_types) do @@ -1488,11 +1467,8 @@ gen_cmd["CallStatic"] = function(self, cmd, func) end table.insert(parts, self:update_stack_top(func, cmd)) - - if self.flags.use_traceback then - table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", - func.loc and func.loc.line or 0)) - end + table.insert(parts, string.format("PALLENE_SETLINE(%d);\n", + func.loc and func.loc.line or 0)) table.insert(parts, self:call_pallene_function(dsts, f_id, cclosure, xs, nil)) table.insert(parts, self:restorestack()) @@ -1528,12 +1504,9 @@ gen_cmd["CallDyn"] = function(self, cmd, func) })) end - local setline = "" - if self.flags.use_traceback then - setline = util.render([[ PALLENE_SETLINE($line); ]], { - line = C.integer(func.loc and func.loc.line or 0) - }) - end + local setline = util.render([[ PALLENE_SETLINE($line); ]], { + line = C.integer(func.loc and func.loc.line or 0) + }) return util.render([[ ${update_stack_top} @@ -1805,6 +1778,12 @@ function Coder:generate_module_header() if self.flags.use_traceback then table.insert(out, "/* Enable Pallene Tracer debugging. */") table.insert(out, "#define PT_DEBUG") + + if not self.flags.pt_dynamic then + table.insert(out, "/* Inlining for maximum performance during debugging. */") + table.insert(out, "#define PT_INLINE") + table.insert(out, "#define PT_IMPLEMENTATION") + end end table.insert(out, "") diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 77c41562..9cac5287 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -34,7 +34,8 @@ do ) -- No Pallene tracebacks - p:flag("--use-traceback", "Use function traceback for debugging") + p:flag("--use-traceback", "Use function traceback for debugging using Pallene Tracer") + p:flag("--pt-dynamic", "Use dynamic linking to Pallene Tracer. Reduced code redundancy, compromised performance") p:option("-O", "Optimization level") :args(1):convert(tonumber) @@ -73,7 +74,8 @@ end function pallenec.main() local flags = { - use_traceback = opts.use_traceback and true or false + use_traceback = opts.use_traceback and true or false, + pt_dynamic = opts.pt_dynamic and true or false } if opts.emit_c then compile("pln", "c", flags) diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index 09199567..b9e40a18 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -46,33 +46,49 @@ return [==[ #include #include -/* Pallene Tracer for tracebacks (dynamically linked). */ +/* Pallene Tracer for function call tracebacks. */ /* Look at `https://github.com/pallene-lang/pallene-tracer` for more info. */ #include #define PALLENE_UNREACHABLE __builtin_unreachable() -/* PALLENE TRACER MACROS */ +/* PALLENE TRACER HELPER MACROS */ +#ifdef PT_DEBUG /* Prepares finalizer function for Lua interface calls. */ -#define PALLENE_PREPARE_FINALIZER() \ - setobj(L, s2v(L->top.p++), &K->uv[1].uv); \ - lua_toclose(L, -1) - -#define PALLENE_C_FRAMEENTER(L, name) \ - pt_fnstack_t *fnstack = pvalue(&K->uv[0].uv); \ - static pt_fn_details_t _details = \ - PALLENE_TRACER_FN_DETAILS(name, PALLENE_SOURCE_FILE); \ - pt_frame_t _frame = \ - PALLENE_TRACER_C_FRAME(_details); \ - PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); - -#define PALLENE_LUA_FRAMEENTER(L, fnptr) \ - pt_fnstack_t *fnstack = pvalue(&K->uv[0].uv); \ - pt_frame_t _frame = \ - PALLENE_TRACER_LUA_FRAME(fnptr); \ - PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); \ - PALLENE_PREPARE_FINALIZER() +#define PALLENE_PREPARE_FINALIZER() \ + setobj(L, s2v(L->top.p++), &K->uv[1].uv); \ + lua_toclose(L, -1) + +#define PALLENE_GET_FNSTACK() \ + pt_fnstack_t *fnstack = pvalue(&K->uv[0].uv) + +#define PALLENE_PREPARE_C_FRAME(name) \ + PALLENE_GET_FNSTACK(); \ + static pt_fn_details_t _details = \ + PALLENE_TRACER_FN_DETAILS(name, PALLENE_SOURCE_FILE); \ + pt_frame_t _frame = \ + PALLENE_TRACER_C_FRAME(_details) + +#define PALLENE_PREPARE_LUA_FRAME(fnptr) \ + PALLENE_GET_FNSTACK(); \ + pt_frame_t _frame = \ + PALLENE_TRACER_LUA_FRAME(fnptr) + +#else +#define PALLENE_PREPARE_FINALIZER() +#define PALLENE_PREPARE_C_FRAME(name) +#define PALLENE_PREPARE_LUA_FRAME(fnptr) +#endif // PT_DEBUG + +#define PALLENE_C_FRAMEENTER(L, name) \ + PALLENE_PREPARE_C_FRAME(name); \ + PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); + +#define PALLENE_LUA_FRAMEENTER(L, fnptr) \ + PALLENE_PREPARE_LUA_FRAME(fnptr); \ + PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); \ + PALLENE_PREPARE_FINALIZER() #define PALLENE_SETLINE(line) PALLENE_TRACER_SETLINE(fnstack, line) #define PALLENE_FRAMEEXIT() PALLENE_TRACER_FRAMEEXIT(fnstack) @@ -96,7 +112,6 @@ static l_noret pallene_runtime_mod_by_zero_error(lua_State *L, const char* file, static l_noret pallene_runtime_number_to_integer_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_array_metatable_error(lua_State *L, const char* file, int line); static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L); -static l_noret pallene_runtime_callstack_overflow_error(lua_State *L); /* Arithmetic operators */ static lua_Integer pallene_int_divi(lua_State *L, lua_Integer m, lua_Integer n, const char* file, int line); @@ -246,11 +261,6 @@ static l_noret pallene_runtime_cant_grow_stack_error(lua_State *L) PALLENE_UNREACHABLE; } -static l_noret pallene_runtime_callstack_overflow_error(lua_State *L) { - luaL_error(L, "pallene callstack overflow"); - PALLENE_UNREACHABLE; -} - /* Lua and Pallene round integer division towards negative infinity, while C rounds towards zero. * Here we inline luaV_div, to allow the C compiler to constant-propagate. For an explanation of the * algorithm, see the comments for luaV_div. */ From ff1cdb0ae6799a0d1d7c22366632d1d725252b0e Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Mon, 5 Aug 2024 14:53:57 +0600 Subject: [PATCH 08/10] Adaptation of inline function support of Pallene Tracer --- src/pallene/c_compiler.lua | 15 +++++---------- src/pallene/coder.lua | 6 ------ src/pallene/pallenec.lua | 4 +--- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/pallene/c_compiler.lua b/src/pallene/c_compiler.lua index e5272db8..6fad4c7d 100644 --- a/src/pallene/c_compiler.lua +++ b/src/pallene/c_compiler.lua @@ -55,21 +55,16 @@ function c_compiler.compile_c_to_o(in_filename, out_filename) }) end -function c_compiler.compile_o_to_so(in_filename, out_filename, _, _, flags) +function c_compiler.compile_o_to_so(in_filename, out_filename) -- There is no need to add the '-x' flag when compiling an object file without a '.o' extension. -- According to GCC, any file name with no recognized suffix is treated as an object file. - local command = { + return run_cc({ CFLAGS_SHARED, "-o", util.shell_quote(out_filename), util.shell_quote(in_filename), - } - - if flags.pt_dynamic or not flags.use_traceback then - table.insert(command, "-lptracer") - table.insert(command, "-Wl,-rpath="..PTLIBDIR) - end - - return run_cc(command) + "-lptracer", + "-Wl,-rpath="..PTLIBDIR, + }) end return c_compiler diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 406d77a2..15aa7762 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -1778,12 +1778,6 @@ function Coder:generate_module_header() if self.flags.use_traceback then table.insert(out, "/* Enable Pallene Tracer debugging. */") table.insert(out, "#define PT_DEBUG") - - if not self.flags.pt_dynamic then - table.insert(out, "/* Inlining for maximum performance during debugging. */") - table.insert(out, "#define PT_INLINE") - table.insert(out, "#define PT_IMPLEMENTATION") - end end table.insert(out, "") diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 9cac5287..6c501bae 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -35,7 +35,6 @@ do -- No Pallene tracebacks p:flag("--use-traceback", "Use function traceback for debugging using Pallene Tracer") - p:flag("--pt-dynamic", "Use dynamic linking to Pallene Tracer. Reduced code redundancy, compromised performance") p:option("-O", "Optimization level") :args(1):convert(tonumber) @@ -74,8 +73,7 @@ end function pallenec.main() local flags = { - use_traceback = opts.use_traceback and true or false, - pt_dynamic = opts.pt_dynamic and true or false + use_traceback = opts.use_traceback and true or false } if opts.emit_c then compile("pln", "c", flags) From a329f07025330c2fb5fe801980153928f39402bb Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Tue, 6 Aug 2024 20:28:56 +0600 Subject: [PATCH 09/10] Traceback API fix --- src/pallene/coder.lua | 4 ++-- src/pallene/pallenelib.lua | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index 15aa7762..f0bb5751 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -456,7 +456,7 @@ function Coder:pallene_entry_point_definition(f_id) local slots_needed = max_frame_size + self.max_lua_call_stack_usage[func] table.insert(prologue, util.render([[ - PALLENE_C_FRAMEENTER(L, "$name"); + PALLENE_C_FRAMEENTER("$name"); ]], { name = func.name })); @@ -610,7 +610,7 @@ function Coder:lua_entry_point_definition(f_id) cargs = cargs + 1 end local frameenter = util.render([[ - PALLENE_LUA_FRAMEENTER(L, $fun_name); + PALLENE_LUA_FRAMEENTER($fun_name); ]], { fun_name = self:lua_entry_point_name(f_id), }) diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index b9e40a18..6cb03050 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -81,13 +81,13 @@ return [==[ #define PALLENE_PREPARE_LUA_FRAME(fnptr) #endif // PT_DEBUG -#define PALLENE_C_FRAMEENTER(L, name) \ +#define PALLENE_C_FRAMEENTER(name) \ PALLENE_PREPARE_C_FRAME(name); \ - PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); + PALLENE_TRACER_FRAMEENTER(fnstack, &_frame); -#define PALLENE_LUA_FRAMEENTER(L, fnptr) \ +#define PALLENE_LUA_FRAMEENTER(fnptr) \ PALLENE_PREPARE_LUA_FRAME(fnptr); \ - PALLENE_TRACER_FRAMEENTER(L, fnstack, &_frame); \ + PALLENE_TRACER_FRAMEENTER(fnstack, &_frame); \ PALLENE_PREPARE_FINALIZER() #define PALLENE_SETLINE(line) PALLENE_TRACER_SETLINE(fnstack, line) From d1617da50e7de8da22aa2205a7f7715ff26ebb97 Mon Sep 17 00:00:00 2001 From: SD Asif Hossein Date: Thu, 8 Aug 2024 14:23:14 +0600 Subject: [PATCH 10/10] Address changes suggested by @srijan-paul --- src/pallene/pallenec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pallene/pallenec.lua b/src/pallene/pallenec.lua index 6c501bae..be6cbbc8 100644 --- a/src/pallene/pallenec.lua +++ b/src/pallene/pallenec.lua @@ -34,7 +34,7 @@ do ) -- No Pallene tracebacks - p:flag("--use-traceback", "Use function traceback for debugging using Pallene Tracer") + p:flag("--use-traceback", "Enable call-stack tracing") p:option("-O", "Optimization level") :args(1):convert(tonumber)