Jump to content

Interpreter Debugging System


Josh
 Share

Recommended Posts

A few people have asked for information on the Lua debugging protocol, so I am posting some of the internals here.

It would be a good idea to start researching Lua IDEs and see if there is something we can utilize for Leadwerks 5.

This is the code that handles the network connection with the editor:

	void Interpreter::DebugUpdate(const std::string& err, const bool wait)
	{
		if (!connected) return;
		Message* message;
		const char* name;
		lua_Debug ar;
		std::string s;
		std::string ts;
		vector<std::string> sarr;
		vector<std::string>::iterator it;
		vector<std::string> pair;
		int size;
		int i;
		long starttime = Time::Millisecs();
		bool connectionstarted=false;
		int handle;
		bool intable=false;
		Object* o;
		int l;
		int countbreakpoints;
		int editmode;
		std::string source;
		int linenumber;
		std::string hex;
		int pointer;
		stringstream ss;
		Stream* stream;
		bool paused = timepausestate;
		Time::Pause();
		Window* window = NULL;

#ifdef _WIN32
		AllowSetForegroundWindow(-1);
#endif
		while (true)
		{
			message = client->Update();
			if (!wait)
			{
				if (message==NULL)
				{
					if (!paused) Time::Resume();
					return;
				}
			}
			if (message != NULL)
			{
				switch (message->id)
				{
				case MESSAGE_EDITBREAKPOINTS:
					stream = BankStream::Create(message->data,false);
					countbreakpoints = stream->ReadInt();
					for (int i=0; i<countbreakpoints; i++)
					{
						editmode=stream->ReadInt();
						source = stream->ReadString();
						linenumber = stream->ReadInt();
						if (editmode==1)
						{
							SetBreakpoint(source,linenumber);
						}
						else
						{
							RemoveBreakpoint(source,linenumber);
						}
					}
					break;
				//case MESSAGE_CONNECT:
				//	//System::Notify("MESSAGE_CONNECT");
				//	connected=true;
				//	if (err!="") client->Send(MESSAGE_SENDERRORMESSAGE,err,MESSAGE_RELIABLE|MESSAGE_SEQUENCED);
				//	break;
				/*case MESSAGE_DEBUGPOINTERINFOREQUEST:
					hex = message->data->PeekString(0);
					//o = (Object*)Int(hex);
					//client->Send(MESSAGE_DEBUGPOINTERINFO,o->Debug());
					if (debugobjectmap[hex]!=NULL)
					{
						client->Send(MESSAGE_DEBUGPOINTERINFO,hex+debugobjectmap[hex]->Debug());
					}
					else
					{
						client->Send(MESSAGE_DEBUGPOINTERINFO,hex);
					}
					break;*/
				case MESSAGE_PAUSE:
					client->Send(MESSAGE_SENDCALLSTACK,GetCallStack(),MESSAGE_RELIABLE|MESSAGE_SEQUENCED);
					DebugUpdate();
					break;
				case MESSAGE_DISCONNECT:
					//Window* window = Window::GetCurrent();
					//if (window) window->Activate();
					//lua_sethook(L,DebugHook,0,0);
					if (!paused) Time::Resume();
					return;
					break;
				case MESSAGE_DEBUGSTEP:
					window = Window::GetCurrent();
					//if (window) window->Activate();
					//lua_sethook(L,DebugHook,LUA_MASKLINE,0);
					steppingmode=STEP_LINE;
					if (!paused) Time::Resume();
					return;
					break;
				case MESSAGE_DEBUGSTEPIN:
					window = Window::GetCurrent();
					//if (window) window->Activate();
					//lua_sethook(L,DebugHook,LUA_MASKCALL,0);
					steppingmode=STEP_IN;
					if (!paused) Time::Resume();
					return;
					break;
				case MESSAGE_DEBUGSTEPOUT:
					window = Window::GetCurrent();
					//if (window) window->Activate();
					//lua_sethook(L,DebugHook,LUA_MASKRET,0);
					steppingmode=STEP_OUT;
					if (!paused) Time::Resume();
					return;
					break;
				case MESSAGE_DEBUGRESUME:
					//lua_sethook(L,DebugHook,0,0);
					//System::Notify("DebugResume!");
					window = Window::GetCurrent();
					if (window) window->Activate();
					steppingmode=0;
					if (!paused) Time::Resume();
					return;
					break;
				}
			}
			Time::Delay(1);
		}
	}

The entire debug tree is sent back to the editor as a single string.  Here is the function that creates it:

	std::string Interpreter::GetCallStack()
	{
		std::string err;
		std::string vname;
		lua_Debug ar;
		int level = 0;
		std::string tab;
		int size = GetCallStackSize();
		int i;
		int stacksize = GetStackSize();
		const char* name;
		debugobjectmap.clear();
		int executionlevel = executionstack.size()-1;

		//System::Notify("Interpreter::GetCallStack()");

		//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);
			err += (tab+"Level"+String(size -1- level)+" {\n");
			lua_getinfo(L, "nSluf", &ar);
			err += (tab+"	source: "+std::string(ar.source)+"\n");
			err += (tab+"	short_src: "+std::string(ar.short_src)+"\n");
			err += (tab+"	linedefined: "+String(ar.linedefined)+"\n");
			err += (tab+"	lastlinedefined: "+String(ar.lastlinedefined)+"\n");
			err += (tab+"	what: "+std::string(ar.what)+"\n");
			err += (tab+"	currentline: "+String(ar.currentline)+"\n");
			//if (ar.name==NULL)
			//{
			//	if (level==0) ar.name="(called from C++)";
			//}
			if (ar.name!=NULL) err += (tab+"	name: "+std::string(ar.name)+"\n");
			if (ar.namewhat!=NULL) err += (tab+"	namewhat: "+std::string(ar.namewhat)+"\n");
			err += (tab+"	nups: "+String(ar.nups)+"\n");

			if (level==size-1)
			{
				if (std::string(ar.what)!="C")
				{
					skipnextdebugstep = true;
				}
			}

			//Display locals
			err += (tab+"	locals {"+"\n");
			i = 1;
			while (true)
			{
				name = lua_getlocal(L, &ar, i++);
				if (name==NULL) break;
				vname=std::string(name);
				if (String::Left(vname,2)!="(*" && String::Left(vname,1)!="_")
				{
					err+=(tab+"		"+vname+"="+GetVariableValue()+"\n");
				}
				lua_pop(L,1);
			}
			err += (tab+"	}\n");
			tab += "	";
		}

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

		//Display globals
		err += ("	Globals {\n");
		lua_getglobal(L,"_G");
		for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
		{
			if (!lua_isfunction(L,-1))
			{
//				if (!lua_isuserdata(L,-1))
//				{
					vname=GetVariableValue(-2,0,false);
					if (skipdebugdefaultglobals)
					{
						/*if (vname=="_G") continue;
						if (vname=="_VERSION") continue;
						if (vname=="coroutine") continue;
						if (vname=="debug") continue;
						if (vname=="io") continue;
						if (vname=="math") continue;
						if (vname=="os") continue;
						if (vname=="package") continue;
						if (vname=="string")continue;
						if (vname=="table") continue;*/
						if (hiddenglobals[vname]==true) continue;
					}
					//Print(vname+"="+GetVariableValue(-1));
					err += ("	"+vname+"="+GetVariableValue(-1)+"\n");
//				}
			}
		}
		err += "	}";
		err += "}";
		lua_pop(L,1);

		SetStackSize(stacksize);

		return err;
	}

Each object has a Debug() function that contributes to this string.  Here is what the entity Debug function looks like:
 

	std::string Entity::Debug()
	{
		int i;
		std::string s;

		s = "{";
		s+="0xhexaddress="+Object::GetAddress(this)+",";
		//s+= "Entity={";
		s+= "position="+position.Debug()+",";
		s+= "rotation="+rotation.Debug()+",";
		s+= "scale="+scale.Debug()+",";
		s+= "mat="+mat.Debug()+",";
		s+= "localaabb="+localaabb.Debug()+",";
		s+= "aabb="+aabb.Debug()+",";
		s+= "recursiveaabb="+recursiveaabb.Debug()+",";
		s+= "color={";
		s+= "0="+color[0].Debug()+",";
		s+= "1="+color[1].Debug()+",";
		s+= "2="+color[2].Debug();
		s+= "},";
		//s+= "color[0]="+color[0].Debug()+",";
		//s+= "color[1]="+color[1].Debug()+",";
		//s+= "color[2]="+color[2].Debug()+",";
		s+= "parent="+Object::GetAddress(parent)+",";
		s+= "kids={";
			for (i=0; i<CountChildren(); i++)
			{
				//s+= "["+String(i)+"]child"+String(i);
				s+= "["+String(i)+"]="+Object::GetAddress(GetChild(i));
				if (i<CountChildren()-1)
				{
					s += ",";
				}
			}
			/*s+= "[0]=child0,";
			s+= "[1]=child1,";
			s+= "[2]=child2";*/
		//s+= "}";
		s+= "}";
		s+= "}";
		return s;
		//return GetObjectAddress(this);
	}

And here is the Vec3 Debug function:

std::string Vec3::Debug()
{
	return "_{"+String(x)+","+String(y)+","+String(z)+"}";
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...