1 /* nl-debug.c --- debugging functions, and functions to trap signals and timers
2 
3     Copyright (C) 2016 Lutz Mueller
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 
21 #include "newlisp.h"
22 #include "protos.h"
23 
24 #ifdef WINDOWS
25 #define fgets win_fgets
26 #endif
27 
28 extern FILE * IOchannel;
29 extern int evalSilent;
30 
31 int matchString(char * buffer,  char * string, int * length);
32 int stringComp(char * buffer, char * string);
33 int debugPrintFunction(CELL * cell);
34 void getDebuggerInput(char * msg);
35 
36 extern SYMBOL * currentFunc;
37 extern SYMBOL * timerEvent;
38 extern SYMBOL * cilkEvent;
39 extern SYMBOL * symHandler[];
40 extern int currentSignal;
41 
42 int currentLevel = 0;
43 int debugStackIdx = 0;
44 UINT * debugStack;
45 UINT tracePrintDevice;
46 
47 char debugPreStr[8] = "#";
48 char debugPostStr[8] = "#";
49 CELL * debugPrintCell = NULL;
50 
51 char headerStr[16] = "\n-----\n\n";
52 char footerStr[32] = " s|tep n|ext c|ont q|uit > ";
53 
54 #define pushDebugStack(A) (*(debugStack + debugStackIdx++) = (UINT)(A))
55 #define popDebugStack() (*(debugStack + --debugStackIdx))
56 
57 #define DEBUG_ENTRY "->"
58 #define DEBUG_EXIT "<-"
59 
openTrace(void)60 void openTrace(void)
61     {
62     if(traceFlag) return;
63     traceFlag = TRACE_TRUE;
64     currentFunc = nilSymbol;
65     debugStackIdx = 0;
66     debugStack = (UINT *)allocMemory(MAX_CPU_STACK * 2 * sizeof(UINT));
67     }
68 
closeTrace(void)69 void closeTrace(void)
70     {
71     if(traceFlag & TRACE_PRINT_EVAL)
72         close(tracePrintDevice);
73     traceFlag = 0;
74     if(debugStack) free(debugStack);
75     debugStack = NULL;
76     }
77 
78 #ifdef DEBUGGER
p_debug(CELL * params)79 CELL * p_debug(CELL * params)
80 {
81 CELL * result;
82 
83 openTrace();
84 traceFlag |= TRACE_IN_DEBUG;
85 result = copyCell(evaluateExpression(params));
86 closeTrace();
87 
88 return(result);
89 }
90 #endif
91 
92 
p_trace(CELL * params)93 CELL * p_trace(CELL * params)
94 {
95 if(params != nilCell)
96     {
97     params = evaluateExpression(params);
98     if(isNumber(params->type))
99         {
100         traceFlag |= TRACE_PRINT_EVAL;
101         getIntegerExt(params, &tracePrintDevice, FALSE);
102         return(stuffInteger(tracePrintDevice));
103         }
104     if(!isNil(params))
105         {
106         openTrace();
107         traceFlag |= TRACE_IN_DEBUG;
108         }
109     else
110         closeTrace();
111     }
112 
113 if(traceFlag & TRACE_IN_DEBUG) return(trueCell);
114 if(traceFlag & TRACE_PRINT_EVAL) return(stuffInteger(tracePrintDevice));
115 return(nilCell);
116 }
117 
118 #ifdef DEBUGGER
p_traceHighlight(CELL * params)119 CELL * p_traceHighlight(CELL * params)
120 {
121 char * pre, * post, * header, * footer;
122 
123 params = getString(params, &pre);
124 params = getString(params, &post);
125 
126 strncpy(debugPreStr, pre, 8);
127 strncpy(debugPostStr, post, 8);
128 *(debugPreStr + 7) = 0;
129 *(debugPostStr + 7) = 0;
130 
131 if(params != nilCell)
132     {
133     params = getString(params, &header);
134     strncpy(headerStr, header, 16);
135     }
136 
137 if(params != nilCell)
138     {
139     getString(params, &footer);
140     strncpy(footerStr, footer, 32);
141     }
142 
143 *(headerStr + 15) = 0;
144 *(footerStr + 31) = 0;
145 
146 return(trueCell);
147 }
148 #endif /* DEBUGGER */
149 
tracePrint(char * label,CELL * expr)150 void tracePrint(char * label, CELL * expr)
151 {
152 UINT printDeviceSave = printDevice;
153 
154 printDevice = tracePrintDevice;
155 varPrintf(OUT_DEVICE, "%d %s: ", recursionCount, label);
156 if(expr) printCell(expr, TRUE, OUT_DEVICE);
157 varPrintf(OUT_DEVICE, "%s", "\n");
158 printDevice = printDeviceSave;
159 }
160 
traceEntry(CELL * cell,CELL * pCell,CELL * args)161 void traceEntry(CELL * cell, CELL * pCell, CELL * args)
162 {
163 if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_DEBUG_NEXT)) return;
164 traceFlag |= TRACE_IN_ENTRY;
165 
166 #ifdef DEBUGGER
167 int defaultFuncFlag = FALSE;
168 #endif
169 
170 if(traceFlag & TRACE_SIGNAL)
171     {
172     traceFlag &= ~TRACE_SIGNAL;
173     executeSymbol(symHandler[currentSignal - 1], stuffInteger(currentSignal), NULL);
174     traceFlag &= ~TRACE_IN_ENTRY;
175     return;
176     }
177 
178 if(traceFlag & TRACE_SIGINT)
179     {
180     traceFlag &= ~TRACE_SIGINT;
181     longjmp(errorJump, ERR_USER_RESET);
182     }
183 
184 if(traceFlag & TRACE_TIMER)
185     {
186     traceFlag &= ~TRACE_TIMER;
187     executeSymbol(timerEvent, NULL, NULL);
188     traceFlag &= ~TRACE_IN_ENTRY;
189     return;
190     }
191 
192 if(traceFlag & TRACE_PRINT_EVAL)
193     {
194     if(cell->type == CELL_EXPRESSION) tracePrint("entry", cell);
195     traceFlag &= ~TRACE_IN_ENTRY;
196     return;
197     }
198 
199 #ifdef DEBUGGER
200 if(debugStackIdx > 1)
201     {
202     if(debugPrintFunction(cell))
203         getDebuggerInput(DEBUG_ENTRY);
204     if(!traceFlag) return;
205     }
206 
207 if(traceFlag & TRACE_DEBUG_NEXT)
208     {
209     traceFlag &= ~TRACE_IN_ENTRY;
210     return;
211     }
212 
213 if(pCell->type == CELL_CONTEXT)
214     {
215     defaultFuncFlag = TRUE;
216     currentFunc = translateCreateSymbol(
217             ((SYMBOL*)pCell->contents)->name,
218             CELL_NIL,
219             (SYMBOL*)pCell->contents,
220             TRUE);
221 
222     pCell = (CELL *)currentFunc->contents;
223     }
224 
225 if((pCell->type == CELL_LAMBDA || pCell->type == CELL_FEXPR)
226     && args->type == CELL_SYMBOL)
227     {
228     if(debugStackIdx == 0) /* startup */
229         traceFlag &= ~TRACE_DEBUG_NEXT;
230 
231     if(!defaultFuncFlag)
232         currentFunc = (SYMBOL *)args->contents;
233     pushDebugStack(recursionCount);
234     pushDebugStack(currentFunc);
235     }
236 #endif /* no_DEBUG */
237 
238 traceFlag &= ~TRACE_IN_ENTRY;
239 }
240 
241 
traceExit(CELL * result,CELL * cell,CELL * pCell,CELL * args)242 void traceExit(CELL * result, CELL * cell, CELL * pCell, CELL * args)
243 {
244 if(traceFlag & (TRACE_IN_ENTRY | TRACE_IN_EXIT | TRACE_SIGNAL | TRACE_SIGINT | TRACE_TIMER)) return;
245 
246 if(traceFlag == TRACE_PRINT_EVAL)
247     {
248     tracePrint("exit", result);
249     return;
250     }
251 
252 traceFlag |= TRACE_IN_EXIT;
253 
254 #ifdef DEBUGGER
255 if(traceFlag & TRACE_DEBUG_NEXT)
256     {
257     if(currentLevel >= recursionCount)
258         traceFlag &= ~TRACE_DEBUG_NEXT;
259     else
260         {
261         traceFlag &= ~TRACE_IN_EXIT;
262         return;
263         }
264     }
265 
266 if( (pCell->type == CELL_LAMBDA || pCell->type == CELL_FEXPR)
267         && args->type == CELL_SYMBOL)
268     {
269     if((UINT)recursionCount == *(debugStack + debugStackIdx - 2) )
270         {
271         debugStackIdx -= 2;
272         if(debugStackIdx > 0)
273             currentFunc = (SYMBOL *)*(debugStack + debugStackIdx - 1);
274         if(debugStackIdx == 0)
275             traceFlag &= ~TRACE_DEBUG_NEXT;
276         }
277     }
278 
279 if(debugPrintFunction(cell))
280     {
281     varPrintf(OUT_CONSOLE, "\nRESULT: ");
282     printCell(result, TRUE, OUT_CONSOLE);
283     varPrintf(OUT_CONSOLE, "\n");
284 
285     if(debugStackIdx > 0)
286         {
287         getDebuggerInput(DEBUG_EXIT);
288         if(!traceFlag) return;
289         }
290     }
291 
292 if(traceFlag & TRACE_DEBUG_NEXT)
293     currentLevel = recursionCount;
294 #endif /* DEBUGGER */
295 
296 traceFlag &= ~TRACE_IN_EXIT;
297 }
298 
299 #ifdef DEBUGGER
getDebuggerInput(char * msg)300 void getDebuggerInput(char * msg)
301 {
302 char command[MAX_LINE];
303 char * context;
304 jmp_buf errorJumpSave;
305 UINT * resultStackIdxSave;
306 SYMBOL * contextSave;
307 
308 while(TRUE)
309     {
310 
311     if(currentContext != mainContext)
312         context = currentContext->name;
313     else context = "";
314 
315 
316     if(!evalSilent)
317         varPrintf(OUT_CONSOLE, "\n[%s %d %s]%s", msg, recursionCount, context, footerStr);
318     else evalSilent = FALSE;
319 
320     if(fgets(command, MAX_LINE - 1, IOchannel) == NULL)
321         fatalError(ERR_IO_ERROR, 0, 0);
322 
323     /* client and server could have different line-termination */
324     if(*(command + 1) == '\n' || *(command + 1) == '\r')
325         {
326         if(*command == 'n')
327             {
328             traceFlag |= TRACE_DEBUG_NEXT;
329             currentLevel = recursionCount;
330             break;
331             }
332         if(*command == 's')
333             {
334             traceFlag &= ~TRACE_DEBUG_NEXT;
335             break;
336             }
337         if(*command == 'q')
338             {
339             closeTrace();
340             longjmp(errorJump, ERR_USER_RESET);
341             }
342         if(*command == 'c')
343             {
344             closeTrace();
345             break;
346             }
347         }
348 
349     resultStackIdxSave = resultStackIdx;
350     memcpy(errorJumpSave, errorJump, sizeof(jmp_buf));
351     contextSave = currentContext;
352     currentContext = currentFunc->context;
353     if(setjmp(errorJump))
354         {
355         cleanupResults(resultStackIdxSave);
356         goto DEBUG_EVAL_END;
357         }
358 
359     executeCommandLine(command, OUT_CONSOLE, NULL);
360 
361     DEBUG_EVAL_END:
362     currentContext = contextSave;
363     memcpy(errorJump, errorJumpSave, sizeof(jmp_buf));
364     }
365 }
366 
367 
debugPrintFunction(CELL * cell)368 int debugPrintFunction(CELL * cell)
369 {
370 int preLen, pos = 0;
371 char * strPos;
372 
373 STREAM strStream = {NULL, NULL, 0, 0, 0};
374 
375 if(currentFunc == nilSymbol) return FALSE;
376 
377 debugPrintCell = cell;
378 openStrStream(&strStream, MAX_STRING, 0);
379 printSymbol(currentFunc, (UINT)&strStream);
380 debugPrintCell = NULL;
381 
382 strPos = strstr(strStream.buffer, debugPreStr);
383 if(strPos != NULL)
384     {
385     preLen = strlen(debugPreStr);
386     while(*(strPos + preLen + pos) <= ' ' && *(strPos + preLen + pos) != 0) ++pos;
387     if(pos) /* if there is white space */
388         {
389         /* swap whitespace and debugPreStr */
390         strncpy(strPos, strPos + preLen, pos);
391         strncpy(strPos + pos, debugPreStr, preLen);
392         }
393     varPrintf(OUT_CONSOLE, "%s", headerStr);
394     varPrintf(OUT_CONSOLE, "%s", strStream.buffer);
395     }
396 
397 closeStrStream(&strStream);
398 return (strPos != NULL);
399 }
400 
401 #endif /* DEBUGGER */
402 /* eof */
403 
404 
405