Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Implementation of the back trace functions for Win32 on the x86 architecture
[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   /* to get the backtrace from the libc */
428   char **backtrace = backtrace_symbols (e->bt, e->used);
429   
430   /* parse the output and build a new backtrace */
431   e->bt_strings = xbt_new(char*,e->used);
432   
433
434   for(i=0; i<e->used; i++) 
435   {
436       e->bt_strings[i] = strdup(backtrace[i]);
437       free(backtrace[i]);
438   }
439   
440   free(backtrace);
441 #endif
442 }    
443
444 /** @brief shows an exception content and the associated stack if available */
445 void xbt_ex_display(xbt_ex_t *e)  {
446   char *thrower=NULL;
447
448   if (e->remote)
449     thrower = bprintf(" on host %s(%d)",e->host,e->pid);
450
451   fprintf(stderr,
452           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
453           "** %s\n"
454           "** Thrown by %s()%s\n",
455           gras_os_myname(),(*xbt_getpid)(),
456           xbt_ex_catname(e->category), e->value, e->msg,
457           e->procname,thrower?thrower:" in this process");
458   CRITICAL1("%s",e->msg);
459
460   if (thrower)
461     free(thrower);
462
463   if (!e->remote && !e->bt_strings)
464     xbt_ex_setup_backtrace(e);
465
466 #if (defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)) || (defined(WIN32) && defined(_M_IX86))
467   /* We have everything to build neat backtraces */
468   {
469     int i;
470     
471     fprintf(stderr,"\n");
472     for (i=0; i<e->used; i++)
473       fprintf(stderr,"%s\n",e->bt_strings[i]);
474     
475   }
476 #else
477   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",  
478           e->file,e->line,e->func);
479 #endif
480   xbt_ex_free(*e);
481 }
482
483
484 /* default __ex_terminate callback function */
485 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
486   xbt_ex_display(e);
487
488   abort();
489 }
490
491 /* the externally visible API */
492 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
493 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
494
495
496 void xbt_ex_free(xbt_ex_t e) {
497   int i;
498
499   if (e.msg) free(e.msg);
500   if (e.remote) {
501     free(e.procname);
502     free(e.file);
503     free(e.func);
504     free(e.host);
505   }
506
507   if (e.bt_strings) {   
508      for (i=0; i<e.used; i++) 
509        free((char*)e.bt_strings[i]);
510      free((char **)e.bt_strings);
511   }
512   /* memset(e,0,sizeof(xbt_ex_t)); */
513 }
514
515 /** \brief returns a short name for the given exception category */
516 const char * xbt_ex_catname(xbt_errcat_t cat) {
517   switch (cat) {
518   case unknown_error:   return  "unknown_err";
519   case arg_error:       return "invalid_arg";
520   case mismatch_error:  return "mismatch";
521   case not_found_error: return "not found";
522   case system_error:    return "system_err";
523   case network_error:   return "network_err";
524   case timeout_error:   return "timeout";
525   case thread_error:    return "thread_err";
526   default:              return "INVALID_ERR";
527   }
528 }
529
530 #ifndef HAVE_EXECINFO_H
531 #  if (defined(WIN32) && defined(_M_IX86))
532 int 
533 backtrace (void **buffer, int size)
534 {
535         int pos = 0;
536         STACKFRAME* stack_frame;
537         int first = 1;
538
539         CONTEXT context = {CONTEXT_FULL};
540         GetThreadContext(GetCurrentThread(), &context);
541         
542         /* ebp pointe sur la base de la pile */
543         /* esp pointe sur le stack pointer <=> sur le dernier élément déposé dans la pile (l'élément courant) */
544         _asm call $+5
545         _asm pop eax 
546         _asm mov context.Eip, eax 
547         _asm mov eax, esp 
548         _asm mov context.Esp, eax 
549         _asm mov context.Ebp, ebp 
550
551         dbg_hlp_init(GetCurrentProcess());
552         
553         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer))
554         {
555                 errno = EINVAL;
556                 return 0;
557         }
558
559         for(pos = 0; pos < size; pos++)
560                 buffer[pos] = NULL;
561         
562         pos = 0;
563
564     while(pos < size)
565     {
566                 stack_frame = (void*)calloc(1,sizeof(STACKFRAME));
567                 
568                 if(!stack_frame)
569                 {
570                         errno = ENOMEM;
571                         break;
572                 }
573                 
574                 stack_frame->AddrPC.Offset = context.Eip;
575             stack_frame->AddrPC.Mode = AddrModeFlat;
576         
577             stack_frame->AddrFrame.Offset = context.Ebp;
578             stack_frame->AddrFrame.Mode = AddrModeFlat;
579         
580             stack_frame->AddrStack.Offset = context.Esp;
581             stack_frame->AddrStack.Mode = AddrModeFlat;
582                 
583                 if((*(dbg_hlp->stack_walk))(
584                                         IMAGE_FILE_MACHINE_I386,
585                                         dbg_hlp->process_handle,
586                                         GetCurrentThread(),
587                                         stack_frame, 
588                                         &context,
589                                         NULL,
590                                         dbg_hlp->sym_function_table_access,
591                                         dbg_hlp->sym_get_module_base,
592                                         NULL)
593                         && !first) 
594                 {
595                         if(stack_frame->AddrReturn.Offset)
596                                 buffer[pos++] = (void*)stack_frame;
597                         else
598                         {
599                                 free(stack_frame); /* no symbol */
600                                 break;
601                         }
602                 }
603                 else
604                 {
605                         free(stack_frame);
606
607                         if(first)
608                                 first = 0;
609                         else
610                                 break;
611                 }               
612     }
613
614     return pos;
615 }
616
617 char ** 
618 backtrace_symbols (void *const *buffer, int size)
619 {
620         int pos;
621         int success = 0;        
622         char** strings;
623         STACKFRAME* stack_frame;
624         char str[MAX_SYM_NAME] = {0};
625         IMAGEHLP_SYMBOL * pSym;
626         unsigned long displacement = 0;
627         IMAGEHLP_LINE line_info = {0};
628         IMAGEHLP_MODULE module = {0};
629         byte __buffer[(sizeof(SYMBOL_INFO) +MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
630
631         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer))
632         {
633                 errno = EINVAL;
634                 return NULL;
635         }
636         
637         strings = (char**)calloc(size,sizeof(char*));
638         
639         if(NULL == strings)
640         {
641                 errno = ENOMEM;
642                 return NULL;
643         }
644         
645         pSym = (IMAGEHLP_SYMBOL*)__buffer;
646
647         pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
648         pSym->MaxNameLength = MAX_SYM_NAME;
649
650
651         line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
652         module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
653         
654         for(pos = 0; pos < size; pos++)
655         {
656                 stack_frame = (STACKFRAME*)(buffer[pos]);
657
658                 if(NULL != stack_frame)
659                 {
660                 
661                         if((*(dbg_hlp->sym_get_sym_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,pSym))
662                         {       
663                                 if((*(dbg_hlp->sym_get_line_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,&line_info))
664                                 {
665                                         
666                                                 sprintf(str,"**   In %s() at %s:%d", pSym->Name,line_info.FileName,line_info.LineNumber); 
667                                                 
668                                                 strings[pos] = strdup(str);
669                                                 memset(str,0,MAX_SYM_NAME);     
670                                         
671                                                 success = 1;
672                                 }
673                         }
674
675                         free(stack_frame);
676                 }
677                 else
678                         break;
679         }
680         
681         if(!success)
682         {
683                 free(strings);
684                 strings = NULL;
685         }
686
687         dbg_hlp_finalize();
688         
689         return strings;
690 }
691
692 void
693 backtrace_symbols_fd(void *const *buffer, int size, int fd)
694 {
695         int pos;
696         int success = 0;        
697         STACKFRAME* stack_frame;
698         char str[MAX_SYM_NAME] = {0};
699         IMAGEHLP_SYMBOL * pSym;
700         unsigned long displacement = 0;
701         IMAGEHLP_LINE line_info = {0};
702         IMAGEHLP_MODULE module = {0};
703
704         byte __buffer[(sizeof(SYMBOL_INFO) +MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
705
706         if((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer) || (-1 == fd))
707         {
708                 errno = EINVAL;
709                 return;
710         }
711         
712         pSym = (IMAGEHLP_SYMBOL*)__buffer;
713
714         pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
715         pSym->MaxNameLength = MAX_SYM_NAME;
716
717
718         line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
719         module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
720         
721         for(pos = 0; pos < size; pos++)
722         {
723                 stack_frame = (STACKFRAME*)(buffer[pos]);
724
725                 if(NULL != stack_frame)
726                 {
727                 
728                         if((*(dbg_hlp->sym_get_sym_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,pSym))
729                         {       
730                                 if((*(dbg_hlp->sym_get_line_from_addr))(dbg_hlp->process_handle,stack_frame->AddrPC.Offset, &displacement,&line_info))
731                                 {
732                                         
733                                                 
734                                                 sprintf(str,"**   In %s() at %s:%d\n", pSym->Name,line_info.FileName,line_info.LineNumber); 
735                                                 
736                                                 if(-1 == write(fd,str,strlen(str)))
737                                                         break;
738
739                                                 memset(str,0,MAX_SYM_NAME);     
740                                         
741                                                 success = 1;
742                                 }
743                         }
744
745                         free(stack_frame);
746                 }
747                 else
748                         break;
749         }
750         
751         dbg_hlp_finalize();
752         
753 }
754
755 static int
756 dbg_hlp_init(HANDLE process_handle)
757 {
758         if(dbg_hlp)
759         {
760                 /* debug help is already loaded */
761                 return 0;
762         }
763
764         /* allocation */
765         dbg_hlp = (xbt_debug_hlp_t)calloc(1,sizeof(s_xbt_debug_hlp_t));
766         
767         if(!dbg_hlp)
768                 return ENOMEM;
769         
770         /* load the library */
771         dbg_hlp->instance = LoadLibraryA("Dbghelp.dll");
772         
773         if(!(dbg_hlp->instance))
774         {
775                 free(dbg_hlp);
776                 dbg_hlp = NULL;
777                 return (int)GetLastError();
778         }
779         
780         /* get the pointers to debug help library exported functions */
781         
782         if(!((dbg_hlp->sym_initialize) = (xbt_pfn_sym_initialize_t)GetProcAddress(dbg_hlp->instance,"SymInitialize")))
783         {
784                 FreeLibrary(dbg_hlp->instance);
785                 free(dbg_hlp);
786                 dbg_hlp = NULL;
787                 return (int)GetLastError();     
788         }
789                 
790         if(!((dbg_hlp->sym_cleanup) = (xbt_pfn_sym_cleanup_t)GetProcAddress(dbg_hlp->instance,"SymCleanup")))
791         {
792                 FreeLibrary(dbg_hlp->instance);
793                 free(dbg_hlp);
794                 dbg_hlp = NULL;
795                 return (int)GetLastError();     
796         }
797         
798         if(!((dbg_hlp->sym_function_table_access) = (xbt_pfn_sym_function_table_access_t)GetProcAddress(dbg_hlp->instance,"SymFunctionTableAccess")))
799         {
800                 FreeLibrary(dbg_hlp->instance);
801                 free(dbg_hlp);
802                 dbg_hlp = NULL;
803                 return (int)GetLastError();     
804         }
805         
806         if(!((dbg_hlp->sym_get_line_from_addr) = (xbt_pfn_sym_get_line_from_addr_t)GetProcAddress(dbg_hlp->instance,"SymGetLineFromAddr")))
807         {
808                 FreeLibrary(dbg_hlp->instance);
809                 free(dbg_hlp);
810                 dbg_hlp = NULL;
811                 return (int)GetLastError();     
812         }
813         
814         if(!((dbg_hlp->sym_get_module_base) = (xbt_pfn_sym_get_module_base_t)GetProcAddress(dbg_hlp->instance,"SymGetModuleBase")))
815         {
816                 FreeLibrary(dbg_hlp->instance);
817                 free(dbg_hlp);
818                 dbg_hlp = NULL;
819                 return (int)GetLastError();     
820         }
821         
822         if(!((dbg_hlp->sym_get_options) = (xbt_pfn_sym_get_options_t)GetProcAddress(dbg_hlp->instance,"SymGetOptions")))
823         {
824                 FreeLibrary(dbg_hlp->instance);
825                 free(dbg_hlp);
826                 dbg_hlp = NULL;
827                 return (int)GetLastError();     
828         }
829         
830         if(!((dbg_hlp->sym_get_sym_from_addr) = (xbt_pfn_sym_get_sym_from_addr_t)GetProcAddress(dbg_hlp->instance,"SymGetSymFromAddr")))
831         {
832                 FreeLibrary(dbg_hlp->instance);
833                 free(dbg_hlp);
834                 dbg_hlp = NULL;
835                 return (int)GetLastError();     
836         }
837         
838         if(!((dbg_hlp->sym_set_options) = (xbt_pfn_sym_set_options_t)GetProcAddress(dbg_hlp->instance,"SymSetOptions")))
839         {
840                 FreeLibrary(dbg_hlp->instance);
841                 free(dbg_hlp);
842                 dbg_hlp = NULL;
843                 return (int)GetLastError();     
844         }
845         
846         if(!((dbg_hlp->stack_walk) = (xbt_pfn_stack_walk_t)GetProcAddress(dbg_hlp->instance,"StackWalk")))
847         {
848                 FreeLibrary(dbg_hlp->instance);
849                 free(dbg_hlp);
850                 dbg_hlp = NULL;
851                 return (int)GetLastError();     
852         }
853         
854         dbg_hlp->process_handle = process_handle;
855
856         (*(dbg_hlp->sym_set_options))((*(dbg_hlp->sym_get_options))() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
857         
858         if(!(*(dbg_hlp->sym_initialize))(dbg_hlp->process_handle,0,1))
859         {
860                 FreeLibrary(dbg_hlp->instance);
861                 free(dbg_hlp);
862                 dbg_hlp = NULL;
863                 return (int)GetLastError();
864         }
865                 
866         
867         return 0;
868 }
869
870 static int
871 dbg_hlp_finalize(void)
872 {
873         if(!dbg_hlp)
874                 return EINVAL;
875                 
876         if(!(*(dbg_hlp->sym_cleanup))(dbg_hlp->process_handle))
877                 return (int)GetLastError();
878         
879         if(!FreeLibrary(dbg_hlp->instance))
880                 return (int)GetLastError();
881         
882         free(dbg_hlp);
883         dbg_hlp = NULL;
884         
885         return 0;
886 }
887 #  endif
888 #else
889 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
890 int backtrace (void **__array, int __size) {
891   return 0;     
892 }
893
894 #endif
895
896 #ifdef SIMGRID_TEST
897 #include <stdio.h>
898 #include "xbt/ex.h"
899
900 XBT_TEST_SUITE("xbt_ex","Exception Handling");
901
902 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
903     xbt_ex_t ex;
904     volatile int n=1;
905
906     xbt_test_add0("basic nested control flow");
907
908     TRY {
909         if (n != 1)
910             xbt_test_fail1("M1: n=%d (!= 1)", n);
911         n++;
912         TRY {
913             if (n != 2)
914                 xbt_test_fail1("M2: n=%d (!= 2)", n);
915             n++;
916             THROW0(unknown_error,0,"something");
917         } CATCH (ex) {
918             if (n != 3)
919                 xbt_test_fail1("M3: n=%d (!= 3)", n);
920             n++;
921             xbt_ex_free(ex);
922         }
923         n++;
924         TRY {
925             if (n != 5)
926                 xbt_test_fail1("M2: n=%d (!= 5)", n);
927             n++;
928             THROW0(unknown_error,0,"something");
929         } CATCH (ex) {
930             if (n != 6)
931                 xbt_test_fail1("M3: n=%d (!= 6)", n);
932             n++;
933             RETHROW;
934             n++;
935         }
936         xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
937     }
938     CATCH(ex) {
939         if (n != 7)
940             xbt_test_fail1("M4: n=%d (!= 7)", n);
941         n++;
942         xbt_ex_free(ex);
943     }
944     if (n != 8)
945         xbt_test_fail1("M5: n=%d (!= 8)", n);
946 }
947
948 XBT_TEST_UNIT("value",test_value,"exception value passing") {
949     xbt_ex_t ex;
950
951     TRY {
952         THROW0(unknown_error, 2, "toto");
953     } CATCH(ex) {
954         xbt_test_add0("exception value passing");
955         if (ex.category != unknown_error)
956             xbt_test_fail1("category=%d (!= 1)", ex.category);
957         if (ex.value != 2)
958             xbt_test_fail1("value=%d (!= 2)", ex.value);
959         if (strcmp(ex.msg,"toto"))
960             xbt_test_fail1("message=%s (!= toto)", ex.msg);
961         xbt_ex_free(ex);
962     }
963 }
964
965 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
966     xbt_ex_t ex;
967     int r1, r2;
968     volatile int v1, v2;
969
970     r1 = r2 = v1 = v2 = 1234;
971     TRY {
972         r2 = 5678;
973         v2 = 5678;
974         THROW0(unknown_error, 0, "toto");
975     } CATCH(ex) {
976         xbt_test_add0("variable preservation");
977         if (r1 != 1234)
978             xbt_test_fail1("r1=%d (!= 1234)", r1);
979         if (v1 != 1234)
980             xbt_test_fail1("v1=%d (!= 1234)", v1);
981         /* r2 is allowed to be destroyed because not volatile */
982         if (v2 != 5678)
983             xbt_test_fail1("v2=%d (!= 5678)", v2);
984         xbt_ex_free(ex);
985     }
986 }
987
988 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
989     xbt_ex_t ex;
990     volatile int v1;
991     int c;
992
993     xbt_test_add0("cleanup handling");
994
995     v1 = 1234;
996     c = 0;
997     TRY {
998         v1 = 5678;
999         THROW0(1, 2, "blah");
1000     } CLEANUP {
1001         if (v1 != 5678)
1002             xbt_test_fail1("v1 = %d (!= 5678)", v1);
1003         c = 1;
1004     } CATCH(ex) {
1005         if (v1 != 5678)
1006             xbt_test_fail1("v1 = %d (!= 5678)", v1);
1007         if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
1008             xbt_test_fail0("unexpected exception contents");
1009         xbt_ex_free(ex);
1010     }
1011     if (!c)
1012         xbt_test_fail0("xbt_ex_free not executed");
1013 }
1014
1015
1016 /*
1017  * The following is the example included in the documentation. It's a good 
1018  * idea to check its syntax even if we don't try to run it.
1019  * And actually, it allows to put comments in the code despite doxygen.
1020  */ 
1021 static char *mallocex(int size) {
1022   return NULL;
1023 }
1024 #define SMALLAMOUNT 10
1025 #define TOOBIG 100000000
1026
1027 #if 0 /* this contains syntax errors, actually */
1028 static void bad_example(void) {
1029   struct {char*first;} *globalcontext;
1030   ex_t ex;
1031
1032   /* BAD_EXAMPLE */
1033   TRY {
1034     char *cp1, *cp2, *cp3;
1035     
1036     cp1 = mallocex(SMALLAMOUNT);
1037     globalcontext->first = cp1;
1038     cp2 = mallocex(TOOBIG);
1039     cp3 = mallocex(SMALLAMOUNT);
1040     strcpy(cp1, "foo");
1041     strcpy(cp2, "bar");
1042   } CLEANUP {
1043     if (cp3 != NULL) free(cp3);
1044     if (cp2 != NULL) free(cp2);
1045     if (cp1 != NULL) free(cp1);
1046   } CATCH(ex) {
1047     printf("cp3=%s", cp3);
1048     RETHROW;
1049   }
1050   /* end_of_bad_example */
1051 }
1052 #endif
1053 typedef struct {char *first;} global_context_t;
1054    
1055 static void good_example(void) {
1056   global_context_t *global_context=malloc(sizeof(global_context_t));
1057   xbt_ex_t ex;
1058
1059   /* GOOD_EXAMPLE */
1060   { /*01*/
1061     char * volatile /*03*/ cp1 = NULL /*02*/;
1062     char * volatile /*03*/ cp2 = NULL /*02*/;
1063     char * volatile /*03*/ cp3 = NULL /*02*/;
1064     TRY {
1065       cp1 = mallocex(SMALLAMOUNT);
1066       global_context->first = cp1;
1067       cp1 = NULL /*05 give away*/;
1068       cp2 = mallocex(TOOBIG);
1069       cp3 = mallocex(SMALLAMOUNT);
1070       strcpy(cp1, "foo");
1071       strcpy(cp2, "bar");
1072     } CLEANUP { /*04*/
1073       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
1074       if (cp3 != NULL)
1075         free(cp3);
1076       if (cp2 != NULL)
1077         free(cp2);
1078       /*05 cp1 was given away */
1079     } CATCH(ex) {
1080       /*05 global context untouched */
1081       RETHROW;
1082     }
1083   }
1084   /* end_of_good_example */
1085 }
1086 #endif /* SIMGRID_TEST */