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