/[ascend]/branches/relerrorlist/ascend/utilities/error.c
ViewVC logotype

Contents of /branches/relerrorlist/ascend/utilities/error.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3214 - (show annotations) (download) (as text)
Sun Sep 17 12:36:16 2017 UTC (4 weeks, 3 days ago) by jpye
File MIME type: text/x-csrc
File size: 16612 byte(s)
still hunting the bug with invisible errors in the PyGTK GUI.

1 #include <string.h>
2
3 #include "error.h"
4
5 #define ERROR_REPORTER_TREE_ACTIVE
6
7 #ifdef ERROR_REPORTER_TREE_ACTIVE
8 # include <ascend/general/ascMalloc.h>
9 # include <ascend/general/panic.h>
10 #endif
11
12 //#define ERROR_DEBUG
13 #ifdef ERROR_DEBUG
14 # define MSG CONSOLE_DEBUG
15 # define TREE_PRINT error_reporter_tree_print
16 #else
17 # define MSG(ARGS...) ((void)0)
18 # define TREE_PRINT(ARGS...) ((void)0)
19 #endif
20
21
22 /**
23 Global variable which stores the pointer to the callback
24 function being used.
25 */
26 static error_reporter_callback_t g_error_reporter_callback;
27
28 /**
29 Global variable which enables caching of *individual* error messages,
30 as used with error_reporter_start() and error_reporter_end_flush().
31 This is not the same as the caching performed by error_reporter_tree_start()
32 which holds/records multiple error messages depending on the iscaching
33 setting.
34 */
35 static error_reporter_meta_t g_error_reporter_cache;
36
37 /**
38 Default error reporter. This error reporter is used whenever the callback
39 pointer is NULL, but can be replaced with another callback (eg for GUI
40 reporting) using error_reporter_set_callback()
41 */
42 int error_reporter_default_callback(ERROR_REPORTER_CALLBACK_ARGS){
43 char *sevmsg="";
44 enum ConsoleColor color = 0;
45 char *endtxt="\n";
46 int res=0;
47 switch(sev){
48 case ASC_PROG_FATAL: color=ASC_FG_BRIGHTRED; sevmsg = "PROGRAM FATAL ERROR: "; break;
49 case ASC_PROG_ERROR: color=ASC_FG_RED; sevmsg = "PROGRAM ERROR: "; break;
50 case ASC_PROG_WARNING: color=ASC_FG_BROWN;sevmsg = "PROGRAM WARNING: "; break;
51 case ASC_PROG_NOTE: color=ASC_FG_BRIGHTGREEN; endtxt=""; break; /* default, keep unembellished for now */
52 case ASC_USER_ERROR: color=ASC_FG_BRIGHTRED; sevmsg = "ERROR: "; break;
53 case ASC_USER_WARNING: color=ASC_FG_BROWN; sevmsg = "WARNING: "; break;
54 case ASC_USER_NOTE: sevmsg = "NOTE: "; break;
55 case ASC_USER_SUCCESS: color=ASC_FG_BRIGHTGREEN; sevmsg = "SUCCESS: "; break;
56 }
57 color_on(ASCERR,color);
58 res = ASC_FPRINTF(ASCERR,"%s",sevmsg);
59 color_off(ASCERR);
60 if(filename!=NULL){
61 //MSG("filename = '%s'",filename);
62 res += ASC_FPRINTF(ASCERR,"%s:",filename);
63 }
64 if(line!=0)res += ASC_FPRINTF(ASCERR,"%d:",line);
65 if(funcname!=NULL){
66 //MSG("funcname = '%s'",funcname);
67 res += ASC_FPRINTF(ASCERR,"%s:",funcname);
68 }else{
69 //MSG("funcname NULL");
70 }
71 if ((filename!=NULL) || (line!=0) || (funcname!=NULL))res += ASC_FPRINTF(ASCERR," ");
72 res += ASC_VFPRINTF(ASCERR,fmt,*args);
73 res += ASC_FPRINTF(ASCERR,"%s",endtxt);
74 return res;
75 }
76
77 /*---------------------------------------------------------------------------
78 ERROR REPORTER TREE (BRANCHABLE ERROR STACK) FUNCTIONALITY
79 */
80
81 #ifdef ERROR_REPORTER_TREE_ACTIVE
82
83 static error_reporter_tree_t *g_error_reporter_tree_current = NULL;
84
85 static error_reporter_meta_t *error_reporter_meta_new(){
86 error_reporter_meta_t *e;
87 e = ASC_NEW(error_reporter_meta_t);
88 e->sev = ASC_USER_SUCCESS;
89 e->iscaching = 0;
90 e->filename = NULL;
91 e->func = NULL;
92 e->line = 0;
93 e->msg[0] = '\0';
94 return e;
95 }
96
97 # define CURRENT g_error_reporter_tree_current
98
99 static void error_reporter_tree_print(error_reporter_tree_t *t1);
100 static int error_reporter_tree_write(error_reporter_tree_t *t);
101 static void error_reporter_tree_free(error_reporter_tree_t *t);
102
103 /// deprecated function for testing purpose only
104 error_reporter_tree_t *error_reporter_get_tree_current(){
105 return CURRENT;
106 }
107
108 static error_reporter_tree_t *error_reporter_tree_new(int iscaching){
109 error_reporter_tree_t *tnew = ASC_NEW(error_reporter_tree_t);
110 tnew->iscaching = iscaching;
111 tnew->next = NULL;
112 tnew->prev = NULL;
113 tnew->head = NULL;
114 tnew->tail = NULL;
115 tnew->err = NULL;
116 tnew->parent = NULL;
117 TREE_PRINT(tnew);
118 return tnew;
119 }
120
121 static int error_reporter_tree_has_caching_parent(error_reporter_tree_t *t){
122 assert(t);
123 if(NULL == t->parent)return 0;
124 error_reporter_tree_t *t1 = t->parent;
125 int iscaching = 0;
126 while(t1){
127 iscaching = iscaching || t1->iscaching;
128 assert(t1->parent != t1);
129 t1 = t1->parent;
130 }
131 return iscaching;
132 }
133
134 #ifdef ERROR_DEBUG
135 static void error_reporter_tree_print1(error_reporter_tree_t *t,int level){
136 if(t->head){
137 assert(t->err == NULL);
138 MSG("%*s+%p%s (head=%p,tail=%p)",2+2*level,"",t,t==CURRENT?" (CURRENT)":"",t->head,t->tail);
139 error_reporter_tree_print1(t->head,level+1);
140 }else if(t->err){
141 assert(t->err != NULL);
142 MSG("%*s-%p %s:%d: %s (parent=%p,next=%p)",2+2*level,"",t,t->err->filename, t->err->line, t->err->msg,t->parent,t->next);
143 }else{
144 MSG("%*s-%p EMPTY NODE",2+2*level,"",t);
145 }
146
147 if(t->next){
148 error_reporter_tree_print1(t->next,level);
149 }
150 }
151
152
153 static void error_reporter_tree_print(error_reporter_tree_t *t1){
154 if(!t1){
155 MSG("null tree");
156 }else{
157 MSG("finding top of tree, starting at %p",t1);
158 while(t1->parent){
159 t1 = t1->parent;
160 }
161 MSG("top of tree is %p",t1);
162 error_reporter_tree_print1(t1,0);
163 }
164 }
165 #endif
166
167 error_reporter_tree_t *error_reporter_tree_start(int iscaching){
168 error_reporter_tree_t *tnew = error_reporter_tree_new(iscaching);
169 if(CURRENT == NULL){
170 MSG("creating tree (caching=%d)",iscaching);
171 CURRENT = tnew;
172 }else{
173 MSG("creating sub-tree (caching=%d)",iscaching);
174 if(CURRENT->head == NULL){
175 MSG("adding at head");
176 CURRENT->head = tnew;
177 }else{
178 MSG("adding at tail");
179 /* link the new tree to the last in the child list */
180 CURRENT->tail->next = tnew;
181 tnew->prev = CURRENT->tail;
182 }
183 /* update the tail of the list */
184 CURRENT->tail = tnew;
185 /* now switch the context to the sub-tree */
186 tnew->parent = CURRENT;
187 CURRENT = tnew;
188 }
189 return CURRENT;
190 }
191
192 int error_reporter_tree_end(error_reporter_tree_t *tree){
193 assert(CURRENT);
194 assert(tree==CURRENT);
195 if(CURRENT->iscaching){
196 if(error_reporter_tree_has_caching_parent(CURRENT)){
197 MSG("no output; caching parent");
198 }else{
199 MSG("outputting now, no caching parent");
200 error_reporter_tree_write(CURRENT);
201 }
202 }
203 if(CURRENT->parent){
204 MSG("ending sub-tree, NOT freeing structures");
205 CURRENT = CURRENT->parent;
206 }else{
207 MSG("ending top tree, freeing structures");
208 TREE_PRINT(CURRENT);
209 error_reporter_tree_free(CURRENT);
210 CURRENT = NULL;
211 }
212 return 0;
213 }
214
215 /** recursive routine to deallocate error_reporter_tree_t structures. */
216 static void error_reporter_tree_free(error_reporter_tree_t *t){
217 assert(t);
218 error_reporter_tree_t *n=NULL;
219 if(t->head){
220 assert(t->err == NULL);
221 MSG("freeing sub-nodes");
222 error_reporter_tree_free(t->head);
223 MSG("done freeing sub-nodes");
224 }
225 if(t->err){
226 MSG("freeing error metadata ('%s')",t->err->msg);
227 assert(t->head == NULL);
228 assert(t->tail == NULL);
229 ASC_FREE(t->err);
230 }
231 if(t->next)n = t->next;
232
233 MSG("freeing node %p%s",t,t==CURRENT?" (CURRENT)":"");
234 ASC_FREE(t);
235
236 if(n){
237 MSG("freeing next node %p",n);
238 error_reporter_tree_free(n);
239 }
240 }
241
242 /** clear all errors nested within the current tree -- they won't be visible
243 to any parent trees after this; they never happened. Must be called INSTEAD OF
244 error_reporter_tree_end(). */
245 void error_reporter_tree_end_clear(error_reporter_tree_t *tree){
246 assert(CURRENT);
247 assert(tree==CURRENT);
248 error_reporter_tree_t *t1 = NULL, *tp, *tn;
249 if(CURRENT->parent){
250 MSG("ending/clearing sub-tree %p",CURRENT);
251 TREE_PRINT(CURRENT);
252 t1 = CURRENT->parent;
253 tn = CURRENT->next;
254 tp = CURRENT->prev;
255 assert(tn==NULL); /* actually, we can't imagine a case where tn != NULL */
256 if(tn){
257 MSG("%p->prev = %p",tn,tp);
258 tn->prev = tp;
259 }
260 if(tp){
261 MSG("%p->next = %p",tp,tn);
262 tp->next = tn;
263 }
264 if(t1->head == CURRENT){
265 MSG("fixing head");
266 if(tp)t1->head = tp;
267 else if(tn)t1->head = tn;
268 else t1->head = NULL;
269 }
270 if(t1->tail == CURRENT){
271 MSG("fixing tail");
272 if(tp)t1->tail = tp;
273 else t1->tail = NULL;
274 }
275 MSG("after pruning");
276 TREE_PRINT(t1);
277 }else{
278 MSG("ending/clearing top tree");
279 }
280 error_reporter_tree_free(CURRENT);
281 CURRENT = t1;
282 MSG("completed tree_end_clear, new current:");
283 TREE_PRINT(CURRENT);
284 }
285
286 static int error_reporter_tree_match_sev(error_reporter_tree_t *t, unsigned match){
287 assert(t);
288 if(t->err && (t->err->sev & match)){
289 /* MSG("SEVERITY MATCH FOR t = %p",t); */
290 return 1;
291 }
292 if(t->next && error_reporter_tree_match_sev(t->next, match)){
293 /* MSG("SEVERITY MATCH IN 'next' FOR t = %p",t); */
294 return 1;
295 }
296 if(t->head && error_reporter_tree_match_sev(t->head, match)){
297 /* MSG("SEVERITTY MATCH IN 'head' FOR t = %p",t); */
298 return 1;
299 }
300 /* MSG("NO MATCH FOR t = %p",t); */
301 return 0;
302 }
303
304 /**
305 traverse the tree, looking for ASC_PROG_ERR, ASC_USER_ERROR, or ASC_PROG_FATAL
306 @return 1 if errors found
307 */
308 int error_reporter_tree_has_error(error_reporter_tree_t *tree){
309 int res;
310 assert(CURRENT);
311 assert(tree==CURRENT);
312 res = error_reporter_tree_match_sev(CURRENT,ASC_ERR_ERR);
313 if(res){
314 MSG("ERROR(S) FOUND IN CURRENT %p",CURRENT);
315 }
316 return res;
317 }
318
319 static int error_reporter_tree_write(error_reporter_tree_t *t){
320 int res = 0;
321 assert(t != NULL);
322
323 if(t->err){
324 // an a simple node -- just an error
325 assert(t->head == NULL);
326 assert(t->tail == NULL);
327 MSG("WRITING MSG FROM CACHE");
328 error_reporter_callback_t cb = g_error_reporter_callback ?
329 g_error_reporter_callback : error_reporter_default_callback;
330 res += (*cb)(t->err->sev,t->err->filename,t->err->line,t->err->func,t->err->msg,NULL);
331 }else{
332 // a parent node -- traverse the sub-nodes
333 assert(t->head);
334 assert(t->tail);
335 error_reporter_tree_t *t1 = t->head;
336 while(t1){
337 res += error_reporter_tree_write(t1);
338 t1 = t1->next;
339 }
340 }
341 return res;
342 }
343
344 #else /* ERROR_REPORTER_TREE_ACTIVE */
345 error_reporter_tree_t *error_reporter_tree_start(int iscaching){
346 (void)iscaching;
347 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Error reporter 'tree' turned off at compile time");
348 return NULL;
349 }
350 int error_reporter_tree_end(error_reporter_tree_t *tree){
351 assert(tree==NULL);
352 return 0;
353 }
354 void error_reporter_tree_end_clear(error_reporter_tree_t *tree){
355 assert(tree==NULL);
356 }
357 int error_reporter_tree_has_error(error_reporter_tree_t *tree){
358 assert(tree==NULL);
359 ERROR_REPORTER_HERE(ASC_PROG_FATAL,"Attempt to check 'tree_has_error' when 'tree' turned off at compile time");
360 return 0;
361 }
362 #endif /* ERROR_REPORTER_TREE_ACTIVE */
363
364 /*--------------------------
365 REALLY WHERE THE REPORTING HAPPENS
366 */
367
368 int
369 va_error_reporter(
370 const error_severity_t sev
371 , const char *errfile
372 , const int errline
373 , const char *errfunc
374 , const char *fmt
375 , va_list *args
376 ){
377 int res = 0;
378 va_list args2;
379
380 error_reporter_callback_t cb = g_error_reporter_callback;
381 if(cb == NULL){
382 //MSG("using default error reporter callback, errfunc = '%s'",errfunc);
383 cb = error_reporter_default_callback;
384 }
385
386 #ifdef ERROR_REPORTER_TREE_ACTIVE
387 error_reporter_tree_t *t;
388 if(sev != ASC_PROG_FATAL){
389 if(CURRENT){
390 va_copy(args2,*args);
391
392 MSG("adding to tree");
393 /* add the error to the tree, don't output anything now */
394 t = error_reporter_tree_new(0);
395 t->err = error_reporter_meta_new();
396 vsnprintf(t->err->msg,ERROR_REPORTER_MAX_MSG,fmt,*args);
397 MSG("t->err->msg = \"%s\"",t->err->msg);
398 t->err->filename = errfile;
399 t->err->func = errfunc;
400 t->err->line = errline;
401 t->err->sev = sev;
402 if(!CURRENT->head){
403 // current tree is empty
404 CURRENT->head = CURRENT->tail = t;
405 t->parent = CURRENT;
406 }else{
407 CURRENT->tail->next = t;
408 t->prev = CURRENT->tail;
409 CURRENT->tail = t;
410 t->parent = CURRENT;
411 }
412 MSG("message '%s' added to tree",t->err->msg);
413 //MSG("message has errfunc '%s'",t->err->func);
414
415 if(CURRENT->iscaching || error_reporter_tree_has_caching_parent(CURRENT)){
416 MSG("caching; no output");
417 return res;
418 }else{
419 MSG("non-caching; outputting again directly");
420 res = (*cb)(sev,errfile,errline,errfunc,fmt,&args2);
421 MSG("res = %d",res);
422 return res;
423 }
424 }else{
425 MSG("no error tree; direct output");
426 return (*cb)(sev,errfile,errline,errfunc,fmt,args);
427 }
428 }
429 #endif
430
431 MSG("fatal error message format = '%s'",fmt);
432 return (*cb)(sev,errfile,errline,errfunc,fmt,args);
433 }
434
435 /*----------------------------
436 DROP-IN replacements for stdio.h / ascPrint.h
437 */
438
439 int vfprintf_error_reporter(FILE *file, const char *fmt, va_list *args){
440 char *msg;
441 int len;
442 int res;
443 if(file==stderr){
444 if(g_error_reporter_cache.iscaching){
445 msg = g_error_reporter_cache.msg;
446 len = strlen(msg);
447 res = vsnprintf(msg+len,ERROR_REPORTER_MAX_MSG-len,fmt,*args);
448 //MSG("Appended \"%s\" to message",msg+len);
449 if(len+res+1>=ERROR_REPORTER_MAX_MSG){
450 SNPRINTF(msg+ERROR_REPORTER_MAX_MSG-16,15,"... (truncated)");
451 ASC_FPRINTF(stderr,"TRUNCATED MESSAGE, FULL MESSAGE FOLLOWS:\n----------START----------\n");
452 ASC_VFPRINTF(stderr,fmt,*args);
453 ASC_FPRINTF(stderr,"\n-----------END----------\n");
454 }
455 }else{
456 MSG("Reporting msg directly, fmt = \"%s\"",fmt);
457 /* Not caching: output all in one go as a ASC_PROG_NOTE */
458 res = va_error_reporter(ASC_PROG_NOTE,NULL,0,NULL,fmt,args);
459 }
460 }else{
461 MSG("Printing directly, fmt = \"%s\"",fmt);
462 res = ASC_VFPRINTF(file,fmt,*args);
463 }
464 return res;
465 }
466
467 /**
468 This function performs caching of the error text if the flag is set
469 */
470 int
471 fprintf_error_reporter(FILE *file, const char *fmt, ...){
472 va_list args;
473 int res;
474
475 va_start(args,fmt);
476 res = vfprintf_error_reporter(file,fmt,&args);
477 va_end(args);
478
479 return res;
480 }
481
482 int
483 fputc_error_reporter(int c, FILE *file){
484 if(file!=stderr){
485 return ASC_FPUTC(c,file);
486 }else if(fprintf_error_reporter(file,"%c",c) == 1){
487 return c;
488 }else{
489 return EOF;
490 }
491 }
492
493 int
494 fflush_error_reporter(FILE *file){
495 if(file!=stderr){
496 return ASC_FFLUSH(file);
497 }else{
498 return error_reporter_end_flush();
499 }
500 }
501
502 /*----------------------------
503 CACHING of multiple-FPRINTF error messages
504 */
505
506 int error_reporter_start(const error_severity_t sev, const char *filename
507 , const int line, const char *func
508 ){
509 if(g_error_reporter_cache.iscaching){
510 MSG("call to error_reporter_start before expected error_reporter_end_flush");
511 error_reporter_end_flush();
512 }
513 g_error_reporter_cache.iscaching = 1;
514 *(g_error_reporter_cache.msg) = '\0';
515 g_error_reporter_cache.sev = sev;
516 g_error_reporter_cache.filename = filename;
517 g_error_reporter_cache.line = line;
518 g_error_reporter_cache.func = func;
519
520 return 1;
521 }
522
523 /**
524 This function will flush the output of a multi-line error message
525 (as written with multiple calls to FPRINTF(stderr,...))
526 */
527 int error_reporter_end_flush(){
528 if(g_error_reporter_cache.iscaching){
529 error_reporter(
530 g_error_reporter_cache.sev
531 ,g_error_reporter_cache.filename
532 ,g_error_reporter_cache.line
533 ,g_error_reporter_cache.func
534 ,g_error_reporter_cache.msg
535 );
536 }else{
537 /* MSG("IGNORING REPEATED CALL TO error_reporter_end_flush()"); */
538 }
539 g_error_reporter_cache.iscaching = 0;
540
541 return 0; /* output must be compatible with fflush */
542 }
543
544 /*--------------------------
545 REPORT the error
546 */
547 int
548 error_reporter(
549 const error_severity_t sev
550 , const char *errfile
551 , const int errline
552 , const char *errfunc
553 , const char *fmt
554 , ...
555 ){
556 int res;
557 va_list args;
558
559 va_start(args,fmt);
560 res = va_error_reporter(sev,errfile,errline,errfunc,fmt,&args);
561 va_end(args);
562
563 return res;
564 }
565
566 /*-------------------------
567 SET the callback function
568 */
569 void error_reporter_set_callback(
570 const error_reporter_callback_t new_callback
571 ){
572 g_error_reporter_callback = new_callback;
573 }
574
575 /*-------------------------
576 OPTIONAL code for systems not supporting variadic macros.
577 Your system probably does support variadic macros, it's just
578 a question of checking what your particular syntax is, and possibly
579 adding some stuff in error.h.
580 */
581
582 #ifdef NO_VARIADIC_MACROS
583 /** error reporter for compilers not supporting variadic macros */
584 int error_reporter_note_no_line(const char *fmt,...){
585 int res;
586 va_list args;
587
588 va_start(args,fmt);
589 res = va_error_reporter(ASC_PROG_NOTE,"unknown-file",0,NULL,fmt,&args);
590 va_end(args);
591
592 return res;
593 }
594
595 /** error reporter 'here' for compilers not supporting variadic macros */
596 ASC_DLLSPEC int error_reporter_here(const error_severity_t sev, const char *fmt,...){
597 int res;
598 va_list args;
599
600 va_start(args,fmt);
601 res = va_error_reporter(sev,"unknown-file",0,NULL,fmt,&args);
602 va_end(args);
603
604 return res;
605 }
606
607 /** error reporter for compilers not supporting variadic macros */
608 int error_reporter_noline(const error_severity_t sev, const char *fmt,...){
609 int res;
610 va_list args;
611
612 va_start(args,fmt);
613 res = va_error_reporter(sev,NULL,0,NULL,fmt,&args);
614 va_end(args);
615
616 return res;
617 }
618
619 /** console debugging for compilers not supporting variadic macros */
620 int console_debug(const char *fmt,...){
621 int res;
622 va_list args;
623
624 va_start(args,fmt);
625 res = Asc_VFPrintf(ASCERR,fmt,args);
626 va_end(args);
627
628 return res;
629 }
630 #endif /* NO_VARIADIC_MACROS */

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22