Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
End of last commit about cleaning up windows backtraces (sorry, codding through the...
[simgrid.git] / src / xbt / backtrace_windows.c
1 /* $Id$ */
2
3 /* backtrace_windows - backtrace displaying on windows platform             */
4 /* This file is included by ex.c on need (windows x86)                      */
5
6 /*  Copyright (c) 2007 The SimGrid team                                     */
7 /*  All rights reserved.                                                    */
8
9 /* This program is free software; you can redistribute it and/or modify it
10  * under the terms of the license (GNU LGPL) which comes with this package. */
11
12 /* 
13  * Win32 (x86) implementation backtrace, backtrace_symbols:
14  *  support for application self-debugging.
15  */
16
17 #include <dbghelp.h>
18
19
20 /* Pointer function to SymInitialize() */
21 typedef BOOL(WINAPI * xbt_pfn_sym_initialize_t) (HANDLE, PSTR, BOOL);
22
23 /* Pointer function to SymCleanup() */
24 typedef BOOL(WINAPI * xbt_pfn_sym_cleanup_t) (HANDLE hProcess);
25
26 /* Pointer function to SymFunctionTableAccess() */
27 typedef PVOID(WINAPI * xbt_pfn_sym_function_table_access_t) (HANDLE, DWORD);
28
29 /* Pointer function to SymGetLineFromAddr() */
30 typedef BOOL(WINAPI * xbt_pfn_sym_get_line_from_addr_t) (HANDLE, DWORD,
31                                                          PDWORD,
32                                                          PIMAGEHLP_LINE);
33
34 /* Pointer function to SymGetModuleBase() */
35 typedef DWORD(WINAPI * xbt_pfn_sym_get_module_base_t) (HANDLE, DWORD);
36
37 /* Pointer function to SymGetOptions() */
38 typedef DWORD(WINAPI * xbt_pfn_sym_get_options_t) (VOID);
39
40 /* Pointer function to SymGetSymFromAddr() */
41 typedef BOOL(WINAPI * xbt_pfn_sym_get_sym_from_addr_t) (HANDLE, DWORD, PDWORD,
42                                                         OUT PIMAGEHLP_SYMBOL);
43
44 /* Pointer function to SymSetOptions() */
45 typedef DWORD(WINAPI * xbt_pfn_sym_set_options_t) (DWORD);
46
47 /* Pointer function to StackWalk() */
48 typedef BOOL(WINAPI * xbt_pfn_stack_walk_t) (DWORD, HANDLE, HANDLE,
49                                              LPSTACKFRAME, PVOID,
50                                              PREAD_PROCESS_MEMORY_ROUTINE,
51                                              PFUNCTION_TABLE_ACCESS_ROUTINE,
52                                              PGET_MODULE_BASE_ROUTINE,
53                                              PTRANSLATE_ADDRESS_ROUTINE);
54
55 /* This structure represents the debug_help library used interface */
56 typedef struct s_xbt_debug_help {
57   HINSTANCE instance;
58   HANDLE process_handle;
59   xbt_pfn_sym_initialize_t sym_initialize;
60   xbt_pfn_sym_cleanup_t sym_cleanup;
61   xbt_pfn_sym_function_table_access_t sym_function_table_access;
62   xbt_pfn_sym_get_line_from_addr_t sym_get_line_from_addr;
63   xbt_pfn_sym_get_module_base_t sym_get_module_base;
64   xbt_pfn_sym_get_options_t sym_get_options;
65   xbt_pfn_sym_get_sym_from_addr_t sym_get_sym_from_addr;
66   xbt_pfn_sym_set_options_t sym_set_options;
67   xbt_pfn_stack_walk_t stack_walk;
68 } s_xbt_debug_hlp_t, *xbt_debug_hlp_t;
69
70
71 /* the address to the unique reference to the debug help library interface */
72 static xbt_debug_hlp_t dbg_hlp = NULL;
73
74 /* Module creation/destruction: nothing to do on linux */
75 void xbt_backtrace_init(void) { 
76   HANDLE process_handle = GetCurrentProcess();
77
78   if (dbg_hlp) {
79     /* debug help is already loaded */
80     return;
81   }
82
83   /* allocation */
84   dbg_hlp = xbt_new0(s_xbt_debug_hlp_t, 1);
85
86   /* load the library */
87   dbg_hlp->instance = LoadLibraryA("Dbghelp.dll");
88
89   if (!dbg_hlp->instance) {
90     free(dbg_hlp);
91     dbg_hlp = NULL;
92     return;
93   }
94  
95   /* get the pointers to debug help library exported functions */
96   dbg_hlp->sym_initialize =
97     (xbt_pfn_sym_initialize_t) GetProcAddress(dbg_hlp->instance, "SymInitialize");
98
99   dbg_hlp->sym_cleanup = 
100     (xbt_pfn_sym_cleanup_t) GetProcAddress(dbg_hlp->instance, "SymCleanup");
101
102   dbg_hlp->sym_function_table_access =
103     (xbt_pfn_sym_function_table_access_t) GetProcAddress(dbg_hlp->instance, "SymFunctionTableAccess");
104
105   dbg_hlp->sym_get_line_from_addr =
106     (xbt_pfn_sym_get_line_from_addr_t) GetProcAddress(dbg_hlp->instance, "SymGetLineFromAddr");
107
108   dbg_hlp->sym_get_module_base =
109     (xbt_pfn_sym_get_module_base_t) GetProcAddress(dbg_hlp->instance, "SymGetModuleBase");
110
111   dbg_hlp->sym_get_options =
112     (xbt_pfn_sym_get_options_t) GetProcAddress(dbg_hlp->instance, "SymGetOptions");
113
114   dbg_hlp->sym_get_sym_from_addr =
115     (xbt_pfn_sym_get_sym_from_addr_t) GetProcAddress(dbg_hlp->instance, "SymGetSymFromAddr");
116
117   dbg_hlp->sym_set_options =
118     (xbt_pfn_sym_set_options_t) GetProcAddress(dbg_hlp->instance, "SymSetOptions");
119
120   dbg_hlp->stack_walk =
121     (xbt_pfn_stack_walk_t) GetProcAddress(dbg_hlp->instance, "StackWalk");
122
123   /* Check that everything worked well */
124   if (!dbg_hlp->sym_initialize ||
125       !dbg_hlp->sym_cleanup ||
126       !dbg_hlp->sym_function_table_access || 
127       !dbg_hlp->sym_get_line_from_addr ||
128       !dbg_hlp->sym_get_module_base ||
129       !dbg_hlp->sym_get_options ||
130       !dbg_hlp->sym_get_sym_from_addr ||
131       !dbg_hlp->sym_set_options ||
132       !dbg_hlp->stack_walk
133       ) {
134     FreeLibrary(dbg_hlp->instance);
135     free(dbg_hlp);
136     dbg_hlp = NULL;
137     return;
138   }
139
140   dbg_hlp->process_handle = process_handle;
141
142   (*(dbg_hlp->sym_set_options)) ((*(dbg_hlp->sym_get_options)) () |
143                                  SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
144
145   if (!(*(dbg_hlp->sym_initialize)) (dbg_hlp->process_handle, 0, 1)) {
146     FreeLibrary(dbg_hlp->instance);
147     free(dbg_hlp);
148     dbg_hlp = NULL;
149   }
150 }
151 void xbt_backtrace_exit(void) { 
152   if (!dbg_hlp)
153     return;
154
155   if ((dbg_hlp->sym_cleanup) (dbg_hlp->process_handle))
156     FreeLibrary(dbg_hlp->instance);
157
158
159   free(dbg_hlp);
160   dbg_hlp = NULL;
161 }
162
163 /*
164  * backtrace() function.
165  *
166  * Returns a backtrace for the calling program, in  the  array
167  * pointed  to  by  buffer.  A backtrace is the series of currently active
168  * function calls for the program.  Each item in the array pointed  to  by
169  * buffer  is  of  type  void *, and is the return address from the corre-
170  * sponding stack frame.  The size argument specifies the  maximum  number
171  * of  addresses that can be stored in buffer.  If the backtrace is larger
172  * than size, then the addresses corresponding to  the  size  most  recent
173  * function  calls  are  returned;  to obtain the complete backtrace, make
174  * sure that buffer and size are large enough.
175  */
176
177 int backtrace(void **buffer, int size);
178
179 /*
180  * backtrace_symbols() function.
181  *
182  * Given the set of addresses returned by  backtrace()  in  buffer,  back-
183  * trace_symbols()  translates the addresses into an array of strings containing
184  * the name, the source file and the line number or the las called functions.
185  */
186 char **backtrace_symbols(void *const *buffer, int size);
187
188 void xbt_ex_setup_backtrace(xbt_ex_t * e)
189 {
190   int i;
191   char **backtrace_syms = backtrace_symbols(e->bt, e->used);
192
193   e->used = backtrace((void **) e->bt, XBT_BACKTRACE_SIZE);
194   e->bt_strings = NULL;
195   /* parse the output and build a new backtrace */
196   e->bt_strings = xbt_new(char *, e->used);
197
198
199   for (i = 0; i < e->used; i++) {
200     e->bt_strings[i] = xbt_strdup(backtrace_syms[i]);
201     free(backtrace_syms[i]);
202   }
203
204   free(backtrace_syms);
205 }
206
207 int backtrace(void **buffer, int size)
208 {
209   int pos = 0;
210   STACKFRAME *stack_frame;
211   int first = 1;
212
213   IMAGEHLP_SYMBOL *pSym;
214   unsigned long offset = 0;
215   IMAGEHLP_LINE line_info = { 0 };
216   byte
217     __buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) +
218               sizeof(ULONG64) - 1) / sizeof(ULONG64)];
219
220   CONTEXT context = { CONTEXT_FULL };
221   GetThreadContext(GetCurrentThread(), &context);
222
223   /* ebp points on stack base */
224   /* esp points on stack pointer, ie on last stacked element (current element) */
225   _asm call $ + 5
226   _asm pop eax
227   _asm mov context.Eip, eax
228   _asm mov eax, esp
229   _asm mov context.Esp, eax
230   _asm mov context.Ebp, ebp 
231
232   if ((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer)) {
233     errno = EINVAL;
234     return 0;
235   }
236
237   for (pos = 0; pos < size; pos++)
238     buffer[pos] = NULL;
239
240   pos = 0;
241
242   pSym = (IMAGEHLP_SYMBOL *) __buffer;
243
244   pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
245   pSym->MaxNameLength = MAX_SYM_NAME;
246
247
248   line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
249
250
251   while (pos < size) {
252     stack_frame = (void *) xbt_new0(STACKFRAME, 1);
253
254     stack_frame->AddrPC.Offset = context.Eip;
255     stack_frame->AddrPC.Mode = AddrModeFlat;
256
257     stack_frame->AddrFrame.Offset = context.Ebp;
258     stack_frame->AddrFrame.Mode = AddrModeFlat;
259
260     stack_frame->AddrStack.Offset = context.Esp;
261     stack_frame->AddrStack.Mode = AddrModeFlat;
262
263     if ((*(dbg_hlp->stack_walk)) (IMAGE_FILE_MACHINE_I386,
264                                   dbg_hlp->process_handle,
265                                   GetCurrentThread(),
266                                   stack_frame,
267                                   &context,
268                                   NULL,
269                                   dbg_hlp->sym_function_table_access,
270                                   dbg_hlp->sym_get_module_base, NULL)
271         && !first) {
272       if (stack_frame->AddrReturn.Offset) {
273
274         if ((*(dbg_hlp->sym_get_sym_from_addr))
275             (dbg_hlp->process_handle, stack_frame->AddrPC.Offset, &offset,
276              pSym)) {
277           if ((*(dbg_hlp->sym_get_line_from_addr))
278               (dbg_hlp->process_handle, stack_frame->AddrPC.Offset, &offset,
279                &line_info))
280             buffer[pos++] = (void *) stack_frame;
281         }
282       } else {
283         free(stack_frame);      /* no symbol or no line info */
284         break;
285       }
286     } else {
287       free(stack_frame);
288
289       if (first)
290         first = 0;
291       else
292         break;
293     }
294   }
295
296   return pos;
297 }
298
299 char **backtrace_symbols(void *const *buffer, int size)
300 {
301   int pos;
302   int success = 0;
303   char **strings;
304   STACKFRAME *stack_frame;
305   IMAGEHLP_SYMBOL *pSym;
306   unsigned long offset = 0;
307   IMAGEHLP_LINE line_info = { 0 };
308   IMAGEHLP_MODULE module = { 0 };
309   byte
310     __buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) +
311               sizeof(ULONG64) - 1) / sizeof(ULONG64)];
312
313   if ((NULL == dbg_hlp) || (size <= 0) || (NULL == buffer)) {
314     errno = EINVAL;
315     return NULL;
316   }
317
318   strings = xbt_new0(char *, size);
319
320   pSym = (IMAGEHLP_SYMBOL *) __buffer;
321
322   pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
323   pSym->MaxNameLength = MAX_SYM_NAME;
324
325
326   line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE);
327   module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
328
329   for (pos = 0; pos < size; pos++) {
330     stack_frame = (STACKFRAME *) (buffer[pos]);
331
332     if (NULL != stack_frame) {
333
334       if ((*(dbg_hlp->sym_get_sym_from_addr))
335           (dbg_hlp->process_handle, stack_frame->AddrPC.Offset, &offset,
336            pSym)) {
337         if ((*(dbg_hlp->sym_get_line_from_addr))
338             (dbg_hlp->process_handle, stack_frame->AddrPC.Offset, &offset,
339              &line_info)) {
340           strings[pos] =
341             bprintf("**   In %s() at %s:%d", pSym->Name, line_info.FileName,
342                     line_info.LineNumber);
343         } else {
344           strings[pos] = bprintf("**   In %s()", pSym->Name);
345         }
346         success = 1;
347       } else {
348         strings[pos] = xbt_strdup("**   <no symbol>");
349       }
350
351       free(stack_frame);
352     } else
353       break;
354   }
355
356   if (!success) {
357     free(strings);
358     strings = NULL;
359   }
360
361   return strings;
362 }