Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
And now, fix the linux layer for backtrace extraction by not using values of backtrac...
[simgrid.git] / src / xbt / backtrace_linux.c
1 /* $Id: ex.c 5173 2008-01-07 22:10:52Z mquinson $ */
2
3 /* backtrace_linux - backtrace displaying on linux platform                 */
4 /* This file is included by ex.c on need (have execinfo.h, popen & addrline)*/
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 extern char **environ;          /* the environment, as specified by the opengroup */
13
14 /* Module creation/destruction: nothing to do on linux */
15 void xbt_backtrace_init(void) { }
16 void xbt_backtrace_exit(void) { }
17
18 void xbt_ex_setup_backtrace(xbt_ex_t * e)
19 {
20   int i;
21
22   /* to get the backtrace from the libc */
23   char **backtrace_syms;
24
25   /* To build the commandline of addr2line */
26   char *cmd, *curr;
27
28   /* to extract the addresses from the backtrace */
29   char **addrs;
30   char buff[256], *p;
31
32   /* To read the output of addr2line */
33   FILE *pipe;
34   char line_func[1024], line_pos[1024];
35
36   /* size (in char) of pointers on this arch */
37   int addr_len = 0;
38
39   /* To search for the right executable path when not trivial */
40   struct stat stat_buf;
41   char *binary_name = NULL;
42    
43   e->used = backtrace((void **) e->bt, XBT_BACKTRACE_SIZE);
44   backtrace_syms = backtrace_symbols(e->bt, e->used);
45   addrs = xbt_new(char *, e->used);
46    
47   e->bt_strings = NULL;
48
49   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
50   if (!e->used)
51     return;
52
53   /* build the commandline */
54   if (stat(xbt_binary_name, &stat_buf)) {
55     /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
56     int i;
57
58     for (i = 0; environ[i]; i++) {
59       if (!strncmp("PATH=", environ[i], 5)) {
60         xbt_dynar_t path = xbt_str_split(environ[i] + 5, ":");
61         unsigned int cpt;
62         char *data;
63
64         xbt_dynar_foreach(path, cpt, data) {
65           if (binary_name)
66             free(binary_name);
67           binary_name = bprintf("%s/%s", data, xbt_binary_name);
68           if (!stat(binary_name, &stat_buf)) {
69             /* Found. */
70             DEBUG1("Looked in the PATH for the binary. Found %s",
71                    binary_name);
72             xbt_dynar_free(&path);
73             break;
74           }
75         }
76         if (stat(binary_name, &stat_buf)) {
77           /* not found */
78           e->used = 1;
79           e->bt_strings = xbt_new(char *, 1);
80
81           e->bt_strings[0] =
82             bprintf("(binary '%s' not found the path)", xbt_binary_name);
83           return;
84         }
85         xbt_dynar_free(&path);
86         break;
87       }
88     }
89   } else {
90     binary_name = xbt_strdup(xbt_binary_name);
91   }
92   cmd = curr =
93     xbt_new(char,
94             strlen(ADDR2LINE) + 25 + strlen(binary_name) + 32 * e->used);
95
96   curr += sprintf(curr, "%s -f -e %s ", ADDR2LINE, binary_name);
97   free(binary_name);
98
99   for (i = 0; i < e->used; i++) {
100     /* retrieve this address */
101     DEBUG2("Retrieving address number %d from '%s'", i, backtrace_syms[i]);
102     snprintf(buff, 256, "%s", strchr(backtrace_syms[i], '[') + 1);
103     p = strchr(buff, ']');
104     *p = '\0';
105     if (strcmp(buff, "(nil)"))
106       addrs[i] = bprintf("%s", buff);
107     else
108       addrs[i] = bprintf("0x0");
109     DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
110
111     /* Add it to the command line args */
112     curr += sprintf(curr, "%s ", addrs[i]);
113   }
114   addr_len = strlen(addrs[0]);
115
116   /* parse the output and build a new backtrace */
117   e->bt_strings = xbt_new(char *, e->used);
118
119   VERB1("Fire a first command: '%s'", cmd);
120   pipe = popen(cmd, "r");
121   if (!pipe) {
122     CRITICAL0("Cannot fork addr2line to display the backtrace");
123     abort();
124   }
125
126   for (i = 0; i < e->used; i++) {
127     DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]);
128     fgets(line_func, 1024, pipe);
129     line_func[strlen(line_func) - 1] = '\0';
130     fgets(line_pos, 1024, pipe);
131     line_pos[strlen(line_pos) - 1] = '\0';
132
133     if (strcmp("??", line_func)) {
134       DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
135       e->bt_strings[i] = bprintf("**   In %s() at %s", line_func, line_pos);
136     } else {
137       /* Damn. The symbol is in a dynamic library. Let's get wild */
138       char *maps_name;
139       FILE *maps;
140       char maps_buff[512];
141
142       long int addr, offset = 0;
143       char *p, *p2;
144
145       char *subcmd;
146       FILE *subpipe;
147       int found = 0;
148
149       /* let's look for the offset of this library in our addressing space */
150       maps_name = bprintf("/proc/%d/maps", (int) getpid());
151       maps = fopen(maps_name, "r");
152
153       sscanf(addrs[i], "%lx", &addr);
154       sprintf(maps_buff, "%#lx", addr);
155
156       if (strcmp(addrs[i], maps_buff)) {
157         CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
158                   addrs[i], addr);
159       }
160       DEBUG2("addr=%s (as string) =%#lx (as number)", addrs[i], addr);
161
162       while (!found) {
163         long int first, last;
164
165         if (fgets(maps_buff, 512, maps) == NULL)
166           break;
167         if (i == 0) {
168           maps_buff[strlen(maps_buff) - 1] = '\0';
169           DEBUG1("map line: %s", maps_buff);
170         }
171         sscanf(maps_buff, "%lx", &first);
172         p = strchr(maps_buff, '-') + 1;
173         sscanf(p, "%lx", &last);
174         if (first < addr && addr < last) {
175           offset = first;
176           found = 1;
177         }
178         if (found) {
179           DEBUG3("%#lx in [%#lx-%#lx]", addr, first, last);
180           DEBUG0
181             ("Symbol found, map lines not further displayed (even if looking for next ones)");
182         }
183       }
184       fclose(maps);
185       free(maps_name);
186
187       if (!found) {
188         VERB0
189           ("Problem while reading the maps file. Following backtrace will be mangled.");
190         DEBUG1("No dynamic. Static symbol: %s", backtrace_syms[i]);
191         e->bt_strings[i] = bprintf("**   In ?? (%s)", backtrace_syms[i]);
192         continue;
193       }
194
195       /* Ok, Found the offset of the maps line containing the searched symbol. 
196          We now need to substract this from the address we got from backtrace.
197        */
198
199       free(addrs[i]);
200       addrs[i] = bprintf("0x%0*lx", addr_len - 2, addr - offset);
201       DEBUG2("offset=%#lx new addr=%s", offset, addrs[i]);
202
203       /* Got it. We have our new address. Let's get the library path and we 
204          are set */
205       p = xbt_strdup(backtrace_syms[i]);
206       if (p[0] == '[') {
207         /* library path not displayed in the map file either... */
208         free(p);
209         sprintf(line_func, "??");
210       } else {
211         p2 = strrchr(p, '(');
212         if (p2)
213           *p2 = '\0';
214         p2 = strrchr(p, ' ');
215         if (p2)
216           *p2 = '\0';
217
218         /* Here we go, fire an addr2line up */
219         subcmd = bprintf("%s -f -e %s %s", ADDR2LINE, p, addrs[i]);
220         free(p);
221         VERB1("Fire a new command: '%s'", subcmd);
222         subpipe = popen(subcmd, "r");
223         if (!subpipe) {
224           CRITICAL0("Cannot fork addr2line to display the backtrace");
225           abort();
226         }
227         fgets(line_func, 1024, subpipe);
228         line_func[strlen(line_func) - 1] = '\0';
229         fgets(line_pos, 1024, subpipe);
230         line_pos[strlen(line_pos) - 1] = '\0';
231         pclose(subpipe);
232         free(subcmd);
233       }
234
235       /* check whether the trick worked */
236       if (strcmp("??", line_func)) {
237         DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
238         e->bt_strings[i] = bprintf("**   In %s() at %s", line_func, line_pos);
239       } else {
240         /* damn, nothing to do here. Let's print the raw address */
241         DEBUG1("Dynamic symbol not found. Raw address = %s",
242                backtrace_syms[i]);
243         e->bt_strings[i] = bprintf("**   In ?? at %s", backtrace_syms[i]);
244       }
245
246     }
247     free(addrs[i]);
248
249     /* Mask the bottom of the stack */
250     if (!strncmp("main", line_func, strlen("main")) ||
251         !strncmp("xbt_thread_context_wrapper", line_func,
252                  strlen("xbt_thread_context_wrapper"))) {
253       int j;
254
255       for (j = i + 1; j < e->used; j++)
256         free(addrs[j]);
257       e->used = i;
258
259       if (!strncmp
260           ("xbt_thread_context_wrapper", line_func,
261            strlen("xbt_thread_context_wrapper"))) {
262         e->used++;
263         e->bt_strings[i] = bprintf("**   (in a separate thread)");
264       }
265     }
266
267
268   }
269   pclose(pipe);
270   free(addrs);
271   free(backtrace_syms);
272   free(cmd);
273 }