re PR libfortran/32345 (Unconditional snprintf use breaks all gfortran exec tests...
[gcc.git] / libgfortran / runtime / backtrace.c
1 /* Copyright (C) 2006, 2007 Free Software Foundation, Inc.
2 Contributed by François-Xavier Coudert
3
4 This file is part of the GNU Fortran 95 runtime library (libgfortran).
5
6 Libgfortran is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 In addition to the permissions in the GNU General Public License, the
12 Free Software Foundation gives you unlimited permission to link the
13 compiled version of this file into combinations with other programs,
14 and to distribute those combinations without any restriction coming
15 from the use of this file. (The General Public License restrictions
16 do apply in other respects; for example, they cover modification of
17 the file, and distribution when not linked into a combine
18 executable.)
19
20 Libgfortran is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with libgfortran; see the file COPYING. If not, write to
27 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
28 Boston, MA 02110-1301, USA. */
29
30
31 #include "config.h"
32 #include <stdio.h>
33 #include <string.h>
34
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38
39 #ifdef HAVE_INTTYPES_H
40 #include <inttypes.h>
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #ifdef HAVE_INTPTR_T
48 # define INTPTR_T intptr_t
49 #else
50 # define INTPTR_T int
51 #endif
52
53 #ifdef HAVE_EXECINFO_H
54 #include <execinfo.h>
55 #endif
56
57 #ifdef HAVE_SYS_WAIT_H
58 #include <sys/wait.h>
59 #endif
60
61 #ifdef HAVE_STRING_H
62 #include <string.h>
63 #endif
64
65 #include <ctype.h>
66
67 #include "libgfortran.h"
68
69
70
71 #ifndef HAVE_STRCASESTR
72 #define HAVE_STRCASESTR 1
73 static char *
74 strcasestr (const char *s1, const char *s2)
75 {
76 const char *p = s1;
77 const size_t len = strlen (s2);
78 const char u = *s2, v = isupper((int) *s2) ? tolower((int) *s2)
79 : (islower((int) *s2) ? toupper((int) *s2)
80 : *s2);
81
82 while (1)
83 {
84 while (*p != u && *p != v && *p)
85 p++;
86 if (*p == 0)
87 return NULL;
88 if (strncasecmp (p, s2, len) == 0)
89 return (char *)p;
90 }
91 }
92 #endif
93
94 #define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVP) \
95 && defined(HAVE_WAIT))
96 #define GLIBC_BACKTRACE (defined(HAVE_BACKTRACE) \
97 && defined(HAVE_BACKTRACE_SYMBOLS))
98 #define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \
99 && defined(HAVE_DUP2) && defined(HAVE_FDOPEN) \
100 && defined(HAVE_CLOSE))
101
102
103 #if GLIBC_BACKTRACE
104 static void
105 dump_glibc_backtrace (int depth, char *str[])
106 {
107 int i;
108
109 for (i = 0; i < depth; i++)
110 st_printf (" + %s\n", str[i]);
111
112 free (str);
113 }
114 #endif
115
116 /* show_backtrace displays the backtrace, currently obtained by means of
117 the glibc backtrace* functions. */
118 void
119 show_backtrace (void)
120 {
121 #if GLIBC_BACKTRACE
122
123 #define DEPTH 50
124 #define BUFSIZE 1024
125
126 void *trace[DEPTH];
127 char **str;
128 int depth;
129
130 depth = backtrace (trace, DEPTH);
131 if (depth <= 0)
132 return;
133
134 str = backtrace_symbols (trace, depth);
135
136 #if CAN_PIPE
137
138 #ifndef STDIN_FILENO
139 #define STDIN_FILENO 0
140 #endif
141
142 #ifndef STDOUT_FILENO
143 #define STDOUT_FILENO 1
144 #endif
145
146 #ifndef STDERR_FILENO
147 #define STDERR_FILENO 2
148 #endif
149
150 /* We attempt to extract file and line information from addr2line. */
151 do
152 {
153 /* Local variables. */
154 int f[2], pid, line, i;
155 FILE *output;
156 char addr_buf[DEPTH][GFC_XTOA_BUF_SIZE], func[BUFSIZE], file[BUFSIZE];
157 char *p, *end;
158 const char *addr[DEPTH];
159
160 /* Write the list of addresses in hexadecimal format. */
161 for (i = 0; i < depth; i++)
162 addr[i] = xtoa ((GFC_UINTEGER_LARGEST) (INTPTR_T) trace[i], addr_buf[i],
163 sizeof (addr_buf[i]));
164
165 /* Don't output an error message if something goes wrong, we'll simply
166 fall back to the pstack and glibc backtraces. */
167 if (pipe (f) != 0)
168 break;
169 if ((pid = fork ()) == -1)
170 break;
171
172 if (pid == 0)
173 {
174 /* Child process. */
175 #define NUM_FIXEDARGS 5
176 char *arg[DEPTH+NUM_FIXEDARGS+1];
177
178 close (f[0]);
179 close (STDIN_FILENO);
180 close (STDERR_FILENO);
181
182 if (dup2 (f[1], STDOUT_FILENO) == -1)
183 _exit (0);
184 close (f[1]);
185
186 arg[0] = (char *) "addr2line";
187 arg[1] = (char *) "-e";
188 arg[2] = full_exe_path ();
189 arg[3] = (char *) "-f";
190 arg[4] = (char *) "-s";
191 for (i = 0; i < depth; i++)
192 arg[NUM_FIXEDARGS+i] = (char *) addr[i];
193 arg[NUM_FIXEDARGS+depth] = NULL;
194 execvp (arg[0], arg);
195 _exit (0);
196 #undef NUM_FIXEDARGS
197 }
198
199 /* Father process. */
200 close (f[1]);
201 wait (NULL);
202 output = fdopen (f[0], "r");
203 i = -1;
204
205 if (fgets (func, sizeof(func), output))
206 {
207 st_printf ("\nBacktrace for this error:\n");
208
209 do
210 {
211 if (! fgets (file, sizeof(file), output))
212 goto fallback;
213
214 i++;
215
216 for (p = func; *p != '\n' && *p != '\r'; p++)
217 ;
218
219 *p = '\0';
220
221 /* Try to recognize the internal libgfortran functions. */
222 if (strncasecmp (func, "*_gfortran", 10) == 0
223 || strncasecmp (func, "_gfortran", 9) == 0
224 || strcmp (func, "main") == 0 || strcmp (func, "_start") == 0)
225 continue;
226
227 if (strcasestr (str[i], "libgfortran.so") != NULL
228 || strcasestr (str[i], "libgfortran.dylib") != NULL
229 || strcasestr (str[i], "libgfortran.a") != NULL)
230 continue;
231
232 /* If we only have the address, use the glibc backtrace. */
233 if (func[0] == '?' && func[1] == '?' && file[0] == '?'
234 && file[1] == '?')
235 {
236 st_printf (" + %s\n", str[i]);
237 continue;
238 }
239
240 /* Extract the line number. */
241 for (end = NULL, p = file; *p; p++)
242 if (*p == ':')
243 end = p;
244 if (end != NULL)
245 {
246 *end = '\0';
247 line = atoi (++end);
248 }
249 else
250 line = -1;
251
252 if (strcmp (func, "MAIN__") == 0)
253 st_printf (" + in the main program\n");
254 else
255 st_printf (" + function %s (0x%s)\n", func, addr[i]);
256
257 if (line <= 0 && strcmp (file, "??") == 0)
258 continue;
259
260 if (line <= 0)
261 st_printf (" from file %s\n", file);
262 else
263 st_printf (" at line %d of file %s\n", line, file);
264 }
265 while (fgets (func, sizeof(func), output));
266
267 free (str);
268 return;
269
270 fallback:
271 st_printf ("** Something went wrong while running addr2line. **\n"
272 "** Falling back to a simpler backtrace scheme. **\n");
273 }
274 }
275 while (0);
276
277 #undef DEPTH
278 #undef BUFSIZE
279
280 #endif
281 #endif
282
283 #if CAN_FORK && defined(HAVE_GETPPID)
284 /* Try to call pstack. */
285 do
286 {
287 /* Local variables. */
288 int pid;
289
290 /* Don't output an error message if something goes wrong, we'll simply
291 fall back to the pstack and glibc backtraces. */
292 if ((pid = fork ()) == -1)
293 break;
294
295 if (pid == 0)
296 {
297 /* Child process. */
298 #define NUM_ARGS 2
299 char *arg[NUM_ARGS+1];
300 char buf[20];
301
302 st_printf ("\nBacktrace for this error:\n");
303 arg[0] = (char *) "pstack";
304 #ifdef HAVE_SNPRINTF
305 snprintf (buf, sizeof(buf), "%d", (int) getppid ());
306 #else
307 sprintf (buf, "%d", (int) getppid ());
308 #endif
309 arg[1] = buf;
310 arg[2] = NULL;
311 execvp (arg[0], arg);
312 #undef NUM_ARGS
313
314 /* pstack didn't work, so we fall back to dumping the glibc
315 backtrace if we can. */
316 #if GLIBC_BACKTRACE
317 dump_glibc_backtrace (depth, str);
318 #else
319 st_printf (" unable to produce a backtrace, sorry!\n");
320 #endif
321
322 _exit (0);
323 }
324
325 /* Father process. */
326 wait (NULL);
327 return;
328 }
329 while(0);
330 #endif
331
332 #if GLIBC_BACKTRACE
333 /* Fallback to the glibc backtrace. */
334 st_printf ("\nBacktrace for this error:\n");
335 dump_glibc_backtrace (depth, str);
336 #endif
337 }