Jump to content
  • Advertisement
Sign in to follow this  
Josh Klint

Lua error handling

This topic is 2704 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
Advertisement
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

dostuff()

Share this post


Link to post
Share on other sites
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;
}

bool Interpreter::Invoke(const int& in, const int& out)
{
try
{
if (lua_pcall(L,in,out,0)==0)
{
return true;
}
else
{
HandleError();
}
}
catch(...)
{
HandleException();
}
}
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
It's been quite some time since I last fiddled with lua / luabind , but I last had this:


std::string Script::getError() {
const char* estr = lua_tostring(state, -1);
lua_pop(state, 1);
return std::string(estr);
}

bool Script::doFile(const filesystem::path& file) {
if (!filesystem::exists(file)) {
logger << Logger::error << "File: " << file.file_string() << " does not exist for doFile call." << std::endl;
return false;
}

try {
luabind::call_function<void>(state, "dofile", file.file_string());
} catch (...) {
logger << Logger::warn << "Failed to parse script file: " << file.file_string() << std::endl
<< getError() << std::endl;
return false;
}
logger << Logger::info << "Parsed script file: " << file.file_string() << std::endl;
return true;
}


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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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());
}


#include "le3.h"

namespace le3
{
Interpreter* CurrentInterpreter = NULL;

void ErrorCallback(lua_State* L)
{
Print("Error callback!");
}

/* int panic(lua_State* L)
{
Print("Panic called!");
return 0;
}*/

Interpreter::Interpreter() : L(NULL)
{
L=lua_open();
//Don't understand how to use this:
//lua_atpanic(L, panic);
//lua_register( L, "_ALERT", panic );

luaL_openlibs(L);
luabind::open(L);

//luabind::set_error_callback(ErrorCallback)
//luabind::register_exception_handler<my_exception>(&translate_my_exception);

using namespace luabind;
module(L)
[
def("RuntimeError", &RuntimeError),
def("DebugStop", &DebugStop)
];
}

Interpreter::~Interpreter()
{
if (L!=NULL)
{
lua_close(L);
L = NULL;
}
}

int Interpreter::GetStackSize()
{
return lua_gettop(L);
}

void Interpreter::SetStackSize(const int& size)
{
int currentsize = GetStackSize();
if (size<currentsize) lua_pop(L,currentsize-size);
}

void Interpreter::HandleError()
{
Print("Error: "+std::string(lua_tostring(L,-1)));
}

std::string Interpreter::GetVariableValue(const int& index)
{
std::string value;

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);

Print(tab+" source: "+std::string(ar.source));
Print(tab+" short_src: "+std::string(ar.short_src));
Print(tab+" linedefined: "+String(ar.linedefined));
Print(tab+" lastlinedefined: "+String(ar.lastlinedefined));
Print(tab+" what: "+std::string(ar.what));
Print(tab+" currentline: "+String(ar.currentline));
if (ar.name!=NULL) Print(tab+" name: "+std::string(ar.name));
if (ar.namewhat!=NULL) Print(tab+" namewhat: "+std::string(ar.namewhat));
Print(tab+" nups: "+String(ar.nups));

//Display locals
Print(tab+" locals:");
i = 1;
while (true)
{
name = lua_getlocal(L, &ar, i++);
if (name==NULL) break;
if (name!="(*temporary)")
{
Print(tab+" "+std::string(name)+" = "+GetVariableValue());
}
lua_pop(L,1);
}

Print("");
tab += " ";
}

//Add closing brackets
for (level=0; level<size; level++)
{
tab = "";
for (i=0; i<size-level-1; i++) tab+=" ";
Print(tab+"}");
}
Print("");

//Display globals
Print("Globals:");
lua_getglobal(L,"_G");
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
{
if (!lua_isfunction(L,-1))
{
Print(GetVariableValue(-2)+" = "+GetVariableValue(-1));
}
}
lua_pop(L,1);
return 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();

if (luaL_loadfile(L,path.c_str())==0)
{
result = Invoke(0,0);
}
else
{
HandleError();
}
SetStackSize(size);
return result;
}

bool Interpreter::Invoke(const int& in, const int& out)
{
try
{
CurrentInterpreter = this;
if (lua_pcall(L,in,out,0)==0)
{
return true;
}
else
{
HandleError();
}
}
catch(luabind::error& e)
{
Print("LuaBind exception occurred. This should never happen.");
exit(1);
}
return false;
}


}

Share this post


Link to post
Share on other sites
Posted · Hidden
Hidden

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.

Share this post


Link to post
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!