1
0
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:
Edouard Griffiths 2026-02-14 03:17:16 +01:00 committed by GitHub
commit e33ffc5982
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 310 additions and 84 deletions

View File

@ -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

View File

@ -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 ##################

View File

@ -20,3 +20,7 @@
#include "loggerwithfile.h"
void installCrashHandler(qtwebapp::LoggerWithFile *logger);
#ifdef _WIN32
void logWindowsStackTrace();
#endif

View File

@ -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;
}

View File

@ -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) {

View File

@ -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
View File

@ -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