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 @@ ...@@ -25,8 +25,10 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#if _WIN32 #if _WIN32 || __CYGWIN__
#if !__CYGWIN__
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) #define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
#endif
#define NOMINMAX 1 #define NOMINMAX 1
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
#define NOSERVICE 1 #define NOSERVICE 1
...@@ -35,6 +37,7 @@ ...@@ -35,6 +37,7 @@
#include <windows.h> #include <windows.h>
#include "windows-sanity.h" #include "windows-sanity.h"
#include "encoding.h" #include "encoding.h"
#include <wchar.h>
#endif #endif
namespace kj { namespace kj {
...@@ -133,7 +136,7 @@ Exception::Type typeOfErrno(int error) { ...@@ -133,7 +136,7 @@ Exception::Type typeOfErrno(int error) {
} }
} }
#if _WIN32 #if _WIN32 || __CYGWIN__
Exception::Type typeOfWin32Error(DWORD error) { Exception::Type typeOfWin32Error(DWORD error) {
switch (error) { switch (error) {
...@@ -355,7 +358,7 @@ void Debug::Fault::init( ...@@ -355,7 +358,7 @@ void Debug::Fault::init(
makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues)); makeDescriptionImpl(SYSCALL, condition, osErrorNumber, nullptr, macroArgs, argValues));
} }
#if _WIN32 #if _WIN32 || __CYGWIN__
void Debug::Fault::init( void Debug::Fault::init(
const char* file, int line, Win32Result osErrorNumber, const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
...@@ -401,7 +404,7 @@ int Debug::getOsErrorNumber(bool nonblocking) { ...@@ -401,7 +404,7 @@ int Debug::getOsErrorNumber(bool nonblocking) {
: result; : result;
} }
#if _WIN32 #if _WIN32 || __CYGWIN__
uint Debug::getWin32ErrorCode() { uint Debug::getWin32ErrorCode() {
return ::GetLastError(); return ::GetLastError();
} }
......
...@@ -161,7 +161,7 @@ namespace kj { ...@@ -161,7 +161,7 @@ namespace kj {
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
errorNumber, code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) errorNumber, code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal())
#if _WIN32 #if _WIN32 || __CYGWIN__
#define KJ_WIN32(call, ...) \ #define KJ_WIN32(call, ...) \
if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
...@@ -241,7 +241,7 @@ namespace kj { ...@@ -241,7 +241,7 @@ namespace kj {
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
errorNumber, code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) errorNumber, code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#if _WIN32 #if _WIN32 || __CYGWIN__
#define KJ_WIN32(call, ...) \ #define KJ_WIN32(call, ...) \
if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
...@@ -311,7 +311,7 @@ namespace kj { ...@@ -311,7 +311,7 @@ namespace kj {
// handleSuccessCase(); // handleSuccessCase();
// } // }
#if _WIN32 #if _WIN32 || __CYGWIN__
#define KJ_WIN32_HANDLE_ERRORS(call) \ #define KJ_WIN32_HANDLE_ERRORS(call) \
if (uint _kjWin32Error = ::kj::_::Debug::win32Call(call).number) \ if (uint _kjWin32Error = ::kj::_::Debug::win32Call(call).number) \
...@@ -358,7 +358,7 @@ public: ...@@ -358,7 +358,7 @@ public:
typedef LogSeverity Severity; // backwards-compatibility typedef LogSeverity Severity; // backwards-compatibility
#if _WIN32 #if _WIN32 || __CYGWIN__
struct Win32Result { struct Win32Result {
uint number; uint number;
inline explicit Win32Result(uint number): number(number) {} inline explicit Win32Result(uint number): number(number) {}
...@@ -387,7 +387,7 @@ public: ...@@ -387,7 +387,7 @@ public:
const char* condition, const char* macroArgs); const char* condition, const char* macroArgs);
Fault(const char* file, int line, int osErrorNumber, Fault(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs); const char* condition, const char* macroArgs);
#if _WIN32 #if _WIN32 || __CYGWIN__
Fault(const char* file, int line, Win32Result osErrorNumber, Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs); const char* condition, const char* macroArgs);
#endif #endif
...@@ -401,7 +401,7 @@ public: ...@@ -401,7 +401,7 @@ public:
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
void init(const char* file, int line, int osErrorNumber, void init(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#if _WIN32 #if _WIN32 || __CYGWIN__
void init(const char* file, int line, Win32Result osErrorNumber, void init(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#endif #endif
...@@ -424,7 +424,7 @@ public: ...@@ -424,7 +424,7 @@ public:
template <typename Call> template <typename Call>
static int syscallError(Call&& call, bool nonblocking); static int syscallError(Call&& call, bool nonblocking);
#if _WIN32 #if _WIN32 || __CYGWIN__
static Win32Result win32Call(int boolean); static Win32Result win32Call(int boolean);
static Win32Result win32Call(void* handle); static Win32Result win32Call(void* handle);
static Win32Result winsockCall(int result); static Win32Result winsockCall(int result);
...@@ -520,7 +520,7 @@ inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type, ...@@ -520,7 +520,7 @@ inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type,
init(file, line, type, condition, macroArgs, nullptr); init(file, line, type, condition, macroArgs, nullptr);
} }
#if _WIN32 #if _WIN32 || __CYGWIN__
inline Debug::Fault::Fault(const char* file, int line, Win32Result osErrorNumber, inline Debug::Fault::Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs) const char* condition, const char* macroArgs)
: exception(nullptr) { : exception(nullptr) {
......
...@@ -23,6 +23,13 @@ ...@@ -23,6 +23,13 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #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 "exception.h"
#include "string.h" #include "string.h"
#include "debug.h" #include "debug.h"
...@@ -51,18 +58,23 @@ ...@@ -51,18 +58,23 @@
#include <execinfo.h> #include <execinfo.h>
#endif #endif
#if _WIN32 #if _WIN32 || __CYGWIN__
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include "windows-sanity.h" #include "windows-sanity.h"
#include <dbghelp.h> #include <dbghelp.h>
#endif #endif
#if (__linux__ || __APPLE__) #if (__linux__ || __APPLE__ || __CYGWIN__)
#include <stdio.h> #include <stdio.h>
#include <pthread.h> #include <pthread.h>
#endif #endif
#if __CYGWIN__
#include <sys/cygwin.h>
#include <ucontext.h>
#endif
#if KJ_HAS_LIBDL #if KJ_HAS_LIBDL
#include "dlfcn.h" #include "dlfcn.h"
#endif #endif
...@@ -81,10 +93,7 @@ StringPtr KJ_STRINGIFY(LogSeverity severity) { ...@@ -81,10 +93,7 @@ StringPtr KJ_STRINGIFY(LogSeverity severity) {
return SEVERITY_STRINGS[static_cast<uint>(severity)]; return SEVERITY_STRINGS[static_cast<uint>(severity)];
} }
#if _WIN32 && _M_X64 #if KJ_USE_WIN32_DBGHELP
// 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.
namespace { namespace {
...@@ -136,6 +145,13 @@ const Dbghelp& getDbghelp() { ...@@ -136,6 +145,13 @@ const Dbghelp& getDbghelp() {
ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount, ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount,
HANDLE thread, CONTEXT& context) { 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(); const Dbghelp& dbghelp = getDbghelp();
if (dbghelp.stackWalk64 == nullptr || if (dbghelp.stackWalk64 == nullptr ||
dbghelp.symFunctionTableAccess64 == nullptr || dbghelp.symFunctionTableAccess64 == nullptr ||
...@@ -179,7 +195,7 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) { ...@@ -179,7 +195,7 @@ ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) {
return nullptr; return nullptr;
} }
#if _WIN32 && _M_X64 #if KJ_USE_WIN32_DBGHELP
CONTEXT context; CONTEXT context;
RtlCaptureContext(&context); RtlCaptureContext(&context);
return getStackTrace(space, ignoreCount, GetCurrentThread(), context); return getStackTrace(space, ignoreCount, GetCurrentThread(), context);
...@@ -207,7 +223,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) { ...@@ -207,7 +223,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
return nullptr; 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 // Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since
// this requires MSVC debug info. // this requires MSVC debug info.
...@@ -232,7 +248,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) { ...@@ -232,7 +248,7 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
return strArray(lines, ""); return strArray(lines, "");
#elif (__linux__ || __APPLE__) && !__ANDROID__ #elif (__linux__ || __APPLE__ || __CYGWIN__) && !__ANDROID__
// We want to generate a human-readable stack trace. // 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 // 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) { ...@@ -274,6 +290,16 @@ String stringifyStackTrace(ArrayPtr<void* const> trace) {
// The Mac OS X equivalent of addr2line is atos. // The Mac OS X equivalent of addr2line is atos.
// (Internally, it uses the private CoreSymbolication.framework library.) // (Internally, it uses the private CoreSymbolication.framework library.)
p = popen(str("xcrun atos -p ", getpid(), ' ', strTrace).cStr(), "r"); 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 #endif
if (p == nullptr) { if (p == nullptr) {
...@@ -415,7 +441,7 @@ void terminateHandler() { ...@@ -415,7 +441,7 @@ void terminateHandler() {
} // namespace } // namespace
#if _WIN32 && _M_X64 #if KJ_USE_WIN32_DBGHELP && !__CYGWIN__
namespace { namespace {
DWORD mainThreadId = 0; DWORD mainThreadId = 0;
...@@ -503,14 +529,36 @@ void printStackTraceOnCrash() { ...@@ -503,14 +529,36 @@ void printStackTraceOnCrash() {
std::set_terminate(&terminateHandler); 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 { namespace {
void crashHandler(int signo, siginfo_t* info, void* context) { void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32]; 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. // ignoreCount = 2 to ignore crashHandler() and signal trampoline.
auto trace = getStackTrace(traceSpace, 2); auto trace = getStackTrace(traceSpace, 2);
#endif
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo), auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", stringifyStackTraceAddresses(trace), "\nstack: ", stringifyStackTraceAddresses(trace),
...@@ -567,10 +615,6 @@ void printStackTraceOnCrash() { ...@@ -567,10 +615,6 @@ void printStackTraceOnCrash() {
// Also override std::terminate() handler with something nicer for KJ. // Also override std::terminate() handler with something nicer for KJ.
std::set_terminate(&terminateHandler); std::set_terminate(&terminateHandler);
} }
#else
void printStackTraceOnCrash() {
std::set_terminate(&terminateHandler);
}
#endif #endif
kj::StringPtr trimSourceFilename(kj::StringPtr filename) { kj::StringPtr trimSourceFilename(kj::StringPtr filename) {
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
// windows-sanity.h, we can be sure that no conflicts will occur regardless of in what order the // 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. // 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. // Not on Windows. Tell the compiler never to try to include this again.
#pragma once #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