2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +00:00

Add support for generating backtraces on Windows

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
This commit is contained in:
Ondřej Surý 2021-04-27 16:20:03 +02:00
parent c3ee539463
commit dfd56b84f5
8 changed files with 296 additions and 101 deletions

View 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]);
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,

View 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

View File

@ -16,8 +16,14 @@
#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" : ".");
(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);
}

View File

@ -13,46 +13,245 @@
#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);
}
#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 */

View File

@ -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 <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

View 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>
@ -39,16 +40,6 @@
#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;

View 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

View 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