Configure cleanup.
[gcc.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 #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 ("\nA fatal error occurred! Backtrace 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 }