Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Output cleanups for when the exception arises in a separate thread
[simgrid.git] / src / xbt / ex.c
1 /* $Id$ */
2
3 /* ex - Exception Handling (modified to fit into SimGrid from OSSP version) */
4
5 /*  Copyright (c) 2005, 2006, 2007 Martin Quinson                           */
6 /*  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>       */
7 /*  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>         */
8 /*  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>           */
9 /*  All rights reserved.                                                    */
10
11 /* This program is free software; you can redistribute it and/or modify it
12  * under the terms of the license (GNU LGPL) which comes with this package. */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include "portable.h" /* execinfo when available */
18 #include "xbt/ex.h"
19 #include "xbt/module.h" /* xbt_binary_name */
20 #include "xbt/synchro.h" /* xbt_thread_self */
21
22 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
23 #include "xbt/ex_interface.h"
24
25 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
26
27 /* default __ex_ctx callback function */
28 ex_ctx_t *__xbt_ex_ctx_default(void) {
29   /* Don't scream: this is a default which is never used (so, yes, 
30      there is one setjump container by running entity).
31
32      This default gets overriden in xbt/xbt_os_thread.c so that it works in
33      real life and in simulation when using threads to implement the simulation
34      processes (ie, with pthreads and on windows).
35
36      It also gets overriden in xbt/context.c when using ucontextes (as well as
37      in Java for now, but after the java overhaul, it will get cleaned out)
38   */
39     static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
40
41     return &ctx;
42 }
43
44 /* Change raw libc symbols to file names and line numbers */
45 void xbt_ex_setup_backtrace(xbt_ex_t *e);
46
47 void xbt_backtrace_current(xbt_ex_t *e) {
48 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
49   e->used     = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
50   e->bt_strings = NULL;
51   xbt_ex_setup_backtrace(e);
52 #endif
53 }
54
55 void xbt_backtrace_display(xbt_ex_t *e) {
56 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
57   int i;
58
59   if (e->used == 0) {
60      fprintf(stderr,"(backtrace not set)\n");
61   } else {      
62      fprintf(stderr,"Backtrace (displayed in thread %p):\n",
63              (void*)xbt_thread_self());
64      for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
65        fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
66   }
67    
68   /* don't fool xbt_ex_free with uninitialized msg field */
69   e->msg=NULL;
70   e->remote=0;
71   xbt_ex_free(*e);
72 #else 
73   ERROR0("No backtrace on this arch");
74 #endif
75 }
76
77 /** \brief show the backtrace of the current point (lovely while debuging) */
78 void xbt_backtrace_display_current(void) {
79   xbt_ex_t e;
80   xbt_backtrace_current(&e);
81   xbt_backtrace_display(&e);
82 }
83
84 #ifndef WIN32
85 extern char **environ; /* the environment, as specified by the opengroup */
86 #endif
87
88 void xbt_ex_setup_backtrace(xbt_ex_t *e)  {
89 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
90   int i;
91   /* to get the backtrace from the libc */
92   char **backtrace = backtrace_symbols (e->bt, e->used);
93   
94   /* To build the commandline of addr2line */
95   char *cmd, *curr;
96   
97   /* to extract the addresses from the backtrace */
98   char **addrs=xbt_new(char*,e->used);
99   char buff[256],*p;
100   
101   /* To read the output of addr2line */
102   FILE *pipe;
103   char line_func[1024],line_pos[1024];
104
105   /* size (in char) of pointers on this arch */
106   int addr_len=0;
107
108   /* To search for the right executable path when not trivial */
109   struct stat stat_buf;
110   char *binary_name = NULL;
111    
112   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
113   if (!e->used)
114      return;
115    
116   /* build the commandline */
117   if (stat(xbt_binary_name,&stat_buf)) {
118     /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
119     int i;
120     for (i=0; environ[i]; i++) {
121       if (!strncmp("PATH=",environ[i], 5)) {    
122         xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
123         unsigned int cpt;
124         char *data;
125         xbt_dynar_foreach(path, cpt, data) {
126           if (binary_name)
127             free(binary_name);
128           binary_name = bprintf("%s/%s",data,xbt_binary_name);
129           if (!stat(binary_name,&stat_buf)) {
130             /* Found. */
131             DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
132             xbt_dynar_free(&path);
133             break;
134           } 
135         }
136         if (stat(binary_name,&stat_buf)) {
137           /* not found */
138           e->used = 1;
139           e->bt_strings = xbt_new(char*,1);
140           e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
141           return;
142         }
143         xbt_dynar_free(&path);
144         break;
145       } 
146     }
147   } else {
148     binary_name = xbt_strdup(xbt_binary_name);
149   }      
150   cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
151    
152   curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
153   free(binary_name);
154    
155   for (i=0; i<e->used;i++) {
156     /* retrieve this address */
157     DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
158     snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
159     p=strchr(buff,']');
160     *p='\0';
161     if (strcmp(buff,"(nil)"))
162        addrs[i]=bprintf("%s", buff);
163     else
164        addrs[i]=bprintf("0x0");
165     DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
166      
167     /* Add it to the command line args */
168     curr+=sprintf(curr,"%s ",addrs[i]);
169   } 
170   addr_len = strlen(addrs[0]);
171
172   /* parse the output and build a new backtrace */
173   e->bt_strings = xbt_new(char*,e->used);
174   
175   VERB1("Fire a first command: '%s'", cmd);
176   pipe = popen(cmd, "r");
177   if (!pipe) {
178     CRITICAL0("Cannot fork addr2line to display the backtrace");
179     abort();
180   }
181
182   for (i=0; i<e->used; i++) {
183     DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]); 
184     fgets(line_func,1024,pipe);
185     line_func[strlen(line_func)-1]='\0';
186     fgets(line_pos,1024,pipe);
187     line_pos[strlen(line_pos)-1]='\0';
188
189     if (strcmp("??",line_func)) {
190       DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
191       e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
192     } else {
193       /* Damn. The symbol is in a dynamic library. Let's get wild */
194       char *maps_name;
195       FILE *maps;
196       char maps_buff[512];
197
198       long int addr,offset=0;
199       char *p,*p2;
200
201       char *subcmd;
202       FILE *subpipe;
203       int found=0;
204
205       /* let's look for the offset of this library in our addressing space */
206       maps_name=bprintf("/proc/%d/maps",(int)getpid());
207       maps=fopen(maps_name,"r");
208
209       sscanf(addrs[i],"%lx",&addr);
210       sprintf(maps_buff,"%#lx",addr);
211       
212       if (strcmp(addrs[i],maps_buff)) {
213         CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
214                   addrs[i], addr);
215       }
216       DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
217
218       while (!found) {
219         long int first, last;
220         if (fgets(maps_buff,512,maps) == NULL) 
221           break;
222         if (i==0) {
223           maps_buff[strlen(maps_buff) -1]='\0';
224           DEBUG1("map line: %s", maps_buff);
225         }
226         sscanf(maps_buff,"%lx",&first);
227         p=strchr(maps_buff,'-')+1;
228         sscanf(p,"%lx",&last);
229         if (first < addr && addr < last) {
230           offset = first;
231           found=1;
232         }
233         if (found) {          
234            DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
235            DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
236         }
237       }
238       fclose(maps);
239       free(maps_name);
240
241       if (!found) {
242         VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
243         DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
244         e->bt_strings[i] = bprintf("**   In ?? (%s)", backtrace[i]);
245         continue;
246       }
247
248       /* Ok, Found the offset of the maps line containing the searched symbol. 
249          We now need to substract this from the address we got from backtrace.
250       */
251       
252       free(addrs[i]);
253       addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
254       DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
255
256       /* Got it. We have our new address. Let's get the library path and we 
257          are set */ 
258       p  = xbt_strdup(backtrace[i]);
259       if (p[0]=='[') {
260          /* library path not displayed in the map file either... */
261          free(p);
262          sprintf(line_func,"??");
263       } else {
264          p2 = strrchr(p,'(');
265          if (p2) *p2= '\0';
266          p2 = strrchr(p,' ');
267          if(p2) *p2= '\0';
268       
269          /* Here we go, fire an addr2line up */
270          subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
271          free(p);
272          VERB1("Fire a new command: '%s'",subcmd);
273          subpipe = popen(subcmd,"r");
274          if (!subpipe) {
275             CRITICAL0("Cannot fork addr2line to display the backtrace");
276             abort();
277          }
278          fgets(line_func,1024,subpipe);
279          line_func[strlen(line_func)-1]='\0';
280          fgets(line_pos,1024,subpipe);
281          line_pos[strlen(line_pos)-1]='\0';
282          pclose(subpipe);
283          free(subcmd);
284       }
285
286       /* check whether the trick worked */
287       if (strcmp("??",line_func)) {
288         DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
289         e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
290       } else {
291         /* damn, nothing to do here. Let's print the raw address */
292         DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
293         e->bt_strings[i] = bprintf("**   In ?? at %s", backtrace[i]);
294       }
295       
296     }
297     free(addrs[i]);
298      
299     /* Mask the bottom of the stack */    
300     if (!strncmp("main",line_func,strlen("main")) ||
301         !strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
302        int j;
303        for (j=i+1; j<e->used; j++)
304          free(addrs[j]);
305        e->used = i;
306
307        if (!strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
308           e->used++;
309           e->bt_strings[i] = bprintf("**   (in a separate thread)");
310        }       
311     }
312      
313     
314   }
315   pclose(pipe);
316   free(addrs);
317   free(backtrace);
318   free(cmd);
319 #endif
320 }    
321
322 /** @brief shows an exception content and the associated stack if available */
323 void xbt_ex_display(xbt_ex_t *e)  {
324   char *thrower=NULL;
325
326   if (e->remote)
327     thrower = bprintf(" on host %s(%d)",e->host,e->pid);
328
329   fprintf(stderr,
330           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
331           "** %s\n"
332           "** Thrown by %s()%s\n",
333           gras_os_myname(),(*xbt_getpid)(),
334           xbt_ex_catname(e->category), e->value, e->msg,
335           e->procname,thrower?thrower:" in this process");
336   CRITICAL1("%s",e->msg);
337
338   if (thrower)
339     free(thrower);
340
341   if (!e->remote && !e->bt_strings)
342     xbt_ex_setup_backtrace(e);
343
344 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
345   /* We have everything to build neat backtraces */
346   {
347     int i;
348     
349     fprintf(stderr,"\n");
350     for (i=0; i<e->used; i++)
351       fprintf(stderr,"%s\n",e->bt_strings[i]);
352     
353   }
354 #else
355   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",  
356           e->file,e->line,e->func);
357 #endif
358   xbt_ex_free(*e);
359 }
360
361
362 /* default __ex_terminate callback function */
363 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
364   xbt_ex_display(e);
365
366   abort();
367 }
368
369 /* the externally visible API */
370 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
371 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
372
373
374 void xbt_ex_free(xbt_ex_t e) {
375   int i;
376
377   if (e.msg) free(e.msg);
378   if (e.remote) {
379     free(e.procname);
380     free(e.file);
381     free(e.func);
382     free(e.host);
383   }
384
385   if (e.bt_strings) {   
386      for (i=0; i<e.used; i++) 
387        free((char*)e.bt_strings[i]);
388      free((char **)e.bt_strings);
389   }
390   /* memset(e,0,sizeof(xbt_ex_t)); */
391 }
392
393 /** \brief returns a short name for the given exception category */
394 const char * xbt_ex_catname(xbt_errcat_t cat) {
395   switch (cat) {
396   case unknown_error:   return  "unknown_err";
397   case arg_error:       return "invalid_arg";
398   case mismatch_error:  return "mismatch";
399   case not_found_error: return "not found";
400   case system_error:    return "system_err";
401   case network_error:   return "network_err";
402   case timeout_error:   return "timeout";
403   case thread_error:    return "thread_err";
404   default:              return "INVALID_ERR";
405   }
406 }
407
408 #ifndef HAVE_EXECINFO_H
409 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
410 int backtrace (void **__array, int __size) {
411   return 0;
412 }
413
414 #endif
415
416 #ifdef SIMGRID_TEST
417 #include <stdio.h>
418 #include "xbt/ex.h"
419
420 XBT_TEST_SUITE("xbt_ex","Exception Handling");
421
422 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
423     xbt_ex_t ex;
424     volatile int n=1;
425
426     xbt_test_add0("basic nested control flow");
427
428     TRY {
429         if (n != 1)
430             xbt_test_fail1("M1: n=%d (!= 1)", n);
431         n++;
432         TRY {
433             if (n != 2)
434                 xbt_test_fail1("M2: n=%d (!= 2)", n);
435             n++;
436             THROW0(unknown_error,0,"something");
437         } CATCH (ex) {
438             if (n != 3)
439                 xbt_test_fail1("M3: n=%d (!= 3)", n);
440             n++;
441             xbt_ex_free(ex);
442         }
443         n++;
444         TRY {
445             if (n != 5)
446                 xbt_test_fail1("M2: n=%d (!= 5)", n);
447             n++;
448             THROW0(unknown_error,0,"something");
449         } CATCH (ex) {
450             if (n != 6)
451                 xbt_test_fail1("M3: n=%d (!= 6)", n);
452             n++;
453             RETHROW;
454             n++;
455         }
456         xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
457     }
458     CATCH(ex) {
459         if (n != 7)
460             xbt_test_fail1("M4: n=%d (!= 7)", n);
461         n++;
462         xbt_ex_free(ex);
463     }
464     if (n != 8)
465         xbt_test_fail1("M5: n=%d (!= 8)", n);
466 }
467
468 XBT_TEST_UNIT("value",test_value,"exception value passing") {
469     xbt_ex_t ex;
470
471     TRY {
472         THROW0(unknown_error, 2, "toto");
473     } CATCH(ex) {
474         xbt_test_add0("exception value passing");
475         if (ex.category != unknown_error)
476             xbt_test_fail1("category=%d (!= 1)", ex.category);
477         if (ex.value != 2)
478             xbt_test_fail1("value=%d (!= 2)", ex.value);
479         if (strcmp(ex.msg,"toto"))
480             xbt_test_fail1("message=%s (!= toto)", ex.msg);
481         xbt_ex_free(ex);
482     }
483 }
484
485 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
486     xbt_ex_t ex;
487     int r1, r2;
488     volatile int v1, v2;
489
490     r1 = r2 = v1 = v2 = 1234;
491     TRY {
492         r2 = 5678;
493         v2 = 5678;
494         THROW0(unknown_error, 0, "toto");
495     } CATCH(ex) {
496         xbt_test_add0("variable preservation");
497         if (r1 != 1234)
498             xbt_test_fail1("r1=%d (!= 1234)", r1);
499         if (v1 != 1234)
500             xbt_test_fail1("v1=%d (!= 1234)", v1);
501         /* r2 is allowed to be destroyed because not volatile */
502         if (v2 != 5678)
503             xbt_test_fail1("v2=%d (!= 5678)", v2);
504         xbt_ex_free(ex);
505     }
506 }
507
508 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
509     xbt_ex_t ex;
510     volatile int v1;
511     int c;
512
513     xbt_test_add0("cleanup handling");
514
515     v1 = 1234;
516     c = 0;
517     TRY {
518         v1 = 5678;
519         THROW0(1, 2, "blah");
520     } CLEANUP {
521         if (v1 != 5678)
522             xbt_test_fail1("v1 = %d (!= 5678)", v1);
523         c = 1;
524     } CATCH(ex) {
525         if (v1 != 5678)
526             xbt_test_fail1("v1 = %d (!= 5678)", v1);
527         if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
528             xbt_test_fail0("unexpected exception contents");
529         xbt_ex_free(ex);
530     }
531     if (!c)
532         xbt_test_fail0("xbt_ex_free not executed");
533 }
534
535
536 /*
537  * The following is the example included in the documentation. It's a good 
538  * idea to check its syntax even if we don't try to run it.
539  * And actually, it allows to put comments in the code despite doxygen.
540  */ 
541 static char *mallocex(int size) {
542   return NULL;
543 }
544 #define SMALLAMOUNT 10
545 #define TOOBIG 100000000
546
547 #if 0 /* this contains syntax errors, actually */
548 static void bad_example(void) {
549   struct {char*first;} *globalcontext;
550   ex_t ex;
551
552   /* BAD_EXAMPLE */
553   TRY {
554     char *cp1, *cp2, *cp3;
555     
556     cp1 = mallocex(SMALLAMOUNT);
557     globalcontext->first = cp1;
558     cp2 = mallocex(TOOBIG);
559     cp3 = mallocex(SMALLAMOUNT);
560     strcpy(cp1, "foo");
561     strcpy(cp2, "bar");
562   } CLEANUP {
563     if (cp3 != NULL) free(cp3);
564     if (cp2 != NULL) free(cp2);
565     if (cp1 != NULL) free(cp1);
566   } CATCH(ex) {
567     printf("cp3=%s", cp3);
568     RETHROW;
569   }
570   /* end_of_bad_example */
571 }
572 #endif
573 typedef struct {char *first;} global_context_t;
574    
575 static void good_example(void) {
576   global_context_t *global_context=malloc(sizeof(global_context_t));
577   xbt_ex_t ex;
578
579   /* GOOD_EXAMPLE */
580   { /*01*/
581     char * volatile /*03*/ cp1 = NULL /*02*/;
582     char * volatile /*03*/ cp2 = NULL /*02*/;
583     char * volatile /*03*/ cp3 = NULL /*02*/;
584     TRY {
585       cp1 = mallocex(SMALLAMOUNT);
586       global_context->first = cp1;
587       cp1 = NULL /*05 give away*/;
588       cp2 = mallocex(TOOBIG);
589       cp3 = mallocex(SMALLAMOUNT);
590       strcpy(cp1, "foo");
591       strcpy(cp2, "bar");
592     } CLEANUP { /*04*/
593       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
594       if (cp3 != NULL)
595         free(cp3);
596       if (cp2 != NULL)
597         free(cp2);
598       /*05 cp1 was given away */
599     } CATCH(ex) {
600       /*05 global context untouched */
601       RETHROW;
602     }
603   }
604   /* end_of_good_example */
605 }
606 #endif /* SIMGRID_TEST */