Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add the backtrace in the table of string only if the symbol and the line information...
[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/str.h"
20 #include "xbt/module.h" /* xbt_binary_name */
21 #include "xbt/synchro.h" /* xbt_thread_self */
22
23 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
24 #include "xbt/ex_interface.h"
25
26 #if (defined(WIN32) && defined(_M_IX86))
27 #include <dbghelp.h>
28 #endif
29
30 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
31
32 #if (defined(WIN32) && defined(_M_IX86))
33
34 /* 
35  * Win32 (x86) implementation backtrace, backtrace_symbols, backtrace_symbols_fd
36  * : support for application self-debugging.
37  */
38
39 /* Pointer function to SymInitialize() */
40 typedef BOOL (WINAPI *xbt_pfn_sym_initialize_t)(HANDLE, PSTR , BOOL);
41
42 /* Pointer function to SymCleanup() */
43 typedef BOOL (WINAPI *xbt_pfn_sym_cleanup_t)(HANDLE hProcess);
44
45 /* Pointer function to SymFunctionTableAccess() */
46 typedef PVOID (WINAPI *xbt_pfn_sym_function_table_access_t)(HANDLE, DWORD);
47
48 /* Pointer function to SymGetLineFromAddr() */
49 typedef BOOL (WINAPI *xbt_pfn_sym_get_line_from_addr_t)(HANDLE, DWORD, PDWORD , PIMAGEHLP_LINE);
50
51 /* Pointer function to SymGetModuleBase() */
52 typedef DWORD (WINAPI *xbt_pfn_sym_get_module_base_t)(HANDLE,DWORD);
53
54 /* Pointer function to SymGetOptions() */
55 typedef DWORD (WINAPI *xbt_pfn_sym_get_options_t)(VOID);
56
57 /* Pointer function to SymGetSymFromAddr() */
58 typedef BOOL (WINAPI *xbt_pfn_sym_get_sym_from_addr_t)(HANDLE, DWORD, PDWORD , OUT PIMAGEHLP_SYMBOL);
59
60 /* Pointer function to SymSetOptions() */
61 typedef DWORD (WINAPI *xbt_pfn_sym_set_options_t)(DWORD);
62
63 /* Pointer function to StackWalk() */
64 typedef BOOL (WINAPI *xbt_pfn_stack_walk_t)(DWORD, HANDLE, HANDLE ,LPSTACKFRAME, PVOID,PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
65
66 /* This structure representes the debug_help library used interface */
67 typedef struct s_xbt_debug_help
68 {
69         HINSTANCE instance;
70         HANDLE process_handle;                                                                                  
71         xbt_pfn_sym_initialize_t sym_initialize;
72         xbt_pfn_sym_cleanup_t sym_cleanup;
73         xbt_pfn_sym_function_table_access_t sym_function_table_access;
74         xbt_pfn_sym_get_line_from_addr_t sym_get_line_from_addr;
75         xbt_pfn_sym_get_module_base_t sym_get_module_base;
76         xbt_pfn_sym_get_options_t sym_get_options;
77         xbt_pfn_sym_get_sym_from_addr_t sym_get_sym_from_addr;
78         xbt_pfn_sym_set_options_t sym_set_options;
79         xbt_pfn_stack_walk_t stack_walk;
80 }s_xbt_debug_hlp_t,* xbt_debug_hlp_t;
81
82
83 /* the address to the unique reference to the debug help library interface */
84 static xbt_debug_hlp_t 
85 dbg_hlp = NULL;
86
87 /* initialize the debug help library */
88 static int
89 dbg_hlp_init(HANDLE process_handle);
90
91 /* finalize the debug help library */
92 static int
93 dbg_hlp_finalize(void);
94
95 /*
96  * backtrace() function.
97  *
98  * Returns a backtrace for the calling program, in  the  array
99  * pointed  to  by  buffer.  A backtrace is the series of currently active
100  * function calls for the program.  Each item in the array pointed  to  by
101  * buffer  is  of  type  void *, and is the return address from the corre-
102  * sponding stack frame.  The size argument specifies the  maximum  number
103  * of  addresses that can be stored in buffer.  If the backtrace is larger
104  * than size, then the addresses corresponding to  the  size  most  recent
105  * function  calls  are  returned;  to obtain the complete backtrace, make
106  * sure that buffer and size are large enough.
107  */
108
109 int 
110 backtrace (void **buffer, int size);
111
112 /*
113  * backtrace_symbols() function.
114  *
115  * Given the set of addresses returned by  backtrace()  in  buffer,  back-
116  * trace_symbols()  translates the addresses into an array of strings containing
117  * the name, the source file and the line number or the las called functions.
118  */
119 char ** 
120 backtrace_symbols (void *const *buffer, int size);
121
122 /*
123  * backtrace_symbols_fd() function.
124  *
125  * Same as backtrace_symbols() function but, the the array of strings is wrotten
126  * in a file (fd is the file descriptor of this file)
127  */
128 void 
129 backtrace_symbols_fd(void *const *buffer, int size, int fd);
130 #endif
131
132 /* default __ex_ctx callback function */
133 ex_ctx_t *__xbt_ex_ctx_default(void) {
134   /* Don't scream: this is a default which is never used (so, yes, 
135      there is one setjump container by running entity).
136
137      This default gets overriden in xbt/xbt_os_thread.c so that it works in
138      real life and in simulation when using threads to implement the simulation
139      processes (ie, with pthreads and on windows).
140
141      It also gets overriden in xbt/context.c when using ucontextes (as well as
142      in Java for now, but after the java overhaul, it will get cleaned out)
143   */
144     static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
145
146     return &ctx;
147 }
148
149 /* Change raw libc symbols to file names and line numbers */
150 void xbt_ex_setup_backtrace(xbt_ex_t *e);
151
152 void xbt_backtrace_current(xbt_ex_t *e) {
153 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE) || (defined(WIN32) && defined(_M_IX86))
154   e->used     = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
155   e->bt_strings = NULL;
156   xbt_ex_setup_backtrace(e);
157 #endif
158 }
159
160 void xbt_backtrace_display(xbt_ex_t *e) {
161 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE) || (defined(WIN32) && defined(_M_IX86))
162   int i;
163
164   if (e->used == 0) {
165      fprintf(stderr,"(backtrace not set)\n");
166   } else {      
167      fprintf(stderr,"Backtrace (displayed in thread %p):\n",
168              (void*)xbt_thread_self());
169      for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
170        fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
171   }
172    
173   /* don't fool xbt_ex_free with uninitialized msg field */
174   e->msg=NULL;
175   e->remote=0;
176   xbt_ex_free(*e);
177 #else 
178
179   ERROR0("No backtrace on this arch");
180 #endif
181 }
182
183 /** \brief show the backtrace of the current point (lovely while debuging) */
184 void xbt_backtrace_display_current(void) {
185   xbt_ex_t e;
186   xbt_backtrace_current(&e);
187   xbt_backtrace_display(&e);
188 }
189
190 #ifndef WIN32
191 extern char **environ; /* the environment, as specified by the opengroup */
192 #endif
193
194 void xbt_ex_setup_backtrace(xbt_ex_t *e)  {
195 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
196   int i;
197   /* to get the backtrace from the libc */
198   char **backtrace = backtrace_symbols (e->bt, e->used);
199   
200   /* To build the commandline of addr2line */
201   char *cmd, *curr;
202   
203   /* to extract the addresses from the backtrace */
204   char **addrs=xbt_new(char*,e->used);
205   char buff[256],*p;
206   
207   /* To read the output of addr2line */
208   FILE *pipe;
209   char line_func[1024],line_pos[1024];
210
211   /* size (in char) of pointers on this arch */
212   int addr_len=0;
213
214   /* To search for the right executable path when not trivial */
215   struct stat stat_buf;
216   char *binary_name = NULL;
217    
218   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
219   if (!e->used)
220      return;
221    
222   /* build the commandline */
223   if (stat(xbt_binary_name,&stat_buf)) {
224     /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
225     int i;
226     for (i=0; environ[i]; i++) {
227       if (!strncmp("PATH=",environ[i], 5)) {    
228         xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
229         unsigned int cpt;
230         char *data;
231         xbt_dynar_foreach(path, cpt, data) {
232           if (binary_name)
233             free(binary_name);
234           binary_name = bprintf("%s/%s",data,xbt_binary_name);
235           if (!stat(binary_name,&stat_buf)) {
236             /* Found. */
237             DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
238             xbt_dynar_free(&path);
239             break;
240           } 
241         }
242         if (stat(binary_name,&stat_buf)) {
243           /* not found */
244           e->used = 1;
245           e->bt_strings = xbt_new(char*,1);
246           e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
247           return;
248         }
249         xbt_dynar_free(&path);
250         break;
251       } 
252     }
253   } else {
254     binary_name = xbt_strdup(xbt_binary_name);
255   }      
256   cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
257    
258   curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
259   free(binary_name);
260    
261   for (i=0; i<e->used;i++) {
262     /* retrieve this address */
263     DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
264     snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
265     p=strchr(buff,']');
266     *p='\0';
267     if (strcmp(buff,"(nil)"))
268        addrs[i]=bprintf("%s", buff);
269     else
270        addrs[i]=bprintf("0x0");
271     DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
272      
273     /* Add it to the command line args */
274     curr+=sprintf(curr,"%s ",addrs[i]);
275   } 
276   addr_len = strlen(addrs[0]);
277
278   /* parse the output and build a new backtrace */
279   e->bt_strings = xbt_new(char*,e->used);
280   
281   VERB1("Fire a first command: '%s'", cmd);
282   pipe = popen(cmd, "r");
283   if (!pipe) {
284     CRITICAL0("Cannot fork addr2line to display the backtrace");
285     abort();
286   }
287
288   for (i=0; i<e->used; i++) {
289     DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]); 
290     fgets(line_func,1024,pipe);
291     line_func[strlen(line_func)-1]='\0';
292     fgets(line_pos,1024,pipe);
293     line_pos[strlen(line_pos)-1]='\0';
294
295     if (strcmp("??",line_func)) {
296       DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
297       e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
298     } else {
299       /* Damn. The symbol is in a dynamic library. Let's get wild */
300       char *maps_name;
301       FILE *maps;
302       char maps_buff[512];
303
304       long int addr,offset=0;
305       char *p,*p2;
306
307       char *subcmd;
308       FILE *subpipe;
309       int found=0;
310
311       /* let's look for the offset of this library in our addressing space */
312       maps_name=bprintf("/proc/%d/maps",(int)getpid());
313       maps=fopen(maps_name,"r");
314
315       sscanf(addrs[i],"%lx",&addr);
316       sprintf(maps_buff,"%#lx",addr);
317       
318       if (strcmp(addrs[i],maps_buff)) {
319         CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
320                   addrs[i], addr);
321       }
322       DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
323
324       while (!found) {
325         long int first, last;
326         if (fgets(maps_buff,512,maps) == NULL) 
327           break;
328         if (i==0) {
329           maps_buff[strlen(maps_buff) -1]='\0';
330           DEBUG1("map line: %s", maps_buff);
331         }
332         sscanf(maps_buff,"%lx",&first);
333         p=strchr(maps_buff,'-')+1;
334         sscanf(p,"%lx",&last);
335         if (first < addr && addr < last) {
336           offset = first;
337           found=1;
338         }
339         if (found) {          
340            DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
341            DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
342         }
343       }
344       fclose(maps);
345       free(maps_name);
346
347       if (!found) {
348         VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
349         DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
350         e->bt_strings[i] = bprintf("**   In ?? (%s)", backtrace[i]);
351         continue;
352       }
353
354       /* Ok, Found the offset of the maps line containing the searched symbol. 
355          We now need to substract this from the address we got from backtrace.
356       */
357       
358       free(addrs[i]);
359       addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
360       DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
361
362       /* Got it. We have our new address. Let's get the library path and we 
363          are set */ 
364       p  = xbt_strdup(backtrace[i]);
365       if (p[0]=='[') {
366          /* library path not displayed in the map file either... */
367          free(p);
368          sprintf(line_func,"??");
369       } else {
370          p2 = strrchr(p,'(');
371          if (p2) *p2= '\0';
372          p2 = strrchr(p,' ');
373          if(p2) *p2= '\0';
374       
375          /* Here we go, fire an addr2line up */
376          subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
377          free(p);
378          VERB1("Fire a new command: '%s'",subcmd);
379          subpipe = popen(subcmd,"r");
380          if (!subpipe) {
381             CRITICAL0("Cannot fork addr2line to display the backtrace");
382             abort();
383          }
384          fgets(line_func,1024,subpipe);
385          line_func[strlen(line_func)-1]='\0';
386          fgets(line_pos,1024,subpipe);
387          line_pos[strlen(line_pos)-1]='\0';
388          pclose(subpipe);
389          free(subcmd);
390       }
391
392       /* check whether the trick worked */
393       if (strcmp("??",line_func)) {
394         DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
395         e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
396       } else {
397         /* damn, nothing to do here. Let's print the raw address */
398         DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
399         e->bt_strings[i] = bprintf("**   In ?? at %s", backtrace[i]);
400       }
401       
402     }
403     free(addrs[i]);
404      
405     /* Mask the bottom of the stack */    
406     if (!strncmp("main",line_func,strlen("main")) ||
407         !strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
408        int j;
409        for (j=i+1; j<e->used; j++)
410          free(addrs[j]);
411        e->used = i;
412
413        if (!strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
414           e->used++;
415           e->bt_strings[i] = bprintf("**   (in a separate thread)");
416        }       
417     }
418      
419     
420   }
421   pclose(pipe);
422   free(addrs);
423   free(backtrace);
424   free(cmd);
425 #elif (defined(WIN32) && defined (_M_IX86))
426 int i;
427 char **backtrace = backtrace_symbols (e->bt, e->used);
428   
429   /* parse the output and build a new backtrace */
430   e->bt_strings = xbt_new(char*,e->used);
431   
432
433   for(i=0; i<e->used; i++) 
434   {
435       e->bt_strings[i] = strdup(backtrace[i]);
436       free(backtrace[i]);
437   }
438   
439   free(backtrace);
440 #endif
441 }    
442
443 /** @brief shows an exception content and the associated stack if available */
444 void xbt_ex_display(xbt_ex_t *e)  {
445   char *thrower=NULL;
446
447   if (e->remote)
448     thrower = bprintf(" on host %s(%d)",e->host,e->pid);
449
450   fprintf(stderr,
451           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
452           "** %s\n"
453           "** Thrown by %s()%s\n",
454           gras_os_myname(),(*xbt_getpid)(),
455           xbt_ex_catname(e->category), e->value, e->msg,
456           e->procname,thrower?thrower:" in this process");
457   CRITICAL1("%s",e->msg);
458
459   if (thrower)
460     free(thrower);
461
462   if (!e->remote && !e->bt_strings)
463     xbt_ex_setup_backtrace(e);
464
465 #if (defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)) || (defined(WIN32) && defined(_M_IX86))
466   /* We have everything to build neat backtraces */
467   {
468     int i;
469     
470     fprintf(stderr,"\n");
471     for (i=0; i<e->used; i++)
472       fprintf(stderr,"%s\n",e->bt_strings[i]);
473     
474   }
475 #else
476   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",  
477           e->file,e->line,e->func);
478 #endif
479   xbt_ex_free(*e);
480 }
481
482
483 /* default __ex_terminate callback function */
484 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
485   xbt_ex_display(e);
486
487   abort();
488 }
489
490 /* the externally visible API */
491 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
492 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
493
494
495 void xbt_ex_free(xbt_ex_t e) {
496   int i;
497
498   if (e.msg) free(e.msg);
499   if (e.remote) {
500     free(e.procname);
501     free(e.file);
502     free(e.func);
503     free(e.host);
504   }
505
506   if (e.bt_strings) {   
507      for (i=0; i<e.used; i++) 
508        free((char*)e.bt_strings[i]);
509      free((char **)e.bt_strings);
510   }
511   /* memset(e,0,sizeof(xbt_ex_t)); */
512 }
513
514 /** \brief returns a short name for the given exception category */
515 const char * xbt_ex_catname(xbt_errcat_t cat) {
516   switch (cat) {
517   case unknown_error:   return  "unknown_err";
518   case arg_error:       return "invalid_arg";
519   case mismatch_error:  return "mismatch";
520   case not_found_error: return "not found";
521   case system_error:    return "system_err";
522   case network_error:   return "network_err";
523   case timeout_error:   return "timeout";
524   case thread_error:    return "thread_err";
525   default:              return "INVALID_ERR";
526   }
527 }
528
529 #ifndef HAVE_EXECINFO_H
530 #  if (defined(WIN32) && defined(_M_IX86))
531 int 
532 backtrace (void **buffer, int size)
533 {
534         int pos = 0;
535         STACKFRAME* stack_frame;
536         int first = 1;
537
538         IMAGEHLP_SYMBOL * pSym;
539         unsigned long displacement = 0;
540         IMAGEHLP_LINE line_info = {0};
541         byte __buffer[(sizeof(SYMBOL_INFO) +MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
542
543         CONTEXT context = {CONTEXT_FULL};
544         GetThreadContext(GetCurrentThread(), &context);
545         
546         /* ebp pointe sur la base de la pile */
547         /* esp pointe sur le stack pointer <=> sur le dernier Ã©lément déposé dans la pile (l'élément courant) */
548         _asm call $+5
549         _asm pop eax 
550         _asm mov context.Eip, eax 
551         _asm mov eax, esp 
552         _asm mov context.Esp, eax 
553         _asm mov context.Ebp, ebp 
554
555         dbg_hlp_init(GetCurrentProcess());
556         
557         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer))
558         {
559                 errno = EINVAL;
560                 return 0;
561         }
562
563         for(pos = 0; pos < size; pos++)
564                 buffer[pos] = NULL;
565         
566         pos = 0;
567
568         pSym = (IMAGEHLP_SYMBOL*)__buffer;
569
570         pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
571         pSym->MaxNameLength = MAX_SYM_NAME;
572
573
574         line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
575         
576
577     while(pos < size)
578     {
579                 stack_frame = (void*)calloc(1,sizeof(STACKFRAME));
580                 
581                 if(!stack_frame)
582                 {
583                         errno = ENOMEM;
584                         break;
585                 }
586                 
587                 stack_frame->AddrPC.Offset = context.Eip;
588             stack_frame->AddrPC.Mode = AddrModeFlat;
589         
590             stack_frame->AddrFrame.Offset = context.Ebp;
591             stack_frame->AddrFrame.Mode = AddrModeFlat;
592         
593             stack_frame->AddrStack.Offset = context.Esp;
594             stack_frame->AddrStack.Mode = AddrModeFlat;
595                 
596                 if((*(dbg_hlp->stack_walk))(
597                                         IMAGE_FILE_MACHINE_I386,
598                                         dbg_hlp->process_handle,
599                                         GetCurrentThread(),
600                                         stack_frame, 
601                                         &context,
602                                         NULL,
603                                         dbg_hlp->sym_function_table_access,
604                                         dbg_hlp->sym_get_module_base,
605                                         NULL)
606                         && !first) 
607                 {
608                         if(stack_frame->AddrReturn.Offset)
609                         {
610
611                                 if((*(dbg_hlp->sym_get_sym_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,pSym))
612                                 {       
613                                         if((*(dbg_hlp->sym_get_line_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,&line_info))
614                                                 buffer[pos++] = (void*)stack_frame;
615                                 }
616                         }
617                         else
618                         {
619                                 free(stack_frame); /* no symbol or no line info */
620                                 break;
621                         }
622                 }
623                 else
624                 {
625                         free(stack_frame);
626
627                         if(first)
628                                 first = 0;
629                         else
630                                 break;
631                 }               
632     }
633
634     return pos;
635 }
636
637 char ** 
638 backtrace_symbols (void *const *buffer, int size)
639 {
640         int pos;
641         int success = 0;        
642         char** strings;
643         STACKFRAME* stack_frame;
644         char str[MAX_SYM_NAME] = {0};
645         IMAGEHLP_SYMBOL * pSym;
646         unsigned long displacement = 0;
647         IMAGEHLP_LINE line_info = {0};
648         IMAGEHLP_MODULE module = {0};
649         byte __buffer[(sizeof(SYMBOL_INFO) +MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
650
651         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer))
652         {
653                 errno = EINVAL;
654                 return NULL;
655         }
656         
657         strings = (char**)calloc(size,sizeof(char*));
658         
659         if(NULL == strings)
660         {
661                 errno = ENOMEM;
662                 return NULL;
663         }
664         
665         pSym = (IMAGEHLP_SYMBOL*)__buffer;
666
667         pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
668         pSym->MaxNameLength = MAX_SYM_NAME;
669
670
671         line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
672         module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
673         
674         for(pos = 0; pos < size; pos++)
675         {
676                 stack_frame = (STACKFRAME*)(buffer[pos]);
677
678                 if(NULL != stack_frame)
679                 {
680                 
681                         if((*(dbg_hlp->sym_get_sym_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,pSym))
682                         {       
683                                 if((*(dbg_hlp->sym_get_line_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,&line_info))
684                                 {
685                                         
686                                                 sprintf(str,"**   In %s() at %s:%d", pSym->Name,line_info.FileName,line_info.LineNumber); 
687                                                 
688                                                 strings[pos] = strdup(str);
689                                                 memset(str,0,MAX_SYM_NAME);     
690                                         
691                                                 success = 1;
692                                 }
693                                 else
694                                 {
695                                         strings[pos] = strdup("<no line>");
696                                 }
697                         }
698                         else
699                         {
700                                 strings[pos] = strdup("<no symbole>");
701                         }
702
703                         free(stack_frame);
704                 }
705                 else
706                         break;
707         }
708         
709         if(!success)
710         {
711                 free(strings);
712                 strings = NULL;
713         }
714
715         dbg_hlp_finalize();
716         
717         return strings;
718 }
719
720 void
721 backtrace_symbols_fd(void *const *buffer, int size, int fd)
722 {
723         int pos;
724         int success = 0;        
725         STACKFRAME* stack_frame;
726         char str[MAX_SYM_NAME] = {0};
727         IMAGEHLP_SYMBOL * pSym;
728         unsigned long displacement = 0;
729         IMAGEHLP_LINE line_info = {0};
730         IMAGEHLP_MODULE module = {0};
731
732         byte __buffer[(sizeof(SYMBOL_INFO) +MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
733
734         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer) || (-1 == fd))
735         {
736                 errno = EINVAL;
737                 return;
738         }
739         
740         pSym = (IMAGEHLP_SYMBOL*)__buffer;
741
742         pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
743         pSym->MaxNameLength = MAX_SYM_NAME;
744
745
746         line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
747         module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
748         
749         for(pos = 0; pos < size; pos++)
750         {
751                 stack_frame = (STACKFRAME*)(buffer[pos]);
752
753                 if(NULL != stack_frame)
754                 {
755                 
756                         if((*(dbg_hlp->sym_get_sym_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,pSym))
757                         {       
758                                 if((*(dbg_hlp->sym_get_line_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,&line_info))
759                                 {
760                                         
761                                                 
762                                                 sprintf(str,"**   In %s() at %s:%d\n", pSym->Name,line_info.FileName,line_info.LineNumber); 
763                                                 
764                                                 if(-1 == write(fd,str,strlen(str)))
765                                                         break;
766
767                                                 memset(str,0,MAX_SYM_NAME);     
768                                         
769                                                 success = 1;
770                                 }
771                         }
772
773                         free(stack_frame);
774                 }
775                 else
776                         break;
777         }
778         
779         dbg_hlp_finalize();
780         
781 }
782
783 static int
784 dbg_hlp_init(HANDLE process_handle)
785 {
786         if(dbg_hlp)
787         {
788                 /* debug help is already loaded */
789                 return 0;
790         }
791
792         /* allocation */
793         dbg_hlp = (xbt_debug_hlp_t)calloc(1,sizeof(s_xbt_debug_hlp_t));
794         
795         if(!dbg_hlp)
796                 return ENOMEM;
797         
798         /* load the library */
799         dbg_hlp->instance = LoadLibraryA("Dbghelp.dll");
800         
801         if(!(dbg_hlp->instance))
802         {
803                 free(dbg_hlp);
804                 dbg_hlp = NULL;
805                 return (int)GetLastError();
806         }
807         
808         /* get the pointers to debug help library exported functions */
809         
810         if(!((dbg_hlp->sym_initialize) = (xbt_pfn_sym_initialize_t)GetProcAddress(dbg_hlp->instance,"SymInitialize")))
811         {
812                 FreeLibrary(dbg_hlp->instance);
813                 free(dbg_hlp);
814                 dbg_hlp = NULL;
815                 return (int)GetLastError();     
816         }
817                 
818         if(!((dbg_hlp->sym_cleanup) = (xbt_pfn_sym_cleanup_t)GetProcAddress(dbg_hlp->instance,"SymCleanup")))
819         {
820                 FreeLibrary(dbg_hlp->instance);
821                 free(dbg_hlp);
822                 dbg_hlp = NULL;
823                 return (int)GetLastError();     
824         }
825         
826         if(!((dbg_hlp->sym_function_table_access) = (xbt_pfn_sym_function_table_access_t)GetProcAddress(dbg_hlp->instance,"SymFunctionTableAccess")))
827         {
828                 FreeLibrary(dbg_hlp->instance);
829                 free(dbg_hlp);
830                 dbg_hlp = NULL;
831                 return (int)GetLastError();     
832         }
833         
834         if(!((dbg_hlp->sym_get_line_from_addr) = (xbt_pfn_sym_get_line_from_addr_t)GetProcAddress(dbg_hlp->instance,"SymGetLineFromAddr")))
835         {
836                 FreeLibrary(dbg_hlp->instance);
837                 free(dbg_hlp);
838                 dbg_hlp = NULL;
839                 return (int)GetLastError();     
840         }
841         
842         if(!((dbg_hlp->sym_get_module_base) = (xbt_pfn_sym_get_module_base_t)GetProcAddress(dbg_hlp->instance,"SymGetModuleBase")))
843         {
844                 FreeLibrary(dbg_hlp->instance);
845                 free(dbg_hlp);
846                 dbg_hlp = NULL;
847                 return (int)GetLastError();     
848         }
849         
850         if(!((dbg_hlp->sym_get_options) = (xbt_pfn_sym_get_options_t)GetProcAddress(dbg_hlp->instance,"SymGetOptions")))
851         {
852                 FreeLibrary(dbg_hlp->instance);
853                 free(dbg_hlp);
854                 dbg_hlp = NULL;
855                 return (int)GetLastError();     
856         }
857         
858         if(!((dbg_hlp->sym_get_sym_from_addr) = (xbt_pfn_sym_get_sym_from_addr_t)GetProcAddress(dbg_hlp->instance,"SymGetSymFromAddr")))
859         {
860                 FreeLibrary(dbg_hlp->instance);
861                 free(dbg_hlp);
862                 dbg_hlp = NULL;
863                 return (int)GetLastError();     
864         }
865         
866         if(!((dbg_hlp->sym_set_options) = (xbt_pfn_sym_set_options_t)GetProcAddress(dbg_hlp->instance,"SymSetOptions")))
867         {
868                 FreeLibrary(dbg_hlp->instance);
869                 free(dbg_hlp);
870                 dbg_hlp = NULL;
871                 return (int)GetLastError();     
872         }
873         
874         if(!((dbg_hlp->stack_walk) = (xbt_pfn_stack_walk_t)GetProcAddress(dbg_hlp->instance,"StackWalk")))
875         {
876                 FreeLibrary(dbg_hlp->instance);
877                 free(dbg_hlp);
878                 dbg_hlp = NULL;
879                 return (int)GetLastError();     
880         }
881         
882         dbg_hlp->process_handle = process_handle;
883
884         (*(dbg_hlp->sym_set_options))((*(dbg_hlp->sym_get_options))() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
885         
886         if(!(*(dbg_hlp->sym_initialize))(dbg_hlp->process_handle,0,1))
887         {
888                 FreeLibrary(dbg_hlp->instance);
889                 free(dbg_hlp);
890                 dbg_hlp = NULL;
891                 return (int)GetLastError();
892         }
893                 
894         
895         return 0;
896 }
897
898 static int
899 dbg_hlp_finalize(void)
900 {
901         if(!dbg_hlp)
902                 return EINVAL;
903                 
904         if(!(*(dbg_hlp->sym_cleanup))(dbg_hlp->process_handle))
905                 return (int)GetLastError();
906         
907         if(!FreeLibrary(dbg_hlp->instance))
908                 return (int)GetLastError();
909         
910         free(dbg_hlp);
911         dbg_hlp = NULL;
912         
913         return 0;
914 }
915 #  endif
916 #else
917 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
918 int backtrace (void **__array, int __size) {
919   return 0;     
920 }
921
922 #endif
923
924 #ifdef SIMGRID_TEST
925 #include <stdio.h>
926 #include "xbt/ex.h"
927
928 XBT_TEST_SUITE("xbt_ex","Exception Handling");
929
930 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
931     xbt_ex_t ex;
932     volatile int n=1;
933
934     xbt_test_add0("basic nested control flow");
935
936     TRY {
937         if (n != 1)
938             xbt_test_fail1("M1: n=%d (!= 1)", n);
939         n++;
940         TRY {
941             if (n != 2)
942                 xbt_test_fail1("M2: n=%d (!= 2)", n);
943             n++;
944             THROW0(unknown_error,0,"something");
945         } CATCH (ex) {
946             if (n != 3)
947                 xbt_test_fail1("M3: n=%d (!= 3)", n);
948             n++;
949             xbt_ex_free(ex);
950         }
951         n++;
952         TRY {
953             if (n != 5)
954                 xbt_test_fail1("M2: n=%d (!= 5)", n);
955             n++;
956             THROW0(unknown_error,0,"something");
957         } CATCH (ex) {
958             if (n != 6)
959                 xbt_test_fail1("M3: n=%d (!= 6)", n);
960             n++;
961             RETHROW;
962             n++;
963         }
964         xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
965     }
966     CATCH(ex) {
967         if (n != 7)
968             xbt_test_fail1("M4: n=%d (!= 7)", n);
969         n++;
970         xbt_ex_free(ex);
971     }
972     if (n != 8)
973         xbt_test_fail1("M5: n=%d (!= 8)", n);
974 }
975
976 XBT_TEST_UNIT("value",test_value,"exception value passing") {
977     xbt_ex_t ex;
978
979     TRY {
980         THROW0(unknown_error, 2, "toto");
981     } CATCH(ex) {
982         xbt_test_add0("exception value passing");
983         if (ex.category != unknown_error)
984             xbt_test_fail1("category=%d (!= 1)", ex.category);
985         if (ex.value != 2)
986             xbt_test_fail1("value=%d (!= 2)", ex.value);
987         if (strcmp(ex.msg,"toto"))
988             xbt_test_fail1("message=%s (!= toto)", ex.msg);
989         xbt_ex_free(ex);
990     }
991 }
992
993 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
994     xbt_ex_t ex;
995     int r1, r2;
996     volatile int v1, v2;
997
998     r1 = r2 = v1 = v2 = 1234;
999     TRY {
1000         r2 = 5678;
1001         v2 = 5678;
1002         THROW0(unknown_error, 0, "toto");
1003     } CATCH(ex) {
1004         xbt_test_add0("variable preservation");
1005         if (r1 != 1234)
1006             xbt_test_fail1("r1=%d (!= 1234)", r1);
1007         if (v1 != 1234)
1008             xbt_test_fail1("v1=%d (!= 1234)", v1);
1009         /* r2 is allowed to be destroyed because not volatile */
1010         if (v2 != 5678)
1011             xbt_test_fail1("v2=%d (!= 5678)", v2);
1012         xbt_ex_free(ex);
1013     }
1014 }
1015
1016 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
1017     xbt_ex_t ex;
1018     volatile int v1;
1019     int c;
1020
1021     xbt_test_add0("cleanup handling");
1022
1023     v1 = 1234;
1024     c = 0;
1025     TRY {
1026         v1 = 5678;
1027         THROW0(1, 2, "blah");
1028     } CLEANUP {
1029         if (v1 != 5678)
1030             xbt_test_fail1("v1 = %d (!= 5678)", v1);
1031         c = 1;
1032     } CATCH(ex) {
1033         if (v1 != 5678)
1034             xbt_test_fail1("v1 = %d (!= 5678)", v1);
1035         if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
1036             xbt_test_fail0("unexpected exception contents");
1037         xbt_ex_free(ex);
1038     }
1039     if (!c)
1040         xbt_test_fail0("xbt_ex_free not executed");
1041 }
1042
1043
1044 /*
1045  * The following is the example included in the documentation. It's a good 
1046  * idea to check its syntax even if we don't try to run it.
1047  * And actually, it allows to put comments in the code despite doxygen.
1048  */ 
1049 static char *mallocex(int size) {
1050   return NULL;
1051 }
1052 #define SMALLAMOUNT 10
1053 #define TOOBIG 100000000
1054
1055 #if 0 /* this contains syntax errors, actually */
1056 static void bad_example(void) {
1057   struct {char*first;} *globalcontext;
1058   ex_t ex;
1059
1060   /* BAD_EXAMPLE */
1061   TRY {
1062     char *cp1, *cp2, *cp3;
1063     
1064     cp1 = mallocex(SMALLAMOUNT);
1065     globalcontext->first = cp1;
1066     cp2 = mallocex(TOOBIG);
1067     cp3 = mallocex(SMALLAMOUNT);
1068     strcpy(cp1, "foo");
1069     strcpy(cp2, "bar");
1070   } CLEANUP {
1071     if (cp3 != NULL) free(cp3);
1072     if (cp2 != NULL) free(cp2);
1073     if (cp1 != NULL) free(cp1);
1074   } CATCH(ex) {
1075     printf("cp3=%s", cp3);
1076     RETHROW;
1077   }
1078   /* end_of_bad_example */
1079 }
1080 #endif
1081 typedef struct {char *first;} global_context_t;
1082    
1083 static void good_example(void) {
1084   global_context_t *global_context=malloc(sizeof(global_context_t));
1085   xbt_ex_t ex;
1086
1087   /* GOOD_EXAMPLE */
1088   { /*01*/
1089     char * volatile /*03*/ cp1 = NULL /*02*/;
1090     char * volatile /*03*/ cp2 = NULL /*02*/;
1091     char * volatile /*03*/ cp3 = NULL /*02*/;
1092     TRY {
1093       cp1 = mallocex(SMALLAMOUNT);
1094       global_context->first = cp1;
1095       cp1 = NULL /*05 give away*/;
1096       cp2 = mallocex(TOOBIG);
1097       cp3 = mallocex(SMALLAMOUNT);
1098       strcpy(cp1, "foo");
1099       strcpy(cp2, "bar");
1100     } CLEANUP { /*04*/
1101       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
1102       if (cp3 != NULL)
1103         free(cp3);
1104       if (cp2 != NULL)
1105         free(cp2);
1106       /*05 cp1 was given away */
1107     } CATCH(ex) {
1108       /*05 global context untouched */
1109       RETHROW;
1110     }
1111   }
1112   /* end_of_good_example */
1113 }
1114 #endif /* SIMGRID_TEST */