1 /*============================================================================
2  * Obtaining a stack backtrace
3  *============================================================================*/
4 
5 /*
6   This file is part of Code_Saturne, a general-purpose CFD tool.
7 
8   Copyright (C) 1998-2021 EDF S.A.
9 
10   This program is free software; you can redistribute it and/or modify it under
11   the terms of the GNU General Public License as published by the Free Software
12   Foundation; either version 2 of the License, or (at your option) any later
13   version.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18   details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22   Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24 
25 /*----------------------------------------------------------------------------*/
26 
27 #include "cs_defs.h"
28 
29 /*-----------------------------------------------------------------------------*/
30 
31 #if defined(HAVE_GLIBC_BACKTRACE) && defined(__GNUC__)
32 #define _GNU_SOURCE
33 #endif
34 
35 /*
36  * Standard C library headers
37  */
38 
39 #include <assert.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #if defined(HAVE_GLIBC_BACKTRACE)
46 #include <memory.h>
47 #include <execinfo.h>
48 #endif
49 
50 #if defined(HAVE_GLIBC_BACKTRACE) && defined(HAVE_CPLUS_DEMANGLE)
51 #include <demangle.h>
52 #endif
53 
54 /*
55  * Optional library and BFT headers
56  */
57 
58 #include "bft_backtrace.h"
59 
60 /*-----------------------------------------------------------------------------*/
61 
62 BEGIN_C_DECLS
63 
64 /*-----------------------------------------------------------------------------
65  * Additional doxygen documentation
66  *----------------------------------------------------------------------------*/
67 
68 /* Associated typedef documentation (for bft_backtrace.h) */
69 
70 /*!
71  * \typedef bft_backtrace_print_t
72  *
73  * \brief Function pointer to backtrace print function.
74  *
75  * \param [in] start_depth    depth of backtrace at which to start printing
76  *                            (0 for all, including backtrace print function)
77  */
78 
79 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
80 
81 /*-----------------------------------------------------------------------------
82  * Local type definitions
83  *-----------------------------------------------------------------------------*/
84 
85 /*
86  * BFT backtrace descriptor
87  */
88 
89 struct _bft_backtrace_t {
90 
91   int       size;        /* Total depth of backtrace */
92 
93   char    **s_file;      /* File names */
94   char    **s_func;      /* Function names */
95   char    **s_addr;      /* Addresses */
96 
97 };
98 
99 /*-----------------------------------------------------------------------------
100  * Local static variable definitions
101  *-----------------------------------------------------------------------------*/
102 
103 static bft_backtrace_print_t  *_bft_backtrace_print = NULL;
104 
105 /*-----------------------------------------------------------------------------
106  * Local function definitions
107  *-----------------------------------------------------------------------------*/
108 
109 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
110 
111 /*============================================================================
112  * Public function definitions
113  *============================================================================*/
114 
115 /*!
116  * \brief Build a backtrace description structure.
117  *
118  * \return pointer to bft_backtrace_t backtrace descriptor (NULL in case of
119  *         error, or if backtracing is unavailable on this architecture).
120  */
121 
122 bft_backtrace_t *
bft_backtrace_create(void)123 bft_backtrace_create(void)
124 {
125 #if defined(HAVE_GLIBC_BACKTRACE)
126 
127   int  i, j, l;
128 
129   bft_backtrace_t  *bt = NULL;
130 
131   /* Create backtrace structure */
132 
133   bt = malloc(sizeof(bft_backtrace_t));
134 
135   if (bt != NULL) {
136 
137     void   *tr_array[200];
138     int  tr_size = backtrace(tr_array, 200);
139     char  **tr_strings = backtrace_symbols(tr_array, tr_size);
140 
141     /* Create arrays; we use malloc() here instead of BFT_MALLOC, as a
142      backtrace is useful mainly in case of severe errors, so we avoid
143      higher level constructs as much as possible at this stage. */
144 
145     if (tr_size < 2 || tr_strings == NULL) {
146       free(bt);
147       return NULL;
148     }
149 
150     bt->size = tr_size - 1;
151 
152     bt->s_file = malloc(tr_size * sizeof(char *));
153     bt->s_func = malloc(tr_size * sizeof(char *));
154     bt->s_addr = malloc(tr_size * sizeof(char *));
155 
156     /* If allocation has failed, free other allocated arrays, and return NULL */
157 
158     if (   bt->s_file == NULL || bt->s_func == NULL || bt->s_addr == NULL) {
159 
160       if (bt->s_file  != NULL)
161         free(bt->s_file);
162       if (bt->s_func  != NULL)
163         free(bt->s_func);
164       if (bt->s_addr  != NULL)
165         free(bt->s_addr);
166 
167       free(bt);
168       return NULL;
169 
170     }
171 
172     for (i = 0; i < bt->size; i++) {
173       bt->s_file[i] = NULL;
174       bt->s_func[i] = NULL;
175       bt->s_addr[i] = NULL;
176     }
177 
178     /* Now parse backtrace strings and build arrays */
179 
180     for (i = 0; i < bt->size; i++) {
181 
182       char *s = tr_strings[i+1]; /* Shift by 1 to ignore current function */
183 
184       const char  *s_addr = NULL;
185       const char  *s_func = NULL;
186       const char  *s_file = NULL;
187 
188       for (l = 0; s[l] != '\0'; l++);
189 
190       /* Remove brackets around adress */
191       for (j = l; j > 0 && s[j] !=']'; j--);
192       if (s[j] == ']') {
193         s[j] = '\0';
194         l = j;
195         for (j = l-1; j > 0 && s[j] !='['; j--);
196         if (s[j] == '[') {
197           s_addr = s+j+1;
198           bt->s_addr[i] = malloc((strlen(s_addr)+1) * sizeof(char));
199           if (bt->s_addr[i] != NULL)
200             strcpy(bt->s_addr[i], s_addr);
201         }
202       }
203       if (j == 0)
204         continue;
205 
206       /* Find function name and position (in parentheses) */
207       while (j > 0 && s[j] != ')')
208         j--;
209       if (s[j] == ')') {
210         s[j] = '\0';
211         while (j > 0 && s[j] != '(')
212           j--;
213         if (j > 0 && s[j] == '(') {
214           s_func = s+j+1;
215           while (j > 0 && (s[j] == '(' || s[j] == ' '))
216             s[j--] = '\0';
217           bt->s_func[i] = malloc((strlen(s_func)+1) * sizeof(char));
218           if (bt->s_func[i] != NULL)
219             strcpy(bt->s_func[i], s_func);
220         }
221       }
222       if (j == 0)
223         continue;
224 
225       /* Find executable or library name */
226 
227       if (s_func == NULL) {/* With no function name found */
228         for (j = 0; j < l && s[j] != ' '; j++);
229         if (s[j] == ' ')
230           s[j] = '\0';
231       }
232 
233       while (j > 0 && s[j] != '/')
234         j--;
235       if (j < l) {
236         s_file = s+j+1;
237         bt->s_file[i] = malloc((strlen(s_file)+1) * sizeof(char));
238         if (bt->s_file[i] != NULL)
239           strcpy(bt->s_file[i], s_file);
240       }
241 
242     }
243 
244     /* Free temporary memory
245        (only main pointer needs to be freed according to glibc documentation) */
246 
247     free((void *)tr_strings);
248 
249   }
250 
251   return bt;
252 
253 #else /* defined(HAVE_GLIBC_BACKTRACE) */
254   return NULL;
255 #endif
256 }
257 
258 /*!
259  * \brief Free a backtrace description structure.
260  *
261  * \param [in, out] bt pointer to backtrace description structure.
262  *
263  * \return NULL pointer.
264  */
265 
266 bft_backtrace_t *
bft_backtrace_destroy(bft_backtrace_t * bt)267 bft_backtrace_destroy(bft_backtrace_t  *bt)
268 {
269   int  i;
270 
271   if (bt != NULL) {
272 
273     for (i = 0; i < bt->size; i++) {
274 
275       if (bt->s_file[i] != NULL)
276         free(bt->s_file[i]);
277       if (bt->s_func[i] != NULL)
278         free(bt->s_func[i]);
279       if (bt->s_addr[i] != NULL)
280         free(bt->s_addr[i]);
281 
282     }
283 
284     if (bt->s_file != NULL)
285         free(bt->s_file);
286     if (bt->s_func != NULL)
287       free(bt->s_func);
288     if (bt->s_addr != NULL)
289       free(bt->s_addr);
290 
291     free(bt);
292 
293   }
294 
295   return NULL;
296 }
297 
298 /*!
299  * \brief Demangle a backtrace description structure (for C++).
300  *
301  * \param [in, out] bt pointer to backtrace description structure.
302  */
303 
304 void
bft_backtrace_demangle(bft_backtrace_t * bt)305 bft_backtrace_demangle(bft_backtrace_t  *bt)
306 {
307 #if defined(HAVE_GLIBC_BACKTRACE) && defined(HAVE_CPLUS_DEMANGLE)
308 
309   int  i, j, l;
310 
311   if (bt != NULL) {
312 
313     for (i = 0; i < bt->size; i++) {
314 
315       char *s_cplus_func_p = NULL;
316       char *s_cplus_func = NULL;
317       int l2 = 0;
318 
319       if (bt->s_func[i] == NULL)
320         continue;
321 
322       for (j = 0; bt->s_func[i][j] != '\0' && bt->s_func[i][j] != '+'; j++);
323 
324       if (bt->s_func[i][j] == '+') {
325         l2 = strlen(bt->s_func[i] + j);
326         bt->s_func[i][j] = '\0';
327       }
328 
329       s_cplus_func_p = cplus_demangle(bt->s_func[i], auto_demangling);
330       printf("%s ; %s\n", bt->s_func[i], s_cplus_func_p);
331 
332       if (s_cplus_func_p == NULL)
333         continue;
334 
335       l = strlen(s_cplus_func_p);
336 
337       if (l == 0)
338         continue;
339 
340       s_cplus_func = malloc(l + l2 + 1);
341 
342       if (s_cplus_func != NULL) {
343         strncpy(s_cplus_func, s_cplus_func_p, l + 1);
344         if (l2 > 0) {
345           bt->s_func[i][j] = '+';
346           strcpy(s_cplus_func + l, bt->s_func[i] + j);
347         }
348         s_cplus_func[l + l2] = '\0';
349         free(bt->s_func[i]);
350         bt->s_func[i] = s_cplus_func;
351 
352       }
353 
354     }
355 
356   }
357 
358 #else
359 
360   CS_UNUSED(bt);
361 
362 #endif /* defined(HAVE_GLIBC_BACKTRACE) && defined(HAVE_CPLUS_DEMANGLE) */
363 }
364 
365 /*!
366  * \brief Return the depth of a backtrace.
367  *
368  * \param [in] bt pointer to backtrace description structure.
369  *
370  * \return backtrace depth.
371  */
372 
373 int
bft_backtrace_size(const bft_backtrace_t * bt)374 bft_backtrace_size(const bft_backtrace_t  *bt)
375 {
376   return bt->size;
377 }
378 
379 /*!
380  * \brief Return file name associated with a backtrace at a given depth.
381  *
382  * \param [in] bt pointer to backtrace description structure.
383  * \param [in] depth index in backtrace structure (< bft_backtrace_size(bt)).
384  *
385  * \return file name at the given depth, or NULL.
386  */
387 
388 const char *
bft_backtrace_file(bft_backtrace_t * bt,int depth)389 bft_backtrace_file(bft_backtrace_t  *bt,
390                    int               depth)
391 {
392   const char *retval = NULL;
393 
394   if (bt != NULL) {
395     if (depth < bt->size)
396       retval = bt->s_file[depth];
397   }
398 
399   return retval;
400 }
401 
402 /*!
403  * \brief Return function name associated with a backtrace at a given depth.
404  *
405  * \param [in] bt pointer to backtrace description structure.
406  * \param [in] depth index in backtrace structure (< bft_backtrace_size(bt)).
407  *
408  * \return function name at the given depth, or NULL.
409  */
410 
411 const char *
bft_backtrace_function(bft_backtrace_t * bt,int depth)412 bft_backtrace_function(bft_backtrace_t  *bt,
413                        int               depth)
414 {
415   const char *retval = NULL;
416 
417   if (bt != NULL) {
418     if (depth < bt->size)
419       retval = bt->s_func[depth];
420   }
421 
422   return retval;
423 }
424 
425 /*!
426  * \brief Return address associated with a backtrace at a given depth.
427  *
428  * \param [in] bt pointer to backtrace description structure.
429  * \param [in] depth index in backtrace structure (< bft_backtrace_size(bt)).
430  *
431  * \return  address at the given depth, or NULL.
432  */
433 
434 const char *
bft_backtrace_address(bft_backtrace_t * bt,int depth)435 bft_backtrace_address(bft_backtrace_t  *bt,
436                       int               depth)
437 {
438   const char *retval = NULL;
439 
440   if (bt != NULL) {
441     if (depth < bt->size)
442       retval = bt->s_addr[depth];
443   }
444 
445   return retval;
446 }
447 
448 /*!
449  * \brief Print a backtrace.
450  *
451  * \param [in] start_depth    depth of backtrace at which to start printing
452  *                            (0 for all, including backtrace print function)
453  */
454 
455 void
bft_backtrace_print(int start_depth)456 bft_backtrace_print(int  start_depth)
457 {
458   if (_bft_backtrace_print != NULL)
459     _bft_backtrace_print(start_depth);
460 }
461 
462 /*!
463  * \brief Returns backtrace print function.
464  *
465  * \returns pointer to the backtrace print function.
466  */
467 
468 bft_backtrace_print_t *
bft_backtrace_print_get(void)469 bft_backtrace_print_get(void)
470 {
471   return _bft_backtrace_print;
472 }
473 
474 /*!
475  * \brief Sets a backtrace print function.
476  *
477  * \param [in] fct pointer to a bft_backtrace_print_t type function.
478  */
479 
480 void
bft_backtrace_print_set(bft_backtrace_print_t * const fct)481 bft_backtrace_print_set(bft_backtrace_print_t  *const fct)
482 {
483   _bft_backtrace_print = fct;
484 }
485 
486 /*-----------------------------------------------------------------------------*/
487 
488 END_C_DECLS
489