mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-04-01 20:55:33 -04:00
Merge pull request #2646 from f4exb/feature-libunwind
Added libunwind dependency
This commit is contained in:
commit
e33ffc5982
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
libusb-1.0-0-dev libhidapi-dev libboost-all-dev libasound2-dev libopencv-dev libopencv-imgcodecs-dev \
|
||||
libxml2-dev bison flex ffmpeg libpostproc-dev libavcodec-dev libavformat-dev \
|
||||
libopus-dev libcodec2-dev libairspy-dev libhackrf-dev \
|
||||
libbladerf-dev libsoapysdr-dev libiio-dev libuhd-dev libhamlib-dev \
|
||||
libbladerf-dev libsoapysdr-dev libiio-dev libuhd-dev libhamlib-dev libunwind-dev \
|
||||
python3-mako python3-cheetah python3-numpy \
|
||||
autoconf automake libtool ninja-build
|
||||
bash cmake/ci/build_sdrplay.sh
|
||||
|
||||
@ -37,9 +37,64 @@ set(ARCH_OPT "native" CACHE STRING "Specify instruction set to use. Will be pass
|
||||
'native' option will figure out host machine compatibilities and set flags accordingly (even with MSVC).")
|
||||
option(ENABLE_QT6 "Build with Qt6 rather than Qt5" OFF)
|
||||
option(ENABLE_PROFILER "Enable runtime profiler" OFF)
|
||||
if(UNIX AND NOT ANDROID)
|
||||
set(DEFAULT_ENABLE_LIBUNWIND ON)
|
||||
else()
|
||||
set(DEFAULT_ENABLE_LIBUNWIND OFF)
|
||||
endif()
|
||||
option(ENABLE_LIBUNWIND "Use libunwind for stack traces in exception handlers" ${DEFAULT_ENABLE_LIBUNWIND})
|
||||
set(VKFFT_BACKEND 1 CACHE STRING "vkFFT Backend: 0 - Vulkan, 1 - CUDA")
|
||||
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
|
||||
|
||||
set(HAVE_LIBUNWIND OFF)
|
||||
if(ENABLE_LIBUNWIND AND UNIX AND NOT ANDROID)
|
||||
find_package(PkgConfig QUIET)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(LIBUNWIND QUIET libunwind)
|
||||
endif()
|
||||
|
||||
# Fall back to find_library if pkg-config doesn't find it
|
||||
if(NOT LIBUNWIND_FOUND)
|
||||
find_library(LIBUNWIND_LIB unwind)
|
||||
if(LIBUNWIND_LIB)
|
||||
set(LIBUNWIND_LIBRARIES ${LIBUNWIND_LIB})
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||
find_library(LIBUNWIND_ARCH_LIB unwind-x86_64)
|
||||
if(LIBUNWIND_ARCH_LIB)
|
||||
list(APPEND LIBUNWIND_LIBRARIES ${LIBUNWIND_ARCH_LIB})
|
||||
endif()
|
||||
endif()
|
||||
set(LIBUNWIND_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LIBUNWIND_FOUND)
|
||||
if(NOT LIBUNWIND_LIBRARIES)
|
||||
set(LIBUNWIND_LIBRARIES unwind)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||
list(JOIN LIBUNWIND_LIBRARIES ";" _libunwind_libs_joined)
|
||||
if(NOT _libunwind_libs_joined MATCHES "unwind-x86_64")
|
||||
find_library(LIBUNWIND_ARCH_LIB unwind-x86_64)
|
||||
if(LIBUNWIND_ARCH_LIB)
|
||||
list(APPEND LIBUNWIND_LIBRARIES ${LIBUNWIND_ARCH_LIB})
|
||||
else()
|
||||
message(WARNING "libunwind-x86_64 not found; stack traces may fail to link on x86_64.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(HAVE_LIBUNWIND ON)
|
||||
message(STATUS "libunwind found - LIBUNWIND_LIBRARIES=${LIBUNWIND_LIBRARIES}")
|
||||
message(STATUS "libunwind include dirs - ${LIBUNWIND_INCLUDE_DIRS}")
|
||||
message(STATUS "libunwind cflags - ${LIBUNWIND_CFLAGS}")
|
||||
message(STATUS "libunwind ldflags - ${LIBUNWIND_LDFLAGS}")
|
||||
else()
|
||||
message(WARNING "libunwind requested but not found; stack traces disabled. Install libunwind-dev or similar package.")
|
||||
set(ENABLE_LIBUNWIND OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Sampling devices enablers
|
||||
option(ENABLE_AARONIARTSA "Enable AaroniaRTSA support" ON)
|
||||
option(ENABLE_AIRSPY "Enable AirSpy support" ON)
|
||||
@ -1071,6 +1126,14 @@ if (BUILD_GUI)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} "-framework AVFoundation" objc)
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBUNWIND)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} ${LIBUNWIND_LIBRARIES})
|
||||
if(LIBUNWIND_INCLUDE_DIRS)
|
||||
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE HAVE_LIBUNWIND=1)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
|
||||
WIN32_EXECUTABLE ${HIDE_CONSOLE})
|
||||
@ -1093,6 +1156,14 @@ if (BUILD_SERVER)
|
||||
sdrsrv
|
||||
logging
|
||||
)
|
||||
|
||||
if(HAVE_LIBUNWIND)
|
||||
target_link_libraries(sdrangelsrv ${LIBUNWIND_LIBRARIES})
|
||||
if(LIBUNWIND_INCLUDE_DIRS)
|
||||
target_include_directories(sdrangelsrv PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
|
||||
endif()
|
||||
target_compile_definitions(sdrangelsrv PRIVATE HAVE_LIBUNWIND=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
############ install ##################
|
||||
|
||||
@ -20,3 +20,7 @@
|
||||
#include "loggerwithfile.h"
|
||||
|
||||
void installCrashHandler(qtwebapp::LoggerWithFile *logger);
|
||||
|
||||
#ifdef _WIN32
|
||||
void logWindowsStackTrace();
|
||||
#endif
|
||||
|
||||
@ -38,6 +38,102 @@ static HWND hExitButton;
|
||||
|
||||
static qtwebapp::LoggerWithFile *crashLogger;
|
||||
|
||||
static void appendStackTrace(char *&reportBufferPtr, int &reportBufferRemaining)
|
||||
{
|
||||
STACKFRAME64 stack;
|
||||
CONTEXT context;
|
||||
HANDLE process;
|
||||
DWORD64 displacement;
|
||||
ULONG frame;
|
||||
BOOL symInit;
|
||||
char symName[(MAX_PATH * sizeof(TCHAR))];
|
||||
char storage[sizeof(IMAGEHLP_SYMBOL64) + (sizeof(symName))];
|
||||
IMAGEHLP_SYMBOL64* symbol;
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
memset(&stack, 0, sizeof(STACKFRAME64));
|
||||
#if defined(_AMD64_)
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Rbp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#else
|
||||
stack.AddrPC.Offset = context.Eip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Esp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Ebp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#endif
|
||||
displacement = 0;
|
||||
process = GetCurrentProcess();
|
||||
symInit = SymInitialize(process, "plugins", TRUE);
|
||||
symbol = (IMAGEHLP_SYMBOL64*) storage;
|
||||
|
||||
if (!symInit)
|
||||
{
|
||||
int written = snprintf(reportBufferPtr, reportBufferRemaining, "(symbol init failed)\r\n");
|
||||
if (written > 0)
|
||||
{
|
||||
reportBufferPtr += written;
|
||||
reportBufferRemaining -= written;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (frame = 0; reportBufferRemaining > 0; frame++)
|
||||
{
|
||||
BOOL result = StackWalk(IMAGE_FILE_MACHINE_AMD64,
|
||||
process,
|
||||
GetCurrentThread(),
|
||||
&stack,
|
||||
&context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL);
|
||||
|
||||
if (result)
|
||||
{
|
||||
symbol->SizeOfStruct = sizeof(storage);
|
||||
symbol->MaxNameLength = sizeof(symName);
|
||||
|
||||
BOOL symResult = SymGetSymFromAddr64(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol);
|
||||
if (symResult) {
|
||||
UnDecorateSymbolName(symbol->Name, (PSTR)symName, sizeof(symName), UNDNAME_COMPLETE);
|
||||
}
|
||||
|
||||
int written = snprintf(
|
||||
reportBufferPtr,
|
||||
reportBufferRemaining,
|
||||
"%02u 0x%p %s\r\n",
|
||||
frame,
|
||||
stack.AddrPC.Offset,
|
||||
symResult ? symbol->Name : "Unknown"
|
||||
);
|
||||
if (written > 0)
|
||||
{
|
||||
if (written <= reportBufferRemaining)
|
||||
{
|
||||
reportBufferPtr += written;
|
||||
reportBufferRemaining -= written;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportBufferPtr += reportBufferRemaining;
|
||||
reportBufferRemaining = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ScaleWindow(HWND wnd, int x, int y, int w, int h)
|
||||
{
|
||||
int dpi = GetDpiForWindow(wnd);
|
||||
@ -273,88 +369,7 @@ static LONG crashHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
|
||||
reportBufferRemaining -= written;
|
||||
|
||||
// Create stack trace
|
||||
|
||||
STACKFRAME64 stack;
|
||||
CONTEXT context;
|
||||
HANDLE process;
|
||||
DWORD64 displacement;
|
||||
ULONG frame;
|
||||
BOOL symInit;
|
||||
char symName[(MAX_PATH * sizeof(TCHAR))];
|
||||
char storage[sizeof(IMAGEHLP_SYMBOL64) + (sizeof(symName))];
|
||||
IMAGEHLP_SYMBOL64* symbol;
|
||||
|
||||
RtlCaptureContext(&context);
|
||||
memset(&stack, 0, sizeof(STACKFRAME64));
|
||||
#if defined(_AMD64_)
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Rbp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#else
|
||||
stack.AddrPC.Offset = context.Eip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Esp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Ebp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#endif
|
||||
displacement = 0;
|
||||
process = GetCurrentProcess();
|
||||
symInit = SymInitialize(process, "plugins", TRUE);
|
||||
symbol = (IMAGEHLP_SYMBOL64*) storage;
|
||||
|
||||
for (frame = 0; reportBufferRemaining > 0; frame++)
|
||||
{
|
||||
BOOL result = StackWalk(IMAGE_FILE_MACHINE_AMD64,
|
||||
process,
|
||||
GetCurrentThread(),
|
||||
&stack,
|
||||
&context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL);
|
||||
|
||||
if (result)
|
||||
{
|
||||
symbol->SizeOfStruct = sizeof(storage);
|
||||
symbol->MaxNameLength = sizeof(symName);
|
||||
|
||||
BOOL symResult = SymGetSymFromAddr64(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol);
|
||||
if (symResult) {
|
||||
UnDecorateSymbolName(symbol->Name, (PSTR)symName, sizeof(symName), UNDNAME_COMPLETE);
|
||||
}
|
||||
|
||||
written = snprintf(
|
||||
reportBufferPtr,
|
||||
reportBufferRemaining,
|
||||
"%02u 0x%p %s\r\n",
|
||||
frame,
|
||||
stack.AddrPC.Offset,
|
||||
symResult ? symbol->Name : "Unknown"
|
||||
);
|
||||
if (written > 0)
|
||||
{
|
||||
if (written <= reportBufferRemaining)
|
||||
{
|
||||
reportBufferPtr += written;
|
||||
reportBufferRemaining -= written;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportBufferPtr += reportBufferRemaining;
|
||||
reportBufferRemaining = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
appendStackTrace(reportBufferPtr, reportBufferRemaining);
|
||||
|
||||
// Append log file
|
||||
if (crashLogger)
|
||||
@ -432,3 +447,22 @@ void installCrashHandler(qtwebapp::LoggerWithFile *logger)
|
||||
crashLogger = logger;
|
||||
SetUnhandledExceptionFilter(crashHandler);
|
||||
}
|
||||
|
||||
void logWindowsStackTrace()
|
||||
{
|
||||
const int reportBufferSize = 64 * 1024;
|
||||
char *reportBuffer = new char[reportBufferSize];
|
||||
char *reportBufferPtr = reportBuffer;
|
||||
int reportBufferRemaining = reportBufferSize;
|
||||
|
||||
int written = snprintf(reportBufferPtr, reportBufferRemaining, "Stack trace (DbgHelp):\r\n");
|
||||
if (written > 0)
|
||||
{
|
||||
reportBufferPtr += written;
|
||||
reportBufferRemaining -= written;
|
||||
}
|
||||
|
||||
appendStackTrace(reportBufferPtr, reportBufferRemaining);
|
||||
qCritical("%s", reportBuffer);
|
||||
delete[] reportBuffer;
|
||||
}
|
||||
|
||||
64
app/main.cpp
64
app/main.cpp
@ -28,6 +28,11 @@
|
||||
#include <QSysInfo>
|
||||
#include <QSettings>
|
||||
#include <exception>
|
||||
#include <cstdlib>
|
||||
#if defined(HAVE_LIBUNWIND)
|
||||
#include <libunwind.h>
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
#include <QGLFormat>
|
||||
@ -47,6 +52,50 @@
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "crashhandler.h"
|
||||
|
||||
static void logExceptionStackTrace()
|
||||
{
|
||||
#if defined(HAVE_LIBUNWIND)
|
||||
unw_context_t context;
|
||||
unw_cursor_t cursor;
|
||||
if (unw_getcontext(&context) != 0) {
|
||||
qCritical("Failed to capture unwind context.");
|
||||
return;
|
||||
}
|
||||
if (unw_init_local(&cursor, &context) != 0) {
|
||||
qCritical("Failed to initialize unwind cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
qCritical("Stack trace (libunwind):");
|
||||
int frame = 0;
|
||||
while (unw_step(&cursor) > 0 && frame < 64) {
|
||||
unw_word_t pc = 0;
|
||||
unw_word_t offset = 0;
|
||||
char symbol[256];
|
||||
symbol[0] = '\0';
|
||||
|
||||
if (unw_get_reg(&cursor, UNW_REG_IP, &pc) != 0) {
|
||||
pc = 0;
|
||||
}
|
||||
|
||||
if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(symbol, nullptr, nullptr, &status);
|
||||
const char *name = (status == 0 && demangled) ? demangled : symbol;
|
||||
qCritical("#%d 0x%llx %s + 0x%llx", frame, static_cast<unsigned long long>(pc), name,
|
||||
static_cast<unsigned long long>(offset));
|
||||
std::free(demangled);
|
||||
} else {
|
||||
qCritical("#%d 0x%llx <unknown>", frame, static_cast<unsigned long long>(pc));
|
||||
}
|
||||
|
||||
++frame;
|
||||
}
|
||||
#else
|
||||
qCritical("Stack trace unavailable (libunwind not enabled).");
|
||||
#endif
|
||||
}
|
||||
|
||||
class SDRangelApplication final : public QApplication
|
||||
{
|
||||
public:
|
||||
@ -66,10 +115,25 @@ public:
|
||||
} else {
|
||||
qCritical() << "Failed on object: (null)";
|
||||
}
|
||||
#ifdef _WIN32
|
||||
logWindowsStackTrace();
|
||||
#else
|
||||
logExceptionStackTrace();
|
||||
#endif
|
||||
} catch (const std::exception &ex) {
|
||||
qCritical("Unhandled exception in event handler: %s", ex.what());
|
||||
#ifdef _WIN32
|
||||
logWindowsStackTrace();
|
||||
#else
|
||||
logExceptionStackTrace();
|
||||
#endif
|
||||
} catch (...) {
|
||||
qCritical("Unhandled non-standard exception in event handler.");
|
||||
#ifdef _WIN32
|
||||
logWindowsStackTrace();
|
||||
#else
|
||||
logExceptionStackTrace();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!s_exitRequested) {
|
||||
|
||||
@ -23,6 +23,11 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QSysInfo>
|
||||
#include <exception>
|
||||
#include <cstdlib>
|
||||
#if defined(HAVE_LIBUNWIND)
|
||||
#include <libunwind.h>
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
#include <vector>
|
||||
@ -33,6 +38,50 @@
|
||||
#include "remotetcpsinkstarter.h"
|
||||
#include "dsp/dsptypes.h"
|
||||
|
||||
static void logExceptionStackTrace()
|
||||
{
|
||||
#if defined(HAVE_LIBUNWIND)
|
||||
unw_context_t context;
|
||||
unw_cursor_t cursor;
|
||||
if (unw_getcontext(&context) != 0) {
|
||||
qCritical("Failed to capture unwind context.");
|
||||
return;
|
||||
}
|
||||
if (unw_init_local(&cursor, &context) != 0) {
|
||||
qCritical("Failed to initialize unwind cursor.");
|
||||
return;
|
||||
}
|
||||
|
||||
qCritical("Stack trace (libunwind):");
|
||||
int frame = 0;
|
||||
while (unw_step(&cursor) > 0 && frame < 64) {
|
||||
unw_word_t pc = 0;
|
||||
unw_word_t offset = 0;
|
||||
char symbol[256];
|
||||
symbol[0] = '\0';
|
||||
|
||||
if (unw_get_reg(&cursor, UNW_REG_IP, &pc) != 0) {
|
||||
pc = 0;
|
||||
}
|
||||
|
||||
if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(symbol, nullptr, nullptr, &status);
|
||||
const char *name = (status == 0 && demangled) ? demangled : symbol;
|
||||
qCritical("#%d 0x%llx %s + 0x%llx", frame, static_cast<unsigned long long>(pc), name,
|
||||
static_cast<unsigned long long>(offset));
|
||||
std::free(demangled);
|
||||
} else {
|
||||
qCritical("#%d 0x%llx <unknown>", frame, static_cast<unsigned long long>(pc));
|
||||
}
|
||||
|
||||
++frame;
|
||||
}
|
||||
#else
|
||||
qCritical("Stack trace unavailable (libunwind not enabled).");
|
||||
#endif
|
||||
}
|
||||
|
||||
class SDRangelServerApplication final : public QCoreApplication
|
||||
{
|
||||
public:
|
||||
@ -52,10 +101,13 @@ public:
|
||||
} else {
|
||||
qCritical() << "Failed on object: (null)";
|
||||
}
|
||||
logExceptionStackTrace();
|
||||
} catch (const std::exception &ex) {
|
||||
qCritical("Unhandled exception in event handler: %s", ex.what());
|
||||
logExceptionStackTrace();
|
||||
} catch (...) {
|
||||
qCritical("Unhandled non-standard exception in event handler.");
|
||||
logExceptionStackTrace();
|
||||
}
|
||||
|
||||
if (!s_exitRequested) {
|
||||
|
||||
3
debian/control
vendored
3
debian/control
vendored
@ -55,7 +55,8 @@ Build-Depends: debhelper (>= 9),
|
||||
libhackrf-dev,
|
||||
libuhd-dev,
|
||||
libhamlib-dev,
|
||||
zlib1g-dev
|
||||
zlib1g-dev,
|
||||
libunwind-dev
|
||||
# TODO:
|
||||
# - more dependencies based on version; newer has more devices
|
||||
# - manage dependencies not present upstream
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user