Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2013 Luca Clementi <luca.clementi (at) gmail.com>
      3  * Copyright (c) 2013-2018 The strace developers.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. The name of the author may not be used to endorse or promote products
     14  *    derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "defs.h"
     29 #include "unwind.h"
     30 
     31 #ifdef USE_DEMANGLE
     32 # if defined HAVE_DEMANGLE_H
     33 #  include <demangle.h>
     34 # elif defined HAVE_LIBIBERTY_DEMANGLE_H
     35 #  include <libiberty/demangle.h>
     36 # endif
     37 #endif
     38 
     39 /*
     40  * Type used in stacktrace capturing
     41  */
     42 struct call_t {
     43 	struct call_t *next;
     44 	char *output_line;
     45 };
     46 
     47 struct unwind_queue_t {
     48 	struct call_t *tail;
     49 	struct call_t *head;
     50 };
     51 
     52 static void queue_print(struct unwind_queue_t *queue);
     53 
     54 static const char asprintf_error_str[] = "???";
     55 
     56 void
     57 unwind_init(void)
     58 {
     59 	if (unwinder.init)
     60 		unwinder.init();
     61 }
     62 
     63 void
     64 unwind_tcb_init(struct tcb *tcp)
     65 {
     66 	if (tcp->unwind_queue)
     67 		return;
     68 
     69 	tcp->unwind_queue = xmalloc(sizeof(*tcp->unwind_queue));
     70 	tcp->unwind_queue->head = NULL;
     71 	tcp->unwind_queue->tail = NULL;
     72 
     73 	tcp->unwind_ctx = unwinder.tcb_init(tcp);
     74 }
     75 
     76 void
     77 unwind_tcb_fin(struct tcb *tcp)
     78 {
     79 	if (!tcp->unwind_queue)
     80 		return;
     81 
     82 	queue_print(tcp->unwind_queue);
     83 	free(tcp->unwind_queue);
     84 	tcp->unwind_queue = NULL;
     85 
     86 	unwinder.tcb_fin(tcp);
     87 	tcp->unwind_ctx = NULL;
     88 }
     89 
     90 /*
     91  * printing an entry in stack to stream or buffer
     92  */
     93 /*
     94  * we want to keep the format used by backtrace_symbols from the glibc
     95  *
     96  * ./a.out() [0x40063d]
     97  * ./a.out() [0x4006bb]
     98  * ./a.out() [0x4006c6]
     99  * /lib64/libc.so.6(__libc_start_main+0xed) [0x7fa2f8a5976d]
    100  * ./a.out() [0x400569]
    101  */
    102 #define STACK_ENTRY_SYMBOL_FMT(SYM)		\
    103 	" > %s(%s+0x%lx) [0x%lx]\n",		\
    104 	binary_filename,			\
    105 	(SYM),					\
    106 	(unsigned long) function_offset,	\
    107 	true_offset
    108 #define STACK_ENTRY_NOSYMBOL_FMT		\
    109 	" > %s() [0x%lx]\n",			\
    110 	binary_filename, true_offset
    111 #define STACK_ENTRY_BUG_FMT			\
    112 	" > BUG IN %s\n"
    113 #define STACK_ENTRY_ERROR_WITH_OFFSET_FMT	\
    114 	" > %s [0x%lx]\n", error, true_offset
    115 #define STACK_ENTRY_ERROR_FMT			\
    116 	" > %s\n", error
    117 
    118 static void
    119 print_call_cb(void *dummy,
    120 	      const char *binary_filename,
    121 	      const char *symbol_name,
    122 	      unwind_function_offset_t function_offset,
    123 	      unsigned long true_offset)
    124 {
    125 	if (symbol_name && (symbol_name[0] != '\0')) {
    126 #ifdef USE_DEMANGLE
    127 		char *demangled_name =
    128 			cplus_demangle(symbol_name,
    129 				       DMGL_AUTO | DMGL_PARAMS);
    130 #endif
    131 		tprintf(STACK_ENTRY_SYMBOL_FMT(
    132 #ifdef USE_DEMANGLE
    133 					       demangled_name ? demangled_name :
    134 #endif
    135 					       symbol_name));
    136 #ifdef USE_DEMANGLE
    137 		free(demangled_name);
    138 #endif
    139 	}
    140 	else if (binary_filename)
    141 		tprintf(STACK_ENTRY_NOSYMBOL_FMT);
    142 	else
    143 		tprintf(STACK_ENTRY_BUG_FMT, __func__);
    144 
    145 	line_ended();
    146 }
    147 
    148 static void
    149 print_error_cb(void *dummy,
    150 	       const char *error,
    151 	       unsigned long true_offset)
    152 {
    153 	if (true_offset)
    154 		tprintf(STACK_ENTRY_ERROR_WITH_OFFSET_FMT);
    155 	else
    156 		tprintf(STACK_ENTRY_ERROR_FMT);
    157 
    158 	line_ended();
    159 }
    160 
    161 static char *
    162 sprint_call_or_error(const char *binary_filename,
    163 		     const char *symbol_name,
    164 		     unwind_function_offset_t function_offset,
    165 		     unsigned long true_offset,
    166 		     const char *error)
    167 {
    168 	char *output_line = NULL;
    169 	int n;
    170 
    171 	if (symbol_name) {
    172 #ifdef USE_DEMANGLE
    173 		char *demangled_name =
    174 			cplus_demangle(symbol_name,
    175 				       DMGL_AUTO | DMGL_PARAMS);
    176 #endif
    177 		n = asprintf(&output_line,
    178 			     STACK_ENTRY_SYMBOL_FMT(
    179 #ifdef USE_DEMANGLE
    180 						    demangled_name ? demangled_name :
    181 #endif
    182 						    symbol_name));
    183 #ifdef USE_DEMANGLE
    184 		free(demangled_name);
    185 #endif
    186 	}
    187 	else if (binary_filename)
    188 		n = asprintf(&output_line, STACK_ENTRY_NOSYMBOL_FMT);
    189 	else if (error)
    190 		n = true_offset
    191 			? asprintf(&output_line, STACK_ENTRY_ERROR_WITH_OFFSET_FMT)
    192 			: asprintf(&output_line, STACK_ENTRY_ERROR_FMT);
    193 	else
    194 		n = asprintf(&output_line, STACK_ENTRY_BUG_FMT, __func__);
    195 
    196 	if (n < 0) {
    197 		perror_func_msg("asprintf");
    198 		output_line = (char *) asprintf_error_str;
    199 	}
    200 
    201 	return output_line;
    202 }
    203 
    204 /*
    205  * queue manipulators
    206  */
    207 static void
    208 queue_put(struct unwind_queue_t *queue,
    209 	  const char *binary_filename,
    210 	  const char *symbol_name,
    211 	  unwind_function_offset_t function_offset,
    212 	  unsigned long true_offset,
    213 	  const char *error)
    214 {
    215 	struct call_t *call;
    216 
    217 	call = xmalloc(sizeof(*call));
    218 	call->output_line = sprint_call_or_error(binary_filename,
    219 						 symbol_name,
    220 						 function_offset,
    221 						 true_offset,
    222 						 error);
    223 	call->next = NULL;
    224 
    225 	if (!queue->head) {
    226 		queue->head = call;
    227 		queue->tail = call;
    228 	} else {
    229 		queue->tail->next = call;
    230 		queue->tail = call;
    231 	}
    232 }
    233 
    234 static void
    235 queue_put_call(void *queue,
    236 	       const char *binary_filename,
    237 	       const char *symbol_name,
    238 	       unwind_function_offset_t function_offset,
    239 	       unsigned long true_offset)
    240 {
    241 	queue_put(queue,
    242 		  binary_filename,
    243 		  symbol_name,
    244 		  function_offset,
    245 		  true_offset,
    246 		  NULL);
    247 }
    248 
    249 static void
    250 queue_put_error(void *queue,
    251 		const char *error,
    252 		unsigned long ip)
    253 {
    254 	queue_put(queue, NULL, NULL, 0, ip, error);
    255 }
    256 
    257 static void
    258 queue_print(struct unwind_queue_t *queue)
    259 {
    260 	struct call_t *call, *tmp;
    261 
    262 	queue->tail = NULL;
    263 	call = queue->head;
    264 	queue->head = NULL;
    265 	while (call) {
    266 		tmp = call;
    267 		call = call->next;
    268 
    269 		tprints(tmp->output_line);
    270 		line_ended();
    271 
    272 		if (tmp->output_line != asprintf_error_str)
    273 			free(tmp->output_line);
    274 
    275 		tmp->output_line = NULL;
    276 		tmp->next = NULL;
    277 		free(tmp);
    278 	}
    279 }
    280 
    281 /*
    282  * printing stack
    283  */
    284 void
    285 unwind_tcb_print(struct tcb *tcp)
    286 {
    287 #if SUPPORTED_PERSONALITIES > 1
    288 	if (tcp->currpers != DEFAULT_PERSONALITY) {
    289 		/* disable stack trace */
    290 		return;
    291 	}
    292 #endif
    293 	if (tcp->unwind_queue->head) {
    294 		debug_func_msg("head: tcp=%p, queue=%p",
    295 			       tcp, tcp->unwind_queue->head);
    296 		queue_print(tcp->unwind_queue);
    297 	} else
    298 		unwinder.tcb_walk(tcp, print_call_cb, print_error_cb, NULL);
    299 }
    300 
    301 /*
    302  * capturing stack
    303  */
    304 void
    305 unwind_tcb_capture(struct tcb *tcp)
    306 {
    307 #if SUPPORTED_PERSONALITIES > 1
    308 	if (tcp->currpers != DEFAULT_PERSONALITY) {
    309 		/* disable stack trace */
    310 		return;
    311 	}
    312 #endif
    313 	if (tcp->unwind_queue->head)
    314 		error_msg_and_die("bug: unprinted entries in queue");
    315 	else {
    316 		debug_func_msg("walk: tcp=%p, queue=%p",
    317 			       tcp, tcp->unwind_queue->head);
    318 		unwinder.tcb_walk(tcp, queue_put_call, queue_put_error,
    319 				  tcp->unwind_queue);
    320 	}
    321 }
    322