diff --git a/bin/named/main.c b/bin/named/main.c index a6c6c3bd37..f033607609 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -205,8 +205,6 @@ assertion_failed(const char *file, int line, isc_assertiontype_t type, const char *cond) { void *tracebuf[BACKTRACE_MAXFRAME]; int nframes; - isc_result_t result; - const char *logsuffix = ""; /* * Handle assertion failures. @@ -219,32 +217,23 @@ assertion_failed(const char *file, int line, isc_assertiontype_t type, */ isc_assertion_setcallback(NULL); - result = isc_backtrace_gettrace(tracebuf, BACKTRACE_MAXFRAME, - &nframes); - if (result == ISC_R_SUCCESS && nframes > 0) { - logsuffix = ", back trace"; - } + nframes = isc_backtrace(tracebuf, BACKTRACE_MAXFRAME); isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, "%s:%d: %s(%s) failed%s", file, line, - isc_assertion_typetotext(type), cond, logsuffix); - if (result == ISC_R_SUCCESS) { -#if HAVE_BACKTRACE_SYMBOLS - char **strs = backtrace_symbols(tracebuf, nframes); - for (int i = 0; i < nframes; i++) { - isc_log_write(named_g_lctx, - NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_MAIN, - ISC_LOG_CRITICAL, "%s", strs[i]); + isc_assertion_typetotext(type), cond, + (nframes > 0) ? ", back trace" : ""); + if (nframes > 0) { + char **strs = isc_backtrace_symbols(tracebuf, nframes); + if (strs != NULL) { + for (int i = 0; i < nframes; i++) { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, + ISC_LOG_CRITICAL, "%s", + strs[i]); + } } -#else /* HAVE_BACKTRACE_SYMBOLS */ - for (int i = 0; i < nframes; i++) { - isc_log_write( - named_g_lctx, NAMED_LOGCATEGORY_GENERAL, - NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, - "#%d %p in ??", i, tracebuf[i]); - } -#endif /* HAVE_BACKTRACE_SYMBOLS */ } isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, diff --git a/configure.ac b/configure.ac index b2599b5aca..2518969534 100644 --- a/configure.ac +++ b/configure.ac @@ -975,10 +975,8 @@ AC_SUBST([ZLIB_LIBS]) # Check if the system supports glibc-compatible backtrace() function. # AC_CHECK_HEADERS([execinfo.h], - [AC_SEARCH_LIBS([backtrace], [execinfo], - [AC_CHECK_FUNCS([backtrace backtrace_symbols])])]) - -AM_CONDITIONAL([HAVE_BACKTRACE], [test "$ac_cv_func_backtrace" = "yes"]) + [AC_SEARCH_LIBS([backtrace_symbols], [execinfo], + [AC_CHECK_FUNCS([backtrace_symbols])])]) # # We do the IPv6 compilation checking after libtool so that we can put diff --git a/lib/isc/assertions.c b/lib/isc/assertions.c index dbda87f442..fc6b7495b4 100644 --- a/lib/isc/assertions.c +++ b/lib/isc/assertions.c @@ -16,8 +16,14 @@ #include #include +#include #include #include +#include + +#if _WIN32 +#include +#endif /* * The maximum number of stack frames to dump on assertion failure. @@ -95,30 +101,15 @@ static void default_callback(const char *file, int line, isc_assertiontype_t type, const char *cond) { void *tracebuf[BACKTRACE_MAXFRAME]; - int nframes; - bool have_backtrace = false; - isc_result_t result; - - result = isc_backtrace_gettrace(tracebuf, BACKTRACE_MAXFRAME, &nframes); - if (result == ISC_R_SUCCESS && nframes > 0) { - have_backtrace = true; - } + int nframes = isc_backtrace(tracebuf, BACKTRACE_MAXFRAME); fprintf(stderr, "%s:%d: %s(%s) failed%s\n", file, line, isc_assertion_typetotext(type), cond, - (have_backtrace) ? ", back trace" : "."); + (nframes > 0) ? ", back trace" : "."); - if (result == ISC_R_SUCCESS) { -#if HAVE_BACKTRACE_SYMBOLS - char **strs = backtrace_symbols(tracebuf, nframes); - for (int i = 0; i < nframes; i++) { - fprintf(stderr, "%s\n", strs[i]); - } -#else /* HAVE_BACKTRACE_SYMBOLS */ - for (int i = 0; i < nframes; i++) { - fprintf(stderr, "#%d %p in ??\n", i, tracebuf[i]); - } -#endif /* HAVE_BACKTRACE_SYMBOLS */ + if (nframes > 0) { + isc_backtrace_symbols_fd(tracebuf, nframes, fileno(stderr)); } + fflush(stderr); } diff --git a/lib/isc/backtrace.c b/lib/isc/backtrace.c index afd116f418..410fc781b1 100644 --- a/lib/isc/backtrace.c +++ b/lib/isc/backtrace.c @@ -13,46 +13,245 @@ #include #include -#ifdef HAVE_BACKTRACE +#ifdef HAVE_BACKTRACE_SYMBOLS #include -#endif /* HAVE_BACKTRACE */ +#endif /* HAVE_BACKTRACE_SYMBOLS */ #include +#include +#include #include #include -#ifdef HAVE_BACKTRACE -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { +#if defined(_WIN32) && defined(_DEBUG) + +#include + +int +isc_backtrace(void **addrs, int maxaddrs, int *nframes) { + USHORT n = CaptureStackBackTrace(1, maxaddrs, addrs, NULL); + + return (n); +} + +#define TRACE_MAX_DEPTH 128 +#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024 + +int +vasprintf(char **strp, const char *format, va_list ap) { + int len, retval; + char *str = NULL; + + len = _vscprintf(format, ap); + if (len == -1) { + return (-1); + } + + str = malloc((size_t)len + 1); + if (str == NULL) { + return (-1); + } + + retval = vsnprintf(str, len + 1, format, ap); + if (retval == -1) { + free(str); + return (-1); + } + + *strp = str; + return (retval); +} + +int +asprintf(char **strp, const char *format, ...) { + va_list ap; + int retval; + + va_start(ap, format); + retval = vasprintf(strp, format, ap); + va_end(ap); + + return (retval); +} + +static char ** +_backtrace_symbols(void *const *buffer, size_t size, bool add_cr) { + HANDLE process = GetCurrentProcess(); + DWORD displacement; + uint8_t symbol_storage[sizeof(SYMBOL_INFO) + + (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * + sizeof(TCHAR)]; + SYMBOL_INFO *symbol = (SYMBOL_INFO *)symbol_storage; + uint8_t line_storage[sizeof(IMAGEHLP_LINE64)]; + IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)line_storage; + char **lines = NULL; + char **outbuf = NULL; + char *cur = NULL; + size_t outsize = 0; + + if (buffer == NULL || size <= 0) { + return (NULL); + } + + lines = malloc(size * sizeof(*lines)); + if (lines == NULL) { + return (NULL); + } + + /* Initialize symbol_info */ + symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + SymInitialize(process, NULL, TRUE); + + /* adjust for the char ** array size */ + outsize = size * sizeof(char *); + for (size_t i = 0; i < size; i++) { + DWORD64 address = (DWORD64)(buffer[i]); + BOOL r; + char *file = NULL; + unsigned int lineno; + int len; + + if (SymFromAddr(process, address, NULL, symbol) && + SymGetLineFromAddr64(process, address, &displacement, line)) + { + file = line->FileName; + lineno = line->LineNumber; + } else { + file = "??"; + lineno = 0; + } + + len = asprintf(&lines[i], "#%-2d %p in %s at %s:%lu%s", i, + (void *)symbol->Address, symbol->Name, + line->FileName, line->LineNumber, + (add_cr) ? "\n" : ""); + if (len == -1) { + goto cleanup; + } + + outsize += strlen(lines[i]) + 1; + } + + outbuf = malloc(outsize); + if (outbuf == NULL) { + goto cleanup; + } + + cur = (char *)&outbuf[size]; + for (size_t i = 0; i < size; i++) { + size_t remaining = outsize - (cur - (char *)outbuf); + size_t copied = strlcpy(cur, lines[i], remaining); + if (copied >= remaining) { + free(outbuf); + outbuf = NULL; + goto cleanup; + } + + outbuf[i] = cur; + cur += copied + 1; + } + +cleanup: + for (size_t i = 0; i < size; i++) { + free(lines[i]); + } + free(lines); + + return (outbuf); +} + +char ** +isc_backtrace_symbols(void *const *buffer, int size) { + if (buffer == NULL || size <= 0) { + return (NULL); + } + return (_backtrace_symbols(buffer, size, false)); +} + +void +isc_backtrace_symbols_fd(void *const *buffer, int size, int fd) { + char **strings = NULL; + size_t sz; + + strings = _backtrace_symbols(buffer, size, true); + if (strings == NULL) { + return; + } + + for (size_t i = 0; i < (size_t)size; i++) { + sz = strlen(strings[i]); + if (write(fd, strings[i], sz) == -1) { + return; + } + } + + free(strings); +} + +#elif HAVE_BACKTRACE_SYMBOLS +int +isc_backtrace(void **addrs, int maxaddrs) { + int n; + /* * Validate the arguments: intentionally avoid using REQUIRE(). * See notes in backtrace.h. */ - if (addrs == NULL || nframes == NULL) { - return (ISC_R_FAILURE); + if (addrs == NULL || maxaddrs <= 0) { + return (-1); } /* * backtrace(3) includes this function itself in the address array, * which should be eliminated from the returned sequence. */ - int n = backtrace(addrs, maxaddrs); + n = backtrace(addrs, maxaddrs); if (n < 2) { - return (ISC_R_NOTFOUND); + return (-1); } n--; memmove(addrs, &addrs[1], sizeof(addrs[0]) * n); - *nframes = n; - return (ISC_R_SUCCESS); + + return (n); } -#else /* HAVE_BACKTRACE */ -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { +char ** +isc_backtrace_symbols(void *const *buffer, int size) { + return (backtrace_symbols(buffer, size)); +} + +void +isc_backtrace_symbols_fd(void *const *buffer, int size, int fd) { + backtrace_symbols_fd(buffer, size, fd); +} + +#else /* HAVE_BACKTRACE_SYMBOLS */ + +int +isc_backtrace(void **addrs, int maxaddrs) { UNUSED(addrs); UNUSED(maxaddrs); - UNUSED(nframes); - return (ISC_R_NOTIMPLEMENTED); + return (-1); } -#endif /* HAVE_BACKTRACE */ + +char ** +isc_backtrace_symbols(void *const *buffer, int size) { + UNUSED(buffer); + UNUSED(size); + + return (NULL); +} + +void +isc_backtrace_symbols_fd(void *const *buffer, int size, int fd) { + UNUSED(buffer); + UNUSED(size); + UNUSED(fd); +} + +#endif /* HAVE_BACKTRACE_SYMBOLS */ diff --git a/lib/isc/include/isc/backtrace.h b/lib/isc/include/isc/backtrace.h index 98cfcaa5fc..224347fc8b 100644 --- a/lib/isc/include/isc/backtrace.h +++ b/lib/isc/include/isc/backtrace.h @@ -22,25 +22,14 @@ * dumping a back trace on a fatal error, normally followed by self termination, * functions defined in this module generally doesn't employ assertion checks * (if it did, a program bug could cause infinite recursive calls to a - * backtrace function). These functions still perform minimal checks and return - * ISC_R_FAILURE if they detect an error, but the caller should therefore be - * very careful about the use of these functions, and generally discouraged to - * use them except in an exit path. The exception is - * isc_backtrace_getsymbolfromindex(), which is expected to be used in a - * non-error-handling context and validates arguments with assertion checks. + * backtrace function). */ -#ifndef ISC_BACKTRACE_H -#define ISC_BACKTRACE_H 1 +#pragma once /*** *** Imports ***/ - -#if HAVE_BACKTRACE_SYMBOLS -#include -#endif /* HAVE_BACKTRACE_SYMBOLS */ - #include /*** @@ -48,8 +37,8 @@ ***/ ISC_LANG_BEGINDECLS -isc_result_t -isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes); +int +isc_backtrace(void **addrs, int maxaddrs); /*%< * Get a back trace of the running process above this function itself. On * success, addrs[i] will store the address of the call point of the i-th @@ -69,6 +58,42 @@ isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes); *\li #ISC_R_NOTFOUND *\li #ISC_R_NOTIMPLEMENTED */ -ISC_LANG_ENDDECLS -#endif /* ISC_BACKTRACE_H */ +char ** +isc_backtrace_symbols(void *const *buffer, int size); +/* + * isc_backtrace_symbols() attempts to transform a call stack obtained by + * backtrace() into an array of human-readable strings using dladdr(). The + * array of strings returned has size elements. It is allocated using + * malloc() and should be released using free(). There is no need to free + * the individual strings in the array. + * + * Notes: + * + *\li On Windows, this is shim implementation using SymFromAddr() + *\li On systems with backtrace_symbols(), it's just a thin wrapper + *\li Otherwise, it returns NULL + *\li See platform NOTES for backtrace_symbols + * + * Returns: + * + *\li On success, backtrace_symbols() returns a pointer to the array + *\li On error, NULL is returned. + */ + +void +isc_backtrace_symbols_fd(void *const *buffer, int size, int fd); +/* + * isc_backtrace_symbols_fd() performs the same operation as + * isc_backtrace_symbols(), but the resulting strings are immediately written to + * the file descriptor fd, and are not returned. isc_backtrace_symbols_fd() + * does not call malloc(3), and so can be employed in situations where the + * latter function might fail. + * + * Notes: + * + *\li See isc_backtrace_symbols() notes + *\li See platform NOTES for backtrace_symbols_fd for caveats + */ + +ISC_LANG_ENDDECLS diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 0248e2bf30..e8e3ed992a 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -39,16 +40,6 @@ #include "openssl_shim.h" #include "uv-compat.h" -#if NETMGR_TRACE -#if HAVE_BACKTRACE -#include -#else /* HAVE_BACKTRACE */ -#define backtrace(buffer, size) 0 -#define backtrace_symbols_fd(buffer, size, fd) \ - fprintf(stderr, ""); -#endif /* HAVE_BACKTRACE */ -#endif /* NETMGR_TRACE */ - /*% * How many isc_nmhandles and isc_nm_uvreqs will we be * caching for reuse in a socket. @@ -1358,7 +1349,7 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, mgr->mctx, ISC_NM_REQS_STACK_SIZE) }; #if NETMGR_TRACE - sock->backtrace_size = backtrace(sock->backtrace, TRACE_SIZE); + sock->backtrace_size = isc_backtrace(sock->backtrace, TRACE_SIZE); ISC_LINK_INIT(sock, active_link); ISC_LIST_INIT(sock->active_handles); LOCK(&mgr->lock); @@ -1506,7 +1497,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer, isc___nmsocket_attach(sock, &handle->sock FLARG_PASS); #if NETMGR_TRACE - handle->backtrace_size = backtrace(handle->backtrace, TRACE_SIZE); + handle->backtrace_size = isc_backtrace(handle->backtrace, TRACE_SIZE); #endif if (peer != NULL) { @@ -3096,8 +3087,8 @@ nmhandle_dump(isc_nmhandle_t *handle) { fprintf(stderr, "Active handle %p, refs %" PRIuFAST32 "\n", handle, isc_refcount_current(&handle->references)); fprintf(stderr, "Created by:\n"); - backtrace_symbols_fd(handle->backtrace, handle->backtrace_size, - STDERR_FILENO); + isc_backtrace_symbols_fd(handle->backtrace, handle->backtrace_size, + STDERR_FILENO); fprintf(stderr, "\n\n"); } @@ -3121,8 +3112,8 @@ nmsocket_dump(isc_nmsocket_t *sock) { atomic_load(&sock->connecting) ? " connecting" : "", sock->accepting ? " accepting" : ""); fprintf(stderr, "Created by:\n"); - backtrace_symbols_fd(sock->backtrace, sock->backtrace_size, - STDERR_FILENO); + isc_backtrace_symbols_fd(sock->backtrace, sock->backtrace_size, + STDERR_FILENO); fprintf(stderr, "\n"); for (handle = ISC_LIST_HEAD(sock->active_handles); handle != NULL; diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 04faf9de85..9160199e6d 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -131,7 +131,9 @@ isc_appctx_destroy isc_assertion_failed isc_assertion_setcallback isc_assertion_typetotext -isc_backtrace_gettrace +isc_backtrace +isc_backtrace_symbols +isc_backtrace_symbols_fd isc_base32_decoderegion isc_base32_decodestring isc_base32_tobuffer diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 856f43391a..19439387ed 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -80,7 +80,7 @@ Console true ..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt) - @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) + @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;Dbghelp.lib;%(AdditionalDependencies) $(ProjectName).def .\$(Configuration)\$(ProjectName).lib @@ -168,7 +168,7 @@ copy InstallFiles ..\Build\Debug\ ..\..\..\config.h .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) @ELSE PKCS11 - BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) + BIND9;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) ..\..\..\config.h .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) @END PKCS11