mirror of
https://github.com/QuasarApp/backward-cpp.git
synced 2025-04-26 10:44:31 +00:00
Windows (Clang9, MSVC2017) implementation
Remaining issues: * some warinings
This commit is contained in:
parent
ec14c48e3e
commit
7cbf4c25e9
@ -102,7 +102,7 @@ if(BACKWARD_TESTS)
|
||||
|
||||
add_executable(${test_name} ${src} ${ARGN} $<TARGET_OBJECTS:test_main>)
|
||||
|
||||
target_link_libraries(${test_name} PRIVATE Backward::Backward test_main)
|
||||
target_link_libraries(${test_name} PRIVATE Backward::Backward)
|
||||
|
||||
add_test(NAME ${name} COMMAND ${test_name})
|
||||
endmacro()
|
||||
|
458
backward.hpp
458
backward.hpp
@ -31,7 +31,7 @@
|
||||
#if defined(BACKWARD_CXX11)
|
||||
#elif defined(BACKWARD_CXX98)
|
||||
#else
|
||||
#if __cplusplus >= 201103L
|
||||
#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
|
||||
#define BACKWARD_CXX11
|
||||
#define BACKWARD_ATLEAST_CXX11
|
||||
#define BACKWARD_ATLEAST_CXX98
|
||||
@ -55,11 +55,14 @@
|
||||
#if defined(BACKWARD_SYSTEM_LINUX)
|
||||
#elif defined(BACKWARD_SYSTEM_DARWIN)
|
||||
#elif defined(BACKWARD_SYSTEM_UNKNOWN)
|
||||
#elif defined(BACKWARD_SYSTEM_WINDOWS)
|
||||
#else
|
||||
#if defined(__linux) || defined(__linux__)
|
||||
#define BACKWARD_SYSTEM_LINUX
|
||||
#elif defined(__APPLE__)
|
||||
#define BACKWARD_SYSTEM_DARWIN
|
||||
#elif defined(_WIN32)
|
||||
#define BACKWARD_SYSTEM_WINDOWS
|
||||
#else
|
||||
#define BACKWARD_SYSTEM_UNKNOWN
|
||||
#endif
|
||||
@ -301,6 +304,49 @@
|
||||
#endif
|
||||
#endif // defined(BACKWARD_SYSTEM_DARWIN)
|
||||
|
||||
#if defined(BACKWARD_SYSTEM_WINDOWS)
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <winnt.h>
|
||||
|
||||
#include <Psapi.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifndef __clang__
|
||||
#undef NOINLINE
|
||||
#define NOINLINE __declspec(noinline)
|
||||
#endif
|
||||
|
||||
#pragma comment(lib, "psapi.lib")
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
||||
// Comment / packing is from stackoverflow:
|
||||
// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227
|
||||
// Some versions of imagehlp.dll lack the proper packing directives themselves
|
||||
// so we need to do it.
|
||||
#pragma pack(push, before_imagehlp, 8)
|
||||
#include <imagehlp.h>
|
||||
#pragma pack(pop, before_imagehlp)
|
||||
|
||||
// TODO maybe these should be undefined somewhere else?
|
||||
#undef BACKWARD_HAS_UNWIND
|
||||
#undef BACKWARD_HAS_BACKTRACE
|
||||
#if BACKWARD_HAS_PDB_SYMBOL == 1
|
||||
#else
|
||||
#undef BACKWARD_HAS_PDB_SYMBOL
|
||||
#define BACKWARD_HAS_PDB_SYMBOL 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if BACKWARD_HAS_UNWIND == 1
|
||||
|
||||
#include <unwind.h>
|
||||
@ -357,12 +403,15 @@ namespace system_tag {
|
||||
struct linux_tag; // seems that I cannot call that "linux" because the name
|
||||
// is already defined... so I am adding _tag everywhere.
|
||||
struct darwin_tag;
|
||||
struct windows_tag;
|
||||
struct unknown_tag;
|
||||
|
||||
#if defined(BACKWARD_SYSTEM_LINUX)
|
||||
typedef linux_tag current_tag;
|
||||
#elif defined(BACKWARD_SYSTEM_DARWIN)
|
||||
typedef darwin_tag current_tag;
|
||||
#elif defined(BACKWARD_SYSTEM_WINDOWS)
|
||||
typedef windows_tag current_tag;
|
||||
#elif defined(BACKWARD_SYSTEM_UNKNOWN)
|
||||
typedef unknown_tag current_tag;
|
||||
#else
|
||||
@ -396,6 +445,13 @@ typedef backtrace_symbol current;
|
||||
#else
|
||||
#error "You shall not pass, until you know what you want."
|
||||
#endif
|
||||
#elif defined(BACKWARD_SYSTEM_WINDOWS)
|
||||
struct pdb_symbol;
|
||||
#if BACKWARD_HAS_PDB_SYMBOL == 1
|
||||
typedef pdb_symbol current;
|
||||
#else
|
||||
#error "You shall not pass, until you know what you want."
|
||||
#endif
|
||||
#endif
|
||||
} // namespace trace_resolver_tag
|
||||
|
||||
@ -747,7 +803,7 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
#else // BACKWARD_HAS_UNWIND == 0
|
||||
#elif defined(BACKWARD_HAS_BACKTRACE)
|
||||
|
||||
template <>
|
||||
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
|
||||
@ -781,7 +837,102 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BACKWARD_HAS_UNWIND
|
||||
#elif defined(BACKWARD_SYSTEM_WINDOWS)
|
||||
|
||||
template <>
|
||||
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
|
||||
public:
|
||||
// We have to load the machine type from the image info
|
||||
// So we first initialize the resolver, and it tells us this info
|
||||
void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; }
|
||||
void set_context(CONTEXT *ctx) { ctx_ = ctx; }
|
||||
void set_thread_handle(HANDLE handle) { thd_ = handle; }
|
||||
|
||||
NOINLINE
|
||||
size_t load_here(size_t depth = 32) {
|
||||
|
||||
CONTEXT localCtx; // used when no context is provided
|
||||
|
||||
if (depth == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ctx_) {
|
||||
ctx_ = &localCtx;
|
||||
RtlCaptureContext(ctx_);
|
||||
}
|
||||
|
||||
if (!thd_) {
|
||||
thd_ = GetCurrentThread();
|
||||
}
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
|
||||
STACKFRAME64 s;
|
||||
memset(&s, 0, sizeof(STACKFRAME64));
|
||||
|
||||
// TODO: 32 bit context capture
|
||||
s.AddrStack.Mode = AddrModeFlat;
|
||||
s.AddrFrame.Mode = AddrModeFlat;
|
||||
s.AddrPC.Mode = AddrModeFlat;
|
||||
#ifdef _M_X64
|
||||
s.AddrPC.Offset = ctx_->Rip;
|
||||
s.AddrStack.Offset = ctx_->Rsp;
|
||||
s.AddrFrame.Offset = ctx_->Rbp;
|
||||
#else
|
||||
s.AddrPC.Offset = ctx_->Eip;
|
||||
s.AddrStack.Offset = ctx_->Esp;
|
||||
s.AddrFrame.Offset = ctx_->Ebp;
|
||||
#endif
|
||||
|
||||
if (!machine_type_) {
|
||||
#ifdef _M_X64
|
||||
machine_type_ = IMAGE_FILE_MACHINE_AMD64;
|
||||
#else
|
||||
machine_type_ = IMAGE_FILE_MACHINE_I386;
|
||||
#endif
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// NOTE: this only works if PDBs are already loaded!
|
||||
SetLastError(0);
|
||||
if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL,
|
||||
SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
||||
break;
|
||||
|
||||
if (s.AddrReturn.Offset == 0)
|
||||
break;
|
||||
|
||||
_stacktrace.push_back(reinterpret_cast<void *>(s.AddrPC.Offset));
|
||||
|
||||
if (size() >= depth)
|
||||
break;
|
||||
}
|
||||
|
||||
return size();
|
||||
}
|
||||
|
||||
size_t load_from(void *addr, size_t depth = 32) {
|
||||
load_here(depth + 8);
|
||||
|
||||
for (size_t i = 0; i < _stacktrace.size(); ++i) {
|
||||
if (_stacktrace[i] == addr) {
|
||||
skip_n_firsts(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
|
||||
return size();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD machine_type_ = 0;
|
||||
HANDLE thd_ = 0;
|
||||
CONTEXT *ctx_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class StackTrace : public StackTraceImpl<system_tag::current_tag> {};
|
||||
|
||||
@ -3068,6 +3219,132 @@ class TraceResolverImpl<system_tag::darwin_tag>
|
||||
|
||||
#endif // BACKWARD_SYSTEM_DARWIN
|
||||
|
||||
#ifdef BACKWARD_SYSTEM_WINDOWS
|
||||
|
||||
// Load all symbol info
|
||||
// Based on:
|
||||
// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227
|
||||
|
||||
struct module_data {
|
||||
std::string image_name;
|
||||
std::string module_name;
|
||||
void *base_address;
|
||||
DWORD load_size;
|
||||
};
|
||||
|
||||
class get_mod_info {
|
||||
HANDLE process;
|
||||
static const int buffer_length = 4096;
|
||||
|
||||
public:
|
||||
get_mod_info(HANDLE h) : process(h) {}
|
||||
|
||||
module_data operator()(HMODULE module) {
|
||||
module_data ret;
|
||||
char temp[buffer_length];
|
||||
MODULEINFO mi;
|
||||
|
||||
GetModuleInformation(process, module, &mi, sizeof(mi));
|
||||
ret.base_address = mi.lpBaseOfDll;
|
||||
ret.load_size = mi.SizeOfImage;
|
||||
|
||||
GetModuleFileNameEx(process, module, temp, sizeof(temp));
|
||||
ret.image_name = temp;
|
||||
GetModuleBaseName(process, module, temp, sizeof(temp));
|
||||
ret.module_name = temp;
|
||||
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
|
||||
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
|
||||
SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address,
|
||||
ret.load_size);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <> class TraceResolverImpl<system_tag::windows_tag> {
|
||||
public:
|
||||
TraceResolverImpl() {
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
|
||||
std::vector<module_data> modules;
|
||||
DWORD cbNeeded;
|
||||
std::vector<HMODULE> module_handles(1);
|
||||
SymInitialize(process, NULL, false);
|
||||
DWORD symOptions = SymGetOptions();
|
||||
symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
|
||||
SymSetOptions(symOptions);
|
||||
EnumProcessModules(process, &module_handles[0],
|
||||
module_handles.size() * sizeof(HMODULE), &cbNeeded);
|
||||
module_handles.resize(cbNeeded / sizeof(HMODULE));
|
||||
EnumProcessModules(process, &module_handles[0],
|
||||
module_handles.size() * sizeof(HMODULE), &cbNeeded);
|
||||
std::transform(module_handles.begin(), module_handles.end(),
|
||||
std::back_inserter(modules), get_mod_info(process));
|
||||
void *base = modules[0].base_address;
|
||||
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
|
||||
image_type = h->FileHeader.Machine;
|
||||
}
|
||||
|
||||
template <class ST> void load_stacktrace(ST &) {}
|
||||
|
||||
static const int max_sym_len = 255;
|
||||
struct symbol_t {
|
||||
SYMBOL_INFO sym;
|
||||
char buffer[max_sym_len];
|
||||
} sym;
|
||||
|
||||
DWORD64 displacement;
|
||||
|
||||
ResolvedTrace resolve(ResolvedTrace t) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
|
||||
char name[256];
|
||||
|
||||
memset(&sym, sizeof(sym), 0);
|
||||
sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym.sym.MaxNameLen = max_sym_len;
|
||||
|
||||
if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {
|
||||
// TODO: error handling everywhere
|
||||
LPTSTR lpMsgBuf;
|
||||
DWORD dw = GetLastError();
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
|
||||
printf(lpMsgBuf);
|
||||
|
||||
// abort();
|
||||
}
|
||||
UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE);
|
||||
|
||||
DWORD offset = 0;
|
||||
IMAGEHLP_LINE line;
|
||||
if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) {
|
||||
t.object_filename = line.FileName;
|
||||
t.source.filename = line.FileName;
|
||||
t.source.line = line.LineNumber;
|
||||
t.source.col = offset;
|
||||
}
|
||||
|
||||
t.source.function = name;
|
||||
t.object_filename = "";
|
||||
t.object_function = name;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
DWORD machine_type() const { return image_type; }
|
||||
|
||||
private:
|
||||
DWORD image_type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class TraceResolver : public TraceResolverImpl<system_tag::current_tag> {};
|
||||
|
||||
/*************** CODE SNIPPET ***************/
|
||||
@ -3379,6 +3656,8 @@ public:
|
||||
return os;
|
||||
}
|
||||
|
||||
TraceResolver const &resolver() const { return _resolver; }
|
||||
|
||||
private:
|
||||
TraceResolver _resolver;
|
||||
SnippetFactory _snippets;
|
||||
@ -3623,6 +3902,179 @@ private:
|
||||
|
||||
#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN
|
||||
|
||||
#ifdef BACKWARD_SYSTEM_WINDOWS
|
||||
|
||||
class SignalHandling {
|
||||
public:
|
||||
SignalHandling(const std::vector<int> & = std::vector<int>())
|
||||
: reporter_thread_([]() {
|
||||
/* We handle crashes in a utility thread:
|
||||
backward structures and some Windows functions called here
|
||||
need stack space, which we do not have when we encounter a
|
||||
stack overflow.
|
||||
To support reporting stack traces during a stack overflow,
|
||||
we create a utility thread at startup, which waits until a
|
||||
crash happens or the program exits normally. */
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx());
|
||||
cv().wait(lk, [] { return crashed() != crash_status::running; });
|
||||
}
|
||||
if (crashed() == crash_status::crashed) {
|
||||
handle_stacktrace(skip_recs());
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx());
|
||||
crashed() = crash_status::ending;
|
||||
}
|
||||
cv().notify_one();
|
||||
}) {
|
||||
SetUnhandledExceptionFilter(crash_handler);
|
||||
|
||||
signal(SIGABRT, signal_handler);
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
|
||||
set_terminate(&terminator);
|
||||
set_unexpected(&terminator);
|
||||
_set_purecall_handler(&terminator);
|
||||
_set_invalid_parameter_handler(&invalid_parameter_handler);
|
||||
}
|
||||
bool loaded() const { return true; }
|
||||
|
||||
~SignalHandling() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx());
|
||||
crashed() = crash_status::normal_exit;
|
||||
}
|
||||
|
||||
cv().notify_one();
|
||||
|
||||
reporter_thread_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
static CONTEXT *ctx() {
|
||||
static CONTEXT data;
|
||||
return &data;
|
||||
}
|
||||
|
||||
enum class crash_status { running, crashed, normal_exit, ending };
|
||||
|
||||
static crash_status &crashed() {
|
||||
static crash_status data;
|
||||
return data;
|
||||
}
|
||||
|
||||
static std::mutex &mtx() {
|
||||
static std::mutex data;
|
||||
return data;
|
||||
}
|
||||
|
||||
static std::condition_variable &cv() {
|
||||
static std::condition_variable data;
|
||||
return data;
|
||||
}
|
||||
|
||||
static HANDLE &thread_handle() {
|
||||
static HANDLE handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
std::thread reporter_thread_;
|
||||
|
||||
// TODO: how not to hardcode these?
|
||||
static const constexpr int signal_skip_recs =
|
||||
#ifdef __clang__
|
||||
// With clang, RtlCaptureContext also captures the stack frame of the
|
||||
// current function Below that, there ar 3 internal Windows functions
|
||||
4
|
||||
#else
|
||||
// With MSVC cl, RtlCaptureContext misses the stack frame of the current
|
||||
// function The first entries during StackWalk are the 3 internal Windows
|
||||
// functions
|
||||
3
|
||||
#endif
|
||||
;
|
||||
|
||||
static int &skip_recs() {
|
||||
static int data;
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void terminator() {
|
||||
crash_handler(signal_skip_recs);
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline void signal_handler(int) {
|
||||
crash_handler(signal_skip_recs);
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline void __cdecl invalid_parameter_handler(const wchar_t *,
|
||||
const wchar_t *,
|
||||
const wchar_t *,
|
||||
unsigned int,
|
||||
uintptr_t) {
|
||||
crash_handler(signal_skip_recs);
|
||||
abort();
|
||||
}
|
||||
|
||||
NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) {
|
||||
// The exception info supplies a trace from exactly where the issue was,
|
||||
// no need to skip records
|
||||
crash_handler(0, info->ContextRecord);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) {
|
||||
|
||||
if (ct == nullptr) {
|
||||
RtlCaptureContext(ctx());
|
||||
} else {
|
||||
memcpy(ctx(), ct, sizeof(CONTEXT));
|
||||
}
|
||||
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
|
||||
GetCurrentProcess(), &thread_handle(), 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
|
||||
skip_recs() = skip;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx());
|
||||
crashed() = crash_status::crashed;
|
||||
}
|
||||
|
||||
cv().notify_one();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx());
|
||||
cv().wait(lk, [] { return crashed() != crash_status::crashed; });
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_stacktrace(int skip_frames = 0) {
|
||||
// printer creates the TraceResolver, which can supply us a machine type
|
||||
// for stack walking. Without this, StackTrace can only guess using some
|
||||
// macros.
|
||||
// StackTrace also requires that the PDBs are already loaded, which is done
|
||||
// in the constructor of TraceResolver
|
||||
Printer printer;
|
||||
|
||||
StackTrace st;
|
||||
st.set_machine_type(printer.resolver().machine_type());
|
||||
st.set_context(ctx());
|
||||
st.set_thread_handle(thread_handle());
|
||||
st.load_here(32 + skip_frames);
|
||||
st.skip_n_firsts(skip_frames);
|
||||
|
||||
printer.address = true;
|
||||
printer.print(st, std::cerr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BACKWARD_SYSTEM_WINDOWS
|
||||
|
||||
#ifdef BACKWARD_SYSTEM_UNKNOWN
|
||||
|
||||
class SignalHandling {
|
||||
|
@ -24,15 +24,26 @@
|
||||
#include "test.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#define strcasecmp _stricmp
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__has_include) && __has_include(<error.h>)
|
||||
#include <error.h>
|
||||
#else
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#ifdef _WIN32
|
||||
char argv0[MAX_PATH];
|
||||
inline const char *getprogname() {
|
||||
return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL;
|
||||
}
|
||||
#elif !defined(__APPLE__)
|
||||
// N.B. getprogname() is an Apple/BSD-ism.
|
||||
// program_invocation_name is a GLIBC-ism, but it's also
|
||||
// supported by libmusl.
|
||||
@ -59,13 +70,71 @@ void error(int status, int errnum, const char *format, ...) {
|
||||
}
|
||||
#endif
|
||||
|
||||
test::test_registry_t test::test_registry;
|
||||
using namespace test;
|
||||
|
||||
bool run_test(TestBase &test) {
|
||||
bool run_test(TestBase &test, bool use_child_process = true) {
|
||||
if (!use_child_process) {
|
||||
exit(static_cast<int>(test.run()));
|
||||
}
|
||||
|
||||
printf("-- running test case: %s\n", test.name);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
test::TestStatus status = test::SUCCESS;
|
||||
|
||||
#ifdef _WIN32
|
||||
char filename[256];
|
||||
GetModuleFileName(NULL, filename, 256); // TODO: check for error
|
||||
std::string cmd_line = filename;
|
||||
cmd_line += " --nofork ";
|
||||
cmd_line += test.name;
|
||||
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessA(nullptr, const_cast<char *>(cmd_line.c_str()), nullptr,
|
||||
nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
|
||||
printf("unable to create process\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
DWORD exit_code;
|
||||
GetExitCodeProcess(pi.hProcess, &exit_code);
|
||||
switch (exit_code) {
|
||||
case 3:
|
||||
status = test::SIGNAL_ABORT;
|
||||
break;
|
||||
case 5:
|
||||
status = test::EXCEPTION_UNCAUGHT;
|
||||
break;
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
status = test::SIGNAL_SEGFAULT;
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
status = test::SIGNAL_SEGFAULT;
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
status = test::SIGNAL_DIVZERO;
|
||||
break;
|
||||
}
|
||||
printf("Exit code: %lu\n", exit_code);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
if (test.expected_status == test::ASSERT_FAIL) {
|
||||
// assert calls abort on windows
|
||||
return (status & test::SIGNAL_ABORT);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
exit(static_cast<int>(test.run()));
|
||||
@ -77,8 +146,6 @@ bool run_test(TestBase &test) {
|
||||
int child_status = 0;
|
||||
waitpid(child_pid, &child_status, 0);
|
||||
|
||||
test::TestStatus status;
|
||||
|
||||
if (WIFEXITED(child_status)) {
|
||||
int exit_status = WEXITSTATUS(child_status);
|
||||
if (exit_status & ~test::STATUS_MASK) {
|
||||
@ -103,10 +170,10 @@ bool run_test(TestBase &test) {
|
||||
default:
|
||||
status = test::SIGNAL_UNCAUGHT;
|
||||
}
|
||||
} else {
|
||||
status = test::SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (test.expected_status == test::FAILED) {
|
||||
return (status & test::FAILED);
|
||||
}
|
||||
@ -120,6 +187,25 @@ bool run_test(TestBase &test) {
|
||||
|
||||
int main(int argc, const char *const argv[]) {
|
||||
|
||||
#ifdef _WIN32
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
#endif
|
||||
|
||||
if (argc == 3 && strcmp("--nofork", argv[1]) == 0) {
|
||||
// Windows has no fork, so we simulate it
|
||||
// we only execute one test, without forking
|
||||
for (test_registry_t::iterator it = test_registry().begin();
|
||||
it != test_registry().end(); ++it) {
|
||||
TestBase &test = **it;
|
||||
if (strcasecmp(argv[2], test.name) == 0) {
|
||||
run_test(test, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t success_cnt = 0;
|
||||
size_t total_cnt = 0;
|
||||
for (test_registry_t::iterator it = test_registry().begin();
|
||||
@ -145,9 +231,9 @@ int main(int argc, const char *const argv[]) {
|
||||
printf("** test case FAILED : %s\n", test.name);
|
||||
}
|
||||
}
|
||||
printf("-- tests passing: %lu/%lu", success_cnt, total_cnt);
|
||||
printf("-- tests passing: %zu/%zu", success_cnt, total_cnt);
|
||||
if (total_cnt) {
|
||||
printf(" (%lu%%)\n", success_cnt * 100 / total_cnt);
|
||||
printf(" (%zu%%)\n", success_cnt * 100 / total_cnt);
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -24,17 +24,19 @@
|
||||
#include "backward.hpp"
|
||||
#include "test/test.hpp"
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
using namespace backward;
|
||||
|
||||
void collect_trace(StackTrace &st) { st.load_here(); }
|
||||
|
||||
TEST(minitrace) {
|
||||
Printer printer;
|
||||
|
||||
StackTrace st;
|
||||
collect_trace(st);
|
||||
|
||||
Printer printer;
|
||||
printer.print(st, stdout);
|
||||
printer.print(st, std::cout);
|
||||
}
|
||||
|
||||
void d(StackTrace &st) { st.load_here(); }
|
||||
@ -46,9 +48,10 @@ void b(StackTrace &st) { return c(st); }
|
||||
NOINLINE void a(StackTrace &st) { return b(st); }
|
||||
|
||||
TEST(smalltrace) {
|
||||
Printer printer;
|
||||
|
||||
StackTrace st;
|
||||
a(st);
|
||||
|
||||
Printer printer;
|
||||
printer.print(st, stdout);
|
||||
printer.print(st, std::cout);
|
||||
}
|
||||
|
@ -25,7 +25,10 @@
|
||||
|
||||
#include "test/test.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
using namespace backward;
|
||||
|
||||
@ -75,9 +78,11 @@ TEST_DIVZERO(divide_by_zero) {
|
||||
int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); }
|
||||
|
||||
TEST_SEGFAULT(stackoverflow) {
|
||||
#ifndef _WIN32
|
||||
struct rlimit limit;
|
||||
limit.rlim_max = 8096;
|
||||
setrlimit(RLIMIT_STACK, &limit);
|
||||
#endif
|
||||
int r = bye_bye_stack(42);
|
||||
std::cout << "r=" << r << std::endl;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user