diff --git a/spec/traceback/anon_lua/anon_lua.pln b/spec/traceback/anon_lua/anon_lua.pln new file mode 100644 index 00000000..f47f2742 --- /dev/null +++ b/spec/traceback/anon_lua/anon_lua.pln @@ -0,0 +1,12 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.call_anon_lua_fn(callback: () -> ()) + callback() +end + +return mod diff --git a/spec/traceback/anon_lua/main.lua b/spec/traceback/anon_lua/main.lua new file mode 100644 index 00000000..2dbc9f40 --- /dev/null +++ b/spec/traceback/anon_lua/main.lua @@ -0,0 +1,15 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local anon = require "spec.traceback.anon_lua.anon_lua" + +local function wrapper() + anon.call_anon_lua_fn(function() + error "Error from an anonymous Lua fn!" + end) +end + +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/depth_recursion/main.lua b/spec/traceback/depth_recursion/main.lua index 1688ee67..a081fb5a 100644 --- a/spec/traceback/depth_recursion/main.lua +++ b/spec/traceback/depth_recursion/main.lua @@ -3,7 +3,7 @@ -- Please refer to the LICENSE and AUTHORS files for details -- SPDX-License-Identifier: MIT -local pallene = require 'spec.traceback.depth_recursion.depth_recursion' +local pallene = require "spec.traceback.depth_recursion.depth_recursion" -- luacheck: globals lua_fn function lua_fn(depth) diff --git a/spec/traceback/module_lua/main.lua b/spec/traceback/module_lua/main.lua index ef764119..c1c0ea47 100644 --- a/spec/traceback/module_lua/main.lua +++ b/spec/traceback/module_lua/main.lua @@ -3,8 +3,8 @@ -- Please refer to the LICENSE and AUTHORS files for details -- SPDX-License-Identifier: MIT -local another_module = require 'spec.traceback.module_lua.another_module' -local pallene = require 'spec.traceback.module_lua.module_lua' +local another_module = require "spec.traceback.module_lua.another_module" +local pallene = require "spec.traceback.module_lua.module_lua" -- luacheck: globals lua_1 function lua_1() diff --git a/spec/traceback/module_pallene/main.lua b/spec/traceback/module_pallene/main.lua index 62f7902c..fc111b2c 100644 --- a/spec/traceback/module_pallene/main.lua +++ b/spec/traceback/module_pallene/main.lua @@ -3,12 +3,12 @@ -- Please refer to the LICENSE and AUTHORS files for details -- SPDX-License-Identifier: MIT -local pallene = require 'spec.traceback.module_pallene.module_pallene' -local pallene_alt = require 'spec.traceback.module_pallene.module_pallene_alt' +local pallene = require "spec.traceback.module_pallene.module_pallene" +local pallene_alt = require "spec.traceback.module_pallene.module_pallene_alt" -- luacheck: globals lua_2 function lua_2() - error "There's an error in everyday life. Shame!" + error "There's an error in everyday life. Alas!" end -- luacheck: globals lua_1 diff --git a/spec/traceback/rect/main.lua b/spec/traceback/rect/main.lua index 3a4da419..dde05702 100644 --- a/spec/traceback/rect/main.lua +++ b/spec/traceback/rect/main.lua @@ -3,7 +3,7 @@ -- Please refer to the LICENSE and AUTHORS files for details -- SPDX-License-Identifier: MIT -local rect = require 'spec.traceback.rect.rect' +local rect = require "spec.traceback.rect.rect" -- Should be local. -- Making it global so that it is visible in the traceback. diff --git a/spec/traceback/stack_overflow/main.lua b/spec/traceback/stack_overflow/main.lua new file mode 100644 index 00000000..bde220fd --- /dev/null +++ b/spec/traceback/stack_overflow/main.lua @@ -0,0 +1,19 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local so = require "spec.traceback.stack_overflow.stack_overflow" + +-- luacheck: globals please_dont_overflow +function please_dont_overflow() + so.no_overflow(please_dont_overflow) +end + +-- luacheck: globals wrapper +function wrapper() + please_dont_overflow() +end + +-- luacheck: globals pallene_tracer_debug_traceback +xpcall(wrapper, pallene_tracer_debug_traceback) diff --git a/spec/traceback/stack_overflow/stack_overflow.pln b/spec/traceback/stack_overflow/stack_overflow.pln new file mode 100644 index 00000000..d02f8015 --- /dev/null +++ b/spec/traceback/stack_overflow/stack_overflow.pln @@ -0,0 +1,12 @@ +-- Copyright (c) 2024, The Pallene Developers +-- Pallene is licensed under the MIT license. +-- Please refer to the LICENSE and AUTHORS files for details +-- SPDX-License-Identifier: MIT + +local mod: module = {} + +function mod.no_overflow(callback: () -> ()) + callback() +end + +return mod diff --git a/spec/traceback_spec.lua b/spec/traceback_spec.lua index af7666fe..726cffeb 100644 --- a/spec/traceback_spec.lua +++ b/spec/traceback_spec.lua @@ -57,7 +57,7 @@ end) it("Multi-module Pallene", function() assert_test("module_pallene", [[ -Runtime error: spec/traceback/module_pallene/main.lua:11: There's an error in everyday life. Shame! +Runtime error: spec/traceback/module_pallene/main.lua:11: There's an error in everyday life. Alas! Stack traceback: C: in function 'error' spec/traceback/module_pallene/main.lua:11: in function 'lua_2' @@ -94,3 +94,46 @@ Stack traceback: ]]) end) +it("Stack overflow", function() + assert_test("stack_overflow", [[ +Runtime error: C stack overflow +Stack traceback: + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + + ... (Skipped 379 frames) ... + + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/stack_overflow.pln:8: in function 'no_overflow' + spec/traceback/stack_overflow/main.lua:10: in function 'please_dont_overflow' + spec/traceback/stack_overflow/main.lua:15: in function 'wrapper' + C: in function 'xpcall' + spec/traceback/stack_overflow/main.lua:19: in
+ C: in function '' +]]) +end) + +it("Anonymous lua functions", function() + assert_test("anon_lua", [[ +Runtime error: spec/traceback/anon_lua/main.lua:10: Error from an anonymous Lua fn! +Stack traceback: + C: in function 'error' + spec/traceback/anon_lua/main.lua:10: in function '' + spec/traceback/anon_lua/anon_lua.pln:8: in function 'call_anon_lua_fn' + spec/traceback/anon_lua/main.lua:9: in function '' + C: in function 'xpcall' + spec/traceback/anon_lua/main.lua:15: in
+ C: in function '' +]]) +end) diff --git a/src/pallene/coder.lua b/src/pallene/coder.lua index d1d1cc20..62666727 100644 --- a/src/pallene/coder.lua +++ b/src/pallene/coder.lua @@ -461,7 +461,9 @@ function Coder:pallene_entry_point_definition(f_id) local void_frameexit = "" if self.flags.use_traceback then table.insert(prologue, util.render([[ - PALLENE_C_FRAMEENTER(L, "$name"); + /**/ + pt_cont_t *cont = pvalue(&K->uv[0].uv); + PALLENE_C_FRAMEENTER(cont, "$name"); /**/ ]], { name = func.name @@ -470,7 +472,7 @@ function Coder:pallene_entry_point_definition(f_id) setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) if #func.typ.ret_types == 0 then - void_frameexit = "PALLENE_FRAMEEXIT(L);" + void_frameexit = "PALLENE_FRAMEEXIT(cont);" end end @@ -594,27 +596,26 @@ function Coder:lua_entry_point_definition(f_id) ]] local frameenter = "" - local setline = "" local frameexit = "" if self.flags.use_traceback then - frameenter = util.render([[ PALLENE_LUA_FRAMEENTER(L, $fun_name); ]], { + frameenter = util.render([[ + /**/ + pt_cont_t *cont = pvalue(&K->uv[0].uv); + PALLENE_LUA_FRAMEENTER(cont, $fun_name); + /**/ + ]], { fun_name = self:lua_entry_point_name(f_id), }) - - setline = string.format("PALLENE_SETLINE(%d);", func.loc and func.loc.line or 0) - - frameexit = "PALLENE_FRAMEEXIT(L);" + frameexit = "PALLENE_FRAMEEXIT(cont);" end local arity_check = util.render([[ int nargs = lua_gettop(L); if (l_unlikely(nargs != $nargs)) { - ${setline} pallene_runtime_arity_error(L, $fname, $nargs, nargs); } ]], { nargs = C.integer(#arg_types), - setline = setline, fname = C.string(fname), }) @@ -657,9 +658,9 @@ function Coder:lua_entry_point_definition(f_id) return (util.render([[ ${fun_decl} { - ${lua_fenter} StackValue *base = L->ci->func.p; ${init_global_userdata} + ${lua_fenter} /**/ ${arity_check} /**/ @@ -675,8 +676,8 @@ function Coder:lua_entry_point_definition(f_id) } ]], { fun_decl = self:lua_entry_point_declaration(f_id), - lua_fenter = frameenter, init_global_userdata = init_global_userdata, + lua_fenter = frameenter, arity_check = arity_check, arg_decls = table.concat(arg_decls, "\n"), init_args = table.concat(init_args, "\n/**/\n"), @@ -697,10 +698,16 @@ end define_union("Constant", { Metatable = {"typ"}, String = {"str"}, + DebugUserdata = {} }) function Coder:init_upvalues() + -- If we are using tracebacks + if self.flags.use_traceback then + table.insert(self.constants, coder.Constant.DebugUserdata()) + end + -- Metatables for _, typ in ipairs(self.module.record_types) do if not typ.is_upvalue_box then @@ -1649,7 +1656,7 @@ end gen_cmd["Return"] = function(self, cmd) local frameexit = "" if self.flags.use_traceback then - frameexit = "PALLENE_FRAMEEXIT(L);" + frameexit = "PALLENE_FRAMEEXIT(cont);" end if #cmd.srcs == 0 then @@ -1866,6 +1873,12 @@ function Coder:generate_luaopen_function() lua_pushstring(L, $str);]], { str = C.string(upv.str) })) + -- Will be used if compiling with `--use-traceback` + elseif tag == "coder.Constant.DebugUserdata" then + table.insert(init_constants, [[ + /* Initialize Pallene Tracer. */ + lua_pushlightuserdata(L, (void *) pallene_tracer_init(L)); + ]]); else tagged_union.error(tag) end @@ -1888,17 +1901,6 @@ function Coder:generate_luaopen_function() init_function = self:lua_entry_point_name(1), }) - local init_pt = "" - - if self.flags.use_traceback then - init_pt = [[ - /* Initialize Pallene Tracer. */ - pallene_tracer_init(L); - /**/ - ]] - end - - -- NOTE: Version compatibility -- --------------------------- -- We have both a compile-time and a run-time test. The compile-time test ensures that the @@ -1914,7 +1916,6 @@ function Coder:generate_luaopen_function() #error "Lua version must be exactly 5.4.6" #endif - ${init_pt} luaL_checkcoreversion(L); /**/ @@ -1939,7 +1940,6 @@ function Coder:generate_luaopen_function() } ]], { name = "luaopen_" .. self.modname, - init_pt = init_pt, n_upvalues = C.integer(#self.constants), init_constants = table.concat(init_constants, "\n"), init_initializers = init_initializers, diff --git a/src/pallene/pallenelib.lua b/src/pallene/pallenelib.lua index 17c891c5..6a0b82d1 100644 --- a/src/pallene/pallenelib.lua +++ b/src/pallene/pallenelib.lua @@ -48,30 +48,40 @@ return [==[ #define PALLENE_UNREACHABLE __builtin_unreachable() /* Part of Pallene Tracer. */ -#define PALLENE_C_FRAMEENTER(L, name) \ +#define PALLENE_C_FRAMEENTER(cont, name) \ static pt_fn_details_t _details = { \ .fn_name = name, \ .mod_name = PALLENE_SOURCE_FILE \ }; \ - pt_frame_t _frame = { \ + pt_frame_t _frame = { \ .type = PALLENE_TRACER_FRAME_TYPE_C, \ .shared = { \ .details = &_details \ } \ }; \ - pallene_tracer_frameenter(L, &_frame) + pallene_tracer_frameenter(cont, &_frame) -#define PALLENE_LUA_FRAMEENTER(L, sig) \ +#define PALLENE_LUA_FRAMEENTER(cont, sig) \ pt_frame_t _frame = { \ .type = PALLENE_TRACER_FRAME_TYPE_LUA, \ .shared = { \ .frame_sig = sig \ } \ }; \ - pallene_tracer_frameenter(L, &_frame) + pallene_tracer_frameenter(cont, &_frame) #define PALLENE_SETLINE(line) pallene_tracer_setline(&_frame, line) -#define PALLENE_FRAMEEXIT(...) pallene_tracer_frameexit(L); +#define PALLENE_FRAMEEXIT(cont) pallene_tracer_frameexit(cont) + +/* Pallene stack reference entry for the registry. */ +#define PALLENE_TRACER_STACK_ENTRY "__PALLENE_TRACER_STACK" + +/* 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. */ @@ -101,12 +111,19 @@ typedef struct pt_frame { struct pt_frame *prev; } 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; +} pt_cont_t; + /* Pallene Tracer. */ -static void pallene_tracer_frameenter(lua_State *L, pt_frame_t *restrict frame); -static void pallene_tracer_setline(pt_frame_t *restrict frame, int line); -static void pallene_tracer_frameexit(lua_State *L); -static int pallene_tracer_debug_traceback(lua_State *L); -static void pallene_tracer_init(lua_State *L); +static void pallene_tracer_frameenter(pt_cont_t *cont, pt_frame_t *restrict frame); +static void pallene_tracer_setline(pt_frame_t *restrict frame, 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); /* Type tags */ static const char *pallene_type_name(lua_State *L, const TValue *v); @@ -211,7 +228,7 @@ static bool _findfield(lua_State *L, int fn_idx, int level) { /* Pushes a function name if found in the global table and returns true. Returns false otherwise. */ -/* Expects the funtion to be pushed in the stack. */ +/* Expects the function to be pushed in the stack. */ static bool _pgf_name(lua_State *L) { int top = lua_gettop(L); @@ -227,182 +244,208 @@ static bool _pgf_name(lua_State *L) { return false; } -/* Private routines end. */ +/* Returns the maximum number of levels in Lua stack. */ +static int _countlevels (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; -static void pallene_tracer_frameenter(lua_State *L, pt_frame_t *restrict frame) { - /* Retrieve the end of the stack. */ - lua_getglobal(L, "__pallene_tracer_stack"); - pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 1); + /* Find an upper bound */ + while (lua_getstack(L, le, &ar)) { + li = le, le *= 2; + } - /* If there is no frame in the stack. */ - /* No matter what type of frame we got (Lua or plain C), it will be - in the general stack. */ - if(l_unlikely(stack == NULL)) { - frame->prev = NULL; - stack = frame; + /* Do a binary search */ + while (li < le) { + int m = (li + le)/2; - goto out; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; } - frame->prev = stack; - stack = frame; - -out: - lua_pushlightuserdata(L, stack); - lua_setglobal(L, "__pallene_tracer_stack"); + return le - 1; } -static void pallene_tracer_setline(pt_frame_t *restrict frame, int line) { - frame->line = line; +/* Counts the number of white and black frames in the Pallene call stack. */ +static void _countframes(pt_frame_t *frame, int *mwhite, int *mblack) { + *mwhite = *mblack = 0; + + while(frame != NULL) { + *mwhite += (frame->type == PALLENE_TRACER_FRAME_TYPE_C); + *mblack += (frame->type == PALLENE_TRACER_FRAME_TYPE_LUA); + frame = frame->prev; + } } -static void pallene_tracer_frameexit(lua_State *L) { - /* Retrieve the end of the stack. */ - lua_getglobal(L, "__pallene_tracer_stack"); - pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 1); +/* 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)++; - /* We are popping the very last frame. */ - if(stack->prev == NULL) { - stack = NULL; - goto out; - } + /* 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); - stack = stack->prev; + 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)); -out: - lua_pushlightuserdata(L, stack); - lua_setglobal(L, "__pallene_tracer_stack"); + *elipsis = false; + } } -static int pallene_tracer_debug_traceback(lua_State *L) { - const char *message = lua_tostring(L, 1); - fprintf(stderr, "Runtime error: %s\nStack traceback:\n", message); +/* Private routines end. */ - /* Lua: 1, Pallene: 0 */ - int context = 1; - int level = 1; - bool l_stack = true; - lua_CFunction f_sig = NULL; +static void pallene_tracer_frameenter(pt_cont_t *cont, pt_frame_t *restrict frame) { + /* If there is no frame in the Pallene stack. */ + if(l_unlikely(cont->stack == NULL)) { + frame->prev = NULL; + cont->stack = frame; - lua_getglobal(L, "__pallene_tracer_stack"); - pt_frame_t *stack = (pt_frame_t *) lua_topointer(L, -1); - lua_pop(L, 1); + return; + } - /* To store lua call stack information, to use it in both contexts. */ - lua_Debug ar; + frame->prev = cont->stack; + cont->stack = frame; +} - /* We will restore to this top everytime. */ - int top = lua_gettop(L); +static void pallene_tracer_setline(pt_frame_t *restrict frame, int line) { + frame->line = line; +} - while(l_stack || stack != NULL) { - /* Generally, we would spend most of our time dealing with Pallene->Pallene calls. */ - if(l_unlikely(context == 1)) { - if(!(l_stack = lua_getstack(L, level++, &ar))) - continue; +static void pallene_tracer_frameexit(pt_cont_t *cont) { + /* We are popping the very last frame. */ + if(cont->stack->prev == NULL) { + cont->stack = NULL; + return; + } - /* We need more info for a good traceback entry. */ - /* Also push the function on the stack. */ - lua_getinfo(L, "Slntf", &ar); + cont->stack = cont->stack->prev; +} - /* We have got a C frame. Time to make a context switch. */ - if(lua_iscfunction(L, -1)) { - /* Set the signature and switch to Pallene stack. */ - f_sig = lua_tocfunction(L, -1); - context = 0; - } else { - /* It's a regular Lua function. */ - - /* 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 ''"); - - fprintf(stderr, " %s:%d: in %s\n", ar.short_src, - ar.currentline, lua_tostring(L, -1)); - } +/* 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_STACK_ENTRY); + pt_frame_t *stack = ((pt_cont_t *) lua_touserdata(L, -1))->stack; + lua_pop(L, 1); - lua_settop(L, top); - } else { - /* We can still use the debug structure storing the last Lua call info. */ - lua_getinfo(L, "f", &ar); + /* Max number of white and black frames. */ + int mwhite, mblack; + _countframes(stack, &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]; - /* Deduce name from global table. */ - if(_pgf_name(L)) - lua_pushfstring(L, "C: in function '%s'", lua_tostring(L, -1)); - else lua_pushliteral(L, "C: in function ''"); + const char *message = lua_tostring(L, 1); + fprintf(stderr, "Runtime error: %s\nStack traceback:\n", message); - if(stack == NULL) { - if(f_sig != NULL) - fprintf(stderr, " %s\n", lua_tostring(L, -1)); + lua_Debug ar; + int top = lua_gettop(L); + int level = 1; - context = 1; - goto end; - } + while(lua_getstack(L, level++, &ar)) { + /* Get additional information regarding the frame. */ + lua_getinfo(L, "Slntf", &ar); - if(f_sig != NULL) { - /* Check if the frame signature matches. */ + /* If the frame is a C frame. */ + if(lua_iscfunction(L, -1)) { + if(stack != NULL) { + /* Check whether this frame is tracked (Pallene C frames). */ pt_frame_t *check = stack; - while(check->type != PALLENE_TRACER_FRAME_TYPE_LUA) check = check->prev; - /* It's an untracked C function. */ - if(f_sig != check->shared.frame_sig) { - fprintf(stderr, " %s\n", lua_tostring(L, -1)); - - /* Now we switch to Lua stack. */ - context = 1; - goto end; + /* If the frame signature matches, we switch to printing Pallene frames. */ + if(lua_tocfunction(L, -1) == check->shared.frame_sig) { + /* Now print all the frames in Pallene stack. */ + while(stack != check) { + sprintf(buf, " %s:%d: in function '%s'\n", + stack->shared.details->mod_name, + stack->line, stack->shared.details->fn_name); + DBG_PRINT(); + + stack = stack->prev; + } + + /* 'check' is guaranteed to be a Lua interface frame. + Which is basically our 'stack' at this point. So, + we simply ignore the Lua interface frame. */ + stack = stack->prev; + + /* We are done. */ + lua_settop(L, top); + continue; } - - /* Bingo! We have found a signature. Erase the signature so that - in the next iteration we don't care about rechecking the - frame signature agian. */ - f_sig = NULL; - } - - /* If we find a Lua interface, we simply ignore and switch. */ - if(stack->type == PALLENE_TRACER_FRAME_TYPE_LUA) { - stack = stack->prev; - - context = 1; - goto end; } - fprintf(stderr, " %s:%d: in function '%s'\n", stack->shared.details->mod_name, - stack->line, stack->shared.details->fn_name); - - stack = stack->prev; + /* Then it's an untracked C frame. */ + if(_pgf_name(L)) + lua_pushfstring(L, "%s", lua_tostring(L, -1)); + else lua_pushliteral(L, ""); - end: - lua_settop(L, top); + 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 -static void pallene_tracer_init(lua_State *L) { - lua_getglobal(L, "__pallene_tracer_stack"); +static pt_cont_t *pallene_tracer_init(lua_State *L) { + pt_cont_t *cont = NULL; - /* Setup the state and pallene traceback fn. */ - if(l_likely(lua_isnil(L, -1) == true)) { - /* The first value is the tail of the LinkedList stack. */ - lua_pushlightuserdata(L, NULL); + /* Try getting the userdata. */ + lua_getfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_STACK_ENTRY); - lua_setglobal(L, "__pallene_tracer_stack"); + /* 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 = NULL; + + lua_setfield(L, LUA_REGISTRYINDEX, PALLENE_TRACER_STACK_ENTRY); /* The debug traceback fn. */ lua_register(L, "pallene_tracer_debug_traceback", pallene_tracer_debug_traceback); + } else { + cont = lua_touserdata(L, -1); } + + return cont; } static const char *pallene_type_name(lua_State *L, const TValue *v)