Commit 3d2a505f authored by Kenton Varda's avatar Kenton Varda

Use Win32 backtracing in Cygwin.

Also required making KJ_WIN32 macros available on Cygwin.

(Cygwin allows direct calls to Win32 functions.)
parent 786bcfee
......@@ -25,8 +25,10 @@
#include <string.h>
#include <errno.h>
#if _WIN32
#if _WIN32 || __CYGWIN__
#if !__CYGWIN__
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
#endif
#define NOMINMAX 1
#define WIN32_LEAN_AND_MEAN 1
#define NOSERVICE 1
......@@ -35,6 +37,7 @@
#include <windows.h>
#include "windows-sanity.h"
#include "encoding.h"
#include <wchar.h>
#endif
namespace kj {
......@@ -133,7 +136,7 @@ Exception::Type typeOfErrno(int error) {
}
}
#if _WIN32
#if _WIN32 || __CYGWIN__
Exception::Type typeOfWin32Error(DWORD error) {
switch (error) {
......@@ -355,7 +358,7 @@ void Debug::Fault::init(
makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues));
}
#if _WIN32
#if _WIN32 || __CYGWIN__
void Debug::Fault::init(
const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
......@@ -401,7 +404,7 @@ int Debug::getOsErrorNumber(bool nonblocking) {
: result;
}
#if _WIN32
#if _WIN32 || __CYGWIN__
uint Debug::getWin32ErrorCode() {
return ::GetLastError();
}
......
......@@ -161,7 +161,7 @@ namespace kj {
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
errorNumber, code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal())
#if _WIN32
#if _WIN32 || __CYGWIN__
#define KJ_WIN32(call, ...) \
if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
......@@ -241,7 +241,7 @@ namespace kj {
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
errorNumber, code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#if _WIN32
#if _WIN32 || __CYGWIN__
#define KJ_WIN32(call, ...) \
if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
......@@ -311,7 +311,7 @@ namespace kj {
// handleSuccessCase();
// }
#if _WIN32
#if _WIN32 || __CYGWIN__
#define KJ_WIN32_HANDLE_ERRORS(call) \
if (uint _kjWin32Error = ::kj::_::Debug::win32Call(call).number) \
......@@ -358,7 +358,7 @@ public:
typedef LogSeverity Severity; // backwards-compatibility
#if _WIN32
#if _WIN32 || __CYGWIN__
struct Win32Result {
uint number;
inline explicit Win32Result(uint number): number(number) {}
......@@ -387,7 +387,7 @@ public:
const char* condition, const char* macroArgs);
Fault(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs);
#if _WIN32
#if _WIN32 || __CYGWIN__
Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs);
#endif
......@@ -401,7 +401,7 @@ public:
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
void init(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#if _WIN32
#if _WIN32 || __CYGWIN__
void init(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#endif
......@@ -424,7 +424,7 @@ public:
template <typename Call>
static int syscallError(Call&& call, bool nonblocking);
#if _WIN32
#if _WIN32 || __CYGWIN__
static Win32Result win32Call(int boolean);
static Win32Result win32Call(void* handle);
static Win32Result winsockCall(int result);
......@@ -520,7 +520,7 @@ inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type,
init(file, line, type, condition, macroArgs, nullptr);
}
#if _WIN32
#if _WIN32 || __CYGWIN__
inline Debug::Fault::Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs)
: exception(nullptr) {
......
......@@ -23,6 +23,13 @@
#define _GNU_SOURCE
#endif
#if (_WIN32 && _M_X64) || (__CYGWIN__ && __x86_64__)
// Currently the Win32 stack-trace code only supports x86_64. We could easily extend it to support
// i386 as well but it requires some code changes around how we read the context to start the
// trace.
#define KJ_USE_WIN32_DBGHELP 1
#endif
#include "exception.h"
#include "string.h"
#include "debug.h"
......@@ -51,18 +58,23 @@
#include <execinfo.h>
#endif
#if _WIN32
#if _WIN32 || __CYGWIN__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "windows-sanity.h"
#include <dbghelp.h>
#endif
#if (__linux__ || __APPLE__)
#if (__linux__ || __APPLE__ || __CYGWIN__)
#include <stdio.h>
#include <pthread.h>
#endif
#if __CYGWIN__
#include <sys/cygwin.h>
#include <ucontext.h>
#endif
#if KJ_HAS_LIBDL
#include "dlfcn.h"
#endif
......@@ -81,10 +93,7 @@ StringPtr KJ_STRINGIFY(LogSeverity severity) {
return SEVERITY_STRINGS[static_cast<uint>(severity)];
}
#if _WIN32 && _M_X64
// Currently the Win32 stack-trace code only supports x86_64. We could easily extend it to support
// i386 as well but it requires some code changes around how we read the context to start the
// trace.
#if KJ_USE_WIN32_DBGHELP
namespace {
......@@ -136,6 +145,13 @@ const Dbghelp& getDbghelp() {
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
HANDLE thread, CONTEXT& context) {
// NOTE: Apparently there is a function CaptureStackBackTrace() that is equivalent to glibc's
// backtrace(). Somehow I missed that when I originally wrote this. However,
// CaptureStackBackTrace() does not accept a CONTEXT parameter; it can only trace the caller.
// That's more problematic on Windows where breakHandler(), sehHandler(), and Cygwin signal
// handlers all depend on the ability to pass a CONTEXT. So we'll keep this code, which works
// after all.
const Dbghelp& dbghelp = getDbghelp();
if (dbghelp.stackWalk64 == nullptr ||
dbghelp.symFunctionTableAccess64 == nullptr ||
......@@ -179,7 +195,7 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
return nullptr;
}
#if _WIN32 && _M_X64
#if KJ_USE_WIN32_DBGHELP
CONTEXT context;
RtlCaptureContext(&context);
return getStackTrace(space, ignoreCount, GetCurrentThread(), context);
......@@ -207,7 +223,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
return nullptr;
}
#if _WIN32 && _M_X64 && _MSC_VER
#if KJ_USE_WIN32_DBGHELP && _MSC_VER
// Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
// this requires MSVC debug info.
......@@ -232,7 +248,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
return strArray(lines, "");
#elif (__linux__ || __APPLE__) && !__ANDROID__
#elif (__linux__ || __APPLE__ || __CYGWIN__) && !__ANDROID__
// We want to generate a human-readable stack trace.
// TODO(someday): It would be really great if we could avoid farming out to another process
......@@ -274,6 +290,16 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
// The Mac OS X equivalent of addr2line is atos.
// (Internally, it uses the private CoreSymbolication.framework library.)
p = popen(str("xcrun atos -p ", getpid(), ' ', strTrace).cStr(), "r");
#elif __CYGWIN__
wchar_t exeWinPath[MAX_PATH];
if (GetModuleFileNameW(nullptr, exeWinPath, sizeof(exeWinPath)) == 0) {
return nullptr;
}
char exePosixPath[MAX_PATH * 2];
if (cygwin_conv_path(CCP_WIN_W_TO_POSIX, exeWinPath, exePosixPath, sizeof(exePosixPath)) < 0) {
return nullptr;
}
p = popen(str("addr2line -e '", exePosixPath, "' ", strTrace).cStr(), "r");
#endif
if (p == nullptr) {
......@@ -415,7 +441,7 @@ void terminateHandler() {
} // namespace
#if _WIN32 && _M_X64
#if KJ_USE_WIN32_DBGHELP && !__CYGWIN__
namespace {
DWORD mainThreadId = 0;
......@@ -503,14 +529,36 @@ void printStackTraceOnCrash() {
std::set_terminate(&terminateHandler);
}
#elif KJ_HAS_BACKTRACE
#elif _WIN32
// Windows, but KJ_USE_WIN32_DBGHELP is not enabled. We can't print useful stack traces, so don't
// try to catch SEH nor ctrl+C.
void printStackTraceOnCrash() {
std::set_terminate(&terminateHandler);
}
#else
namespace {
void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32];
#if KJ_USE_WIN32_DBGHELP
// Win32 backtracing can't trace its way out of a Cygwin signal handler. However, Cygwin gives
// us direct access to the CONTEXT, which we can pass to the Win32 tracing functions.
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
// Cygwin's mcontext_t has the same layout as CONTEXT.
// TODO(someday): Figure out why this produces garbage for SIGINT from ctrl+C. It seems to work
// correctly for SIGSEGV.
CONTEXT win32Context;
static_assert(sizeof(ucontext->uc_mcontext) >= sizeof(win32Context),
"mcontext_t should be an extension of CONTEXT");
memcpy(&win32Context, &ucontext->uc_mcontext, sizeof(win32Context));
auto trace = getStackTrace(traceSpace, 0, GetCurrentThread(), win32Context);
#else
// ignoreCount = 2 to ignore crashHandler() and signal trampoline.
auto trace = getStackTrace(traceSpace, 2);
#endif
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", stringifyStackTraceAddresses(trace),
......@@ -567,10 +615,6 @@ void printStackTraceOnCrash() {
// Also override std::terminate() handler with something nicer for KJ.
std::set_terminate(&terminateHandler);
}
#else
void printStackTraceOnCrash() {
std::set_terminate(&terminateHandler);
}
#endif
kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
......
......@@ -36,7 +36,7 @@
// windows-sanity.h, we can be sure that no conflicts will occur regardless of in what order the
// application chooses to include these headers vs. windows.h.
#if !_WIN32
#if !_WIN32 && !__CYGWIN__
// Not on Windows. Tell the compiler never to try to include this again.
#pragma once
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment