Logo AND Algorithmique Numérique Distribuée

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