|
12 | 12 | * - 128-bit `uuid_t` keys and `enum slot64_t : std::uint64_t` make most sense for |
13 | 13 | * for database users, implementing portable, concurrent systems. |
14 | 14 | */ |
| 15 | +#include <cassert> // `assert` |
| 16 | +#include <cmath> // `std::abs` |
| 17 | +#include <csignal> // `std::signal`, `SIGSEGV`, ... |
| 18 | +#include <cstdio> // `std::fprintf` |
| 19 | +#include <cstdlib> // `std::_Exit` |
| 20 | + |
15 | 21 | #include <algorithm> // `std::shuffle` |
16 | | -#include <cassert> // `assert` |
17 | | -#include <cmath> // `std::abs` |
18 | 22 | #include <random> // `std::default_random_engine` |
19 | 23 | #include <stdexcept> // `std::terminate` |
20 | 24 | #include <unordered_map> // `std::unordered_map` |
21 | 25 | #include <vector> // `std::vector` |
22 | 26 |
|
| 27 | +// Back-trace support. Prefer the C++23 `<stacktrace>` library when the |
| 28 | +// toolchain + stdlib expose it (`__cpp_lib_stacktrace`); otherwise fall back |
| 29 | +// to the OS-native facility so that unit-test crashes in CI log something |
| 30 | +// useful beyond a bare exit code. |
| 31 | +#if defined(__has_include) |
| 32 | +#if __has_include(<stacktrace>) |
| 33 | +#include <stacktrace> |
| 34 | +#endif |
| 35 | +#endif |
| 36 | +#if defined(__cpp_lib_stacktrace) && __cpp_lib_stacktrace >= 202011L |
| 37 | +#define USEARCH_HAS_STD_STACKTRACE 1 |
| 38 | +#else |
| 39 | +#define USEARCH_HAS_STD_STACKTRACE 0 |
| 40 | +#if defined(_WIN32) |
| 41 | +// `windows.h` must precede `dbghelp.h` — the latter uses `PSTR` and friends |
| 42 | +// that are only defined after `windows.h`. The blank line keeps clang-format |
| 43 | +// from re-sorting the two headers into a single alphabetized block. |
| 44 | +#include <windows.h> |
| 45 | + |
| 46 | +#include <dbghelp.h> |
| 47 | +#pragma comment(lib, "Dbghelp.lib") |
| 48 | +#else |
| 49 | +#include <execinfo.h> |
| 50 | +#include <unistd.h> |
| 51 | +#endif |
| 52 | +#endif |
| 53 | + |
23 | 54 | #define SZ_USE_X86_AVX512 0 // Sanitizers hate AVX512 |
24 | 55 | #include <stringzilla/stringzilla.hpp> // Levenshtein distance implementation |
25 | 56 |
|
@@ -1179,7 +1210,63 @@ void test_isolate() { |
1179 | 1210 | } |
1180 | 1211 | } |
1181 | 1212 |
|
| 1213 | +static void usearch_write_backtrace(int signal_number) { |
| 1214 | + std::fprintf(stderr, "\n[usearch] Fatal signal %d. Back-trace:\n", signal_number); |
| 1215 | +#if USEARCH_HAS_STD_STACKTRACE |
| 1216 | + // C++23 `std::stacktrace` covers every platform the library can reach. |
| 1217 | + auto const current_trace = std::stacktrace::current(); |
| 1218 | + std::size_t frame_index = 0; |
| 1219 | + for (auto const& frame : current_trace) { |
| 1220 | + std::fprintf(stderr, " #%2zu %s\n", frame_index, std::to_string(frame).c_str()); |
| 1221 | + ++frame_index; |
| 1222 | + } |
| 1223 | +#elif defined(_WIN32) |
| 1224 | + // Fallback for MSVC stdlibs without `<stacktrace>`: DbgHelp API. |
| 1225 | + constexpr USHORT backtrace_depth_limit = 64; |
| 1226 | + void* backtrace_frames[backtrace_depth_limit]; |
| 1227 | + USHORT backtrace_depth = CaptureStackBackTrace(0, backtrace_depth_limit, backtrace_frames, nullptr); |
| 1228 | + HANDLE current_process = GetCurrentProcess(); |
| 1229 | + SymInitialize(current_process, nullptr, TRUE); |
| 1230 | + |
| 1231 | + unsigned char symbol_info_buffer[sizeof(SYMBOL_INFO) + 256 * sizeof(char)]; |
| 1232 | + SYMBOL_INFO* symbol_info = reinterpret_cast<SYMBOL_INFO*>(symbol_info_buffer); |
| 1233 | + symbol_info->MaxNameLen = 255; |
| 1234 | + symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO); |
| 1235 | + |
| 1236 | + for (USHORT frame_index = 0; frame_index < backtrace_depth; ++frame_index) { |
| 1237 | + if (SymFromAddr(current_process, reinterpret_cast<DWORD64>(backtrace_frames[frame_index]), 0, symbol_info)) |
| 1238 | + std::fprintf(stderr, " #%2u %s + 0x%llx\n", static_cast<unsigned>(frame_index), symbol_info->Name, |
| 1239 | + static_cast<unsigned long long>(reinterpret_cast<DWORD64>(backtrace_frames[frame_index]) - |
| 1240 | + symbol_info->Address)); |
| 1241 | + else |
| 1242 | + std::fprintf(stderr, " #%2u %p\n", static_cast<unsigned>(frame_index), backtrace_frames[frame_index]); |
| 1243 | + } |
| 1244 | +#else |
| 1245 | + // Fallback for POSIX stdlibs without `<stacktrace>`: `<execinfo.h>`. |
| 1246 | + constexpr int backtrace_depth_limit = 64; |
| 1247 | + void* backtrace_frames[backtrace_depth_limit]; |
| 1248 | + int const backtrace_depth = backtrace(backtrace_frames, backtrace_depth_limit); |
| 1249 | + backtrace_symbols_fd(backtrace_frames, backtrace_depth, STDERR_FILENO); |
| 1250 | +#endif |
| 1251 | + std::fflush(stderr); |
| 1252 | +} |
| 1253 | + |
| 1254 | +static void usearch_crash_handler(int signal_number) { |
| 1255 | + usearch_write_backtrace(signal_number); |
| 1256 | + // Restore the default disposition and re-raise so the shell / CI sees the true exit status. |
| 1257 | + std::signal(signal_number, SIG_DFL); |
| 1258 | + std::raise(signal_number); |
| 1259 | +} |
| 1260 | + |
| 1261 | +static void install_crash_handlers() { |
| 1262 | + int const fatal_signals[] = {SIGSEGV, SIGABRT, SIGILL, SIGFPE}; |
| 1263 | + for (int signal_number : fatal_signals) |
| 1264 | + std::signal(signal_number, &usearch_crash_handler); |
| 1265 | +} |
| 1266 | + |
1182 | 1267 | int main(int, char**) { |
| 1268 | + install_crash_handlers(); |
| 1269 | + |
1183 | 1270 | std::printf("Hardware acceleration compiled: %s\n", hardware_acceleration_compiled()); |
1184 | 1271 | std::printf("Hardware acceleration available: %s\n", hardware_acceleration_available()); |
1185 | 1272 |
|
|
0 commit comments