//*************************************************************************** // // ABLDBUG.CPP // //*************************************************************************** #include "MW4Headers.hpp" #include #include #include #include #include #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 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 {+|-} set/remove breakpt"); print("setmod [Driver name] set current module (or list them)"); print("watch [f|s]{+|-}{.} set/remove variable watch (fetch & store)"); print("watchremove removes all current watches"); print("print 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); } //*************************************************************************** }