404 lines
9.1 KiB
C
404 lines
9.1 KiB
C
/* nl-debug.c --- debugging functions, and functions to trap signals and timers
|
|
|
|
Copyright (C) 2016 Lutz Mueller
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
#include "newlisp.h"
|
|
#include "protos.h"
|
|
|
|
#ifdef WINDOWS
|
|
#define fgets win_fgets
|
|
#endif
|
|
|
|
extern FILE * IOchannel;
|
|
extern int evalSilent;
|
|
|
|
int matchString(char * buffer, char * string, int * length);
|
|
int stringComp(char * buffer, char * string);
|
|
int debugPrintFunction(CELL * cell);
|
|
void getDebuggerInput(char * msg);
|
|
|
|
extern SYMBOL * currentFunc;
|
|
extern SYMBOL * timerEvent;
|
|
extern SYMBOL * cilkEvent;
|
|
extern SYMBOL * symHandler[];
|
|
extern int currentSignal;
|
|
|
|
int currentLevel = 0;
|
|
int debugStackIdx = 0;
|
|
UINT * debugStack;
|
|
UINT tracePrintDevice;
|
|
|
|
char debugPreStr[8] = "#";
|
|
char debugPostStr[8] = "#";
|
|
CELL * debugPrintCell = NULL;
|
|
|
|
char headerStr[16] = "\n-----\n\n";
|
|
char footerStr[32] = " s|tep n|ext c|ont q|uit > ";
|
|
|
|
#define pushDebugStack(A) (*(debugStack + debugStackIdx++) = (UINT)(A))
|
|
#define popDebugStack() (*(debugStack + --debugStackIdx))
|
|
|
|
#define DEBUG_ENTRY "->"
|
|
#define DEBUG_EXIT "<-"
|
|
|
|
void openTrace(void)
|
|
{
|
|
if(traceFlag) return;
|
|
traceFlag = TRACE_TRUE;
|
|
currentFunc = nilSymbol;
|
|
debugStackIdx = 0;
|
|
debugStack = (UINT *)allocMemory(MAX_CPU_STACK * 2 * sizeof(UINT));
|
|
}
|
|
|
|
void closeTrace(void)
|
|
{
|
|
if(traceFlag & TRACE_PRINT_EVAL)
|
|
close(tracePrintDevice);
|
|
traceFlag = 0;
|
|
if(debugStack) free(debugStack);
|
|
debugStack = NULL;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
CELL * p_debug(CELL * params)
|
|
{
|
|
CELL * result;
|
|
|
|
openTrace();
|
|
traceFlag |= TRACE_IN_DEBUG;
|
|
result = copyCell(evaluateExpression(params));
|
|
closeTrace();
|
|
|
|
return(result);
|
|
}
|
|
#endif
|
|
|
|
|
|
CELL * p_trace(CELL * params)
|
|
{
|
|
if(params != nilCell)
|
|
{
|
|
params = evaluateExpression(params);
|
|
if(isNumber(params->type))
|
|
{
|
|
traceFlag |= TRACE_PRINT_EVAL;
|
|
getIntegerExt(params, &tracePrintDevice, FALSE);
|
|
return(stuffInteger(tracePrintDevice));
|
|
}
|
|
if(!isNil(params))
|
|
{
|
|
openTrace();
|
|
traceFlag |= TRACE_IN_DEBUG;
|
|
}
|
|
else
|
|
closeTrace();
|
|
}
|
|
|
|
if(traceFlag & TRACE_IN_DEBUG) return(trueCell);
|
|
if(traceFlag & TRACE_PRINT_EVAL) return(stuffInteger(tracePrintDevice));
|
|
return(nilCell);
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
CELL * p_traceHighlight(CELL * params)
|
|
{
|
|
char * pre, * post, * header, * footer;
|
|
|
|
params = getString(params, &pre);
|
|
params = getString(params, &post);
|
|
|
|
strncpy(debugPreStr, pre, 8);
|
|
strncpy(debugPostStr, post, 8);
|
|
*(debugPreStr + 7) = 0;
|
|
*(debugPostStr + 7) = 0;
|
|
|
|
if(params != nilCell)
|
|
{
|
|
params = getString(params, &header);
|
|
strncpy(headerStr, header, 16);
|
|
}
|
|
|
|
if(params != nilCell)
|
|
{
|
|
getString(params, &footer);
|
|
strncpy(footerStr, footer, 32);
|
|
}
|
|
|
|
*(headerStr + 15) = 0;
|
|
*(footerStr + 31) = 0;
|
|
|
|
return(trueCell);
|
|
}
|
|
#endif /* DEBUGGER */
|
|
|
|
void tracePrint(char * label, CELL * expr)
|
|
{
|
|
UINT printDeviceSave = printDevice;
|
|
|
|
printDevice = tracePrintDevice;
|
|
varPrintf(OUT_DEVICE, "%d %s: ", recursionCount, label);
|
|
if(expr) printCell(expr, TRUE, OUT_DEVICE);
|
|
varPrintf(OUT_DEVICE, "%s", "\n");
|
|
printDevice = printDeviceSave;
|
|
}
|
|
|
|
void traceEntry(CELL * cell, CELL * pCell, CELL * args)
|
|
{
|
|
if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_DEBUG_NEXT)) return;
|
|
traceFlag |= TRACE_IN_ENTRY;
|
|
|
|
#ifdef DEBUGGER
|
|
int defaultFuncFlag = FALSE;
|
|
#endif
|
|
|
|
if(traceFlag & TRACE_SIGNAL)
|
|
{
|
|
traceFlag &= ~TRACE_SIGNAL;
|
|
executeSymbol(symHandler[currentSignal - 1], stuffInteger(currentSignal), NULL);
|
|
traceFlag &= ~TRACE_IN_ENTRY;
|
|
return;
|
|
}
|
|
|
|
if(traceFlag & TRACE_SIGINT)
|
|
{
|
|
traceFlag &= ~TRACE_SIGINT;
|
|
longjmp(errorJump, ERR_USER_RESET);
|
|
}
|
|
|
|
if(traceFlag & TRACE_TIMER)
|
|
{
|
|
traceFlag &= ~TRACE_TIMER;
|
|
executeSymbol(timerEvent, NULL, NULL);
|
|
traceFlag &= ~TRACE_IN_ENTRY;
|
|
return;
|
|
}
|
|
|
|
if(traceFlag & TRACE_PRINT_EVAL)
|
|
{
|
|
if(cell->type == CELL_EXPRESSION) tracePrint("entry", cell);
|
|
traceFlag &= ~TRACE_IN_ENTRY;
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if(debugStackIdx > 1)
|
|
{
|
|
if(debugPrintFunction(cell))
|
|
getDebuggerInput(DEBUG_ENTRY);
|
|
if(!traceFlag) return;
|
|
}
|
|
|
|
if(traceFlag & TRACE_DEBUG_NEXT)
|
|
{
|
|
traceFlag &= ~TRACE_IN_ENTRY;
|
|
return;
|
|
}
|
|
|
|
if(pCell->type == CELL_CONTEXT)
|
|
{
|
|
defaultFuncFlag = TRUE;
|
|
currentFunc = translateCreateSymbol(
|
|
((SYMBOL*)pCell->contents)->name,
|
|
CELL_NIL,
|
|
(SYMBOL*)pCell->contents,
|
|
TRUE);
|
|
|
|
pCell = (CELL *)currentFunc->contents;
|
|
}
|
|
|
|
if((pCell->type == CELL_LAMBDA || pCell->type == CELL_FEXPR)
|
|
&& args->type == CELL_SYMBOL)
|
|
{
|
|
if(debugStackIdx == 0) /* startup */
|
|
traceFlag &= ~TRACE_DEBUG_NEXT;
|
|
|
|
if(!defaultFuncFlag)
|
|
currentFunc = (SYMBOL *)args->contents;
|
|
pushDebugStack(recursionCount);
|
|
pushDebugStack(currentFunc);
|
|
}
|
|
#endif /* no_DEBUG */
|
|
|
|
traceFlag &= ~TRACE_IN_ENTRY;
|
|
}
|
|
|
|
|
|
void traceExit(CELL * result, CELL * cell, CELL * pCell, CELL * args)
|
|
{
|
|
if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_SIGNAL | TRACE_SIGINT | TRACE_TIMER)) return;
|
|
|
|
if(traceFlag == TRACE_PRINT_EVAL)
|
|
{
|
|
tracePrint("exit", result);
|
|
return;
|
|
}
|
|
|
|
traceFlag |= TRACE_IN_EXIT;
|
|
|
|
#ifdef DEBUGGER
|
|
if(traceFlag & TRACE_DEBUG_NEXT)
|
|
{
|
|
if(currentLevel >= recursionCount)
|
|
traceFlag &= ~TRACE_DEBUG_NEXT;
|
|
else
|
|
{
|
|
traceFlag &= ~TRACE_IN_EXIT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( (pCell->type == CELL_LAMBDA || pCell->type == CELL_FEXPR)
|
|
&& args->type == CELL_SYMBOL)
|
|
{
|
|
if((UINT)recursionCount == *(debugStack + debugStackIdx - 2) )
|
|
{
|
|
debugStackIdx -= 2;
|
|
if(debugStackIdx > 0)
|
|
currentFunc = (SYMBOL *)*(debugStack + debugStackIdx - 1);
|
|
if(debugStackIdx == 0)
|
|
traceFlag &= ~TRACE_DEBUG_NEXT;
|
|
}
|
|
}
|
|
|
|
if(debugPrintFunction(cell))
|
|
{
|
|
varPrintf(OUT_CONSOLE, "\nRESULT: ");
|
|
printCell(result, TRUE, OUT_CONSOLE);
|
|
varPrintf(OUT_CONSOLE, "\n");
|
|
|
|
if(debugStackIdx > 0)
|
|
{
|
|
getDebuggerInput(DEBUG_EXIT);
|
|
if(!traceFlag) return;
|
|
}
|
|
}
|
|
|
|
if(traceFlag & TRACE_DEBUG_NEXT)
|
|
currentLevel = recursionCount;
|
|
#endif /* DEBUGGER */
|
|
|
|
traceFlag &= ~TRACE_IN_EXIT;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
void getDebuggerInput(char * msg)
|
|
{
|
|
char command[MAX_LINE];
|
|
char * context;
|
|
jmp_buf errorJumpSave;
|
|
UINT * resultStackIdxSave;
|
|
SYMBOL * contextSave;
|
|
|
|
while(TRUE)
|
|
{
|
|
|
|
if(currentContext != mainContext)
|
|
context = currentContext->name;
|
|
else context = "";
|
|
|
|
|
|
if(!evalSilent)
|
|
varPrintf(OUT_CONSOLE, "\n[%s %d %s]%s", msg, recursionCount, context, footerStr);
|
|
else evalSilent = FALSE;
|
|
|
|
if(fgets(command, MAX_LINE - 1, IOchannel) == NULL)
|
|
fatalError(ERR_IO_ERROR, 0, 0);
|
|
|
|
/* client and server could have different line-termination */
|
|
if(*(command + 1) == '\n' || *(command + 1) == '\r')
|
|
{
|
|
if(*command == 'n')
|
|
{
|
|
traceFlag |= TRACE_DEBUG_NEXT;
|
|
currentLevel = recursionCount;
|
|
break;
|
|
}
|
|
if(*command == 's')
|
|
{
|
|
traceFlag &= ~TRACE_DEBUG_NEXT;
|
|
break;
|
|
}
|
|
if(*command == 'q')
|
|
{
|
|
closeTrace();
|
|
longjmp(errorJump, ERR_USER_RESET);
|
|
}
|
|
if(*command == 'c')
|
|
{
|
|
closeTrace();
|
|
break;
|
|
}
|
|
}
|
|
|
|
resultStackIdxSave = resultStackIdx;
|
|
memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
|
|
contextSave = currentContext;
|
|
currentContext = currentFunc->context;
|
|
if(setjmp(errorJump))
|
|
{
|
|
cleanupResults(resultStackIdxSave);
|
|
goto DEBUG_EVAL_END;
|
|
}
|
|
|
|
executeCommandLine(command, OUT_CONSOLE, NULL);
|
|
|
|
DEBUG_EVAL_END:
|
|
currentContext = contextSave;
|
|
memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
|
|
}
|
|
}
|
|
|
|
|
|
int debugPrintFunction(CELL * cell)
|
|
{
|
|
int preLen, pos = 0;
|
|
char * strPos;
|
|
|
|
STREAM strStream = {NULL, NULL, 0, 0, 0};
|
|
|
|
if(currentFunc == nilSymbol) return FALSE;
|
|
|
|
debugPrintCell = cell;
|
|
openStrStream(&strStream, MAX_STRING, 0);
|
|
printSymbol(currentFunc, (UINT)&strStream);
|
|
debugPrintCell = NULL;
|
|
|
|
strPos = strstr(strStream.buffer, debugPreStr);
|
|
if(strPos != NULL)
|
|
{
|
|
preLen = strlen(debugPreStr);
|
|
while(*(strPos + preLen + pos) <= ' ' && *(strPos + preLen + pos) != 0) ++pos;
|
|
if(pos) /* if there is white space */
|
|
{
|
|
/* swap whitespace and debugPreStr */
|
|
strncpy(strPos, strPos + preLen, pos);
|
|
strncpy(strPos + pos, debugPreStr, preLen);
|
|
}
|
|
varPrintf(OUT_CONSOLE, "%s", headerStr);
|
|
varPrintf(OUT_CONSOLE, "%s", strStream.buffer);
|
|
}
|
|
|
|
closeStrStream(&strStream);
|
|
return (strPos != NULL);
|
|
}
|
|
|
|
#endif /* DEBUGGER */
|
|
/* eof */
|
|
|
|
|