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