OSDN Git Service

PR 46686 Fix incorrect comment.
[pf3gnuchains/gcc-fork.git] / libgfortran / runtime / backtrace.c
1 /* Copyright (C) 2006, 2007, 2009, 2011 Free Software Foundation, Inc.
2    Contributed by François-Xavier Coudert
3
4 This file is part of the GNU Fortran 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 3, or (at your option)
9 any later version.
10
11 Libgfortran is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 <http://www.gnu.org/licenses/>.  */
24
25 #include "libgfortran.h"
26
27 #include <string.h>
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif
36
37 #include <limits.h>
38
39 #include "unwind.h"
40
41
42 /* Macros for common sets of capabilities: can we fork and exec, can
43    we use glibc-style backtrace functions, and can we use pipes.  */
44 #define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVE) \
45                   && defined(HAVE_WAIT))
46 #define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \
47                   && defined(HAVE_DUP2) && defined(HAVE_FDOPEN) \
48                   && defined(HAVE_CLOSE))
49
50 #ifndef PATH_MAX
51 #define PATH_MAX 4096
52 #endif
53
54
55 /* GDB style #NUM index for each stack frame.  */
56
57 static void 
58 bt_header (int num)
59 {
60   st_printf ("#%d  ", num);
61 }
62
63
64 /* fgets()-like function that reads a line from a fd, without
65    needing to malloc() a buffer, and does not use locks, hence should
66    be async-signal-safe.  */
67
68 static char *
69 fd_gets (char *s, int size, int fd)
70 {
71   for (int i = 0; i < size; i++)
72     {
73       char c;
74       ssize_t nread = read (fd, &c, 1);
75       if (nread == 1)
76         {
77           s[i] = c;
78           if (c == '\n')
79             {
80               if (i + 1 < size)
81                 s[i+1] = '\0';
82               else
83                 s[i] = '\0';
84               break;
85             }
86         }
87       else
88         {
89           s[i] = '\0';
90           if (i == 0)
91             return NULL;
92           break;
93         }
94     }
95   return s;
96 }
97
98
99 extern char *addr2line_path;
100
101 /* Struct containing backtrace state.  */
102 typedef struct
103 {
104   int frame_number;
105   int direct_output;
106   int outfd;
107   int infd;
108   int error;
109 }
110 bt_state;
111
112 static _Unwind_Reason_Code
113 trace_function (struct _Unwind_Context *context, void *state_ptr)
114 {
115   bt_state* state = (bt_state*) state_ptr;
116   _Unwind_Ptr ip;
117 #ifdef HAVE_GETIPINFO
118   int ip_before_insn = 0;
119   ip = _Unwind_GetIPInfo (context, &ip_before_insn);
120   
121   /* If the unwinder gave us a 'return' address, roll it back a little
122      to ensure we get the correct line number for the call itself.  */
123   if (! ip_before_insn)
124     --ip;
125 #else  
126   ip = _Unwind_GetIP (context);
127 #endif
128
129   if (state->direct_output)
130     {
131       bt_header(state->frame_number);
132       st_printf ("%p\n", (void*) ip);
133     }
134   else
135     {
136       char addr_buf[GFC_XTOA_BUF_SIZE], func[1024], file[PATH_MAX];
137       char *p;
138       const char* addr = gfc_xtoa (ip, addr_buf, sizeof (addr_buf));
139       write (state->outfd, addr, strlen (addr));
140       write (state->outfd, "\n", 1);
141
142       if (! fd_gets (func, sizeof(func), state->infd))
143         {
144           state->error = 1;
145           goto done;
146         }
147       if (! fd_gets (file, sizeof(file), state->infd))
148         {
149           state->error = 1;
150           goto done;
151         }
152             
153         for (p = func; *p != '\n' && *p != '\r'; p++)
154           ;
155         *p = '\0';
156         
157         /* _start is a setup routine that calls main(), and main() is
158            the frontend routine that calls some setup stuff and then
159            calls MAIN__, so at this point we should stop.  */
160         if (strcmp (func, "_start") == 0 || strcmp (func, "main") == 0)
161           return _URC_END_OF_STACK;
162         
163         bt_header (state->frame_number);
164         estr_write ("0x");
165         estr_write (addr);
166
167         if (func[0] != '?' && func[1] != '?')
168           {
169             estr_write (" in ");
170             estr_write (func);
171           }
172         
173         if (strncmp (file, "??", 2) == 0)
174           estr_write ("\n");
175         else
176           {
177             estr_write (" at ");
178             estr_write (file);
179           }
180     }
181
182  done:
183
184   state->frame_number++;
185   
186   return _URC_NO_REASON;
187 }
188
189
190 /* Display the backtrace.  */
191
192 void
193 show_backtrace (void)
194 {
195   bt_state state;
196   state.frame_number = 0;
197   state.error = 0;
198
199   estr_write ("\nA fatal error occurred! Backtrace for this error:\n");
200
201 #if CAN_PIPE
202
203   if (addr2line_path == NULL)
204     goto fallback_noerr;
205
206   /* We attempt to extract file and line information from addr2line.  */
207   do
208   {
209     /* Local variables.  */
210     int f[2], pid, inp[2];
211
212     /* Don't output an error message if something goes wrong, we'll simply
213        fall back to printing the addresses.  */
214     if (pipe (f) != 0)
215       break;
216     if (pipe (inp) != 0)
217       break;
218     if ((pid = fork ()) == -1)
219       break;
220
221     if (pid == 0)
222       {
223         /* Child process.  */
224 #define NUM_FIXEDARGS 7
225         char *arg[NUM_FIXEDARGS];
226         char *newenv[] = { NULL };
227
228         close (f[0]);
229
230         close (inp[1]);
231         if (dup2 (inp[0], STDIN_FILENO) == -1)
232           _exit (1);
233         close (inp[0]);
234
235         close (STDERR_FILENO);
236
237         if (dup2 (f[1], STDOUT_FILENO) == -1)
238           _exit (1);
239         close (f[1]);
240
241         arg[0] = addr2line_path;
242         arg[1] = (char *) "-e";
243         arg[2] = full_exe_path ();
244         arg[3] = (char *) "-f";
245         arg[4] = (char *) "-s";
246         arg[5] = (char *) "-C";
247         arg[6] = NULL;
248         execve (addr2line_path, arg, newenv);
249         _exit (1);
250 #undef NUM_FIXEDARGS
251       }
252
253     /* Father process.  */
254     close (f[1]);
255     close (inp[0]);
256
257     state.outfd = inp[1];
258     state.infd = f[0];
259     state.direct_output = 0;
260     _Unwind_Backtrace (trace_function, &state);
261     if (state.error)
262       goto fallback;
263     close (inp[1]);
264     wait (NULL);
265     return;
266
267 fallback:
268     estr_write ("** Something went wrong while running addr2line. **\n"
269                 "** Falling back to a simpler backtrace scheme. **\n");
270   }
271   while (0);
272
273 #endif /* CAN_PIPE */
274
275 fallback_noerr:
276   /* Fallback to the simple backtrace without addr2line.  */
277   state.direct_output = 1;
278   _Unwind_Backtrace (trace_function, &state);
279 }