]>
Commit | Line | Data |
---|---|---|
e3c063ce | 1 | /* Copyright (C) 2006-2013 Free Software Foundation, Inc. |
868d75db FXC |
2 | Contributed by François-Xavier Coudert |
3 | ||
d30fe1c5 | 4 | This file is part of the GNU Fortran runtime library (libgfortran). |
868d75db FXC |
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 | |
748086b7 | 8 | the Free Software Foundation; either version 3, or (at your option) |
868d75db FXC |
9 | any later version. |
10 | ||
868d75db FXC |
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 | ||
748086b7 JJ |
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/>. */ | |
868d75db | 24 | |
36ae8a61 | 25 | #include "libgfortran.h" |
868d75db | 26 | |
868d75db | 27 | #include <string.h> |
74544378 | 28 | #include <stdlib.h> |
868d75db | 29 | |
868d75db FXC |
30 | #ifdef HAVE_UNISTD_H |
31 | #include <unistd.h> | |
32 | #endif | |
33 | ||
868d75db FXC |
34 | #ifdef HAVE_SYS_WAIT_H |
35 | #include <sys/wait.h> | |
36 | #endif | |
37 | ||
1ff101ff JB |
38 | #include <limits.h> |
39 | ||
40 | #include "unwind.h" | |
868d75db | 41 | |
868d75db | 42 | |
869eea24 JB |
43 | /* Macros for common sets of capabilities: can we fork and exec, and |
44 | can we use pipes to communicate with the subprocess. */ | |
1ff101ff | 45 | #define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVE) \ |
1cc0507d | 46 | && defined(HAVE_WAIT)) |
1cc0507d | 47 | #define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \ |
869eea24 | 48 | && defined(HAVE_DUP2) && defined(HAVE_CLOSE)) |
868d75db | 49 | |
1ff101ff JB |
50 | #ifndef PATH_MAX |
51 | #define PATH_MAX 4096 | |
52 | #endif | |
53 | ||
1cc0507d | 54 | |
eec2794c | 55 | /* GDB style #NUM index for each stack frame. */ |
c861db66 | 56 | |
eec2794c JB |
57 | static void |
58 | bt_header (int num) | |
59 | { | |
1ff101ff | 60 | st_printf ("#%d ", num); |
c861db66 | 61 | } |
868d75db FXC |
62 | |
63 | ||
eec2794c JB |
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. */ | |
868d75db | 67 | |
eec2794c JB |
68 | static char * |
69 | fd_gets (char *s, int size, int fd) | |
70 | { | |
71 | for (int i = 0; i < size; i++) | |
1028b2bd | 72 | { |
eec2794c JB |
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'; | |
8bea6ce4 JB |
90 | if (i == 0) |
91 | return NULL; | |
eec2794c JB |
92 | break; |
93 | } | |
1028b2bd | 94 | } |
eec2794c | 95 | return s; |
868d75db | 96 | } |
eec2794c | 97 | |
868d75db | 98 | |
155732f5 JB |
99 | extern char *addr2line_path; |
100 | ||
1ff101ff JB |
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; | |
155732f5 | 111 | |
1ff101ff JB |
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. */ | |
eec2794c | 191 | |
868d75db | 192 | void |
f0f67c96 | 193 | backtrace (void) |
868d75db | 194 | { |
1ff101ff JB |
195 | bt_state state; |
196 | state.frame_number = 0; | |
197 | state.error = 0; | |
868d75db | 198 | |
868d75db FXC |
199 | #if CAN_PIPE |
200 | ||
155732f5 JB |
201 | if (addr2line_path == NULL) |
202 | goto fallback_noerr; | |
203 | ||
868d75db FXC |
204 | /* We attempt to extract file and line information from addr2line. */ |
205 | do | |
206 | { | |
207 | /* Local variables. */ | |
1ff101ff | 208 | int f[2], pid, inp[2]; |
868d75db FXC |
209 | |
210 | /* Don't output an error message if something goes wrong, we'll simply | |
161f270d | 211 | fall back to printing the addresses. */ |
868d75db FXC |
212 | if (pipe (f) != 0) |
213 | break; | |
eec2794c JB |
214 | if (pipe (inp) != 0) |
215 | break; | |
868d75db FXC |
216 | if ((pid = fork ()) == -1) |
217 | break; | |
218 | ||
219 | if (pid == 0) | |
220 | { | |
221 | /* Child process. */ | |
eec2794c JB |
222 | #define NUM_FIXEDARGS 7 |
223 | char *arg[NUM_FIXEDARGS]; | |
155732f5 | 224 | char *newenv[] = { NULL }; |
868d75db FXC |
225 | |
226 | close (f[0]); | |
eec2794c JB |
227 | |
228 | close (inp[1]); | |
229 | if (dup2 (inp[0], STDIN_FILENO) == -1) | |
230 | _exit (1); | |
231 | close (inp[0]); | |
232 | ||
868d75db FXC |
233 | close (STDERR_FILENO); |
234 | ||
235 | if (dup2 (f[1], STDOUT_FILENO) == -1) | |
eec2794c | 236 | _exit (1); |
868d75db FXC |
237 | close (f[1]); |
238 | ||
155732f5 | 239 | arg[0] = addr2line_path; |
868d75db FXC |
240 | arg[1] = (char *) "-e"; |
241 | arg[2] = full_exe_path (); | |
242 | arg[3] = (char *) "-f"; | |
243 | arg[4] = (char *) "-s"; | |
eec2794c JB |
244 | arg[5] = (char *) "-C"; |
245 | arg[6] = NULL; | |
155732f5 | 246 | execve (addr2line_path, arg, newenv); |
eec2794c | 247 | _exit (1); |
868d75db FXC |
248 | #undef NUM_FIXEDARGS |
249 | } | |
250 | ||
251 | /* Father process. */ | |
252 | close (f[1]); | |
eec2794c | 253 | close (inp[0]); |
868d75db | 254 | |
1ff101ff JB |
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; | |
eec2794c | 261 | close (inp[1]); |
f0f67c96 | 262 | close (f[0]); |
eec2794c JB |
263 | wait (NULL); |
264 | return; | |
868d75db FXC |
265 | |
266 | fallback: | |
eec2794c | 267 | estr_write ("** Something went wrong while running addr2line. **\n" |
1ff101ff | 268 | "** Falling back to a simpler backtrace scheme. **\n"); |
eec2794c | 269 | } |
868d75db FXC |
270 | while (0); |
271 | ||
7ed26a67 | 272 | fallback_noerr: |
eec2794c JB |
273 | #endif /* CAN_PIPE */ |
274 | ||
1ff101ff JB |
275 | /* Fallback to the simple backtrace without addr2line. */ |
276 | state.direct_output = 1; | |
277 | _Unwind_Backtrace (trace_function, &state); | |
868d75db | 278 | } |
f0f67c96 | 279 | iexport(backtrace); |