Skip to content

Commit 4150d41

Browse files
fix: more defensive auto thread stack guarantee (#1196)
* fix: provide less aggressive auto thread stack guarantee * verbose-log is off * update changelog * ensure no unused `success` variable * update inline docs to reflect that sentry_init() is no longer a requirement * clarify error logs when SetThreadStackGuarantee failed * redo dynamic loading of thread-stack-guarantee functions from DllMain since all come directly from kernel32 which guaranteed to be loaded. * remove superflous 0 Co-authored-by: JoshuaMoelans <[email protected]> * clarify 32KiB as a lower bound * replace copy-pasted GetCurrentThreadStackLimits error message * adding unit-test * fix syntax error in calling convention of unit-test utilities * separate cmake patterns in editorconfig * clion ignores editorconfig for cmake -> fix inconsistent space-based indentation * further whitespace fixes * parameterize stack-overflow integration tests expose handler stack size as first step where applicable. --------- Co-authored-by: JoshuaMoelans <[email protected]>
1 parent f8a5724 commit 4150d41

24 files changed

+733
-257
lines changed

.editorconfig

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,11 @@ charset = utf-8
99
[Makefile]
1010
indent_style = tab
1111

12-
[{CMakeLists.txt, *.cmake*}]
12+
[CMakeLists.txt]
13+
indent_style = tab
14+
15+
[*.cmake]
16+
indent_style = tab
17+
18+
[*.cmake.in]
1319
indent_style = tab

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
- Provide an option for downstream SDKs to attach a view hierarchy file. ([#1191](https://github.com/getsentry/sentry-native/pull/1191))
88

9+
**Fixes**:
10+
11+
- Provide a more defensive automatic thread stack guarantee. ([#1196](https://github.com/getsentry/sentry-native/pull/1196))
12+
913
## 0.8.3
1014

1115
**Features**:

CMakeLists.txt

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
if(WIN32)
2-
cmake_minimum_required (VERSION 3.18)
2+
cmake_minimum_required(VERSION 3.18)
33

44
# enables support for CMAKE_MSVC_RUNTIME_LIBRARY
55
cmake_policy(SET CMP0091 NEW)
66
else()
77
# The Android tools ship with this ancient version, which we need to support.
8-
cmake_minimum_required (VERSION 3.10)
8+
cmake_minimum_required(VERSION 3.10)
99
cmake_policy(SET CMP0077 NEW)
1010
endif()
1111
set(SENTRY_TOOLCHAINS_DIR "${CMAKE_CURRENT_LIST_DIR}/toolchains")
@@ -178,11 +178,23 @@ if(SENTRY_BACKEND_CRASHPAD AND ANDROID)
178178
endif()
179179

180180
set(SENTRY_SDK_NAME "" CACHE STRING "The SDK name to report when sending events.")
181+
set(SENTRY_HANDLER_STACK_SIZE 64 CACHE STRING "The stack size (in KiB) that should be reserved for the crash handler.")
182+
if (WIN32)
183+
set(SENTRY_THREAD_STACK_GUARANTEE_FACTOR 10 CACHE STRING "The factor by which a threads stack should be larger than the stack guarantee for its handler.")
184+
option(SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT "Automatically sets the thread stack guarantee for each thread via `DllMain` or for the `sentry_init()` when building statically" ON)
185+
option(SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG "Enables logging of successfully set thread stack guarantees" OFF)
186+
endif()
181187

182188
message(STATUS "SENTRY_TRANSPORT=${SENTRY_TRANSPORT}")
183189
message(STATUS "SENTRY_BACKEND=${SENTRY_BACKEND}")
184190
message(STATUS "SENTRY_LIBRARY_TYPE=${SENTRY_LIBRARY_TYPE}")
185191
message(STATUS "SENTRY_SDK_NAME=${SENTRY_SDK_NAME}")
192+
message(STATUS "SENTRY_HANDLER_STACK_SIZE=${SENTRY_HANDLER_STACK_SIZE}")
193+
if (WIN32)
194+
message(STATUS "SENTRY_THREAD_STACK_GUARANTEE_FACTOR=${SENTRY_THREAD_STACK_GUARANTEE_FACTOR}")
195+
message(STATUS "SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT=${SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT}")
196+
message(STATUS "SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG=${SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG}")
197+
endif()
186198

187199
if(ANDROID)
188200
set(SENTRY_WITH_LIBUNWINDSTACK TRUE)
@@ -253,6 +265,17 @@ target_sources(sentry PRIVATE "${PROJECT_SOURCE_DIR}/include/sentry.h")
253265
add_library(sentry::sentry ALIAS sentry)
254266
add_subdirectory(src)
255267

268+
target_compile_definitions(sentry PRIVATE SENTRY_HANDLER_STACK_SIZE=${SENTRY_HANDLER_STACK_SIZE})
269+
if(WIN32)
270+
target_compile_definitions(sentry PRIVATE SENTRY_THREAD_STACK_GUARANTEE_FACTOR=${SENTRY_THREAD_STACK_GUARANTEE_FACTOR})
271+
if (SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT)
272+
target_compile_definitions(sentry PRIVATE SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT)
273+
endif()
274+
if (SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG)
275+
target_compile_definitions(sentry PRIVATE SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG)
276+
endif()
277+
endif()
278+
256279
if (NOT SENTRY_SDK_NAME STREQUAL "")
257280
target_compile_definitions(sentry PRIVATE SENTRY_SDK_NAME="${SENTRY_SDK_NAME}")
258281
endif()
@@ -636,7 +659,10 @@ if(SENTRY_BUILD_EXAMPLES)
636659
if(MSVC)
637660
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/wd5105 /wd4717>)
638661
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # clang-cl
662+
# ignore the warning for the intentionally infinite recursion
639663
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:-Wno-infinite-recursion>)
664+
# ensure that clang-cl builds an unoptimized `sentry_example` in all build types
665+
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/Od>)
640666
endif()
641667

642668
# to test handling SEH by-passing exceptions we need to enable the control flow guard
@@ -660,7 +686,7 @@ if(SENTRY_BUILD_EXAMPLES)
660686
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/minidump.dmp" "$<TARGET_FILE_DIR:sentry_example>/minidump.dmp")
661687

662688
add_custom_command(TARGET sentry_example POST_BUILD
663-
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/view-hierarchy.json" "$<TARGET_FILE_DIR:sentry_example>/view-hierarchy.json")
689+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/tests/fixtures/view-hierarchy.json" "$<TARGET_FILE_DIR:sentry_example>/view-hierarchy.json")
664690

665691
add_test(NAME sentry_example COMMAND sentry_example)
666692
endif()

README.md

+42-19
Original file line numberDiff line numberDiff line change
@@ -187,26 +187,23 @@ $ export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)
187187
The following options can be set when running the cmake generator, for example
188188
using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
189189

190-
- `SENTRY_BUILD_SHARED_LIBS` (Default: ON):
190+
- `SENTRY_BUILD_SHARED_LIBS` (Default: `ON`):
191191
By default, `sentry` is built as a shared library. Setting this option to
192192
`OFF` will build `sentry` as a static library instead.
193193
If sentry is used as a subdirectory of another project, the value `BUILD_SHARED_LIBS` will be inherited by default.
194194

195195
When using `sentry` as a static library, make sure to `#define SENTRY_BUILD_STATIC 1` before including the sentry header.
196196

197-
- `SENTRY_PIC` (Default: ON):
197+
- `SENTRY_PIC` (Default: `ON`):
198198
By default, `sentry` is built as a position-independent library.
199199

200-
- `SENTRY_EXPORT_SYMBOLS` (Default: ON):
201-
By default, `sentry` exposes all symbols in the dynamic symbol table. You might want to disable it if the program intends to `dlopen` third-party shared libraries and avoid symbol collisions.
202-
203-
- `SENTRY_BUILD_RUNTIMESTATIC` (Default: OFF):
200+
- `SENTRY_BUILD_RUNTIMESTATIC` (Default: `OFF`):
204201
Enables linking with the static MSVC runtime. Has no effect if the compiler is not MSVC.
205202

206-
- `SENTRY_LINK_PTHREAD` (Default: ON):
203+
- `SENTRY_LINK_PTHREAD` (Default: `ON`):
207204
Links platform threads library like `pthread` on UNIX targets.
208205

209-
- `SENTRY_BUILD_FORCE32` (Default: OFF):
206+
- `SENTRY_BUILD_FORCE32` (Default: `OFF`):
210207
Forces cross-compilation from 64-bit host to 32-bit target. Only affects Linux.
211208

212209
- `CMAKE_SYSTEM_VERSION` (Default: depending on Windows SDK version):
@@ -246,32 +243,58 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
246243
- **none**: This builds `sentry-native` without a backend, so it does not handle
247244
crashes. It is primarily used for tests.
248245

249-
- `SENTRY_INTEGRATION_QT` (Default: OFF):
246+
- `SENTRY_INTEGRATION_QT` (Default: `OFF`):
250247
Builds the Qt integration, which turns Qt log messages into breadcrumbs.
251248

252-
- `SENTRY_BREAKPAD_SYSTEM` (Default: OFF):
249+
- `SENTRY_BREAKPAD_SYSTEM` (Default: `OFF`):
253250
This instructs the build system to use system-installed breakpad libraries instead of the in-tree version.
254251

255-
- `SENTRY_TRANSPORT_COMPRESSION` (Default: OFF):
252+
- `SENTRY_TRANSPORT_COMPRESSION` (Default: `OFF`):
256253
Adds Gzip transport compression. Requires `zlib`.
257254

258255
- `SENTRY_FOLDER` (Default: not defined):
259-
Sets the sentry-native projects folder name for generators that support project hierarchy (like Microsoft Visual Studio).
260-
To use this feature, you need to enable hierarchy via [`USE_FOLDERS` property](https://cmake.org/cmake/help/latest/prop_gbl/USE_FOLDERS.html)
256+
Sets the sentry-native projects folder name for generators that support project hierarchy (like Microsoft Visual
257+
Studio). To use this feature, you need to enable hierarchy via [`USE_FOLDERS` property](https://cmake.org/cmake/help/latest/prop_gbl/USE_FOLDERS.html)
261258

262-
- `CRASHPAD_ENABLE_STACKTRACE` (Default: OFF):
263-
This enables client-side stackwalking when using the crashpad backend. Stack unwinding will happen on the client's machine
264-
and the result will be submitted to Sentry attached to the generated minidump.
265-
Note that this feature is still experimental.
259+
- `CRASHPAD_ENABLE_STACKTRACE` (Default: `OFF`):
260+
This enables client-side stackwalking when using the crashpad backend. Stack unwinding will happen on the client's
261+
machine and the result will be submitted to Sentry attached to the generated minidump. **Note that this feature is
262+
still experimental**.
266263

267-
- `SENTRY_SDK_NAME` (Default: sentry.native or sentry.native.android):
264+
- `SENTRY_SDK_NAME` (Default: `sentry.native` or `sentry.native.android`):
268265
Sets the SDK name that should be included in the reported events. If you're overriding this, also define
269266
the same value using `target_compile_definitions()` on your own targets that include `sentry.h`.
270267

268+
- `SENTRY_HANDLER_STACK_SIZE` (Default: `64`):
269+
This specifies the size in KiB of the stack reserved for the crash handler (including hooks like `on_crash` and
270+
`before_send`) on Windows, Linux and the `inproc` backend in macOS. Reserving the stack is necessary in case of a
271+
stack-overflow, where the handler could otherwise no longer execute. This parameter allows users to specify their
272+
target stack size in KiB, because some applications might require a different value from our default. This value can
273+
be as small as 16KiB (on `crashpad` and `breakpad`) for handlers to work, but we recommend 32KiB as the lower bound
274+
on 64-bit systems. **The value should be a multiple of the page size**.
275+
276+
- `SENTRY_THREAD_STACK_GUARANTEE_FACTOR` (Default: `10`, only for Windows):
277+
Defines the factor by which the thread's stack reserve must be bigger than the specified guarantee for the handler.
278+
_Example_: if the `SENTRY_HANDLER_STACK_SIZE` is defined as 64KiB then the thread's stack reserve must at least have
279+
a size of 640KiB.
280+
- `SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT` (Default: `ON`, only for Windows):
281+
Ensures that all threads created after the SDK's initialization will be configured to use the
282+
`SENTRY_HANDLER_STACK_SIZE` as its handler stack guarantee.
283+
284+
_Note_: assigning this to all threads only works when building the SDK as a shared library. If you build it as a
285+
static library and this option is enabled, only the `sentry_init()` thread will have a stack guarantee for the handler
286+
(other threads must be manually initialized via `sentry_set_thread_stack_guarantee()`).
287+
288+
- `SENTRY_THREAD_STACK_GUARANTEE_VERBOSE_LOG` (Default: `OFF`, only for Windows):
289+
Adds info level logs for every successfully set thread stack guarantee. This is `OFF` by default, because depending
290+
on the number of threads used (by all dependencies) this could flood the logs. But it will be helpful to anyone
291+
tuning the thread stack guarantee parameters. Warnings and errors in the process of setting thread stack guarantees
292+
will always be logged.
293+
271294
### Support Matrix
272295

273296
| Feature | Windows | macOS | Linux | Android | iOS |
274-
| ---------- | ------- | ----- | ----- | ------- | ----- |
297+
|------------|---------|-------|-------|---------|-------|
275298
| Transports | | | | | |
276299
| - curl | ||| (✓)*** | |
277300
| - winhttp || | | | |

cmake/utils.cmake

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
# Generates a version resource file from the `sentry.rc.in` template for the `TGT` argument and adds it as a source.
22
function(sentry_add_version_resource TGT FILE_DESCRIPTION)
3-
# generate a resource output-path from the target name
4-
set(RESOURCE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TGT}.rc")
5-
set(RESOURCE_PATH_TMP "${RESOURCE_PATH}.in")
3+
# generate a resource output-path from the target name
4+
set(RESOURCE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${TGT}.rc")
5+
set(RESOURCE_PATH_TMP "${RESOURCE_PATH}.in")
66

7-
# Extract major, minor and patch version from SENTRY_VERSION
8-
string(REPLACE "." ";" _SENTRY_VERSION_LIST "${SENTRY_VERSION}")
9-
list(GET _SENTRY_VERSION_LIST 0 SENTRY_VERSION_MAJOR)
10-
list(GET _SENTRY_VERSION_LIST 1 SENTRY_VERSION_MINOR)
11-
list(GET _SENTRY_VERSION_LIST 2 SENTRY_VERSION_PATCH)
7+
# Extract major, minor and patch version from SENTRY_VERSION
8+
string(REPLACE "." ";" _SENTRY_VERSION_LIST "${SENTRY_VERSION}")
9+
list(GET _SENTRY_VERSION_LIST 0 SENTRY_VERSION_MAJOR)
10+
list(GET _SENTRY_VERSION_LIST 1 SENTRY_VERSION_MINOR)
11+
list(GET _SENTRY_VERSION_LIST 2 SENTRY_VERSION_PATCH)
1212

13-
# Produce the resource file with configure-time replacements
14-
configure_file("${SENTRY_SOURCE_DIR}/sentry.rc.in" "${RESOURCE_PATH_TMP}" @ONLY)
13+
# Produce the resource file with configure-time replacements
14+
configure_file("${SENTRY_SOURCE_DIR}/sentry.rc.in" "${RESOURCE_PATH_TMP}" @ONLY)
1515

16-
# Replace the `ORIGINAL_FILENAME` at generate-time using the generator expression `TARGET_FILE_NAME`
17-
file(GENERATE OUTPUT ${RESOURCE_PATH} INPUT ${RESOURCE_PATH_TMP})
16+
# Replace the `ORIGINAL_FILENAME` at generate-time using the generator expression `TARGET_FILE_NAME`
17+
file(GENERATE OUTPUT ${RESOURCE_PATH} INPUT ${RESOURCE_PATH_TMP})
1818

19-
# Finally add the generated resource file to the target sources
20-
target_sources("${TGT}" PRIVATE "${RESOURCE_PATH}")
21-
endfunction()
19+
# Finally add the generated resource file to the target sources
20+
target_sources("${TGT}" PRIVATE "${RESOURCE_PATH}")
21+
endfunction()

0 commit comments

Comments
 (0)