Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ if(SENTRY_TRANSPORT_COMPRESSION)
endif()

set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden)
set_property(TARGET sentry PROPERTY CXX_VISIBILITY_PRESET hidden)
if(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /safeseh")
Expand Down Expand Up @@ -1016,6 +1017,11 @@ elseif(SENTRY_BACKEND_NATIVE)
message(STATUS "Sentry crash daemon executable: enabled")
endif()

option(SENTRY_INTEGRATION_CPP "Build C++ runtime integration" OFF)
if(SENTRY_INTEGRATION_CPP)
sentry_check_cpp_language_features()
endif()

option(SENTRY_INTEGRATION_QT "Build Qt integration")
if(SENTRY_INTEGRATION_QT)
if(QT_DEFAULT_MAJOR_VERSION)
Expand Down Expand Up @@ -1078,6 +1084,9 @@ if(SENTRY_BUILD_TESTS)
add_subdirectory(tests/fixtures/test_platform)
endif()
add_subdirectory(tests/fixtures/screenshot)
if(SENTRY_INTEGRATION_CPP)
add_subdirectory(tests/fixtures/cpp)
endif()
if(WIN32 AND NOT XBOX)
add_subdirectory(tests/fixtures/appx)
endif()
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,30 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
- `SENTRY_INTEGRATION_QT` (Default: `OFF`):
Builds the Qt integration, which turns Qt log messages into breadcrumbs.

- `SENTRY_INTEGRATION_CPP` (Default: `OFF`):
Reports fatal events from uncaught C++ exceptions as `C++ Exception`, with
the dynamic `typeid()` name and the value returned by
`std::exception::what()` combined in the exception value. Enabling this
integration adds a C++ runtime dependency while the public SDK API and ABI
remain C. It installs a process-global `std::terminate` handler and chains to
the handler that was installed before Sentry. Restoration is best-effort
because the standard terminate API cannot atomically compare and replace
handlers; Sentry does not knowingly overwrite a handler installed later by
the application.

Metadata capture uses fixed 255-byte type and 1023-byte value limits and
preserves empty values. Raw `typeid()` names are implementation-defined
and can be mangled. Capture is best-effort during process termination. MSVC
exceptions intercepted before `std::terminate` are decoded from the runtime
exception record; malformed or unsupported records are ignored without
changing the native crash event.

With the `native` backend, use `SENTRY_CRASH_REPORTING_MODE_NATIVE` to
preserve this metadata. In the default
`SENTRY_CRASH_REPORTING_MODE_NATIVE_WITH_MINIDUMP` mode, server-side minidump
processing replaces the exception type and value with the minidump crash
reason.

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

Expand Down
20 changes: 20 additions & 0 deletions cmake/utils.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,23 @@ function(sentry_get_property NAME)
endif()
set("SENTRY_${NAME}" "${prop}" PARENT_SCOPE)
endfunction()

function(sentry_check_cpp_language_features)
include(CheckCXXSourceCompiles)
unset(SENTRY_CPP_LANGUAGE_FEATURES CACHE)
check_cxx_source_compiles("\
#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)\n\
#error C++ exceptions are disabled\n\
#endif\n\
#if !defined(__cpp_rtti) && !defined(__GXX_RTTI) && !defined(_CPPRTTI)\n\
#error C++ RTTI is disabled\n\
#endif\n\
#include <exception>\n\
#include <typeinfo>\n\
int main() { try { throw 1; } catch (...) { return typeid(int) == typeid(int) ? 0 : 1; } }"
SENTRY_CPP_LANGUAGE_FEATURES)
if(NOT SENTRY_CPP_LANGUAGE_FEATURES)
message(FATAL_ERROR
"SENTRY_INTEGRATION_CPP requires C++ exceptions and RTTI")
endif()
endfunction()
3 changes: 3 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,9 @@ typedef enum {
* Native stacktrace is primary event, minidump is attachment only.
* Best of both worlds: fast client-side unwinding with full minidump
* available for deep debugging when needed.
* Server-side minidump processing replaces exception metadata from
* `SENTRY_INTEGRATION_CPP`; use `SENTRY_CRASH_REPORTING_MODE_NATIVE` to
* preserve it.
*/
SENTRY_CRASH_REPORTING_MODE_NATIVE_WITH_MINIDUMP = 2,
} sentry_crash_reporting_mode_t;
Expand Down
7 changes: 7 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,13 @@ if(PROSPERO)
endif()

# integrations
if(SENTRY_INTEGRATION_CPP)
target_compile_definitions(sentry PRIVATE SENTRY_INTEGRATION_CPP)
sentry_target_sources_cwd(sentry
integrations/sentry_integration_cpp.cpp
integrations/sentry_integration_cpp.h
)
endif()
if(SENTRY_INTEGRATION_QT)
target_compile_definitions(sentry PRIVATE SENTRY_INTEGRATION_QT)
sentry_target_sources_cwd(sentry
Expand Down
59 changes: 40 additions & 19 deletions src/backends/native/sentry_crash_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -3164,21 +3164,38 @@ build_native_event(const sentry_crash_context_t *ctx,
# error Unsupported platform
#endif

sentry_value_t exc = sentry_value_new_object();
sentry_value_set_by_key(exc, "type", sentry_value_new_string(signal_name));

char value_buf[128];
snprintf(value_buf, sizeof(value_buf), "Fatal crash: %s", signal_name);
sentry_value_set_by_key(exc, "value", sentry_value_new_string(value_buf));
sentry_value_t exceptions = sentry_value_get_by_key(event, "exception");
sentry_value_t exc_values = sentry_value_get_by_key(exceptions, "values");
sentry_value_t exc = sentry_value_get_by_index(exc_values, 0);
sentry_value_t mechanism = sentry_value_get_by_key(exc, "mechanism");
bool has_cpp_exception = sentry__string_eq("cpp_exception",
sentry_value_as_string(sentry_value_get_by_key(mechanism, "type")));
if (!has_cpp_exception) {
exc = sentry_value_new_object();
}
if (sentry_value_get_type(sentry_value_get_by_key(exc, "type"))
!= SENTRY_VALUE_TYPE_STRING) {
sentry_value_set_by_key(
exc, "type", sentry_value_new_string(signal_name));
}
if (sentry_value_get_type(sentry_value_get_by_key(exc, "value"))
!= SENTRY_VALUE_TYPE_STRING) {
char value_buf[128];
snprintf(value_buf, sizeof(value_buf), "Fatal crash: %s", signal_name);
sentry_value_set_by_key(
exc, "value", sentry_value_new_string(value_buf));
}

// Add mechanism
sentry_value_t mechanism = sentry_value_new_object();
sentry_value_set_by_key(
mechanism, "type", sentry_value_new_string(mechanism_type));
sentry_value_set_by_key(
mechanism, "synthetic", sentry_value_new_bool(true));
sentry_value_set_by_key(
mechanism, "handled", sentry_value_new_bool(handled));
if (!has_cpp_exception) {
mechanism = sentry_value_new_object();
sentry_value_set_by_key(
mechanism, "type", sentry_value_new_string(mechanism_type));
sentry_value_set_by_key(
mechanism, "synthetic", sentry_value_new_bool(true));
sentry_value_set_by_key(
mechanism, "handled", sentry_value_new_bool(handled));
}

// Add signal metadata
sentry_value_t meta = sentry_value_new_object();
Expand All @@ -3197,7 +3214,9 @@ build_native_event(const sentry_crash_context_t *ctx,
sentry_value_set_by_key(meta, "signal", signal_info);
sentry_value_set_by_key(mechanism, "meta", meta);

sentry_value_set_by_key(exc, "mechanism", mechanism);
if (!has_cpp_exception) {
sentry_value_set_by_key(exc, "mechanism", mechanism);
}

// Add stacktrace to exception
#if defined(SENTRY_PLATFORM_WINDOWS)
Expand All @@ -3220,11 +3239,13 @@ build_native_event(const sentry_crash_context_t *ctx,
sentry_value_set_by_key(exc, "stacktrace", build_stacktrace_from_ctx(ctx));

// Wrap exception in values array
sentry_value_t exceptions = sentry_value_new_object();
sentry_value_t exc_values = sentry_value_new_list();
sentry_value_append(exc_values, exc);
sentry_value_set_by_key(exceptions, "values", exc_values);
sentry_value_set_by_key(event, "exception", exceptions);
if (!has_cpp_exception) {
exceptions = sentry_value_new_object();
exc_values = sentry_value_new_list();
sentry_value_append(exc_values, exc);
sentry_value_set_by_key(exceptions, "values", exc_values);
sentry_value_set_by_key(event, "exception", exceptions);
}

// Always add threads with names to the event JSON
{
Expand Down
24 changes: 12 additions & 12 deletions src/backends/sentry_backend_breakpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,28 +143,28 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor,
= sentry__trace_finish(SENTRY_SPAN_STATUS_ABORTED);
sentry_uuid_t event_id = sentry_uuid_nil();

bool should_handle = true;

if (options->on_crash_func) {
sentry_ucontext_t *uctx = nullptr;
sentry_ucontext_t *uctx = nullptr;

#if defined(SENTRY_PLATFORM_DARWIN) \
&& !(defined(SENTRY_BREAKPAD_SYSTEM) || defined(SENTRY_PLATFORM_IOS))
sentry_ucontext_t uctx_data;
uctx_data.user_context = user_context;
uctx = &uctx_data;
sentry_ucontext_t uctx_data;
uctx_data.user_context = user_context;
uctx = &uctx_data;
#endif

#ifdef SENTRY_PLATFORM_WINDOWS
sentry_ucontext_t uctx_data;
uctx_data.exception_ptrs = *exinfo;
uctx = &uctx_data;
sentry_ucontext_t uctx_data;
uctx_data.exception_ptrs = *exinfo;
uctx = &uctx_data;
#endif

bool should_handle = true;

if (options->on_crash_func) {
SENTRY_SIGNAL_SAFE_LOG("DEBUG invoking `on_crash` hook");
event = options->on_crash_func(uctx, event, options->on_crash_data);
should_handle = !sentry_value_is_null(event);
}
event = sentry__invoke_on_crash(uctx, event);
should_handle = !sentry_value_is_null(event);

sentry__transport_suspend(options->transport);

Expand Down
19 changes: 10 additions & 9 deletions src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,20 +469,21 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context)
sentry_value_set_by_key(
crash_event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL));

if (options->on_crash_func) {
sentry_ucontext_t uctx;
sentry_ucontext_t uctx;
# ifdef SENTRY_PLATFORM_WINDOWS
uctx.exception_ptrs = *ExceptionInfo;
uctx.exception_ptrs = *ExceptionInfo;
# else
uctx.signum = signum;
uctx.siginfo = info;
uctx.user_context = user_context;
uctx.signum = signum;
uctx.siginfo = info;
uctx.user_context = user_context;
# endif

if (options->on_crash_func) {
SENTRY_DEBUG("invoking `on_crash` hook");
crash_event = options->on_crash_func(
&uctx, crash_event, options->on_crash_data);
} else if (options->before_send_func) {
}
crash_event = sentry__invoke_on_crash(&uctx, crash_event);

if (!options->on_crash_func && options->before_send_func) {
SENTRY_DEBUG("invoking `before_send` hook");
crash_event = options->before_send_func(
crash_event, nullptr, options->before_send_data);
Expand Down
12 changes: 8 additions & 4 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1077,11 +1077,15 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx,
= sentry__trace_finish(SENTRY_SPAN_STATUS_ABORTED);
sentry_uuid_t event_id = sentry_uuid_nil();

if (options->on_crash_func && !skip_hooks) {
SENTRY_DEBUG("invoking `on_crash` hook");
event = options->on_crash_func(uctx, event, options->on_crash_data);
if (!skip_hooks) {
if (options->on_crash_func) {
SENTRY_DEBUG("invoking `on_crash` hook");
}
event = sentry__invoke_on_crash(uctx, event);
should_handle = !sentry_value_is_null(event);
} else if (skip_hooks && options->on_crash_func) {
}

if (skip_hooks && options->on_crash_func) {
SENTRY_DEBUG("skipping `on_crash` hook due to recursive crash");
}

Expand Down
6 changes: 2 additions & 4 deletions src/backends/sentry_backend_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -1333,11 +1333,9 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx)
// Call on_crash hook if configured
if (options->on_crash_func) {
SENTRY_DEBUG("invoking `on_crash` hook");
sentry_value_t result
= options->on_crash_func(uctx, event, options->on_crash_data);
should_handle = !sentry_value_is_null(result);
event = result;
}
event = sentry__invoke_on_crash(uctx, event);
should_handle = !sentry_value_is_null(event);

if (should_handle) {
// Apply before_send hook if on_crash wasn't set
Expand Down
Loading
Loading