Update copyright years in libgfortran.
[gcc.git] / libgfortran / runtime / backtrace.c
1 /* Copyright (C) 2006-2013 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, and
44 can we use pipes to communicate with the subprocess. */
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_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 backtrace (void)
194 {
195 bt_state state;
196 state.frame_number = 0;
197 state.error = 0;
198
199 #if CAN_PIPE
200
201 if (addr2line_path == NULL)
202 goto fallback_noerr;
203
204 /* We attempt to extract file and line information from addr2line. */
205 do
206 {
207 /* Local variables. */
208 int f[2], pid, inp[2];
209
210 /* Don't output an error message if something goes wrong, we'll simply
211 fall back to printing the addresses. */
212 if (pipe (f) != 0)
213 break;
214 if (pipe (inp) != 0)
215 break;
216 if ((pid = fork ()) == -1)
217 break;
218
219 if (pid == 0)
220 {
221 /* Child process. */
222 #define NUM_FIXEDARGS 7
223 char *arg[NUM_FIXEDARGS];
224 char *newenv[] = { NULL };
225
226 close (f[0]);
227
228 close (inp[1]);
229 if (dup2 (inp[0], STDIN_FILENO) == -1)
230 _exit (1);
231 close (inp[0]);
232
233 close (STDERR_FILENO);
234
235 if (dup2 (f[1], STDOUT_FILENO) == -1)
236 _exit (1);
237 close (f[1]);
238
239 arg[0] = addr2line_path;
240 arg[1] = (char *) "-e";
241 arg[2] = full_exe_path ();
242 arg[3] = (char *) "-f";
243 arg[4] = (char *) "-s";
244 arg[5] = (char *) "-C";
245 arg[6] = NULL;
246 execve (addr2line_path, arg, newenv);
247 _exit (1);
248 #undef NUM_FIXEDARGS
249 }
250
251 /* Father process. */
252 close (f[1]);
253 close (inp[0]);
254
255 state.outfd = inp[1];
256 state.infd = f[0];
257 state.direct_output = 0;
258 _Unwind_Backtrace (trace_function, &state);
259 if (state.error)
260 goto fallback;
261 close (inp[1]);
262 close (f[0]);
263 wait (NULL);
264 return;
265
266 fallback:
267 estr_write ("** Something went wrong while running addr2line. **\n"
268 "** Falling back to a simpler backtrace scheme. **\n");
269 }
270 while (0);
271
272 fallback_noerr:
273 #endif /* CAN_PIPE */
274
275 /* Fallback to the simple backtrace without addr2line. */
276 state.direct_output = 1;
277 _Unwind_Backtrace (trace_function, &state);
278 }
279 iexport(backtrace);