Skip to content

Commit e33ffc5

Browse files
authored
Merge pull request #2646 from f4exb/feature-libunwind
Added libunwind dependency
2 parents 3ecf974 + 6f9585c commit e33ffc5

File tree

7 files changed

+310
-84
lines changed

7 files changed

+310
-84
lines changed

.github/workflows/linux.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
libusb-1.0-0-dev libhidapi-dev libboost-all-dev libasound2-dev libopencv-dev libopencv-imgcodecs-dev \
3737
libxml2-dev bison flex ffmpeg libpostproc-dev libavcodec-dev libavformat-dev \
3838
libopus-dev libcodec2-dev libairspy-dev libhackrf-dev \
39-
libbladerf-dev libsoapysdr-dev libiio-dev libuhd-dev libhamlib-dev \
39+
libbladerf-dev libsoapysdr-dev libiio-dev libuhd-dev libhamlib-dev libunwind-dev \
4040
python3-mako python3-cheetah python3-numpy \
4141
autoconf automake libtool ninja-build
4242
bash cmake/ci/build_sdrplay.sh

CMakeLists.txt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,64 @@ set(ARCH_OPT "native" CACHE STRING "Specify instruction set to use. Will be pass
3737
'native' option will figure out host machine compatibilities and set flags accordingly (even with MSVC).")
3838
option(ENABLE_QT6 "Build with Qt6 rather than Qt5" OFF)
3939
option(ENABLE_PROFILER "Enable runtime profiler" OFF)
40+
if(UNIX AND NOT ANDROID)
41+
set(DEFAULT_ENABLE_LIBUNWIND ON)
42+
else()
43+
set(DEFAULT_ENABLE_LIBUNWIND OFF)
44+
endif()
45+
option(ENABLE_LIBUNWIND "Use libunwind for stack traces in exception handlers" ${DEFAULT_ENABLE_LIBUNWIND})
4046
set(VKFFT_BACKEND 1 CACHE STRING "vkFFT Backend: 0 - Vulkan, 1 - CUDA")
4147
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
4248

49+
set(HAVE_LIBUNWIND OFF)
50+
if(ENABLE_LIBUNWIND AND UNIX AND NOT ANDROID)
51+
find_package(PkgConfig QUIET)
52+
53+
if(PKG_CONFIG_FOUND)
54+
pkg_check_modules(LIBUNWIND QUIET libunwind)
55+
endif()
56+
57+
# Fall back to find_library if pkg-config doesn't find it
58+
if(NOT LIBUNWIND_FOUND)
59+
find_library(LIBUNWIND_LIB unwind)
60+
if(LIBUNWIND_LIB)
61+
set(LIBUNWIND_LIBRARIES ${LIBUNWIND_LIB})
62+
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
63+
find_library(LIBUNWIND_ARCH_LIB unwind-x86_64)
64+
if(LIBUNWIND_ARCH_LIB)
65+
list(APPEND LIBUNWIND_LIBRARIES ${LIBUNWIND_ARCH_LIB})
66+
endif()
67+
endif()
68+
set(LIBUNWIND_FOUND TRUE)
69+
endif()
70+
endif()
71+
72+
if(LIBUNWIND_FOUND)
73+
if(NOT LIBUNWIND_LIBRARIES)
74+
set(LIBUNWIND_LIBRARIES unwind)
75+
endif()
76+
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
77+
list(JOIN LIBUNWIND_LIBRARIES ";" _libunwind_libs_joined)
78+
if(NOT _libunwind_libs_joined MATCHES "unwind-x86_64")
79+
find_library(LIBUNWIND_ARCH_LIB unwind-x86_64)
80+
if(LIBUNWIND_ARCH_LIB)
81+
list(APPEND LIBUNWIND_LIBRARIES ${LIBUNWIND_ARCH_LIB})
82+
else()
83+
message(WARNING "libunwind-x86_64 not found; stack traces may fail to link on x86_64.")
84+
endif()
85+
endif()
86+
endif()
87+
set(HAVE_LIBUNWIND ON)
88+
message(STATUS "libunwind found - LIBUNWIND_LIBRARIES=${LIBUNWIND_LIBRARIES}")
89+
message(STATUS "libunwind include dirs - ${LIBUNWIND_INCLUDE_DIRS}")
90+
message(STATUS "libunwind cflags - ${LIBUNWIND_CFLAGS}")
91+
message(STATUS "libunwind ldflags - ${LIBUNWIND_LDFLAGS}")
92+
else()
93+
message(WARNING "libunwind requested but not found; stack traces disabled. Install libunwind-dev or similar package.")
94+
set(ENABLE_LIBUNWIND OFF)
95+
endif()
96+
endif()
97+
4398
# Sampling devices enablers
4499
option(ENABLE_AARONIARTSA "Enable AaroniaRTSA support" ON)
45100
option(ENABLE_AIRSPY "Enable AirSpy support" ON)
@@ -1071,6 +1126,14 @@ if (BUILD_GUI)
10711126
target_link_libraries(${CMAKE_PROJECT_NAME} "-framework AVFoundation" objc)
10721127
endif()
10731128

1129+
if(HAVE_LIBUNWIND)
1130+
target_link_libraries(${CMAKE_PROJECT_NAME} ${LIBUNWIND_LIBRARIES})
1131+
if(LIBUNWIND_INCLUDE_DIRS)
1132+
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
1133+
endif()
1134+
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE HAVE_LIBUNWIND=1)
1135+
endif()
1136+
10741137
if(WIN32)
10751138
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES
10761139
WIN32_EXECUTABLE ${HIDE_CONSOLE})
@@ -1093,6 +1156,14 @@ if (BUILD_SERVER)
10931156
sdrsrv
10941157
logging
10951158
)
1159+
1160+
if(HAVE_LIBUNWIND)
1161+
target_link_libraries(sdrangelsrv ${LIBUNWIND_LIBRARIES})
1162+
if(LIBUNWIND_INCLUDE_DIRS)
1163+
target_include_directories(sdrangelsrv PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
1164+
endif()
1165+
target_compile_definitions(sdrangelsrv PRIVATE HAVE_LIBUNWIND=1)
1166+
endif()
10961167
endif()
10971168

10981169
############ install ##################

app/crashhandler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@
2020
#include "loggerwithfile.h"
2121

2222
void installCrashHandler(qtwebapp::LoggerWithFile *logger);
23+
24+
#ifdef _WIN32
25+
void logWindowsStackTrace();
26+
#endif

app/crashhandlerwin.cpp

Lines changed: 116 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,102 @@ static HWND hExitButton;
3838

3939
static qtwebapp::LoggerWithFile *crashLogger;
4040

41+
static void appendStackTrace(char *&reportBufferPtr, int &reportBufferRemaining)
42+
{
43+
STACKFRAME64 stack;
44+
CONTEXT context;
45+
HANDLE process;
46+
DWORD64 displacement;
47+
ULONG frame;
48+
BOOL symInit;
49+
char symName[(MAX_PATH * sizeof(TCHAR))];
50+
char storage[sizeof(IMAGEHLP_SYMBOL64) + (sizeof(symName))];
51+
IMAGEHLP_SYMBOL64* symbol;
52+
53+
RtlCaptureContext(&context);
54+
memset(&stack, 0, sizeof(STACKFRAME64));
55+
#if defined(_AMD64_)
56+
stack.AddrPC.Offset = context.Rip;
57+
stack.AddrPC.Mode = AddrModeFlat;
58+
stack.AddrStack.Offset = context.Rsp;
59+
stack.AddrStack.Mode = AddrModeFlat;
60+
stack.AddrFrame.Offset = context.Rbp;
61+
stack.AddrFrame.Mode = AddrModeFlat;
62+
#else
63+
stack.AddrPC.Offset = context.Eip;
64+
stack.AddrPC.Mode = AddrModeFlat;
65+
stack.AddrStack.Offset = context.Esp;
66+
stack.AddrStack.Mode = AddrModeFlat;
67+
stack.AddrFrame.Offset = context.Ebp;
68+
stack.AddrFrame.Mode = AddrModeFlat;
69+
#endif
70+
displacement = 0;
71+
process = GetCurrentProcess();
72+
symInit = SymInitialize(process, "plugins", TRUE);
73+
symbol = (IMAGEHLP_SYMBOL64*) storage;
74+
75+
if (!symInit)
76+
{
77+
int written = snprintf(reportBufferPtr, reportBufferRemaining, "(symbol init failed)\r\n");
78+
if (written > 0)
79+
{
80+
reportBufferPtr += written;
81+
reportBufferRemaining -= written;
82+
}
83+
return;
84+
}
85+
86+
for (frame = 0; reportBufferRemaining > 0; frame++)
87+
{
88+
BOOL result = StackWalk(IMAGE_FILE_MACHINE_AMD64,
89+
process,
90+
GetCurrentThread(),
91+
&stack,
92+
&context,
93+
NULL,
94+
SymFunctionTableAccess64,
95+
SymGetModuleBase64,
96+
NULL);
97+
98+
if (result)
99+
{
100+
symbol->SizeOfStruct = sizeof(storage);
101+
symbol->MaxNameLength = sizeof(symName);
102+
103+
BOOL symResult = SymGetSymFromAddr64(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol);
104+
if (symResult) {
105+
UnDecorateSymbolName(symbol->Name, (PSTR)symName, sizeof(symName), UNDNAME_COMPLETE);
106+
}
107+
108+
int written = snprintf(
109+
reportBufferPtr,
110+
reportBufferRemaining,
111+
"%02u 0x%p %s\r\n",
112+
frame,
113+
stack.AddrPC.Offset,
114+
symResult ? symbol->Name : "Unknown"
115+
);
116+
if (written > 0)
117+
{
118+
if (written <= reportBufferRemaining)
119+
{
120+
reportBufferPtr += written;
121+
reportBufferRemaining -= written;
122+
}
123+
else
124+
{
125+
reportBufferPtr += reportBufferRemaining;
126+
reportBufferRemaining = 0;
127+
}
128+
}
129+
}
130+
else
131+
{
132+
break;
133+
}
134+
}
135+
}
136+
41137
static void ScaleWindow(HWND wnd, int x, int y, int w, int h)
42138
{
43139
int dpi = GetDpiForWindow(wnd);
@@ -273,88 +369,7 @@ static LONG crashHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
273369
reportBufferRemaining -= written;
274370

275371
// Create stack trace
276-
277-
STACKFRAME64 stack;
278-
CONTEXT context;
279-
HANDLE process;
280-
DWORD64 displacement;
281-
ULONG frame;
282-
BOOL symInit;
283-
char symName[(MAX_PATH * sizeof(TCHAR))];
284-
char storage[sizeof(IMAGEHLP_SYMBOL64) + (sizeof(symName))];
285-
IMAGEHLP_SYMBOL64* symbol;
286-
287-
RtlCaptureContext(&context);
288-
memset(&stack, 0, sizeof(STACKFRAME64));
289-
#if defined(_AMD64_)
290-
stack.AddrPC.Offset = context.Rip;
291-
stack.AddrPC.Mode = AddrModeFlat;
292-
stack.AddrStack.Offset = context.Rsp;
293-
stack.AddrStack.Mode = AddrModeFlat;
294-
stack.AddrFrame.Offset = context.Rbp;
295-
stack.AddrFrame.Mode = AddrModeFlat;
296-
#else
297-
stack.AddrPC.Offset = context.Eip;
298-
stack.AddrPC.Mode = AddrModeFlat;
299-
stack.AddrStack.Offset = context.Esp;
300-
stack.AddrStack.Mode = AddrModeFlat;
301-
stack.AddrFrame.Offset = context.Ebp;
302-
stack.AddrFrame.Mode = AddrModeFlat;
303-
#endif
304-
displacement = 0;
305-
process = GetCurrentProcess();
306-
symInit = SymInitialize(process, "plugins", TRUE);
307-
symbol = (IMAGEHLP_SYMBOL64*) storage;
308-
309-
for (frame = 0; reportBufferRemaining > 0; frame++)
310-
{
311-
BOOL result = StackWalk(IMAGE_FILE_MACHINE_AMD64,
312-
process,
313-
GetCurrentThread(),
314-
&stack,
315-
&context,
316-
NULL,
317-
SymFunctionTableAccess64,
318-
SymGetModuleBase64,
319-
NULL);
320-
321-
if (result)
322-
{
323-
symbol->SizeOfStruct = sizeof(storage);
324-
symbol->MaxNameLength = sizeof(symName);
325-
326-
BOOL symResult = SymGetSymFromAddr64(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol);
327-
if (symResult) {
328-
UnDecorateSymbolName(symbol->Name, (PSTR)symName, sizeof(symName), UNDNAME_COMPLETE);
329-
}
330-
331-
written = snprintf(
332-
reportBufferPtr,
333-
reportBufferRemaining,
334-
"%02u 0x%p %s\r\n",
335-
frame,
336-
stack.AddrPC.Offset,
337-
symResult ? symbol->Name : "Unknown"
338-
);
339-
if (written > 0)
340-
{
341-
if (written <= reportBufferRemaining)
342-
{
343-
reportBufferPtr += written;
344-
reportBufferRemaining -= written;
345-
}
346-
else
347-
{
348-
reportBufferPtr += reportBufferRemaining;
349-
reportBufferRemaining = 0;
350-
}
351-
}
352-
}
353-
else
354-
{
355-
break;
356-
}
357-
}
372+
appendStackTrace(reportBufferPtr, reportBufferRemaining);
358373

359374
// Append log file
360375
if (crashLogger)
@@ -432,3 +447,22 @@ void installCrashHandler(qtwebapp::LoggerWithFile *logger)
432447
crashLogger = logger;
433448
SetUnhandledExceptionFilter(crashHandler);
434449
}
450+
451+
void logWindowsStackTrace()
452+
{
453+
const int reportBufferSize = 64 * 1024;
454+
char *reportBuffer = new char[reportBufferSize];
455+
char *reportBufferPtr = reportBuffer;
456+
int reportBufferRemaining = reportBufferSize;
457+
458+
int written = snprintf(reportBufferPtr, reportBufferRemaining, "Stack trace (DbgHelp):\r\n");
459+
if (written > 0)
460+
{
461+
reportBufferPtr += written;
462+
reportBufferRemaining -= written;
463+
}
464+
465+
appendStackTrace(reportBufferPtr, reportBufferRemaining);
466+
qCritical("%s", reportBuffer);
467+
delete[] reportBuffer;
468+
}

0 commit comments

Comments
 (0)