2010-12-23 00:01:24 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
|
|
|
* Copyright (c) 2009 Peter Wortmann
|
2013-01-09 23:23:06 +00:00
|
|
|
* Copyright (c) 2009, 2011-2012 Günther Brammer
|
2011-09-01 10:47:54 +00:00
|
|
|
* Copyright (c) 2010 Martin Plicht
|
2011-09-01 14:58:52 +00:00
|
|
|
* Copyright (c) 2010 Benjamin Herr
|
2010-12-23 00:01:24 +00:00
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
#include <C4Include.h>
|
2011-11-01 22:17:41 +00:00
|
|
|
#include "C4AulDebug.h"
|
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
#include <C4Version.h>
|
|
|
|
#include <C4GameControl.h>
|
|
|
|
#include <C4Game.h>
|
|
|
|
#include <C4MessageInput.h>
|
2010-01-15 00:47:20 +00:00
|
|
|
#include <C4Log.h>
|
2010-04-26 18:16:04 +00:00
|
|
|
#include <C4Object.h>
|
2010-01-13 18:26:03 +00:00
|
|
|
#include "C4AulExec.h"
|
|
|
|
|
2010-02-24 18:16:17 +00:00
|
|
|
#ifndef NOAULDEBUG
|
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
// *** C4AulDebug
|
|
|
|
|
|
|
|
C4AulDebug::C4AulDebug()
|
2010-03-28 18:58:01 +00:00
|
|
|
: fInit(false), fConnected(false)
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
ZeroMem(&PeerAddr, sizeof PeerAddr);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
C4AulDebug::~C4AulDebug()
|
2010-01-15 04:46:16 +00:00
|
|
|
{
|
|
|
|
for (std::list<StdStrBuf*>::iterator it = StackTrace.begin(); it != StackTrace.end(); it++)
|
|
|
|
{delete *it;}
|
2011-03-02 23:58:43 +00:00
|
|
|
if (pDebug == this) pDebug = NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-13 19:44:17 +00:00
|
|
|
bool C4AulDebug::InitDebug(const char *szPassword, const char *szHost)
|
2011-03-02 23:58:43 +00:00
|
|
|
{
|
|
|
|
// Create debug object
|
|
|
|
if (!pDebug) pDebug = new C4AulDebug();
|
|
|
|
// Initialize
|
|
|
|
pDebug->SetPassword(szPassword);
|
|
|
|
pDebug->SetAllowed(szHost);
|
|
|
|
pDebug->SetEngine(&AulExec);
|
2013-10-13 19:44:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool C4AulDebug::Listen(uint16_t iPort, bool fWait)
|
|
|
|
{
|
|
|
|
if (!Init(iPort))
|
2011-03-02 23:58:43 +00:00
|
|
|
{ LogFatal("C4Aul debugger failed to initialize!"); return false; }
|
|
|
|
// Log
|
|
|
|
LogF("C4Aul debugger initialized on port %d", iPort);
|
|
|
|
// Add to application
|
2013-10-13 19:44:17 +00:00
|
|
|
Application.Add(this);
|
2011-03-02 23:58:43 +00:00
|
|
|
// Wait for connection
|
|
|
|
if (fWait)
|
|
|
|
{
|
|
|
|
Log("C4Aul debugger waiting for connection...");
|
2013-10-13 19:44:17 +00:00
|
|
|
while (!isConnected())
|
2011-03-02 23:58:43 +00:00
|
|
|
if (!Application.ScheduleProcs())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Done
|
|
|
|
return true;
|
2010-01-15 04:46:16 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
void C4AulDebug::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Enlarge buffer
|
|
|
|
int iSize = rPacket.getSize(),
|
2010-03-28 18:58:01 +00:00
|
|
|
iPos = rOutBuf.getSize();
|
2010-01-13 18:26:03 +00:00
|
|
|
rOutBuf.Grow(iSize + 2);
|
|
|
|
// Write packet
|
|
|
|
rOutBuf.Write(rPacket, iPos);
|
|
|
|
// Terminate
|
|
|
|
uint8_t *pPos = getMBufPtr<uint8_t>(rOutBuf, iPos + iSize);
|
|
|
|
*pPos = '\r'; *(pPos + 1) = '\n';
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
size_t C4AulDebug::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 21:08:06 +00:00
|
|
|
// Find line separation
|
2010-03-28 18:58:01 +00:00
|
|
|
const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
|
|
|
|
if (!pSep)
|
2010-01-13 18:26:03 +00:00
|
|
|
return 0;
|
2010-04-01 21:08:06 +00:00
|
|
|
// Check if it's windows-style separation
|
2010-01-13 18:26:03 +00:00
|
|
|
int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
|
2010-03-28 18:58:01 +00:00
|
|
|
iLength = iSize - 1;
|
|
|
|
if (iLength && *(pSep - 1) == '\r')
|
2010-01-13 18:26:03 +00:00
|
|
|
iLength--;
|
|
|
|
// Copy the line
|
|
|
|
StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
|
|
|
|
// Password line?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fConnected)
|
2013-10-13 19:44:17 +00:00
|
|
|
{
|
|
|
|
ProcessLineResult result = ProcessLine(Buf);
|
|
|
|
// Send answer
|
|
|
|
SendLine(result.okay ? "OK" : "ERR", result.answer.length() > 0 ? result.answer.c_str() : NULL);
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (!Password.getSize() || Password == Buf)
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
fConnected = true;
|
2010-03-28 18:58:01 +00:00
|
|
|
SendLine("HLO", "This is " C4ENGINEINFOLONG ", " C4VERSION);
|
2010-01-13 18:26:03 +00:00
|
|
|
Log("C4Aul debugger connected successfully!");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
else
|
|
|
|
C4NetIOTCP::Close(PeerAddr);
|
|
|
|
// Consume line
|
|
|
|
return iSize;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
bool C4AulDebug::OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const addr_t *pOwnAddr, C4NetIO *pNetIO)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
assert(pNetIO == this);
|
|
|
|
// Already have a connection?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fConnected) return false;
|
2010-01-13 18:26:03 +00:00
|
|
|
// Check address
|
2010-03-28 18:58:01 +00:00
|
|
|
if (AllowedAddr.sin_addr.s_addr)
|
|
|
|
if (AllowedAddr.sin_addr.s_addr != AddrPeer.sin_addr.s_addr ||
|
|
|
|
(AllowedAddr.sin_port && AllowedAddr.sin_port != AddrPeer.sin_port))
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
LogF("C4AulDebug blocked connection from %s:%d", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
|
|
|
|
return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
// Log
|
|
|
|
LogF("C4AulDebug got connection from %s:%d", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
|
|
|
|
// Accept connection
|
|
|
|
PeerAddr = AddrPeer;
|
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
void C4AulDebug::OnDisconn(const C4NetIO::addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
LogF("C4AulDebug lost connection (%s)", szReason);
|
|
|
|
fConnected = false;
|
|
|
|
eState = DS_Go;
|
|
|
|
ZeroMem(&PeerAddr, sizeof PeerAddr);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
void C4AulDebug::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Won't get called
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
bool C4AulDebug::SetAllowed(const char *szHost)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Clear
|
|
|
|
ZeroMem(&AllowedAddr, sizeof(AllowedAddr));
|
|
|
|
// No host?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!szHost || !*szHost) return true;
|
2010-01-13 18:26:03 +00:00
|
|
|
// Resolve the address
|
|
|
|
return ResolveAddress(szHost, &AllowedAddr, 0);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
bool C4AulDebug::Init(uint16_t iPort)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (fInit) Close();
|
|
|
|
if (iPort == P_NONE) return false;
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Register self as callback for network events
|
|
|
|
C4NetIOTCP::SetCallback(this);
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
// Start listening
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!C4NetIOTCP::Init(iPort))
|
2010-01-13 18:26:03 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Okay
|
|
|
|
fInit = true;
|
|
|
|
eState = DS_Go;
|
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
bool C4AulDebug::Close()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!fInit) return true;
|
2010-01-13 18:26:03 +00:00
|
|
|
fInit = fConnected = false;
|
|
|
|
return C4NetIOTCP::Close();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
2010-03-21 18:18:27 +00:00
|
|
|
bool C4AulDebug::Close(const addr_t &addr)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!fInit) return true;
|
2010-03-21 18:18:27 +00:00
|
|
|
bool success = C4NetIOTCP::Close(addr);
|
|
|
|
if (success)
|
|
|
|
fInit = fConnected = false;
|
|
|
|
return success;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-21 18:18:27 +00:00
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
void C4AulDebug::OnLog(const char *szLine)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
if (!fConnected) return;
|
2010-01-13 18:26:03 +00:00
|
|
|
SendLine("LOG", szLine);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
2013-10-13 19:44:17 +00:00
|
|
|
C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Get command
|
|
|
|
StdStrBuf Cmd;
|
|
|
|
Cmd.CopyUntil(Line.getData(), ' ');
|
|
|
|
// Get data
|
|
|
|
const char *szData = Line.getPtr(Cmd.getLength());
|
2010-03-28 18:58:01 +00:00
|
|
|
if (*szData) szData++;
|
2010-01-13 18:26:03 +00:00
|
|
|
// Identify command
|
|
|
|
const char *szCmd = Cmd.getData();
|
2010-03-28 18:58:01 +00:00
|
|
|
if (SEqualNoCase(szCmd, "HELP"))
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(false, "Yeah, like I'm going to explain that /here/");
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "BYE") || SEqualNoCase(szCmd, "QUIT"))
|
2010-01-13 18:26:03 +00:00
|
|
|
C4NetIOTCP::Close(PeerAddr);
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "SAY"))
|
2010-01-13 18:26:03 +00:00
|
|
|
::Control.DoInput(CID_Message, new C4ControlMessage(C4CMT_Normal, szData), CDT_Direct);
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "CMD"))
|
2010-01-13 18:26:03 +00:00
|
|
|
::MessageInput.ProcessCommand(szData);
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "STP") || SEqualNoCase(szCmd, "S"))
|
2010-01-13 18:26:03 +00:00
|
|
|
eState = DS_Step;
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "GO") || SEqualNoCase(szCmd, "G"))
|
2010-01-13 18:26:03 +00:00
|
|
|
eState = DS_Go;
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "STO") || SEqualNoCase(szCmd, "O"))
|
2010-01-13 18:26:03 +00:00
|
|
|
eState = DS_StepOver;
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "STR") || SEqualNoCase(szCmd, "R"))
|
2010-01-13 18:26:03 +00:00
|
|
|
eState = DS_StepOut;
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "EXC") || SEqualNoCase(szCmd, "E"))
|
2010-04-26 18:16:04 +00:00
|
|
|
{
|
|
|
|
C4AulScriptContext* context = pExec->GetContext(pExec->GetContextDepth()-1);
|
2012-06-03 22:03:49 +00:00
|
|
|
int32_t objectNum = C4ControlScript::SCOPE_Global;
|
|
|
|
if (context && context->Obj && context->Obj->GetObject())
|
|
|
|
objectNum = context->Obj->GetObject()->Number;
|
2013-09-04 18:18:06 +00:00
|
|
|
::Control.DoInput(CID_Script, new C4ControlScript(szData, objectNum, true), CDT_Decide);
|
2010-04-26 18:16:04 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "PSE"))
|
|
|
|
if (Game.IsPaused())
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
Game.Unpause();
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(true, "Game unpaused.");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
else
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
Game.Pause();
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(true, "Game paused.");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-15 00:47:20 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "LST"))
|
|
|
|
{
|
2012-06-07 19:07:31 +00:00
|
|
|
for (C4AulScript* script = ScriptEngine.Child0; script; script = script->Next)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-16 01:32:53 +00:00
|
|
|
SendLine(RelativePath(script->ScriptName));
|
2010-01-15 00:47:20 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2010-01-15 00:47:20 +00:00
|
|
|
// toggle breakpoint
|
|
|
|
else if (SEqualNoCase(szCmd, "TBR"))
|
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
using namespace std;
|
2010-12-22 01:10:58 +00:00
|
|
|
// FIXME: this doesn't find functions which were included/appended
|
2013-10-13 19:44:17 +00:00
|
|
|
string scriptPath = szData;
|
|
|
|
size_t colonPos = scriptPath.find(':');
|
|
|
|
if (colonPos == string::npos)
|
|
|
|
return ProcessLineResult(false, "Missing line in breakpoint request");
|
|
|
|
int line = atoi(&scriptPath[colonPos+1]);
|
|
|
|
scriptPath.erase(colonPos);
|
|
|
|
|
|
|
|
C4AulScript *script;
|
2010-01-15 00:47:20 +00:00
|
|
|
for (script = ScriptEngine.Child0; script; script = script->Next)
|
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
if (SEqualNoCase(RelativePath(script->ScriptName), scriptPath.c_str()))
|
2010-01-15 00:47:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2013-10-13 19:44:17 +00:00
|
|
|
auto sh = script ? script->GetScriptHost() : NULL;
|
|
|
|
if (sh)
|
2010-01-15 00:47:20 +00:00
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
C4AulBCC * found = NULL;
|
|
|
|
for (auto script = ::ScriptEngine.Child0; script; script = script->Next)
|
|
|
|
for (C4PropList *props = script->GetPropList(); props; props = props->GetPrototype())
|
|
|
|
for (auto fname = props->EnumerateOwnFuncs(); fname; fname = props->EnumerateOwnFuncs(fname))
|
2010-01-15 00:47:20 +00:00
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
C4Value val;
|
|
|
|
if (!props->GetPropertyByS(fname, &val)) continue;
|
|
|
|
auto func = val.getFunction();
|
|
|
|
if (!func) continue;
|
|
|
|
auto sfunc = func->SFunc();
|
|
|
|
if (!sfunc) continue;
|
|
|
|
if (sfunc->pOrgScript != sh) continue;
|
|
|
|
for (auto chunk = sfunc->GetCode(); chunk->bccType != AB_EOFN; chunk++)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
if (chunk->bccType == AB_DEBUG)
|
2010-10-25 21:48:08 +00:00
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
int lineOfThisOne = sfunc->GetLineOfCode(chunk);
|
|
|
|
if (lineOfThisOne == line)
|
2012-10-21 16:14:32 +00:00
|
|
|
{
|
2013-10-13 19:44:17 +00:00
|
|
|
found = chunk;
|
|
|
|
goto Found;
|
2012-10-21 16:14:32 +00:00
|
|
|
}
|
2010-10-25 21:48:08 +00:00
|
|
|
}
|
2010-01-15 00:47:20 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-13 19:44:17 +00:00
|
|
|
Found:
|
|
|
|
if (found)
|
|
|
|
found->Par.i = !found->Par.i; // activate breakpoint
|
2010-01-15 00:47:20 +00:00
|
|
|
else
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(false, "Can't set breakpoint (wrong line?)");
|
2010-01-15 00:47:20 +00:00
|
|
|
}
|
|
|
|
else
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(false, "Can't find script");
|
2010-01-15 04:46:16 +00:00
|
|
|
}
|
|
|
|
else if (SEqualNoCase(szCmd, "SST"))
|
|
|
|
{
|
|
|
|
std::list<StdStrBuf*>::iterator it = StackTrace.begin();
|
|
|
|
for (it++; it != StackTrace.end(); it++)
|
|
|
|
{
|
|
|
|
SendLine("AT", (*it)->getData());
|
|
|
|
}
|
|
|
|
SendLine("EST");
|
2010-01-15 00:47:20 +00:00
|
|
|
}
|
2010-01-21 15:22:33 +00:00
|
|
|
else if (SEqualNoCase(szCmd, "VAR"))
|
|
|
|
{
|
2010-03-22 09:54:41 +00:00
|
|
|
|
2010-01-21 15:22:33 +00:00
|
|
|
C4Value *val = NULL;
|
|
|
|
int varIndex;
|
|
|
|
C4AulScriptContext* pCtx = pExec->GetContext(pExec->GetContextDepth() - 1);
|
|
|
|
if (pCtx)
|
|
|
|
{
|
|
|
|
if ((varIndex = pCtx->Func->ParNamed.GetItemNr(szData)) != -1)
|
|
|
|
{
|
|
|
|
val = &pCtx->Pars[varIndex];
|
|
|
|
}
|
2010-03-22 09:54:41 +00:00
|
|
|
else if ((varIndex = pCtx->Func->VarNamed.GetItemNr(szData)) != -1)
|
2010-01-21 15:22:33 +00:00
|
|
|
{
|
|
|
|
val = &pCtx->Vars[varIndex];
|
|
|
|
}
|
|
|
|
}
|
2010-03-22 09:54:41 +00:00
|
|
|
const char* typeName = val ? GetC4VName(val->GetType()) : "any";
|
|
|
|
StdStrBuf output = FormatString("%s %s %s", szData, typeName, val ? val->GetDataString().getData() : "Unknown");
|
2010-01-21 15:22:33 +00:00
|
|
|
SendLine("VAR", output.getData());
|
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
else
|
2013-10-13 19:44:17 +00:00
|
|
|
return ProcessLineResult(false, "Can't do that");
|
|
|
|
|
|
|
|
return ProcessLineResult(true, "");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
bool C4AulDebug::SendLine(const char *szType, const char *szData)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
StdStrBuf Line = szData ? FormatString("%s %s", szType, szData) : StdStrBuf(szType);
|
|
|
|
return Send(C4NetIOPacket(Line.getData(), Line.getSize(), false, PeerAddr));
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
2013-10-13 19:44:17 +00:00
|
|
|
void C4AulDebug::DebugStep(C4AulBCC *pCPos, C4Value* stackTop)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Already stopped? Ignore.
|
|
|
|
// This means we are doing some calculation with suspended script engine.
|
|
|
|
// We do /not/ want to have recursive suspensions...
|
2010-03-28 18:58:01 +00:00
|
|
|
if (eState == DS_Stop)
|
2010-01-13 18:26:03 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Have break point?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pCPos->Par.i)
|
2010-01-13 18:26:03 +00:00
|
|
|
eState = DS_Step;
|
|
|
|
|
2010-01-15 14:31:51 +00:00
|
|
|
int iCallDepth = pExec->GetContextDepth();
|
2010-01-13 18:26:03 +00:00
|
|
|
// Stop?
|
2010-03-28 18:58:01 +00:00
|
|
|
switch (eState)
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
// Continue normally
|
2010-03-28 18:58:01 +00:00
|
|
|
case DS_Go: return;
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Always stop
|
2010-03-28 18:58:01 +00:00
|
|
|
case DS_Stop: break;
|
|
|
|
case DS_Step: break;
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Only stop for same level or above
|
2010-03-28 18:58:01 +00:00
|
|
|
case DS_StepOver:
|
|
|
|
if (iCallDepth > iStepCallDepth)
|
|
|
|
return;
|
|
|
|
break;
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Only stop above
|
2010-03-28 18:58:01 +00:00
|
|
|
case DS_StepOut:
|
|
|
|
if (iCallDepth >= iStepCallDepth)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-13 19:44:17 +00:00
|
|
|
|
|
|
|
// Get current script context
|
|
|
|
C4AulScriptContext *pCtx = pExec->GetContext(iCallDepth-1);
|
|
|
|
//bool isReturn = pCPos[1].bccType = AB_RETURN;
|
2010-01-13 18:26:03 +00:00
|
|
|
|
2013-10-14 04:58:04 +00:00
|
|
|
if (!fConnected)
|
|
|
|
{
|
|
|
|
// not connected anymore? nevermind
|
|
|
|
eState = DS_Go;
|
|
|
|
return;
|
|
|
|
}
|
2013-10-16 11:43:02 +00:00
|
|
|
|
|
|
|
// Maybe got a command in the meantime?
|
|
|
|
Execute(0);
|
2013-10-14 04:58:04 +00:00
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
// Let's stop here
|
|
|
|
eState = DS_Stop;
|
|
|
|
iStepCallDepth = iCallDepth;
|
|
|
|
Game.HaltCount++;
|
|
|
|
|
|
|
|
// No valid stop position? Just continue
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!pCtx)
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
Game.HaltCount--;
|
|
|
|
eState = DS_Step;
|
|
|
|
return;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Signal
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pCPos && pCPos->bccType == AB_DEBUG && pCPos->Par.i)
|
2010-01-13 18:26:03 +00:00
|
|
|
SendLine("STP", FormatString("Stopped on breakpoint %d", pCPos->Par.i).getData());
|
|
|
|
else
|
|
|
|
SendLine("STP", "Stepped");
|
|
|
|
|
|
|
|
// Position
|
2010-01-15 04:46:16 +00:00
|
|
|
ObtainStackTrace(pCtx, pCPos);
|
|
|
|
SendLine("POS", StackTrace.front()->getData());
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Suspend until we get some command
|
2013-10-14 04:58:04 +00:00
|
|
|
while (fConnected && eState == DS_Stop)
|
2010-03-28 18:58:01 +00:00
|
|
|
if (!Application.ScheduleProcs())
|
|
|
|
{
|
2010-01-13 18:26:03 +00:00
|
|
|
Close();
|
|
|
|
return;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
|
|
|
// Do whatever we've been told.
|
|
|
|
Game.HaltCount--;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-01-13 18:26:03 +00:00
|
|
|
|
2010-04-26 18:13:36 +00:00
|
|
|
void C4AulDebug::ControlScriptEvaluated(const char* script, const char* result)
|
|
|
|
{
|
|
|
|
SendLine("EVR", FormatString("%s=%s", script, result).getData());
|
|
|
|
}
|
|
|
|
|
2010-01-16 01:32:53 +00:00
|
|
|
const char* C4AulDebug::RelativePath(StdStrBuf &path)
|
|
|
|
{
|
|
|
|
const char* p = path.getData();
|
|
|
|
const char* result = Config.AtRelativePath(p);
|
|
|
|
if (p != result)
|
|
|
|
return result;
|
2010-01-16 04:24:10 +00:00
|
|
|
// try path relative to scenario container
|
|
|
|
StdStrBuf scenarioContainerPath;
|
|
|
|
GetParentPath(::Game.ScenarioFile.GetName(), &scenarioContainerPath);
|
|
|
|
return GetRelativePathS(p, scenarioContainerPath.getData());
|
2010-01-16 01:32:53 +00:00
|
|
|
}
|
|
|
|
|
2010-01-15 04:46:16 +00:00
|
|
|
void C4AulDebug::ObtainStackTrace(C4AulScriptContext* pCtx, C4AulBCC* pCPos)
|
|
|
|
{
|
|
|
|
for (std::list<StdStrBuf*>::iterator it = StackTrace.begin(); it != StackTrace.end(); it++)
|
|
|
|
{delete *it;}
|
|
|
|
StackTrace.clear();
|
|
|
|
for (int ctxNum = pExec->GetContextDepth()-1; ctxNum >= 0; ctxNum--)
|
|
|
|
{
|
|
|
|
C4AulScriptContext* c = pExec->GetContext(ctxNum);
|
|
|
|
C4AulBCC* _cpos = c == pCtx ? pCPos : c->CPos;
|
|
|
|
if (_cpos)
|
|
|
|
{
|
|
|
|
StdStrBuf* format = new StdStrBuf(FormatCodePos(c, _cpos));
|
|
|
|
StackTrace.push_back(format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-13 18:26:03 +00:00
|
|
|
StdStrBuf C4AulDebug::FormatCodePos(C4AulScriptContext *pCtx, C4AulBCC *pCPos)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-10-27 21:53:42 +00:00
|
|
|
if (pCtx->Func->pOrgScript)
|
|
|
|
return FormatString("%s:%d",
|
|
|
|
RelativePath(pCtx->Func->pOrgScript->ScriptName),
|
|
|
|
pCtx->Func->GetLineOfCode(pCPos));
|
|
|
|
else
|
|
|
|
return StdStrBuf("(eval)");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-02-23 18:29:26 +00:00
|
|
|
|
2011-03-02 23:58:43 +00:00
|
|
|
C4AulDebug * C4AulDebug::pDebug = NULL;
|
|
|
|
|
2010-03-16 11:39:58 +00:00
|
|
|
#endif
|