Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1519 lines
39 KiB
C++
1519 lines
39 KiB
C++
//***************************************************************************
|
|
//
|
|
// ABLDBUG.CPP
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include "MW4Headers.hpp"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#ifndef ABLGEN_H
|
|
#include "ablgen.h"
|
|
#endif
|
|
|
|
#ifndef ABLERR_H
|
|
#include "ablerr.h"
|
|
#endif
|
|
|
|
#ifndef ABLSCAN_H
|
|
#include "ablscan.h"
|
|
#endif
|
|
|
|
#ifndef ABLSYMT_H
|
|
#include "ablsymt.h"
|
|
#endif
|
|
|
|
#ifndef ABLEXEC_H
|
|
#include "ablexec.h"
|
|
#endif
|
|
|
|
#ifndef ABLDBUG_H
|
|
#include "abldbug.h"
|
|
#endif
|
|
|
|
#ifndef ABLHEAP_H
|
|
#include "ablheap.h"
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
namespace ABL
|
|
{
|
|
|
|
//***************************************************************************
|
|
|
|
HANDLE g_DebugSemaphore = NULL;
|
|
|
|
//----------
|
|
// EXTERNALS
|
|
|
|
extern long level;
|
|
extern long execLineNumber;
|
|
//extern long execStatementCount;
|
|
extern TokenCodeType codeToken;
|
|
|
|
extern char* codeBuffer;
|
|
extern char* codeBufferPtr;
|
|
extern char* codeSegmentPtr;
|
|
extern char* statementStartPtr;
|
|
|
|
extern StackItemPtr tos;
|
|
//extern StackItemPtr stackFrameBasePtr;
|
|
extern SymTableNodePtr CurRoutineIdPtr;
|
|
extern SymTableNodePtr symTableDisplay[];
|
|
|
|
extern long errorCount;
|
|
extern char curChar;
|
|
extern TokenCodeType curToken;
|
|
extern long lineNumber;
|
|
extern Literal curLiteral;
|
|
extern long bufferOffset;
|
|
extern char sourceBuffer[MAXLEN_SOURCELINE];
|
|
//extern long bufferOffset;
|
|
extern char* bufferp;
|
|
//extern char* tokenp;
|
|
extern char wordString[MAXLEN_TOKENSTRING];
|
|
|
|
extern TypePtr IntegerTypePtr;
|
|
extern TypePtr CharTypePtr;
|
|
extern TypePtr RealTypePtr;
|
|
extern TypePtr BooleanTypePtr;
|
|
extern Type DummyType;
|
|
|
|
extern UserHeapPtr AblStackHeap;
|
|
|
|
extern ModuleEntryPtr ModuleRegistry;
|
|
extern ABLModulePtr* ModuleInstanceRegistry;
|
|
extern long MaxModules;
|
|
extern long NumModulesRegistered;
|
|
extern long NumModuleInstances;
|
|
extern long MaxWatchesPerModule;
|
|
extern long MaxBreakPointsPerModule;
|
|
extern char* TokenStrings[NUM_TOKENS];
|
|
|
|
|
|
//extern StackItem* stack;
|
|
//extern StackItemPtr stackFrameBasePtr;
|
|
extern StackItemPtr StaticDataPtr;
|
|
//extern SymTableNodePtr CurRoutineIdPtr;
|
|
|
|
//extern long MaxLoopIterations;
|
|
|
|
void CheckMouse(void);
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long MaxBreaks = 50;
|
|
long MaxWatches = 50;
|
|
DebuggerPtr debugger = NULL;
|
|
|
|
char Debugger::message[512];
|
|
|
|
extern bool takeScreenShot;
|
|
|
|
#ifdef USE_IFACE
|
|
|
|
extern aTextObject* ABLDebuggerIn;
|
|
extern ScrollingTextWindow* ABLDebuggerOut;
|
|
|
|
#endif
|
|
|
|
//***************************************************************************
|
|
// WATCH MANAGER class
|
|
//***************************************************************************
|
|
|
|
void* WatchManager::operator new (size_t mySize)
|
|
{
|
|
|
|
void *result = AblStackHeap->Malloc(mySize);
|
|
return(result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void WatchManager::operator delete (void* us)
|
|
{
|
|
|
|
AblStackHeap->Free(us);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long WatchManager::init (long max)
|
|
{
|
|
|
|
maxWatches = max;
|
|
numWatches = 0;
|
|
|
|
watches = (WatchPtr)AblStackHeap->Malloc(max * sizeof(Watch));
|
|
if (!watches)
|
|
return(-1);
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void WatchManager::destroy (void)
|
|
{
|
|
|
|
if (watches)
|
|
{
|
|
AblStackHeap->Free(watches);
|
|
watches = NULL;
|
|
}
|
|
|
|
maxWatches = 0;
|
|
numWatches = 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
WatchPtr WatchManager::add (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
//------------------------------------------
|
|
// This routine assumes idPtr is non-NULL...
|
|
switch (idPtr->defn.key)
|
|
{
|
|
case DFN_CONST:
|
|
case DFN_VAR:
|
|
case DFN_VALPARAM:
|
|
case DFN_REFPARAM:
|
|
//case DFN_FIELD:
|
|
if (idPtr->info)
|
|
return((WatchPtr)idPtr->info);
|
|
else if (numWatches < maxWatches)
|
|
{
|
|
idPtr->info = (char*)&watches[numWatches];
|
|
watches[numWatches].idPtr = idPtr;
|
|
watches[numWatches].store = false;
|
|
watches[numWatches].breakOnStore = false;
|
|
watches[numWatches].fetch = false;
|
|
watches[numWatches].breakOnFetch = false;
|
|
return(&watches[numWatches++]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long WatchManager::remove (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
if (!idPtr)
|
|
return(1);
|
|
|
|
if (!idPtr->info)
|
|
return(2);
|
|
|
|
//------------------------------------------
|
|
// Find this id's watch in the watch list...
|
|
long removeIndex;
|
|
for (removeIndex = 0; removeIndex < numWatches; removeIndex++)
|
|
if (&watches[removeIndex] == (WatchPtr)idPtr->info)
|
|
break;
|
|
|
|
//--------------------------------------------------------
|
|
// The id no longer points to a watch in the watch list...
|
|
idPtr->info = NULL;
|
|
numWatches--;
|
|
|
|
//-------------------------------------
|
|
// Fill in the gap in the watch list...
|
|
for (long i = removeIndex; removeIndex < numWatches; removeIndex++)
|
|
{
|
|
watches[i].idPtr = watches[i + 1].idPtr;
|
|
watches[i].store = watches[i + 1].store;
|
|
watches[i].breakOnStore = watches[i + 1].breakOnStore;
|
|
watches[i].fetch = watches[i + 1].fetch;
|
|
watches[i].breakOnFetch = watches[i + 1].breakOnFetch;
|
|
watches[i].idPtr->info = (char*)&watches[i];
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long WatchManager::removeAll (void)
|
|
{
|
|
|
|
for (long i = 0; i < numWatches; i++)
|
|
{
|
|
watches[i].idPtr->info = NULL;
|
|
watches[i].idPtr = NULL;
|
|
watches[i].store = false;
|
|
watches[i].breakOnStore = false;
|
|
watches[i].fetch = false;
|
|
watches[i].breakOnFetch = false;
|
|
}
|
|
|
|
long numRemoved = numWatches;
|
|
numWatches = 0;
|
|
return(numRemoved);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long WatchManager::setStore (SymTableNodePtr idPtr, bool state, bool breakToDebug)
|
|
{
|
|
|
|
if (!idPtr)
|
|
return(1);
|
|
|
|
WatchPtr watch = (WatchPtr)idPtr->info;
|
|
if (state)
|
|
{
|
|
if (!watch)
|
|
watch = add(idPtr);
|
|
if (!watch)
|
|
return(2);
|
|
watch->store = true;
|
|
watch->breakOnStore = breakToDebug;
|
|
}
|
|
else
|
|
{
|
|
if (watch)
|
|
{
|
|
if (watch->fetch)
|
|
{
|
|
watch->store = false;
|
|
watch->breakOnStore = false;
|
|
}
|
|
else
|
|
remove(idPtr);
|
|
}
|
|
}
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long WatchManager::setFetch (SymTableNodePtr idPtr, bool state, bool breakToDebug)
|
|
{
|
|
|
|
if (!idPtr)
|
|
return(1);
|
|
|
|
WatchPtr watch = (WatchPtr)idPtr->info;
|
|
if (state)
|
|
{
|
|
if (!watch)
|
|
watch = add(idPtr);
|
|
if (!watch)
|
|
return(2);
|
|
watch->fetch = true;
|
|
watch->breakOnFetch = breakToDebug;
|
|
}
|
|
else
|
|
{
|
|
if (watch)
|
|
{
|
|
if (watch->store)
|
|
{
|
|
watch->fetch = false;
|
|
watch->breakOnFetch = false;
|
|
}
|
|
else
|
|
remove(idPtr);
|
|
}
|
|
}
|
|
return(NO_ERR);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool WatchManager::getStore (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
if (!idPtr->info)
|
|
return(false);
|
|
return(((WatchPtr)idPtr->info)->store);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool WatchManager::getFetch (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
if (!idPtr->info)
|
|
return(false);
|
|
return(((WatchPtr)idPtr->info)->fetch);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void WatchManager::print (void)
|
|
{
|
|
|
|
for (long i = 0; i < numWatches; i++)
|
|
{
|
|
//SymTableNodePtr idPtr = watches[i].idPtr;
|
|
// Display info here...
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
// BREAK POINT MANAGER class
|
|
//***************************************************************************
|
|
|
|
void* BreakPointManager::operator new (size_t mySize)
|
|
{
|
|
|
|
void *result = AblStackHeap->Malloc(mySize);
|
|
return(result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void BreakPointManager::operator delete (void* us)
|
|
{
|
|
|
|
AblStackHeap->Free(us);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long BreakPointManager::init (long max)
|
|
{
|
|
|
|
maxBreakPoints = max;
|
|
numBreakPoints = 0;
|
|
|
|
breakPoints = (long*)AblStackHeap->Malloc(max * sizeof(long));
|
|
if (!breakPoints)
|
|
return(-1);
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void BreakPointManager::destroy (void)
|
|
{
|
|
|
|
if (breakPoints)
|
|
{
|
|
AblStackHeap->Free(breakPoints);
|
|
breakPoints = NULL;
|
|
}
|
|
|
|
maxBreakPoints = 0;
|
|
numBreakPoints = 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long BreakPointManager::add (long lineNumber)
|
|
{
|
|
|
|
//----------------------------------------------------------------
|
|
// This does not check to make sure the line number is within
|
|
// the source code's range--if it isn't, the break is simply never
|
|
// hit...
|
|
|
|
if (numBreakPoints == maxBreakPoints)
|
|
return(1);
|
|
else if (lineNumber <= 0)
|
|
return(2);
|
|
else
|
|
{
|
|
//------------------------------------------------
|
|
// Insert it into the list (in ascending order)...
|
|
long insertPoint = 0;
|
|
for (insertPoint = 0; insertPoint < numBreakPoints; insertPoint++)
|
|
{
|
|
if (lineNumber == breakPoints[insertPoint])
|
|
return(NO_ERR);
|
|
else if (lineNumber < breakPoints[insertPoint])
|
|
break;
|
|
}
|
|
for (long i = insertPoint; i < numBreakPoints; i++)
|
|
breakPoints[i + 1] = breakPoints[i];
|
|
breakPoints[insertPoint] = lineNumber;
|
|
numBreakPoints++;
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long BreakPointManager::remove (long lineNumber)
|
|
{
|
|
|
|
long index;
|
|
for (index = 0; index < numBreakPoints; index++)
|
|
if (lineNumber == breakPoints[index])
|
|
break;
|
|
|
|
numBreakPoints--;
|
|
|
|
//------------------
|
|
// Shift 'em down...
|
|
for (long i = index; i < numBreakPoints; i++)
|
|
breakPoints[i] = breakPoints[i + 1];
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long BreakPointManager::removeAll (void)
|
|
{
|
|
|
|
long numRemoved = numBreakPoints;
|
|
numBreakPoints = 0;
|
|
return(numRemoved);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool BreakPointManager::isBreakPoint (long lineNumber)
|
|
{
|
|
|
|
if (numBreakPoints > 0)
|
|
{
|
|
for (long i = 0; i < numBreakPoints; i++)
|
|
if (lineNumber == breakPoints[i])
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void BreakPointManager::print (void)
|
|
{
|
|
|
|
//--------------------------------------------------------------
|
|
// If no line number, do default action--list all breakpoints...
|
|
for (long i = 0; i < numBreakPoints; i++)
|
|
{
|
|
// print info...
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
// DEBUGGER class
|
|
//***************************************************************************
|
|
|
|
void* Debugger::operator new (size_t mySize)
|
|
{
|
|
|
|
void *result = AblStackHeap->Malloc(mySize);
|
|
return(result);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::operator delete (void* us)
|
|
{
|
|
|
|
AblStackHeap->Free(us);
|
|
}
|
|
void Debugger::init (void)
|
|
{
|
|
module = NULL;
|
|
watchManager = NULL;
|
|
breakPointManager = NULL;
|
|
debugModule = module;
|
|
enabled = false;
|
|
debugCommand = false;
|
|
halt = false;
|
|
trace = false;
|
|
step = false;
|
|
traceEntry = false;
|
|
traceExit = false;
|
|
printCallback = NULL;
|
|
if (!g_DebugSemaphore)
|
|
g_DebugSemaphore = CreateSemaphore (NULL,0,1,NULL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::init (void (*callback)(char* s), ABLModulePtr _module)
|
|
{
|
|
|
|
printCallback = callback;
|
|
module = _module;
|
|
|
|
if (module)
|
|
{
|
|
watchManager = module->getWatchManager();
|
|
breakPointManager = module->getBreakPointManager();
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::destroy (void)
|
|
{
|
|
if (g_DebugSemaphore)
|
|
CloseHandle (g_DebugSemaphore);
|
|
g_DebugSemaphore = NULL;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::print (char* s)
|
|
{
|
|
|
|
if (printCallback)
|
|
(*printCallback)(s);
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::setModule (ABLModulePtr _module)
|
|
{
|
|
|
|
module = _module;
|
|
breakPointManager = module->getBreakPointManager();
|
|
watchManager = module->getWatchManager();
|
|
step = module->getStep();
|
|
trace = traceEntry = traceExit = module->getTrace();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::setWatch (long states)
|
|
{
|
|
|
|
getToken();
|
|
|
|
switch (curToken)
|
|
{
|
|
case TKN_SEMICOLON:
|
|
print("Variables currently watched:\n");
|
|
//-------------------------------------------------------------
|
|
// No variable, so default watch action--display all watches...
|
|
watchManager->print();
|
|
break;
|
|
case TKN_IDENTIFIER:
|
|
SymTableNodePtr idPtr = NULL;
|
|
searchAndFindAllSymTables(idPtr);
|
|
getToken();
|
|
//-----------------
|
|
// STORE setting...
|
|
bool breakToDebug = ((states & WATCH_BREAK) != 0);
|
|
if (states & WATCH_STORE_OFF)
|
|
watchManager->setStore(idPtr, false, breakToDebug);
|
|
else if (states & WATCH_STORE_ON)
|
|
watchManager->setStore(idPtr, true, breakToDebug);
|
|
//-----------------
|
|
// FETCH setting...
|
|
if (states & WATCH_FETCH_OFF)
|
|
watchManager->setFetch(idPtr, false, breakToDebug);
|
|
else if (states & WATCH_FETCH_ON)
|
|
watchManager->setFetch(idPtr, true, breakToDebug);
|
|
break;
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::addBreakPoint (void)
|
|
{
|
|
|
|
getToken();
|
|
|
|
switch (curToken)
|
|
{
|
|
case TKN_SEMICOLON:
|
|
print("Variables currently watched:\n");
|
|
breakPointManager->print();
|
|
break;
|
|
case TKN_NUMBER:
|
|
if (curLiteral.type == LIT_INTEGER)
|
|
breakPointManager->add(curLiteral.value.integer);
|
|
getToken();
|
|
break;
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::removeBreakPoint (void)
|
|
{
|
|
|
|
getToken();
|
|
|
|
switch (curToken)
|
|
{
|
|
case TKN_SEMICOLON:
|
|
breakPointManager->removeAll();
|
|
break;
|
|
case TKN_NUMBER:
|
|
if (curLiteral.type == LIT_INTEGER)
|
|
breakPointManager->remove(curLiteral.value.integer);
|
|
getToken();
|
|
break;
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::sprintStatement (char* dest)
|
|
{
|
|
|
|
//---------------------------------------------------------------------
|
|
// First, rebuild the the current statement from the module code. Then,
|
|
// spit it out as we do so...
|
|
bool done = false;
|
|
char* cp = statementStartPtr;
|
|
|
|
do
|
|
{
|
|
TokenCodeType token = (TokenCodeType)*cp;
|
|
cp++;
|
|
switch (token)
|
|
{
|
|
case TKN_STATEMENT_MARKER:
|
|
done = true;
|
|
break;
|
|
case TKN_SEMICOLON:
|
|
case TKN_END_IF:
|
|
case TKN_END_WHILE:
|
|
case TKN_END_FOR:
|
|
case TKN_END_FUNCTION:
|
|
case TKN_END_MODULE:
|
|
case TKN_END_LIBRARY:
|
|
case TKN_END_CASE:
|
|
case TKN_END_SWITCH:
|
|
case TKN_THEN:
|
|
//case TKN_UNTIL:
|
|
done = true;
|
|
//--------------------------
|
|
// Do NOT want break here...
|
|
default:
|
|
//done = FALSE;
|
|
switch (token)
|
|
{
|
|
case TKN_ADDRESS_MARKER:
|
|
cp += sizeof(Address);
|
|
break;
|
|
case TKN_IDENTIFIER:
|
|
case TKN_NUMBER:
|
|
case TKN_STRING: {
|
|
SymTableNodePtr symbol = *((SymTableNodePtr*)cp);
|
|
strcat(dest, " ");
|
|
strcat(dest, symbol->name);
|
|
cp += sizeof(SymTableNodePtr);
|
|
}
|
|
break;
|
|
default:
|
|
strcat(dest, " ");
|
|
strcat(dest, TokenStrings[token]);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} while (!done);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::sprintLineNumber (char* dest)
|
|
{
|
|
|
|
//--------------------------
|
|
// PRINT LINE NUMBER HERE...
|
|
sprintf(dest, "LINE#");
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::sprintDataValue (char* dest, StackItemPtr data, TypePtr dataType)
|
|
{
|
|
|
|
if ((dataType->form == FRM_ENUM) && (dataType != BooleanTypePtr))
|
|
dataType = IntegerTypePtr;
|
|
|
|
if (dataType == IntegerTypePtr)
|
|
sprintf(dest, "%d", data->integer);
|
|
else if (dataType == RealTypePtr)
|
|
sprintf(dest, "%0.6f", data->real);
|
|
else if (dataType == BooleanTypePtr)
|
|
sprintf(dest, "%s", (data->integer == 1) ? "true" : "false");
|
|
else if (dataType == CharTypePtr)
|
|
sprintf(dest, "%c", data->byte);
|
|
else if (dataType->form == FRM_ARRAY)
|
|
{
|
|
if (dataType->info.array.elementTypePtr == CharTypePtr)
|
|
{
|
|
// PRINT CHAR ARRAY HERE...
|
|
sprintf(dest, "CHAR ARRAY");
|
|
}
|
|
else
|
|
{
|
|
// PRINT ARRAY HERE...
|
|
sprintf(dest, "ARRAY");
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::sprintSimpleValue (char* dest, SymTableNodePtr symbol)
|
|
{
|
|
|
|
//--------------------------------------------------------------------
|
|
// This code is adapted from execVariable(). If that function changes,
|
|
// this better too!
|
|
TypePtr typePtr = (TypePtr)(symbol->typePtr);
|
|
|
|
// if (!symbol->info)
|
|
//{
|
|
// sprintf (dest,"variable is not defined at this time");
|
|
// return(NO_ERR);
|
|
// }
|
|
|
|
if (symbol->defn.key == DFN_CONST)
|
|
{
|
|
if (typePtr == IntegerTypePtr)
|
|
sprintf(dest, "%d", symbol->defn.info.constant.value.integer);
|
|
else if (typePtr == CharTypePtr)
|
|
sprintf(dest, "%c", symbol->defn.info.constant.value.character);
|
|
else
|
|
sprintf(dest, "%.4f", symbol->defn.info.constant.value.real);
|
|
}
|
|
else
|
|
{
|
|
//--------------------------------------------------------------------
|
|
// First, point to the variable's stack item. If the variable's scope
|
|
// level is less than the current scope level, follow the static links
|
|
// to the proper stack frame base...
|
|
StackItemPtr dataPtr = NULL;
|
|
switch (symbol->defn.info.data.varType)
|
|
{
|
|
case VAR_TYPE_NORMAL: {
|
|
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
|
|
if (headerPtr == NULL)
|
|
{
|
|
sprintf (dest,"Variable not active at this time");
|
|
return (NO_ERR);
|
|
}
|
|
long delta = level - symbol->level;
|
|
while (delta-- > 0)
|
|
headerPtr = (StackFrameHeaderPtr)headerPtr->staticLink.address;
|
|
dataPtr = (StackItemPtr)headerPtr + symbol->defn.info.data.offset;
|
|
}
|
|
break;
|
|
case VAR_TYPE_ETERNAL:
|
|
dataPtr = (StackItemPtr)stack + symbol->defn.info.data.offset;
|
|
break;
|
|
case VAR_TYPE_STATIC:
|
|
dataPtr = (StackItemPtr)StaticDataPtr + symbol->defn.info.data.offset;
|
|
break;
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
// If it's a scalar or enumeration reference parameter, that item
|
|
// points to the actual item...
|
|
if ((symbol->defn.key == DFN_REFPARAM) && (typePtr->form != FRM_ARRAY)/* && (typePtr->form != FRM_RECORD)*/)
|
|
dataPtr = (StackItemPtr)dataPtr->address;
|
|
|
|
TypePtr baseTypePtr = baseType(typePtr);
|
|
|
|
if (dataPtr == NULL)
|
|
{
|
|
sprintf (dest,"Variable not active at this time");
|
|
return (NO_ERR);
|
|
}
|
|
if ((typePtr->form != FRM_ARRAY) /*&& (typePtr->form != FRM_RECORD)*/)
|
|
{
|
|
ABL_Assert(dataPtr != NULL, 0, " Debugger.sprintSimpleValue(): dataPtr is NULL ");
|
|
if ((baseTypePtr == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
|
|
sprintf(dest, "%d", *((long*)dataPtr));
|
|
else if (baseTypePtr == CharTypePtr)
|
|
sprintf(dest, "\"%c\"", *((char*)dataPtr));
|
|
else
|
|
sprintf(dest, "%.4f", *((float*)dataPtr));
|
|
}
|
|
else
|
|
sprintf(dest, "ARRAY");
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::sprintArrayValue (char* dest, SymTableNodePtr symbol, char* subscriptString)
|
|
{
|
|
|
|
//--------------------------------------------------------------------
|
|
// This code is adapted from execVariable(). If that function changes,
|
|
// this better too!
|
|
|
|
//--------------------------------------------------------------------
|
|
// First, point to the variable's stack item. If the variable's scope
|
|
// level is less than the current scope level, follow the static links
|
|
// to the proper stack frame base...
|
|
if (symbol->defn.key == DFN_CONST)
|
|
sprintf(dest, "\"%s\"", symbol->defn.info.constant.value.stringPtr);
|
|
else
|
|
{
|
|
StackItemPtr dataPtr = NULL;
|
|
switch (symbol->defn.info.data.varType)
|
|
{
|
|
case VAR_TYPE_NORMAL: {
|
|
StackFrameHeaderPtr headerPtr = (StackFrameHeaderPtr)stackFrameBasePtr;
|
|
long delta = level - symbol->level;
|
|
while (delta-- > 0)
|
|
headerPtr = (StackFrameHeaderPtr)headerPtr->staticLink.address;
|
|
dataPtr = (StackItemPtr)headerPtr + symbol->defn.info.data.offset;
|
|
}
|
|
break;
|
|
case VAR_TYPE_ETERNAL:
|
|
dataPtr = (StackItemPtr)stack + symbol->defn.info.data.offset;
|
|
break;
|
|
case VAR_TYPE_STATIC:
|
|
dataPtr = (StackItemPtr)StaticDataPtr + symbol->defn.info.data.offset;
|
|
break;
|
|
}
|
|
|
|
TypePtr typePtr = (TypePtr)(symbol->typePtr);
|
|
|
|
ABL_Assert(dataPtr != NULL, 0, " Debugger.sprintArrayValue(): dataPtr is NULL ");
|
|
|
|
Address elementAddress = (Address)dataPtr->address;
|
|
if (subscriptString)
|
|
{
|
|
char* cp = subscriptString;
|
|
|
|
//-----------------------------
|
|
// Get past the open bracket...
|
|
cp++;
|
|
|
|
char* token = strtok(&subscriptString[1], ",]");
|
|
while (token)
|
|
{
|
|
//----------------
|
|
// Read integer...
|
|
long index = atoi(token);
|
|
//-------------------------
|
|
// Range check the index...
|
|
if ((index < 0) || (index >= typePtr->info.array.elementCount))
|
|
return(1);
|
|
elementAddress += (index * typePtr->info.array.elementTypePtr->size);
|
|
|
|
typePtr = typePtr->info.array.elementTypePtr;
|
|
|
|
token = strtok(NULL, ",]");
|
|
}
|
|
}
|
|
|
|
TypePtr baseTypePtr = baseType(typePtr);
|
|
if ((typePtr->form != FRM_ARRAY) /*&& (typePtr->form != FRM_RECORD)*/)
|
|
{
|
|
if ((baseTypePtr == IntegerTypePtr) || (typePtr->form == FRM_ENUM))
|
|
sprintf(dest, "%d", *((long*)elementAddress));
|
|
else if (baseTypePtr == CharTypePtr)
|
|
sprintf(dest, "\"%c\"", *((char*)elementAddress));
|
|
else
|
|
sprintf(dest, "%.4f", *((float*)elementAddress));
|
|
}
|
|
else if (typePtr->info.array.elementTypePtr == CharTypePtr)
|
|
sprintf(dest, "\"%s\"", (char*)elementAddress);
|
|
else
|
|
sprintf(dest, "Could you be more specific?");
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::sprintValue (char* dest, char* exprString)
|
|
{
|
|
|
|
char* subscript = strchr(exprString, '[');
|
|
if (!subscript)
|
|
{
|
|
//------------------------------------------
|
|
// Probably a simple variable or constant...
|
|
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
|
|
if (!symbol)
|
|
return(1);
|
|
if (symbol->typePtr->form != FRM_ARRAY)
|
|
{
|
|
sprintSimpleValue(dest, symbol);
|
|
return(NO_ERR);
|
|
}
|
|
}
|
|
|
|
//-----------------
|
|
// Must be array...
|
|
if (subscript)
|
|
{
|
|
//--------------------------------
|
|
// Looking for specific element...
|
|
char subscriptString[255];
|
|
strcpy(subscriptString, subscript);
|
|
*subscript = NULL;
|
|
|
|
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
|
|
if (!symbol)
|
|
return(1);
|
|
sprintArrayValue(dest, symbol, subscriptString);
|
|
}
|
|
else
|
|
{
|
|
//-------------------------
|
|
// Print the whole thing...
|
|
SymTableNodePtr symbol = debugModule->findSymbol(exprString, CurRoutineIdPtr);
|
|
if (!symbol)
|
|
return(1);
|
|
sprintArrayValue(dest, symbol, NULL);
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// We have the symbol, so parse the subscripts...
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::traceStatementExecution (void)
|
|
{
|
|
|
|
bool halt = step;
|
|
//--------------------------------------
|
|
// Do we have a breakpoint on this line?
|
|
if (breakPointManager)
|
|
{
|
|
if (breakPointManager->isBreakPoint(execLineNumber))
|
|
{
|
|
sprintf(message, "HIT BP: (%d) %s [%d]", module->getId(), module->getName(), execLineNumber);
|
|
print(message);
|
|
halt = true;
|
|
}
|
|
}
|
|
|
|
// if (trace) {
|
|
// message[0] = NULL;
|
|
// sprintLineNumber(message);
|
|
// print(message);
|
|
// }
|
|
|
|
//------------------------------------------------------------------
|
|
// If we've hit a breakpoint or are currently halted due to
|
|
// some other debugging thingy, wait for another debugger command...
|
|
if (halt)
|
|
debugMode();
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::AddActualParam (SymTableNodePtr formalIdPtr,StackItemPtr tos)
|
|
{
|
|
if (!traceEntry)
|
|
return;
|
|
|
|
char text[100];
|
|
TypePtr formalTypePtr = (TypePtr)(formalIdPtr->typePtr);
|
|
if (formalIdPtr->defn.key == DFN_VALPARAM)
|
|
{
|
|
if ((formalTypePtr->form == FRM_ARRAY))
|
|
{
|
|
Str_Copy (text,"ARRAY,",sizeof (text));
|
|
}
|
|
else if (formalTypePtr == RealTypePtr)
|
|
{
|
|
sprintf (text,"%f,",tos->real);
|
|
}
|
|
else if (formalTypePtr == IntegerTypePtr)
|
|
{
|
|
sprintf (text,"%d,",tos->integer);
|
|
}
|
|
else if (formalTypePtr == BooleanTypePtr)
|
|
{
|
|
if (tos->integer)
|
|
Str_Copy (text,"true,",sizeof (text));
|
|
else
|
|
Str_Copy (text,"false,",sizeof (text));
|
|
}
|
|
else if (formalTypePtr == CharTypePtr)
|
|
{
|
|
text[0] = tos->byte;
|
|
text[1] = ',';
|
|
text[2] = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Str_Copy (text,"REF,",sizeof (text));
|
|
}
|
|
Str_Cat (message,text,sizeof (message));
|
|
}
|
|
|
|
void Debugger::EndActualParams (void)
|
|
{
|
|
if (!traceEntry)
|
|
return;
|
|
if (strlen (message) > 1)
|
|
message[strlen (message)-1] = 0; // remove extra comma
|
|
Str_Cat (message,")",sizeof (message));
|
|
print(message);
|
|
}
|
|
void Debugger::StartActualParams (SymTableNodePtr idPtr)
|
|
{
|
|
if (!traceEntry)
|
|
return;
|
|
|
|
sprintf (message,"%s Parameters: (", idPtr->name);
|
|
}
|
|
|
|
long Debugger::traceRoutineEntry (SymTableNodePtr idPtr)
|
|
{
|
|
if (traceEntry)
|
|
{
|
|
sprintf(message, "ENTER (%d) %s:%s", module->getId(), module->getName(), idPtr->name);
|
|
print(message);
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::traceRoutineExit (SymTableNodePtr idPtr)
|
|
{
|
|
|
|
if (traceExit)
|
|
{
|
|
sprintf(message, "EXIT (%d) %s:%s", module->getId(), module->getName(), idPtr->name);
|
|
print(message);
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::traceDataStore (SymTableNodePtr id, TypePtr idType, StackItemPtr target, TypePtr targetType)
|
|
{
|
|
|
|
//SymTableNodePtr idPtr = debugModule->findSymbol(strParam1);
|
|
//if (!idPtr) {
|
|
// print("Unknown identifier in current scope.\n");
|
|
// return;
|
|
// }
|
|
// char message[255];
|
|
// sprintSymbolValue(message, idPtr);
|
|
// print(message);
|
|
// }
|
|
|
|
if (id->info && ((WatchPtr)id->info)->store)
|
|
{
|
|
char valString[255];
|
|
sprintDataValue(valString, target, targetType);
|
|
if (idType->form == FRM_ARRAY)
|
|
sprintf(message, "STORE: (%d) %s [%d] -> %s[#] = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
|
|
//else if (idType->form == FRM_RECORD)
|
|
// sprintf(message, "STORE AT LINE %d - %s.# = %s\n", execLineNumber, id->name, valString);
|
|
else
|
|
sprintf(message, "STORE: (%d) %s [%d] -> %s = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
|
|
print(message);
|
|
if (((WatchPtr)id->info)->breakOnStore)
|
|
debugMode();
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
long Debugger::traceDataFetch (SymTableNodePtr id, TypePtr idType, StackItemPtr data)
|
|
{
|
|
|
|
TypePtr idTypePtr = id->typePtr;
|
|
if (id->info && ((WatchPtr)id->info)->fetch)
|
|
{
|
|
char valString[255];
|
|
sprintDataValue(valString, data, idType);
|
|
if (idTypePtr->form == FRM_ARRAY)
|
|
sprintf(message, "FETCH: (%d) %s [%d] - %s[#] = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
|
|
//else if (idTypePtr->form == FRM_RECORD)
|
|
// sprintf(message, "STORE AT LINE %d - %s.# = %s\n", id->name, valString);
|
|
else
|
|
sprintf(message, "FETCH: (%d) %s [%d] - %s = %s\n", module->getId(), module->getName(), execLineNumber, id->name, valString);
|
|
print(message);
|
|
if (((WatchPtr)id->info)->breakOnFetch)
|
|
debugMode();
|
|
}
|
|
|
|
return(NO_ERR);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::showValue (void)
|
|
{
|
|
|
|
getToken();
|
|
|
|
if (curToken == TKN_SEMICOLON)
|
|
print("Bad Expression.\n");
|
|
else
|
|
{
|
|
//------------------------------------------------------------
|
|
// NOTE: Need a SOFT FATAL for parsing expressions here so the
|
|
// debugger's errors don't Fatal out of the game!
|
|
//------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------
|
|
// It's important that the expression parser return an error code
|
|
// rather than fatal!
|
|
TypePtr typePtr = expression();
|
|
if (errorCount > 0)
|
|
return;
|
|
|
|
char* savedCodeSegmentPtr = codeSegmentPtr;
|
|
TokenCodeType savedCodeToken = codeToken;
|
|
execExpression();
|
|
|
|
if (typePtr->form == FRM_ARRAY)
|
|
{
|
|
print("SHOW ARRAY\n");
|
|
}
|
|
else
|
|
{
|
|
char message[255];
|
|
sprintDataValue(message, tos, typePtr);
|
|
strcat(message, "\n");
|
|
print(message);
|
|
}
|
|
|
|
pop();
|
|
codeSegmentPtr = savedCodeSegmentPtr;
|
|
codeToken = savedCodeToken;
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::assignVariable (void)
|
|
{
|
|
|
|
getToken();
|
|
|
|
#if 0
|
|
if (curToken == TKN_SEMICOLON)
|
|
print("Need a variable.\n");
|
|
else if (curToken == TKN_IDENTIFIER)
|
|
{
|
|
//----------------------------------
|
|
// Parse the assignment statement...
|
|
SymTableNodePtr idPtr = NULL;
|
|
searchAndFindAllSymTables(idPtr);
|
|
assigmentStatement(idPtr);
|
|
if (errorCount > 0)
|
|
return;
|
|
|
|
//-------------------
|
|
// Now, execute it...
|
|
char* savedCodeSegmentPtr = codeSegmentPtr;
|
|
long savedCodeToken = codeToken;
|
|
codeSegmentPtr = codeBuffer + 1;
|
|
getCodeToken();
|
|
idPtr = getSymTableCodePtr();
|
|
execAssignmentStatement(idPtr);
|
|
|
|
//----------------------------
|
|
// Restore the code segment...
|
|
codeSegmentPtr = savedCodeSegmentPtr;
|
|
codeToken = savedCodeToken;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::displayModuleInstanceRegistry (void)
|
|
{
|
|
|
|
char buffer1[1000], buffer2[100];
|
|
for (long i = 0; i < ((NumModuleInstances + 1) / 2); i++)
|
|
{
|
|
sprintf(buffer1, "(%02d) %-50s ", ModuleInstanceRegistry[i * 2]->getId(), ModuleInstanceRegistry[i * 2]->getName());
|
|
if ((i * 2 + 1) < NumModuleInstances)
|
|
{
|
|
sprintf(buffer2, "(%02d) %-50s ", ModuleInstanceRegistry[i * 2 + 1]->getId(), ModuleInstanceRegistry[i * 2 + 1]->getName());
|
|
strcat(buffer1, buffer2);
|
|
}
|
|
print(buffer1);
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::processCommand (long commandId, char* strParam1, long numParam1, ABLModulePtr moduleParam1)
|
|
{
|
|
|
|
switch (commandId)
|
|
{
|
|
case DEBUG_COMMAND_SET_MODULE:
|
|
if (debugModule)
|
|
{
|
|
debugModule->setTrace(FALSE);
|
|
trace = FALSE;
|
|
traceEntry = FALSE;
|
|
traceExit = FALSE;
|
|
}
|
|
if (moduleParam1)
|
|
{
|
|
debugModule = moduleParam1;
|
|
print(" ");
|
|
sprintf(message, "SET MODULE: %s", debugModule->getName());
|
|
print(message);
|
|
}
|
|
else
|
|
{
|
|
print(" ");
|
|
displayModuleInstanceRegistry();
|
|
if (debugModule)
|
|
sprintf(message, "CURRENT MODULE: %s", debugModule->getName());
|
|
else
|
|
sprintf (message, "No module being debugged");
|
|
print(message);
|
|
}
|
|
break;
|
|
case DEBUG_COMMAND_TRACE:
|
|
if (numParam1)
|
|
{
|
|
debugModule->setTrace(true);
|
|
debugModule->setStep(FALSE);
|
|
if (module == debugModule)
|
|
{
|
|
trace = true;
|
|
traceEntry = true;
|
|
traceExit = true;
|
|
step = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debugModule->setTrace(FALSE);
|
|
if (module == debugModule)
|
|
{
|
|
trace = FALSE;
|
|
traceEntry = FALSE;
|
|
traceExit = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case DEBUG_COMMAND_STEP:
|
|
if (numParam1)
|
|
{
|
|
debugModule->setStep(true);
|
|
debugModule->setTrace(FALSE);
|
|
if (module == debugModule)
|
|
{
|
|
step = true;
|
|
trace = FALSE;
|
|
traceEntry = FALSE;
|
|
traceExit = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debugModule->setStep(FALSE);
|
|
if (module == debugModule)
|
|
step = FALSE;
|
|
}
|
|
break;
|
|
case DEBUG_COMMAND_BREAKPOINT_SET:
|
|
print(" ");
|
|
debugModule->getBreakPointManager()->add(numParam1);
|
|
sprintf(message, "SET BP: %s (%d)", debugModule->getName(), numParam1);
|
|
print(message);
|
|
break;
|
|
case DEBUG_COMMAND_BREAKPOINT_REMOVE:
|
|
print(" ");
|
|
debugModule->getBreakPointManager()->remove(numParam1);
|
|
sprintf(message, "REMOVE BP: %s (%d)", debugModule->getName(), numParam1);
|
|
print(message);
|
|
break;
|
|
case DEBUG_COMMAND_WATCH_SET: {
|
|
print(" ");
|
|
SymTableNodePtr idPtr = debugModule->findSymbol(strParam1);
|
|
if (!idPtr)
|
|
{
|
|
idPtr = searchSymTableDisplay(strlwr(strParam1));
|
|
if (!idPtr)
|
|
{
|
|
print("Unknown identifier in current scope.\n");
|
|
return;
|
|
}
|
|
}
|
|
bool breakToDebug = ((numParam1 & WATCH_BREAK) != 0);
|
|
if (numParam1 & WATCH_STORE_ON)
|
|
{
|
|
long result = debugModule->getWatchManager()->setStore(idPtr, true, breakToDebug);
|
|
if (result == 2)
|
|
{
|
|
print("Reached max watch limit--unable to set watch.\n");
|
|
return;
|
|
}
|
|
}
|
|
if (numParam1 & WATCH_FETCH_ON)
|
|
{
|
|
long result = debugModule->getWatchManager()->setFetch(idPtr, true, breakToDebug);
|
|
if (result == 2)
|
|
{
|
|
print("Reached max watch limit--unable to set watch.\n");
|
|
return;
|
|
}
|
|
}
|
|
if (numParam1 & WATCH_STORE_OFF)
|
|
debugModule->getWatchManager()->setStore(idPtr, FALSE, breakToDebug);
|
|
if (numParam1 & WATCH_FETCH_OFF)
|
|
debugModule->getWatchManager()->setFetch(idPtr, FALSE, breakToDebug);
|
|
bool store = debugModule->getWatchManager()->getStore(idPtr);
|
|
bool fetch = debugModule->getWatchManager()->getFetch(idPtr);
|
|
if (store || fetch)
|
|
{
|
|
sprintf(message, "SET WATCH: %s.%s (", debugModule->getName(), strParam1);
|
|
if (store)
|
|
strcat(message, "s");
|
|
if (fetch)
|
|
strcat(message, "f");
|
|
strcat(message, ")");
|
|
}
|
|
else
|
|
sprintf(message, "REMOVE WATCH: %s.%s", debugModule->getName(), strParam1);
|
|
print(message);
|
|
}
|
|
break;
|
|
case DEBUG_COMMAND_WATCH_REMOVE_ALL:
|
|
debugModule->getWatchManager()->removeAll();
|
|
sprintf(message, "REMOVE ALL WATCHS: %s", debugModule->getName());
|
|
print(message);
|
|
break;
|
|
case DEBUG_COMMAND_PRINT: {
|
|
if (atbreakpoint)
|
|
{
|
|
print(" ");
|
|
long err = sprintValue(message, strParam1);
|
|
switch (err)
|
|
{
|
|
case NO_ERR:
|
|
print(message);
|
|
break;
|
|
case 1:
|
|
print("Unknown identifier in current scope.");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReleaseSemaphore (g_DebugSemaphore,1,NULL);
|
|
print ("Cannot view variables when not at a breakpoint");
|
|
}
|
|
}
|
|
break;
|
|
case DEBUG_COMMAND_CONTINUE:
|
|
debugCommand = FALSE;
|
|
Verify (g_DebugSemaphore);
|
|
ReleaseSemaphore (g_DebugSemaphore,1,NULL);
|
|
break;
|
|
case DEBUG_COMMAND_HELP:
|
|
print(" ");
|
|
print("break {+|-} <line#> set/remove breakpt");
|
|
print("setmod [Driver name] set current module (or list them)");
|
|
print("watch [f|s]{+|-}{.} <variable> set/remove variable watch (fetch & store)");
|
|
print("watchremove removes all current watches");
|
|
print("print <variable> display current value of variable");
|
|
print("step {+|-} start/stop step mode");
|
|
print("trace {+|-} start/stop trace mode");
|
|
print("info current module info");
|
|
print("help help");
|
|
break;
|
|
case DEBUG_COMMAND_INFO:
|
|
print(" ");
|
|
if (debugModule)
|
|
{
|
|
sprintf(message, "CURRENT MODULE: %s", debugModule->getName());
|
|
print(message);
|
|
ModuleInfo moduleInfo;
|
|
debugModule->getInfo(&moduleInfo);
|
|
sprintf(message, "%d static vars, %d bytes, %d largest",
|
|
moduleInfo.numStaticVars,
|
|
moduleInfo.totalSizeStaticVars,
|
|
moduleInfo.largestStaticVar);
|
|
print(message);
|
|
}
|
|
else
|
|
{
|
|
print ("No module currently being debugged");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Debugger::debugMode (void)
|
|
{
|
|
|
|
//--------------------------------------------------------------------
|
|
// Some assumptions: this will never be called during a smacker movie.
|
|
|
|
//-------------------------------------------------------------------------------
|
|
// When we enter debug mode, we grab control of all message handling from windows
|
|
// until we exit ABL debug mode. This way, the ABL system is preserved for
|
|
// debugging...
|
|
if (!debugModule)
|
|
return;
|
|
debugModule = module;
|
|
|
|
message[0] = NULL;
|
|
sprintStatement(message);
|
|
print(message);
|
|
|
|
Verify (g_DebugSemaphore);
|
|
atbreakpoint = true;
|
|
WaitForSingleObject (g_DebugSemaphore,INFINITE); // because process command continue releases this.
|
|
atbreakpoint = false;
|
|
// ReleaseSemaphore (g_DebugSemaphore,1,NULL);
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
DebuggerPtr ABLi_getDebugger (void)
|
|
{
|
|
|
|
return(debugger);
|
|
}
|
|
|
|
//***************************************************************************
|
|
} |