OSDN Git Service

PR libfortran/21185
[pf3gnuchains/gcc-fork.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 #include "libgfortran.h"
31
32 #include <string.h>
33
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37
38 #ifdef HAVE_INTTYPES_H
39 #include <inttypes.h>
40 #endif
41
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #ifdef HAVE_INTPTR_T
47 # define INTPTR_T intptr_t
48 #else
49 # define INTPTR_T int
50 #endif
51
52 #ifdef HAVE_EXECINFO_H
53 #include <execinfo.h>
54 #endif
55
56 #ifdef HAVE_SYS_WAIT_H
57 #include <sys/wait.h>
58 #endif
59
60 #include <ctype.h>
61
62
63
64 static char *
65 local_strcasestr (const char *s1, const char *s2)
66 {
67 #ifdef HAVE_STRCASESTR
68   return strcasestr (s1, s2);
69 #else
70
71   const char *p = s1;
72   const size_t len = strlen (s2);
73   const char u = *s2, v = isupper((int) *s2) ? tolower((int) *s2)
74                                   : (islower((int) *s2) ? toupper((int) *s2)
75                                                         : *s2);
76
77   while (1)
78     {
79       while (*p != u && *p != v && *p)
80         p++;
81       if (*p == 0)
82         return NULL;
83       if (strncasecmp (p, s2, len) == 0)
84         return (char *)p;
85     }
86 #endif
87 }
88
89 #define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVP) \
90                   && defined(HAVE_WAIT))
91 #define GLIBC_BACKTRACE (defined(HAVE_BACKTRACE) \
92                          && defined(HAVE_BACKTRACE_SYMBOLS))
93 #define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \
94                   && defined(HAVE_DUP2) && defined(HAVE_FDOPEN) \
95                   && defined(HAVE_CLOSE))
96
97
98 #if GLIBC_BACKTRACE
99 static void
100 dump_glibc_backtrace (int depth, char *str[])
101 {
102   int i;
103
104   for (i = 0; i < depth; i++)
105     st_printf ("  + %s\n", str[i]);
106
107   free (str);
108 }
109 #endif
110
111 /* show_backtrace displays the backtrace, currently obtained by means of
112    the glibc backtrace* functions.  */
113 void
114 show_backtrace (void)
115 {
116 #if GLIBC_BACKTRACE
117
118 #define DEPTH 50
119 #define BUFSIZE 1024
120
121   void *trace[DEPTH];
122   char **str;
123   int depth;
124
125   depth = backtrace (trace, DEPTH);
126   if (depth <= 0)
127     return;
128
129   str = backtrace_symbols (trace, depth);
130
131 #if CAN_PIPE
132
133 #ifndef STDIN_FILENO
134 #define STDIN_FILENO 0
135 #endif
136
137 #ifndef STDOUT_FILENO
138 #define STDOUT_FILENO 1
139 #endif
140
141 #ifndef STDERR_FILENO
142 #define STDERR_FILENO 2
143 #endif
144
145   /* We attempt to extract file and line information from addr2line.  */
146   do
147   {
148     /* Local variables.  */
149     int f[2], pid, line, i;
150     FILE *output;
151     char addr_buf[DEPTH][GFC_XTOA_BUF_SIZE], func[BUFSIZE], file[BUFSIZE];
152     char *p, *end;
153     const char *addr[DEPTH];
154
155     /* Write the list of addresses in hexadecimal format.  */
156     for (i = 0; i < depth; i++)
157       addr[i] = xtoa ((GFC_UINTEGER_LARGEST) (INTPTR_T) trace[i], addr_buf[i],
158                       sizeof (addr_buf[i]));
159
160     /* Don't output an error message if something goes wrong, we'll simply
161        fall back to the pstack and glibc backtraces.  */
162     if (pipe (f) != 0)
163       break;
164     if ((pid = fork ()) == -1)
165       break;
166
167     if (pid == 0)
168       {
169         /* Child process.  */
170 #define NUM_FIXEDARGS 5
171         char *arg[DEPTH+NUM_FIXEDARGS+1];
172
173         close (f[0]);
174         close (STDIN_FILENO);
175         close (STDERR_FILENO);
176
177         if (dup2 (f[1], STDOUT_FILENO) == -1)
178           _exit (0);
179         close (f[1]);
180
181         arg[0] = (char *) "addr2line";
182         arg[1] = (char *) "-e";
183         arg[2] = full_exe_path ();
184         arg[3] = (char *) "-f";
185         arg[4] = (char *) "-s";
186         for (i = 0; i < depth; i++)
187           arg[NUM_FIXEDARGS+i] = (char *) addr[i];
188         arg[NUM_FIXEDARGS+depth] = NULL;
189         execvp (arg[0], arg);
190         _exit (0);
191 #undef NUM_FIXEDARGS
192       }
193
194     /* Father process.  */
195     close (f[1]);
196     wait (NULL);
197     output = fdopen (f[0], "r");
198     i = -1;
199
200     if (fgets (func, sizeof(func), output))
201       {
202         st_printf ("\nBacktrace for this error:\n");
203
204         do
205           {
206             if (! fgets (file, sizeof(file), output))
207               goto fallback;
208
209             i++;
210
211             for (p = func; *p != '\n' && *p != '\r'; p++)
212               ;
213
214             *p = '\0';
215
216             /* Try to recognize the internal libgfortran functions.  */
217             if (strncasecmp (func, "*_gfortran", 10) == 0
218                 || strncasecmp (func, "_gfortran", 9) == 0
219                 || strcmp (func, "main") == 0 || strcmp (func, "_start") == 0
220                 || strcmp (func, "_gfortrani_handler") == 0)
221               continue;
222
223             if (local_strcasestr (str[i], "libgfortran.so") != NULL
224                 || local_strcasestr (str[i], "libgfortran.dylib") != NULL
225                 || local_strcasestr (str[i], "libgfortran.a") != NULL)
226               continue;
227
228             /* If we only have the address, use the glibc backtrace.  */
229             if (func[0] == '?' && func[1] == '?' && file[0] == '?'
230                 && file[1] == '?')
231               {
232                 st_printf ("  + %s\n", str[i]);
233                 continue;
234               }
235
236             /* Extract the line number.  */
237             for (end = NULL, p = file; *p; p++)
238               if (*p == ':')
239                 end = p;
240             if (end != NULL)
241               {
242                 *end = '\0';
243                 line = atoi (++end);
244               }
245             else
246               line = -1;
247
248             if (strcmp (func, "MAIN__") == 0)
249               st_printf ("  + in the main program\n");
250             else
251               st_printf ("  + function %s (0x%s)\n", func, addr[i]);
252
253             if (line <= 0 && strcmp (file, "??") == 0)
254               continue;
255
256             if (line <= 0)
257               st_printf ("    from file %s\n", file);
258             else
259               st_printf ("    at line %d of file %s\n", line, file);
260           }
261         while (fgets (func, sizeof(func), output));
262
263         free (str);
264         return;
265
266 fallback:
267         st_printf ("** Something went wrong while running addr2line. **\n"
268                    "** Falling back  to a simpler  backtrace scheme. **\n");
269       }
270     }
271   while (0);
272
273 #undef DEPTH
274 #undef BUFSIZE
275
276 #endif
277 #endif
278
279 #if CAN_FORK && defined(HAVE_GETPPID)
280   /* Try to call pstack.  */
281   do
282   {
283     /* Local variables.  */
284     int pid;
285
286     /* Don't output an error message if something goes wrong, we'll simply
287        fall back to the pstack and glibc backtraces.  */
288     if ((pid = fork ()) == -1)
289       break;
290
291     if (pid == 0)
292       {
293         /* Child process.  */
294 #define NUM_ARGS 2
295         char *arg[NUM_ARGS+1];
296         char buf[20];
297
298         st_printf ("\nBacktrace for this error:\n");
299         arg[0] = (char *) "pstack";
300 #ifdef HAVE_SNPRINTF
301         snprintf (buf, sizeof(buf), "%d", (int) getppid ());
302 #else
303         sprintf (buf, "%d", (int) getppid ());
304 #endif
305         arg[1] = buf;
306         arg[2] = NULL;
307         execvp (arg[0], arg);
308 #undef NUM_ARGS
309
310         /* pstack didn't work, so we fall back to dumping the glibc
311            backtrace if we can.  */
312 #if GLIBC_BACKTRACE
313         dump_glibc_backtrace (depth, str);
314 #else
315         st_printf ("  unable to produce a backtrace, sorry!\n");
316 #endif
317
318         _exit (0);
319       }
320
321     /* Father process.  */
322     wait (NULL);
323     return;
324   }
325   while(0);
326 #endif
327
328 #if GLIBC_BACKTRACE
329   /* Fallback to the glibc backtrace.  */
330   st_printf ("\nBacktrace for this error:\n");
331   dump_glibc_backtrace (depth, str);
332 #endif
333 }