Logo AND Algorithmique Numérique Distribuée

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