OSDN Git Service

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