I want to be able to determine which line of a script was being executed when a crash in the program occurs.
Here is my C++ function to crash the program: void GenerateCrash()
{
Entity* entity = NULL
entity->SetColor(1,1,1,1);
}
Here is my Lua script, using LuaBind to expose the command: print("Generating crash...")
GenerateCrash()
I know lua_pcall has an error function you can pass to it, but I can't find any example of how this works. I guess you indicate the position in the stack that you pushed the error function to, but what is the syntax of this function?
I found one thread with some information:
http://www.gamedev.net/topic/446781-lua-more-detailed-error-information/page__view__findpost__p__3960907
I am getting the stack traceback and running a script I have loaded. I am not calling a specific function, I am just calling the main entry point of the script: lua_getglobal(L, "debug");
lua_getfield(L,-1,"traceback");
lua_remove(L,-2);
lua_getglobal(L, ""); //is this a problem? How do I go back to the "main" entry point?
if (lua_pcall(L,0,LUA_MULTRET,-2)!=0)
{
Print(lua_tostring(L,-1));
}
The addition of the traceback function (indicated by the -2 argument in lua_pcall) causes the addition of this line text to the printed error message:
"stack traceback:"
No further information is available. The error occurs inside a Lua function, so it should have something to print: print("Running script...")
function dostuff()
a = 5
b = 6
model = nil
model:SetPosition(1,2,3,0)
c = a +b
end
Here's my Lua functions. If I throw an exception in the try block, it gets caught. If the Lua script calls a C++ function that throws an exception, no exception is caught by the catch statement. void Interpreter::HandleError()
{
Print("Error: "+std::string(lua_tostring(L,-1)));
}
void Interpreter::HandleException()
{
std::string err = "Lua debugger not found.";
lua_getglobal(L,"debug");
if (lua_istable(L,-1)!=0)
{
lua_getfield(L,-1,"traceback");
if (lua_isfunction(L,-1)!=0)
{
if (lua_pcall(L,0,1,0)==0)
{
if (lua_isstring(L,-1)!=0)
{
err = lua_tostring(L,-1);
}
}
}
}
Print("Error: "+err);
}
bool Interpreter::ExecuteString(std::string& source)
{
bool result = false;
int errorhandler = 0;
int size = GetStackSize();
if (luaL_loadbuffer(L,source.c_str(),source.length(),"")==0)
{
result = Invoke(0,0);
}
else
{
HandleError();
}
SetStackSize(size);
return result;
}
bool Interpreter::ExecuteFile(const std::string& path)
{
bool result = false;
int errorhandler = 0;
int size = GetStackSize();
Bank* bank = LoadBank(path);
if (bank==NULL) return false;
if (luaL_loadbuffer(L,bank->buf,bank->GetSize(),path.c_str())==0)
{
result = Invoke(0,0);
}
else
{
HandleError();
}
delete bank;
SetStackSize(size);
return result;
}
I created a new C++ function: void RuntimeError(const std::string& error)
{
throw std::exception(error.c_str());
}
Here is the Lua script that calls it: print("Creating an error...")
RuntimeError("An error has occurred!")
Here is the code that runs the script: bool Interpreter::Invoke(const int& in, const int& out)
{
try
{
if (lua_pcall(L,in,out,0)==0)
{
return true;
}
else
{
//If you throw an exception, it gets displayed here, which isn't what we want:
HandleError();
}
}
//Exceptions should be getting caught here:
catch(luabind::error& e)
{
Print("Caught the exception!");
exit(1);
}
return false;
}
Instead of catching an exception, the Lua error log just prints out "std-exception: 'An error has occurred!'"
I am using MSVC, and I know about the issue described here:
http://www.rasterbar.com/products/luabind/docs.html#structured-exceptions-msvc
The code they said to add has no effect on behavior.
Where I'd replaced the global lua error function like so:
-- Change the standard error function so that it shows the debug traceback.
local error = error
_G.error = function(m, l)
error("\nerror msg: "..m.."\n"..debug.traceback(), (l or 1) + 1)
end
It's been too long for me to really remember if there were any problems with this, but I think it worked more or less correctly, at least in returning a useful error message.
Ah, I had to read the Luabind docs more carefully. LuaBind exceptions are just errors that occur in the LuaBind code, which should hopefully never happen. I was thinking exceptions you throw would get turned into LuaBind exceptions, but they actually just get turned into a regular Lua error you can print out.
The problem now is by the time pcall returns, the Lua stack is not in the same state as it was when the error occurred, so you can't just catch the exception and print out the Lua call stack.
I don't know if it's possible to call debug.trace once an exception has been thrown, before lua_pcall returns, but it is possible to make my own RuntimeError() function which first performs a debug.trace if it is being called from a Lua state.
I'll share my interpreter code. This prints out all the variables in a Lua state and the call stack. You can call the Interpreter::GetCallStack() function inside a Lua program and print out the result. I just made a DebugStop() function and exposed it to Lua to do this: void DebugStop()
{
if (CurrentInterpreter!=NULL) Print(CurrentInterpreter->GetCallStack());
}
if (lua_isnumber(L,index))
{
return (String(lua_tonumber(L,index)));
}
else
{
if (lua_isstring(L,index))
{
return (lua_tostring(L,index));
}
else
{
if (lua_istable(L,index))
{
return "{}";
//This can cause an infinite loop if you have tables that reference each other
/*value="{";
int size = GetStackSize();
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
std::string var = GetVariableValue(-2);
Print(var);
value += ""+var;
value += "="+GetVariableValue(-1);
value += ", ";
}
SetStackSize(size);
value += "}";
return value;*/
}
else
{
if lua_isnil(L,index)
{
return ("nil");
}
}
}
}
return "";
}
int Interpreter::GetCallStackSize()
{
int level = 0;
lua_Debug ar;
while (true)
{
if (lua_getstack(L, level, &ar) == 0) return level;
level += 1;
}
}
std::string Interpreter::GetCallStack()
{
std::string err;
lua_Debug ar;
int level = 0;
std::string tab;
int size = GetCallStackSize();
int i;
const char* name;
//Display call stack. I'm going to make the main function '0' and reverse the level numbers
//because that makes more sense to me, but remember 0 = the current level.
for (level=0; level<size; level++)
{
lua_getstack(L, size -1- level, &ar);
Print(tab+"[Level "+String(level)+"]");
Print(tab+"{");
lua_getinfo(L, "nSluf", &ar);
Ah, I had to read the Luabind docs more carefully. LuaBind exceptions are just errors that occur in the LuaBind code, which should hopefully never happen. I was thinking exceptions you throw would get turned into LuaBind exceptions, but they actually just get turned into a regular Lua error you can print out.
The problem now is by the time pcall returns, the Lua stack is not in the same state as it was when the error occurred, so you can't just catch the exception and print out the Lua call stack.
I don't know if it's possible to call debug.trace once an exception has been thrown, before lua_pcall returns, but it is possible to make my own RuntimeError() function which first performs a debug.trace if it is being called from a Lua state.
Yes I agree. You have a very insightful post my friend.