]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add support for generating backtraces on Windows
authorOndřej Surý <ondrej@sury.org>
Tue, 27 Apr 2021 14:20:03 +0000 (16:20 +0200)
committerOndřej Surý <ondrej@sury.org>
Mon, 3 May 2021 18:31:52 +0000 (20:31 +0200)
This commit adds support for generating backtraces on Windows and
refactors the isc_backtrace API to match the Linux/BSD API (without
the isc_ prefix)

 * isc_backtrace_gettrace() was renamed to isc_backtrace(), the third
   argument was removed and the return type was changed to int
 * isc_backtrace_symbols() was added
 * isc_backtrace_symbols_fd() was added and used as appropriate

bin/named/main.c
configure.ac
lib/isc/assertions.c
lib/isc/backtrace.c
lib/isc/include/isc/backtrace.h
lib/isc/netmgr/netmgr.c
lib/isc/win32/libisc.def.in
lib/isc/win32/libisc.vcxproj.in

index a6c6c3bd37593a3ec0836a1d88f8f50578bbf2cf..f033607609271b7a449bd6984616b859f9b6c6f5 100644 (file)
@@ -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]);
-                       }
-#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]);
+                             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]);
+                               }
                        }
-#endif /* HAVE_BACKTRACE_SYMBOLS */
                }
                isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
                              NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
index b2599b5aca32e0609925ba4e993686d73df1240f..25189695342d63776486c44a16487eaf6a18de0c 100644 (file)
@@ -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
index dbda87f44257d104457cba4aedb0067f2f243032..fc6b7495b487f2f5a86a3fa00e8165895168f5dd 100644 (file)
 
 #include <isc/assertions.h>
 #include <isc/backtrace.h>
+#include <isc/platform.h>
 #include <isc/print.h>
 #include <isc/result.h>
+#include <isc/strerr.h>
+
+#if _WIN32
+#include <dbghelp.h>
+#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" : ".");
-
-       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 */
+               (nframes > 0) ? ", back trace" : ".");
+
+       if (nframes > 0) {
+               isc_backtrace_symbols_fd(tracebuf, nframes, fileno(stderr));
        }
+
        fflush(stderr);
 }
index afd116f41828521edc75da9709c13cbb823bc044..410fc781b1f2c6bab03d27bad1276e8d03e6bca2 100644 (file)
 
 #include <stdlib.h>
 #include <string.h>
-#ifdef HAVE_BACKTRACE
+#ifdef HAVE_BACKTRACE_SYMBOLS
 #include <execinfo.h>
-#endif /* HAVE_BACKTRACE */
+#endif /* HAVE_BACKTRACE_SYMBOLS */
 
 #include <isc/backtrace.h>
+#include <isc/platform.h>
+#include <isc/print.h>
 #include <isc/result.h>
 #include <isc/util.h>
 
-#ifdef HAVE_BACKTRACE
-isc_result_t
-isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
+#if defined(_WIN32) && defined(_DEBUG)
+
+#include <dbghelp.h>
+
+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);
+}
+
+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 */
-isc_result_t
-isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
+#else /* HAVE_BACKTRACE_SYMBOLS */
+
+int
+isc_backtrace(void **addrs, int maxaddrs) {
        UNUSED(addrs);
        UNUSED(maxaddrs);
-       UNUSED(nframes);
 
-       return (ISC_R_NOTIMPLEMENTED);
+       return (-1);
+}
+
+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 */
+
+#endif /* HAVE_BACKTRACE_SYMBOLS */
index 98cfcaa5fc3750033c1098503d04ab7df8ff722b..224347fc8b50cae266825d763b39d0b65e5a8655 100644 (file)
  * 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 <execinfo.h>
-#endif /* HAVE_BACKTRACE_SYMBOLS */
-
 #include <isc/types.h>
 
 /***
@@ -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
index 0248e2bf302d4932f39056d58f643de7b531f7a6..e8e3ed992a23981cd6d463cb925264191515efd8 100644 (file)
@@ -14,6 +14,7 @@
 #include <uv.h>
 
 #include <isc/atomic.h>
+#include <isc/backtrace.h>
 #include <isc/buffer.h>
 #include <isc/condition.h>
 #include <isc/errno.h>
 #include "openssl_shim.h"
 #include "uv-compat.h"
 
-#if NETMGR_TRACE
-#if HAVE_BACKTRACE
-#include <execinfo.h>
-#else /* HAVE_BACKTRACE */
-#define backtrace(buffer, size) 0
-#define backtrace_symbols_fd(buffer, size, fd) \
-       fprintf(stderr, "<not available>");
-#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;
index 04faf9de85b86e6ac6f0c1cec120722a4ba42e85..9160199e6d515ea9cae1c25461b6c5d23a659d9a 100644 (file)
@@ -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
index 856f43391add05e0ef0ca507afde810d48cb3902..19439387ed9a744cd915522e7c0ff2a8c3f0d97b 100644 (file)
@@ -80,7 +80,7 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
-      <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@NGHTTP2_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile>
       <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary>
     </Link>
@@ -168,7 +168,7 @@ copy InstallFiles ..\Build\Debug\
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
       <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @ELSE PKCS11
-      <PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>BIND9;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
       <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@NGHTTP2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @END PKCS11