1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Thu Feb 12 14:48:49 2009
19 ****************************************************************************/
20 /*
21 * Error package for editor.
22 */
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include "tkgate.h"
28 
29 #define ERRORHEADLINES 2
30 
31 #define ERROR_DEBUG 1
32 
33 int exit_flag = 0;		/* Flag to see if we are currently exiting */
34 
35 /*
36     Set the position of an error
37 */
SetErrorPosition(int x,int y)38 void SetErrorPosition(int x,int y)
39 {
40   ob_touch(TkGate.errl);
41   TkGate.ErrorMarkTimeout = 2;
42   TkGate.errl->ErrorXPos = x;
43   TkGate.errl->ErrorYPos = y;
44 }
45 
DrawErrorPositionMark()46 void DrawErrorPositionMark()
47 {
48   int x = TkGate.errl->ErrorXPos;
49   int y = TkGate.errl->ErrorYPos;
50 
51   line(x-20,y-20,x+20,y+20);
52   line(x-20,y+20,x+20,y-20);
53 }
54 
ClearErrorMark()55 void ClearErrorMark()
56 {
57   if (TkGate.ErrorMarkTimeout == 1) {
58     DrawErrorPositionMark();
59     TkGate.ErrorMarkTimeout = 0;
60   }
61 }
62 
63 
new_ErrorList()64 ErrorList *new_ErrorList()
65 {
66   ErrorList *el = (ErrorList*) ob_malloc(sizeof(ErrorList),"ErrorList");
67   el->errlist = 0;
68   el->curerr = 0;
69   el->errors = 0;
70   el->ErrorCount = 0;
71   el->ErrorXPos = 0;
72   el->ErrorYPos = 0;
73   el->el_esList = 0;
74   return el;
75 }
76 
77 /*****************************************************************************
78  *
79  * If 'line' is part of a script, return the object for that script
80  *
81  *****************************************************************************/
ErrorList_getScript(ErrorList * el,int line)82 static GEScript *ErrorList_getScript(ErrorList *el, int line)
83 {
84   GEScript *es;
85 
86   for (es = el->el_esList;es;es = es->es_next) {
87     if (line >= es->es_beginLine && (es->es_endLine == 0 || line <= es->es_endLine))
88       return es;
89   }
90 
91   return 0;
92 }
93 
94 
new_GError(const char * errText)95 static GateError *new_GError(const char *errText)
96 {
97   GateError *e = (GateError*) ob_malloc(sizeof(GateError),"GError");
98 
99   e->e_id = 0;
100   e->e_level = 0;
101   e->e_fileName = 0;
102   e->e_fileLine = 0;
103   e->e_modName = 0;
104   e->e_modLine = 0;
105   e->e_instName = 0;
106   e->e_message = 0;
107   e->e_inst = 0;
108   e->e_portName = 0;
109   e->e_next = e->e_last = 0;
110 
111   if (errText) {
112     char etype[STRMAX],fileName[STRMAX],modName[STRMAX],instName[STRMAX],portName[STRMAX],msg[STRMAX];
113     int fileLine, modLine;
114     GEScript *es;
115 
116 
117     if (sscanf(errText,"%s file %s %d %s %d %s %s : %[^\n]",
118 	       etype,fileName,&fileLine,modName,&modLine,instName,portName,msg) == 8) {
119 
120       es = ErrorList_getScript(TkGate.errl,fileLine);
121 
122       Error_decode(msg);
123 
124       if (strcmp(etype,"warning") == 0) {
125 	e->e_level = 0;
126       } else
127 	e->e_level = 1;
128 
129       e->e_fileName = ob_strdup(fileName);
130       e->e_fileLine = fileLine;
131       e->e_modName = ob_strdup(modName);
132       e->e_modLine = modLine;
133       e->e_instName = (*instName != '-') ? ob_strdup(instName) : 0;
134       e->e_portName = (*portName != '-') ? ob_strdup(portName) : 0;
135 
136       if (es) {
137 	char msg2[STRMAX];
138 
139 	sprintf(msg2,"script <%s> - line %d, %s",es->es_name, fileLine - es->es_beginLine + 1, msg);
140 
141 	e->e_instName = ob_strdup(es->es_name);
142 	e->e_message = ob_strdup(msg2);
143       } else
144 	e->e_message = ob_strdup(msg);
145     }
146   }
147 
148   return e;
149 }
150 
delete_GError(GateError * e)151 static void delete_GError(GateError *e)
152 {
153   if (e->e_fileName) ob_free(e->e_fileName);
154   if (e->e_modName) ob_free(e->e_modName);
155   if (e->e_instName) ob_free(e->e_instName);
156   if (e->e_portName) ob_free(e->e_portName);
157   if (e->e_message) ob_free(e->e_message);
158   ob_free(e);
159 }
160 
Error_add(GateError * e)161 static void Error_add(GateError *e)
162 {
163   char *prefix = (e->e_level == 0) ? "Warning, " : "";
164   GModuleDef *m;
165   char msgbuf[STRMAX];
166 
167   if (TkGate.errl->errors) {
168     DoTcl("ErrBox::unpost");
169   }
170 
171   if (!TkGate.errl->curerr) {
172     DoTcl("ErrBox::post");
173     ob_touch(TkGate.errl);
174     TkGate.errl->ErrorCount = 0;
175     TkGate.errl->errlist = TkGate.errl->curerr = e;
176     ob_touch(e);
177     ob_touch(TkGate.errl->errlist);
178 
179     e->e_id = TkGate.errl->ErrorCount = 1;
180     TkGate.errl->errlist->e_last = 0;
181     TkGate.errl->errlist->e_next = 0;
182   } else {
183     ob_touch(e);
184     ob_touch(TkGate.errl);
185     ob_touch(TkGate.errl->curerr);
186 
187     e->e_id = TkGate.errl->ErrorCount++;
188     TkGate.errl->curerr->e_next = e;
189     TkGate.errl->curerr->e_next->e_last = TkGate.errl->curerr;
190     TkGate.errl->curerr = TkGate.errl->curerr->e_next;
191     TkGate.errl->curerr->e_next = 0;
192   }
193 
194   if (e->e_modName)
195     m = env_findModule(e->e_modName);
196   else
197     m = 0;
198 
199   if (m && m->m_type == MT_TEXTHDL)
200     sprintf(msgbuf,"%s, line %d: %s%s",e->e_modName,e->e_modLine,prefix,e->e_message);
201   else
202     sprintf(msgbuf,"%s: %s%s",e->e_modName,prefix,e->e_message);
203 
204   DoTclL("ErrBox::addError",msgbuf,NULL);
205 }
206 
Error_report(const char * errText)207 void Error_report(const char *errText)
208 {
209   GateError *e = new_GError(errText);
210   Error_add(e);
211 }
212 
213 /*****************************************************************************
214  *
215  * Translate list format error list into an array for easy access.
216  *
217  *****************************************************************************/
Error_close()218 void Error_close()
219 {
220   GateError *e;
221   int i = 0;
222 
223   ob_touch(TkGate.errl);
224   TkGate.errl->errors = (GateError**) ob_malloc(sizeof(GateError*)*TkGate.errl->ErrorCount,"GError*[]");
225   for (e = TkGate.errl->errlist;e;e = e->e_next, i++) {
226     TkGate.errl->errors[i] = e;
227   }
228 }
229 
230 /*****************************************************************************
231  *
232  * Delete all messages from the error list
233  *
234  *****************************************************************************/
Error_purge()235 void Error_purge()
236 {
237   GateError *e;
238 
239   ob_touch(TkGate.errl);
240 
241   ClearErrorMark();
242   while (TkGate.errl->errlist) {
243     e = TkGate.errl->errlist;
244     TkGate.errl->errlist = TkGate.errl->errlist->e_next;
245     delete_GError(e);
246   }
247 
248   ob_free(TkGate.errl->errors);
249   TkGate.errl->errors = 0;
250   TkGate.errl->curerr = TkGate.errl->errlist = 0;
251 
252   TkGate.ErrorMarkTimeout = 0;
253   FlagRedraw();
254 }
255 
256 /*****************************************************************************
257  *
258  * Open the specified error in the current editstate.
259  *
260  * Parameter:
261  *      e		Error object to open
262  *      es		Current edit state
263  *
264  * Returns:		New edit state
265  *
266  *****************************************************************************/
Error_open(GateError * e,EditState * es)267 EditState *Error_open(GateError *e,EditState *es)
268 {
269   GModuleDef *m;
270   int x,y;
271 
272   if (!e) return es;				/* No error object specified */
273 
274   m = env_findModule(e->e_modName);		/* Find module the error ocurrs in */
275   if (!m) {
276     message(0,msgLookup("err.noerr"));
277     return es;
278   }
279 
280 
281   if (tkgate_currentMode() == MM_SIMULATE) {
282     SimInterface_navigateToModule(&es,e->e_modName);
283   } else {
284     /*
285      * Navigate to the appropriate module.
286      */
287     if (es->env != m) {
288       while (es)
289 	editstate_pop(&es);
290       editstate_push(&es,m,0);
291       editstate_setCurrent(es);
292       ClearErrorMark();
293       net_unselect(0);
294       cpath_reshow();
295       SetModified(MF_MODULE);
296     }
297   }
298 
299   /*
300    * For errors in HDL modules we just go to the offending line in the selected module.
301    */
302   if (m->m_type == MT_TEXTHDL) {
303     SynchronizeInterface();
304     FlagRedraw();
305 
306     DoTcl("HdlEditor::highlightLine %d",e->e_modLine);
307     return es;
308   }
309 
310   x = TkGate.width/2;
311   y = TkGate.height/2;
312 
313   if (e->e_instName) {
314     /*
315      * Error is on a specific instance, or on a port of that instance.
316      */
317     GCElement *g = GModuleDef_findGate(m,e->e_instName);
318     if (!g) return es;
319 
320     x = g->xpos;
321     y = g->ypos;
322     if (g->typeinfo->code == GC_BLOCK) {
323       x += g->u.block.gwidth/2;
324       y += g->u.block.gheight/2 + 10;
325     }
326 
327     if (e->e_portName) {
328       GWire *w = GCElement_getPort(g,e->e_portName);
329       if (w) {
330 	x = w->nodes->x;
331 	y = w->nodes->y;
332       }
333     }
334   } else if (e->e_portName) {
335     /*
336      * If no instance was specified, then e_portName is actually the name of a net.
337      */
338     GNet *net;
339     char *localName = strrchr(e->e_portName,'.');
340     if (!localName) localName = e->e_portName; else localName++;
341 
342     net = GModuleDef_findNet(m,localName);
343     if (net) {
344       x = net->n_driver->nodes->x;
345       y = net->n_driver->nodes->y;
346     }
347   } else {
348     /* don't know how to navigate to error */
349     return es;
350   }
351 
352   TkGate_setOrigin(TkGate.width/2 - x,TkGate.height/2 - y);
353   ob_touch(es);
354   editstate_saveOrig(es);
355   editstate_setCurrent(es);
356   SetErrorPosition(x,y);
357 
358   SynchronizeInterface();
359   FlagRedraw();
360 
361   return es;
362 }
363 
364 /*****************************************************************************
365  *
366  * Generate pointless messages if enabled.
367  *
368  * Parameters:
369  *      m		Argument for pointless messages
370  *
371  *
372  *
373  *****************************************************************************/
badermessage(const char * m)374 void badermessage(const char *m)
375 {
376   int which;
377 
378   which = random() & 63;
379   if (which > 7) return;
380 
381   /*
382    * Be mean to Miles Bader.
383    */
384   if (TkGate.baderp) {
385     switch (which) {
386     case 0 :
387       message(1,msgLookup("miles.msg1"),article(m),m);
388       return;
389     case 1 :
390       message(1,msgLookup("miles.msg2"),article(m),m);
391       return;
392     case 2 :
393       message(1,msgLookup("miles.msg3"),article(m),m);
394       return;
395     case 3 :
396       message(1,msgLookup("miles.msg4"),article(m),m);
397       return;
398     case 4 :
399       message(1,msgLookup("miles.msg5"),article(m),m);
400       return;
401     case 5 :
402       message(1,msgLookup("miles.msg6"),article(m),m);
403       return;
404     case 6 :
405       message(1,msgLookup("miles.msg7"));
406       return;
407     case 7 :
408       message(1,msgLookup("miles.msg8a"));
409       message(1,msgLookup("miles.msg8b"));
410       return;
411     }
412     return;
413   }
414 
415   /*
416    * In bat mode liven things up a little
417    */
418   if (TkGate.batp) {
419     switch (which) {
420     case 0 :
421       message(1,"** BIFF **");
422       return;
423     case 1 :
424       message(1,"** POW **");
425       return;
426     case 2 :
427       message(1,"** CRASH **");
428       return;
429     case 3 :
430       message(1,"** OOOF **");
431       return;
432     case 4 :
433       message(1,"** WHAM **");
434       return;
435     case 5 :
436       message(1,"** CRUNCH **");
437       return;
438     case 6 :
439       message(1,"** BANG **");
440       return;
441     case 7 :
442       message(1,"** SOCK **");
443       return;
444     default :
445       break;
446     }
447     return;
448   }
449 
450   /*
451    * Generate a star trek message.
452    */
453   if (TkGate.startrekp) {
454     switch (which) {
455     case 0 :
456       message(1,"Beam me up Scotty!");
457       return;
458     case 1 :
459       message(1,"Engage!");
460       return;
461     case 2 :
462       message(1,"He's dead Jim!");
463       return;
464     case 3 :
465       message(1,"Earl Grey Tea Hot!");
466       return;
467     case 4 :
468       message(1,"Every sixteenth hour, I turn into a liquid!");
469       return;
470     case 5 :
471       message(1,"Ugly bags of mostly water.");
472       return;
473     case 6 :
474       message(1,"Microbrain! Growl for me, let me know you still care!");
475       return;
476     case 7 :
477       message(1,"I'm a doctor, not an engineer!");
478       return;
479     default :
480       break;
481     }
482     return;
483   }
484 }
485 
lookupSignalName(int s)486 char *lookupSignalName(int s)
487 {
488   static char buf[16];
489 
490   switch (s) {
491   case SIGHUP : return "SIGHUP";
492   case SIGQUIT : return "SIGQUIT";
493   case SIGILL : return "SIGILL";
494   case SIGTRAP : return "SIGTRAP";
495   case SIGFPE : return "SIGFPE";
496   case SIGBUS : return "SIGBUS";
497   case SIGSEGV : return "SIGEGV";
498 #ifdef SIGEMT
499   case SIGEMT: return "SIGEMT";
500 #endif
501 #ifdef SIGSYS
502   case SIGSYS : return "SIGSYS";
503 #endif
504   default:
505     sprintf(buf,"UNKNOWN-%d",s);
506     return buf;
507   }
508 }
509 
510 
511 /*****************************************************************************
512  *
513  * Make a last ditch effort to save the users circuit in a panic file.
514  *
515  * Parameters:
516  *      s 			Signal number that caused panic
517  *
518  * This function is called when an internal problem causes an unrecoverable
519  * signal such as a segmentation fault to occur.  When called, an attempt will
520  * be made to save the current circuit to a file named "PANIC.v".  We try to
521  * use only the bare minimum of available functions to avoid causing a second
522  * panic while doing the save. For this reason, we deliberately avoid using
523  * localized messages and have the English messages hard coded for this function.
524  * If we do get a secondary call to panicSave(), then we give up.
525  *
526  *****************************************************************************/
panicSave(int s)527 void panicSave(int s)
528 {
529   static int in_panic_save = 0;
530 
531   if (exit_flag) {
532     /*
533      * We got a segfault, etc. while trying to exit TkGate.  Just go ahead and
534      * exit leaving a message to remind me about the problem.  The segfault seems
535      * to occur intermittently deep in the Tcl internals and I do not know exactly
536      * what is causing it.
537      */
538 
539 #if 0
540     printf("cheated death again!\n");
541 #endif
542     exit(0);
543   }
544 
545   ob_set_mode(OM_DISABLED);	/* Turn off undo/redo object management */
546 
547   /*
548    * If we get a panic while saving a panic file we will just have to give up.
549    */
550   if (in_panic_save) {
551     printf("**** Signal %s received during panic save.  Sorry...\n",lookupSignalName(s));
552     undoSignals();
553     abort();
554   }
555   in_panic_save = 1;
556 
557 
558   /*
559    * Print signal number if panic was caused by a signal.
560    */
561   if (s) {
562     printf("**** Signal %s received ****\n",lookupSignalName(s));
563   }
564 
565 
566   /*
567    * Tell the user the bad news.
568    */
569   if (TkGate.baderp) {
570     printf("Look what you did *now*, Miles!\n");
571     printf("Even though this is your fault, I'll try to save in 'PANIC.v' anyway.\n");
572   } else if (TkGate.startrekp) {
573     printf("Warp core breach in progress!  All hands abandon ship!\n");
574     printf("Launching escape pod 'PANIC.v'.\n");
575   } else {
576     printf("TkGate has encountered an unrecoverable error and must exit.  Before doing this\n");
577     printf("we will make one last attempt to save your circuit in a panic file.  If you have\n");
578     printf("checkpointing enabled, you can also try using your checkpoint file as a backup.\n\n");
579     printf("Attempting to save circuit in file 'PANIC.v'...\n");
580   }
581 
582   VerilogWriteModules("PANIC.v",VSO_NOHDLCHECK);
583 
584   if (TkGate.baderp) {
585     printf("Looks like I bailed you out once again Miles.\n");
586   } else if (TkGate.startrekp) {
587     printf("Escape pod is away.\n");
588   } else {
589     printf("Panic save completed.\n");
590   }
591 
592   undoSignals();
593   abort();
594 }
595 
596 /*****************************************************************************
597  *
598  * Test a pointer to see if it is an obviously bad address.
599  *
600  *****************************************************************************/
badaddr(void * S)601 int badaddr(void *S)
602 {
603   return ((intptr_t)S & 0x1) != 0;
604 }
605 
606 /*****************************************************************************
607  *
608  * Change the editstate/current module to a specified path.
609  *
610  *****************************************************************************/
Error_navigateToModule(EditState ** es,const char * path)611 void Error_navigateToModule(EditState **es,const char *path)
612 {
613   char buf[STRMAX],*T,*N;
614   GModuleDef *M;
615 
616   while (*es) editstate_pop(es);
617 
618   M = TkGate.circuit->root_mod;
619   strcpy(buf,path);
620   editstate_push(es,M,0);
621   for (T = strtok(buf,".");T;T = N) {
622     N = strtok(0,".");
623     if (N) {
624       GCElement *g = (GCElement*) SHash_find(M->m_gates,T);
625       if (g && GCElement_isModule(g)) {
626 	M = env_findModule(g->u.block.moduleName);
627 	editstate_push(es,M,g);
628       }
629     }
630   }
631   cpath_reshow();
632 }
633 
634 /*****************************************************************************
635  *
636  * Decode an error message.
637  *
638  * Parameters:
639  *      emsg			Error message to be decode.
640  *
641  *****************************************************************************/
Error_decode(char * emsg)642 void Error_decode(char *emsg)
643 {
644   char argbuf[STRMAX];
645   char name[STRMAX];
646   char *args[MAXARGS];
647   char *p;
648   int n = 0;
649   int i;
650 
651   /*
652    * If message does not start with a '`', then don't do any decoding.
653    */
654   if (*emsg != '`')
655     return;
656 
657 
658   strcpy(argbuf,emsg+1);
659 
660   /*
661    * Break the string in argbuf into arguments.  Adjust args[] to
662    * point to each argument and insert null characters between arguments.
663    */
664   for (p = argbuf;*p;p++) {
665     if (isgraph(*p)) {
666       args[n++] = p;
667       if (*p == '"') {
668 	p++;
669 	while (*p && *p != '"')
670 	  if (*p == '\\') {
671 	    p++;
672 	    if (*p) p++;
673 	  } else
674 	    p++;
675 	if (*p) p++;
676       } else {
677 	while (isgraph(*p)) p++;
678       }
679       if (!*p) break;
680       *p = 0;
681     }
682   }
683 
684   /*
685    * Unmarshall any quoted strings
686    */
687   for (i = 0;i < n;i++) {
688     if (*args[i] != '"') continue;
689 
690     args[i]++;
691     for (p = args[i];*p && *p != '"';p++)
692       if (*p == '\\')
693 	memmove(p,p+1,strlen(p));
694     *p = 0;
695   }
696 
697   if (n == 0) {
698     strcpy(emsg,"<null-message>");
699     return;
700   }
701 
702   /*
703    * Contruct the error message here.  We use a kludgy sprintf with all
704    * of the possible arguments even if we don't use most of them.
705    */
706   sprintf(name,"verga.err.%s",args[0]);
707   sprintf(emsg,msgLookup(name),args[1],args[2],args[3],args[4],args[5]
708 	  ,args[6],args[7],args[8],args[9],args[10],args[11],args[12]
709 	  ,args[13],args[14],args[15]);
710 #if MAXARGS != 16
711 #error Expecting MAXARGS to be 16 -- Fix code here if changing MAXARGS
712 #endif
713 }
714 
Error_scriptBegin(const char * name,int line)715 void Error_scriptBegin(const char *name,int line)
716 {
717   GEScript *es = (GEScript*) ob_malloc(sizeof(GEScript),"GEScript");
718 
719   es->es_name = ob_strdup(name);
720   es->es_beginLine = line;
721   es->es_endLine = 0;
722 
723   ob_touch(TkGate.errl);
724   es->es_next = TkGate.errl->el_esList;
725   TkGate.errl->el_esList = es;
726 }
727 
Error_scriptEnd(const char * name,int line)728 void Error_scriptEnd(const char *name,int line)
729 {
730   if (!TkGate.errl->el_esList) {
731     logError(ERL_ERROR,"got embedded script end <%s> but never got a begin.",name);
732     return;
733   }
734   if (strcmp(TkGate.errl->el_esList->es_name,name) != 0) {
735     logError(ERL_ERROR,"embedded script end <%s> does not match script begin <%s>.",name,TkGate.errl->el_esList->es_name);
736     return;
737   }
738   ob_touch(TkGate.errl->el_esList);
739   TkGate.errl->el_esList->es_endLine = line;
740 }
741 
742