1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2015 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <assert.h>
22 #include <fcntl.h>
23 #include <iostream>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 #include "Common.hh" // for HAVE_EXECINFO_H
32 #ifdef HAVE_EXECINFO_H
33 # include <execinfo.h>
34 # include <cxxabi.h>
35 #define EXEC(x) x
36 #else
37 #define EXEC(x)
38 #endif
39
40 #include "Backtrace.hh"
41
42 using namespace std;
43
44 Backtrace::_lines_status
45 Backtrace::lines_status = Backtrace::LINES_not_checked;
46
47 std::vector<Backtrace::PC_src> Backtrace::pc_2_src;
48
49 //-----------------------------------------------------------------------------
50 const char *
find_src(int64_t pc)51 Backtrace::find_src(int64_t pc)
52 {
53 int pos = 0;
54 int len = pc_2_src.size();
55
56 if (len == 0) return 0; // database not set up.
57 if (pc == -1LL) return 0; // no pc
58
59 while (len > 1)
60 {
61 const int low_len = len/2;
62 const int middle = pos + low_len;
63 const int64_t middle_pc = pc_2_src[middle].pc;
64
65 if (pc < middle_pc) { len = low_len; }
66 else if (pc > middle_pc) { pos = middle; len -= low_len; }
67 else { pos = middle; break; }
68 }
69
70 return pc_2_src[pos].src_loc;
71 }
72 //-----------------------------------------------------------------------------
73 void
open_lines_file()74 Backtrace::open_lines_file()
75 {
76 if (lines_status != LINES_not_checked) return;
77
78 // we are here for the first time.
79 // Assume that apl2.lines is outdated until proven otherwise.
80 //
81 lines_status = LINES_outdated;
82
83 struct stat st;
84 if (stat("apl2", &st)) return; // stat apl2 failed
85
86 const time_t apl2_time = st.st_mtime;
87
88 const int fd = open("apl2.lines", O_RDONLY);
89 if (fd == -1) return;
90
91 if (fstat(fd, &st)) // stat apl2.lines failed
92 {
93 close(fd);
94 return;
95 }
96
97 const time_t apl2_lines_time = st.st_mtime;
98
99 if (apl2_lines_time < apl2_time)
100 {
101 close(fd);
102 cerr << endl
103 << "Cannot show line numbers, since apl2.lines is older "
104 "than apl2." << endl
105 << "Run 'make apl2.lines' to fix this" << endl;
106 return;
107 }
108
109 pc_2_src.reserve(100000);
110 char buffer[1000];
111 FILE * file = fdopen(fd, "r");
112 assert(file);
113
114 const char * src_line = 0;
115 bool new_line = false;
116 int64_t prev_pc = -1LL;
117
118 for (;;)
119 {
120 const char * s = fgets(buffer, sizeof(buffer) - 1, file);
121 if (s == 0) break; // end of file.
122
123 buffer[sizeof(buffer) - 1] = 0;
124 int slen = strlen(buffer);
125 if (slen && buffer[slen - 1] == '\n') buffer[--slen] = 0;
126 if (slen && buffer[slen - 1] == '\r') buffer[--slen] = 0;
127
128 // we look for 2 types of line: abolute source file paths
129 // and code lines.
130 //
131 if (s[0] == '/') // source file path
132 {
133 src_line = strdup(strrchr(s, '/') + 1);
134 new_line = true;
135 continue;
136 }
137
138 if (!new_line) continue;
139
140 if (s[8] == ':') // opcode address
141 {
142 long long pc = -1LL;
143 if (1 == sscanf(s, " %llx:", &pc))
144 {
145 if (pc < prev_pc)
146 {
147 assert(0 && "apl2.lines not ordered by PC");
148 break;
149 }
150
151 PC_src pcs = { pc, src_line };
152 pc_2_src.push_back(pcs);
153 prev_pc = pc;
154 new_line = false;
155 }
156 continue;
157 }
158 }
159
160 fclose(file); // also closes fd
161
162 cerr << "read " << pc_2_src.size() << " line numbers" << endl;
163 lines_status = LINES_valid;
164 }
165 //-----------------------------------------------------------------------------
166 void
show_item(int idx,char * s)167 Backtrace::show_item(int idx, char * s)
168 {
169 #ifdef HAVE_EXECINFO_H
170 //
171 // s looks like this:
172 //
173 // ./apl(_ZN10APL_parser9nextTokenEv+0x1dc) [0x80778dc]
174 //
175
176 // split off address from s.
177 //
178 const char * addr = "????????";
179 int64_t pc = -1LL;
180 {
181 char * space = strchr(s, ' ');
182 if (space)
183 {
184 *space = 0;
185 addr = space + 2;
186 char * e = strchr(space + 2, ']');
187 if (e) *e = 0;
188 pc = strtoll(addr, 0, 16);
189 }
190 }
191
192 const char * src_loc = find_src(pc);
193
194 // split off function from s.
195 //
196 char * fun = 0;
197 {
198 char * opar = strchr(s, '(');
199 if (opar)
200 {
201 *opar = 0;
202 fun = opar + 1;
203 char * e = strchr(opar + 1, ')');
204 if (e) *e = 0;
205 }
206 }
207
208 // split off offset from fun.
209 //
210 int offs = 0;
211 if (fun)
212 {
213 char * plus = strchr(fun, '+');
214 if (plus)
215 {
216 *plus++ = 0;
217 sscanf(plus, "%X", &offs);
218 }
219 }
220
221 char obuf[200] = "@@@@";
222 if (fun)
223 {
224 strncpy(obuf, fun, sizeof(obuf) - 1);
225 obuf[sizeof(obuf) - 1] = 0;
226
227 size_t obuflen = sizeof(obuf) - 1;
228 int status = 0;
229 // cerr << "mangled fun is: " << fun << endl;
230 __cxxabiv1::__cxa_demangle(fun, obuf, &obuflen, &status);
231 switch(status)
232 {
233 case 0: // demangling succeeded
234 break;
235
236 case -2: // not a valid name under the C++ ABI mangling rules.
237 break;
238
239 default:
240 cerr << "__cxa_demangle() returned " << status << endl;
241 break;
242 }
243 }
244
245 // cerr << setw(2) << idx << ": ";
246
247 cerr << HEX(pc);
248
249 // cerr << left << setw(20) << s << right << " ";
250
251 // indent.
252 for (int i = -1; i < idx; ++i) cerr << " ";
253
254 cerr << obuf;
255
256 // if (offs) cerr << " +" << offs;
257
258 if (src_loc) cerr << " at " << src_loc;
259 cerr << endl;
260 #endif
261 }
262 //-----------------------------------------------------------------------------
263 void
show(const char * file,int line)264 Backtrace::show(const char * file, int line)
265 {
266 // CYGWIN, for example, has no execinfo.h and the functions declared there
267 //
268 #ifndef HAVE_EXECINFO_H
269 cerr << "Cannot show function call stack since execinfo.h seems not"
270 " to exist on this OS (WINDOWs ?)." << endl;
271 return;
272
273 #else
274
275 open_lines_file();
276
277 void * buffer[200];
278 const int size = backtrace(buffer, sizeof(buffer)/sizeof(*buffer));
279
280 char ** strings = backtrace_symbols(buffer, size);
281
282 cerr << endl
283 << "----------------------------------------" << endl
284 << "-- Stack trace at " << file << ":" << line << endl
285 << "----------------------------------------" << endl;
286
287 if (strings == 0)
288 {
289 cerr << "backtrace_symbols() failed. Using backtrace_symbols_fd()"
290 " instead..." << endl << endl;
291 // backtrace_symbols_fd(buffer, size, STDERR_FILENO);
292 for (int b = size - 1; b > 0; --b)
293 {
294 for (int s = b + 1; s < size; ++s) cerr << " ";
295 backtrace_symbols_fd(buffer + b, 1, STDERR_FILENO);
296 }
297 cerr << "========================================" << endl;
298 return;
299 }
300
301 for (int i = 1; i < size - 1; ++i)
302 {
303 // make a copy of strings[i] that can be
304 // messed up in show_item().
305 //
306 const char * si = strings[size - i - 1];
307 char cc[strlen(si) + 1];
308 strcpy(cc, si);
309 cc[sizeof(cc) - 1] = 0;
310 show_item(i - 1, cc);
311 }
312 cerr << "========================================" << endl;
313
314 #if 0
315 // crashes at times
316 free(strings); // but not strings[x] !
317 #endif
318
319 #endif
320 }
321 //-----------------------------------------------------------------------------
322 #ifdef HAVE_EXECINFO_H
323 int
demangle_line(char * result,size_t result_max,const char * buf)324 Backtrace::demangle_line(char * result, size_t result_max, const char * buf)
325 {
326 std::string tmp;
327 tmp.reserve(result_max + 1);
328 for (const char * b = buf; *b && b < (buf + result_max); ++b)
329 tmp += *b;
330 tmp.append(0);
331
332 char * e = 0;
333 int status = 3;
334 char * p = strchr(&tmp[0], '(');
335 if (p == 0) goto error;
336 else ++p;
337
338 e = strchr(p, '+');
339 if (e == 0) goto error;
340 else *e = 0;
341
342 // cerr << "mangled fun is: " << p << endl;
343 __cxxabiv1::__cxa_demangle(p, result, &result_max, &status);
344 if (status) goto error;
345 return 0;
346
347 error:
348 strncpy(result, buf, result_max);
349 result[result_max - 1] = 0;
350 return status;
351 }
352 //-----------------------------------------------------------------------------
353 const char *
caller(int offset)354 Backtrace::caller(int offset)
355 {
356 void * buffer[200];
357 const int size = backtrace(buffer, sizeof(buffer)/sizeof(*buffer));
358 char ** strings = backtrace_symbols(buffer, size);
359
360 char * demangled = static_cast<char *>(malloc(200));
361 if (demangled)
362 {
363 *demangled = 0;
364 demangle_line(demangled, 200, strings[offset]);
365 return demangled;
366 }
367 return strings[offset];
368 }
369 //-----------------------------------------------------------------------------
370 #endif // HAVE_EXECINFO_H
371