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