| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 1999 - 2013, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Matt Jordan <mjordan@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \file
 | 
					
						
							|  |  |  |  * \brief Asterisk backtrace generation | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file provides backtrace generation utilities | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** MODULEINFO
 | 
					
						
							|  |  |  | 	<support_level>core</support_level> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/backtrace.h"
 | 
					
						
							|  |  |  | #include "asterisk/utils.h"
 | 
					
						
							|  |  |  | #include "asterisk/strings.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef HAVE_BKTR
 | 
					
						
							|  |  |  | #include <execinfo.h>
 | 
					
						
							|  |  |  | #if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
 | 
					
						
							|  |  |  | #include <dlfcn.h>
 | 
					
						
							|  |  |  | #include <bfd.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ast_bt *__ast_bt_create(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 	struct ast_bt *bt = ast_std_calloc(1, sizeof(*bt)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 	if (!bt) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bt->alloced = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_bt_get_addresses(bt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return bt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __ast_bt_get_addresses(struct ast_bt *bt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *__ast_bt_destroy(struct ast_bt *bt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 	if (bt && bt->alloced) { | 
					
						
							|  |  |  | 		ast_std_free(bt); | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char **__ast_bt_get_symbols(void **addresses, size_t num_frames) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 	char **strings; | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | #if defined(BETTER_BACKTRACES)
 | 
					
						
							|  |  |  | 	int stackfr; | 
					
						
							|  |  |  | 	bfd *bfdobj;           /* bfd.h */ | 
					
						
							|  |  |  | 	Dl_info dli;           /* dlfcn.h */ | 
					
						
							|  |  |  | 	long allocsize; | 
					
						
							|  |  |  | 	asymbol **syms = NULL; /* bfd.h */ | 
					
						
							|  |  |  | 	bfd_vma offset;        /* bfd.h */ | 
					
						
							|  |  |  | 	const char *lastslash; | 
					
						
							|  |  |  | 	asection *section; | 
					
						
							|  |  |  | 	const char *file, *func; | 
					
						
							|  |  |  | 	unsigned int line; | 
					
						
							|  |  |  | 	char address_str[128]; | 
					
						
							|  |  |  | 	char msg[1024]; | 
					
						
							|  |  |  | 	size_t strings_size; | 
					
						
							|  |  |  | 	size_t *eachlen; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(BETTER_BACKTRACES)
 | 
					
						
							|  |  |  | 	strings_size = num_frames * sizeof(*strings); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 	eachlen = ast_std_calloc(num_frames, sizeof(*eachlen)); | 
					
						
							|  |  |  | 	strings = ast_std_calloc(num_frames, sizeof(*strings)); | 
					
						
							|  |  |  | 	if (!eachlen || !strings) { | 
					
						
							|  |  |  | 		ast_std_free(eachlen); | 
					
						
							|  |  |  | 		ast_std_free(strings); | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (stackfr = 0; stackfr < num_frames; stackfr++) { | 
					
						
							|  |  |  | 		int found = 0, symbolcount; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		msg[0] = '\0'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!dladdr(addresses[stackfr], &dli)) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (strcmp(dli.dli_fname, "asterisk") == 0) { | 
					
						
							|  |  |  | 			char asteriskpath[256]; | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 			if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) { | 
					
						
							|  |  |  | 				/* This will fail to find symbols */ | 
					
						
							|  |  |  | 				dli.dli_fname = "asterisk"; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		lastslash = strrchr(dli.dli_fname, '/'); | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 		if ((bfdobj = bfd_openr(dli.dli_fname, NULL)) && | 
					
						
							|  |  |  | 			bfd_check_format(bfdobj, bfd_object) && | 
					
						
							|  |  |  | 			(allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 && | 
					
						
							|  |  |  | 			(syms = ast_std_malloc(allocsize)) && | 
					
						
							|  |  |  | 			(symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) { | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (bfdobj->flags & DYNAMIC) { | 
					
						
							|  |  |  | 				offset = addresses[stackfr] - dli.dli_fbase; | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 				offset = addresses[stackfr] - (void *) 0; | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (section = bfdobj->sections; section; section = section->next) { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 				if (!bfd_get_section_flags(bfdobj, section) & SEC_ALLOC || | 
					
						
							|  |  |  | 					section->vma > offset || | 
					
						
							|  |  |  | 					section->size + section->vma < offset) { | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) { | 
					
						
							|  |  |  | 					continue; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* file can possibly be null even with a success result from bfd_find_nearest_line */ | 
					
						
							|  |  |  | 				file = file ? file : ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Stack trace output */ | 
					
						
							|  |  |  | 				found++; | 
					
						
							|  |  |  | 				if ((lastslash = strrchr(file, '/'))) { | 
					
						
							|  |  |  | 					const char *prevslash; | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--) { | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 					if (prevslash >= file) { | 
					
						
							|  |  |  | 						lastslash = prevslash; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (dli.dli_saddr == NULL) { | 
					
						
							|  |  |  | 					address_str[0] = '\0'; | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					snprintf(address_str, sizeof(address_str), " (%p+%lX)", | 
					
						
							|  |  |  | 						dli.dli_saddr, | 
					
						
							|  |  |  | 						(unsigned long) (addresses[stackfr] - dli.dli_saddr)); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				snprintf(msg, sizeof(msg), "%s:%u %s()%s", | 
					
						
							|  |  |  | 					lastslash ? lastslash + 1 : file, line, | 
					
						
							|  |  |  | 					S_OR(func, "???"), | 
					
						
							|  |  |  | 					address_str); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				break; /* out of section iteration */ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (bfdobj) { | 
					
						
							|  |  |  | 			bfd_close(bfdobj); | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 			ast_std_free(syms); | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Default output, if we cannot find the information within BFD */ | 
					
						
							|  |  |  | 		if (!found) { | 
					
						
							|  |  |  | 			if (dli.dli_saddr == NULL) { | 
					
						
							|  |  |  | 				address_str[0] = '\0'; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				snprintf(address_str, sizeof(address_str), " (%p+%lX)", | 
					
						
							|  |  |  | 					dli.dli_saddr, | 
					
						
							|  |  |  | 					(unsigned long) (addresses[stackfr] - dli.dli_saddr)); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			snprintf(msg, sizeof(msg), "%s %s()%s", | 
					
						
							|  |  |  | 				lastslash ? lastslash + 1 : dli.dli_fname, | 
					
						
							|  |  |  | 				S_OR(dli.dli_sname, "<unknown>"), | 
					
						
							|  |  |  | 				address_str); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(msg)) { | 
					
						
							|  |  |  | 			char **tmp; | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			eachlen[stackfr] = strlen(msg) + 1; | 
					
						
							|  |  |  | 			if (!(tmp = ast_std_realloc(strings, strings_size + eachlen[stackfr]))) { | 
					
						
							|  |  |  | 				ast_std_free(strings); | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 				strings = NULL; | 
					
						
							|  |  |  | 				break; /* out of stack frame iteration */ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			strings = tmp; | 
					
						
							|  |  |  | 			strings[stackfr] = (char *) strings + strings_size; | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 			strcpy(strings[stackfr], msg);/* Safe since we just allocated the room. */ | 
					
						
							|  |  |  | 			strings_size += eachlen[stackfr]; | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (strings) { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 		/* Recalculate the offset pointers because of the reallocs. */ | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 		strings[0] = (char *) strings + num_frames * sizeof(*strings); | 
					
						
							|  |  |  | 		for (stackfr = 1; stackfr < num_frames; stackfr++) { | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 			strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1]; | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 	ast_std_free(eachlen); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | #else /* !defined(BETTER_BACKTRACES) */
 | 
					
						
							| 
									
										
										
										
											2013-08-23 18:07:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-08 22:09:07 +00:00
										 |  |  | 	strings = backtrace_symbols(addresses, num_frames); | 
					
						
							|  |  |  | #endif /* defined(BETTER_BACKTRACES) */
 | 
					
						
							|  |  |  | 	return strings; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* HAVE_BKTR */
 |