(root)/
strace-6.5/
src/
unwind.c
       1  /*
       2   * Copyright (c) 2013 Luca Clementi <luca.clementi@gmail.com>
       3   * Copyright (c) 2013-2023 The strace developers.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   */
       7  
       8  #include "defs.h"
       9  #include "unwind.h"
      10  
      11  #ifdef USE_DEMANGLE
      12  /* Avoids including libiberty.h that has several undesirable definitions */
      13  # define LIBIBERTY_H
      14  
      15  # if defined HAVE_DEMANGLE_H
      16  #  include <demangle.h>
      17  # elif defined HAVE_LIBIBERTY_DEMANGLE_H
      18  #  include <libiberty/demangle.h>
      19  # endif /* HAVE_DEMANGLE_H */
      20  #endif /* USE_DEMANGLE */
      21  
      22  /*
      23   * Type used in stacktrace capturing
      24   */
      25  struct call_t {
      26  	struct call_t *next;
      27  	char *output_line;
      28  };
      29  
      30  struct unwind_queue_t {
      31  	struct call_t *tail;
      32  	struct call_t *head;
      33  };
      34  
      35  static void queue_print(struct unwind_queue_t *queue);
      36  
      37  static const char asprintf_error_str[] = "???";
      38  
      39  void
      40  unwind_init(void)
      41  {
      42  	if (unwinder.init)
      43  		unwinder.init();
      44  }
      45  
      46  void
      47  unwind_tcb_init(struct tcb *tcp)
      48  {
      49  	if (tcp->unwind_queue)
      50  		return;
      51  
      52  	tcp->unwind_queue = xmalloc(sizeof(*tcp->unwind_queue));
      53  	tcp->unwind_queue->head = NULL;
      54  	tcp->unwind_queue->tail = NULL;
      55  
      56  	tcp->unwind_ctx = unwinder.tcb_init(tcp);
      57  }
      58  
      59  void
      60  unwind_tcb_fin(struct tcb *tcp)
      61  {
      62  	if (!tcp->unwind_queue)
      63  		return;
      64  
      65  	queue_print(tcp->unwind_queue);
      66  	free(tcp->unwind_queue);
      67  	tcp->unwind_queue = NULL;
      68  
      69  	unwinder.tcb_fin(tcp);
      70  	tcp->unwind_ctx = NULL;
      71  }
      72  
      73  /*
      74   * printing an entry in stack to stream or buffer
      75   */
      76  /*
      77   * we want to keep the format used by backtrace_symbols from the glibc
      78   *
      79   * ./a.out() [0x40063d]
      80   * ./a.out() [0x4006bb]
      81   * ./a.out() [0x4006c6]
      82   * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
      83   * ./a.out() [0x400569]
      84   */
      85  #define STACK_ENTRY_SYMBOL_FMT(SYM)		\
      86  	" > %s(%s+0x%lx) [0x%lx]\n",		\
      87  	binary_filename,			\
      88  	(SYM),					\
      89  	(unsigned long) function_offset,	\
      90  	true_offset
      91  #define STACK_ENTRY_NOSYMBOL_FMT		\
      92  	" > %s() [0x%lx]\n",			\
      93  	binary_filename, true_offset
      94  #define STACK_ENTRY_BUG_FMT			\
      95  	" > BUG IN %s\n"
      96  #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT	\
      97  	" > %s [0x%lx]\n", error, true_offset
      98  #define STACK_ENTRY_ERROR_FMT			\
      99  	" > %s\n", error
     100  
     101  static void
     102  print_call_cb(void *dummy,
     103  	      const char *binary_filename,
     104  	      const char *symbol_name,
     105  	      unwind_function_offset_t function_offset,
     106  	      unsigned long true_offset)
     107  {
     108  	if (symbol_name && (symbol_name[0] != '\0')) {
     109  #ifdef USE_DEMANGLE
     110  		char *demangled_name =
     111  			cplus_demangle(symbol_name,
     112  				       DMGL_AUTO | DMGL_PARAMS);
     113  #endif
     114  		tprintf_string(STACK_ENTRY_SYMBOL_FMT(
     115  #ifdef USE_DEMANGLE
     116  						      demangled_name ? demangled_name :
     117  #endif
     118  						      symbol_name));
     119  #ifdef USE_DEMANGLE
     120  		free(demangled_name);
     121  #endif
     122  	}
     123  	else if (binary_filename)
     124  		tprintf_string(STACK_ENTRY_NOSYMBOL_FMT);
     125  	else
     126  		tprintf_string(STACK_ENTRY_BUG_FMT, __func__);
     127  
     128  	line_ended();
     129  }
     130  
     131  static void
     132  print_error_cb(void *dummy,
     133  	       const char *error,
     134  	       unsigned long true_offset)
     135  {
     136  	if (true_offset)
     137  		tprintf_string(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
     138  	else
     139  		tprintf_string(STACK_ENTRY_ERROR_FMT);
     140  
     141  	line_ended();
     142  }
     143  
     144  static char *
     145  sprint_call_or_error(const char *binary_filename,
     146  		     const char *symbol_name,
     147  		     unwind_function_offset_t function_offset,
     148  		     unsigned long true_offset,
     149  		     const char *error)
     150  {
     151  	char *output_line = NULL;
     152  	int n;
     153  
     154  	if (symbol_name) {
     155  #ifdef USE_DEMANGLE
     156  		char *demangled_name =
     157  			cplus_demangle(symbol_name,
     158  				       DMGL_AUTO | DMGL_PARAMS);
     159  #endif
     160  		n = asprintf(&output_line,
     161  			     STACK_ENTRY_SYMBOL_FMT(
     162  #ifdef USE_DEMANGLE
     163  						    demangled_name ? demangled_name :
     164  #endif
     165  						    symbol_name));
     166  #ifdef USE_DEMANGLE
     167  		free(demangled_name);
     168  #endif
     169  	}
     170  	else if (binary_filename)
     171  		n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
     172  	else if (error)
     173  		n = true_offset
     174  			? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
     175  			: asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
     176  	else
     177  		n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __func__);
     178  
     179  	if (n < 0) {
     180  		perror_func_msg("asprintf");
     181  		output_line = (char *) asprintf_error_str;
     182  	}
     183  
     184  	return output_line;
     185  }
     186  
     187  /*
     188   * queue manipulators
     189   */
     190  static void
     191  queue_put(struct unwind_queue_t *queue,
     192  	  const char *binary_filename,
     193  	  const char *symbol_name,
     194  	  unwind_function_offset_t function_offset,
     195  	  unsigned long true_offset,
     196  	  const char *error)
     197  {
     198  	struct call_t *call;
     199  
     200  	call = xmalloc(sizeof(*call));
     201  	call->output_line = sprint_call_or_error(binary_filename,
     202  						 symbol_name,
     203  						 function_offset,
     204  						 true_offset,
     205  						 error);
     206  	call->next = NULL;
     207  
     208  	if (!queue->head) {
     209  		queue->head = call;
     210  		queue->tail = call;
     211  	} else {
     212  		queue->tail->next = call;
     213  		queue->tail = call;
     214  	}
     215  }
     216  
     217  static void
     218  queue_put_call(void *queue,
     219  	       const char *binary_filename,
     220  	       const char *symbol_name,
     221  	       unwind_function_offset_t function_offset,
     222  	       unsigned long true_offset)
     223  {
     224  	queue_put(queue,
     225  		  binary_filename,
     226  		  symbol_name,
     227  		  function_offset,
     228  		  true_offset,
     229  		  NULL);
     230  }
     231  
     232  static void
     233  queue_put_error(void *queue,
     234  		const char *error,
     235  		unsigned long ip)
     236  {
     237  	queue_put(queue, NULL, NULL, 0, ip, error);
     238  }
     239  
     240  static void
     241  queue_print(struct unwind_queue_t *queue)
     242  {
     243  	struct call_t *call, *tmp;
     244  
     245  	queue->tail = NULL;
     246  	call = queue->head;
     247  	queue->head = NULL;
     248  	while (call) {
     249  		tmp = call;
     250  		call = call->next;
     251  
     252  		tprints_string(tmp->output_line);
     253  		line_ended();
     254  
     255  		if (tmp->output_line != asprintf_error_str)
     256  			free(tmp->output_line);
     257  
     258  		tmp->output_line = NULL;
     259  		tmp->next = NULL;
     260  		free(tmp);
     261  	}
     262  }
     263  
     264  /*
     265   * printing stack
     266   */
     267  void
     268  unwind_tcb_print(struct tcb *tcp)
     269  {
     270  #if defined(USE_LIBUNWIND) && (SUPPORTED_PERSONALITIES > 1)
     271  	if (tcp->currpers != DEFAULT_PERSONALITY) {
     272  		/* disable stack trace */
     273  		return;
     274  	}
     275  #endif
     276  	if (tcp->unwind_queue->head) {
     277  		debug_func_msg("head: tcp=%p, queue=%p",
     278  			       tcp, tcp->unwind_queue->head);
     279  		queue_print(tcp->unwind_queue);
     280  	} else
     281  		unwinder.tcb_walk(tcp, print_call_cb, print_error_cb, NULL);
     282  }
     283  
     284  /*
     285   * capturing stack
     286   */
     287  void
     288  unwind_tcb_capture(struct tcb *tcp)
     289  {
     290  #if defined(USE_LIBUNWIND) && (SUPPORTED_PERSONALITIES > 1)
     291  	if (tcp->currpers != DEFAULT_PERSONALITY) {
     292  		/* disable stack trace */
     293  		return;
     294  	}
     295  #endif
     296  	if (tcp->unwind_queue->head)
     297  		error_msg_and_die("bug: unprinted entries in queue");
     298  	else {
     299  		debug_func_msg("walk: tcp=%p, queue=%p",
     300  			       tcp, tcp->unwind_queue->head);
     301  		unwinder.tcb_walk(tcp, queue_put_call, queue_put_error,
     302  				  tcp->unwind_queue);
     303  	}
     304  }