diff --git a/.cursor/rules/01-project-overview.mdc b/.cursor/rules/01-project-overview.mdc new file mode 100644 index 000000000..a8fda33f2 --- /dev/null +++ b/.cursor/rules/01-project-overview.mdc @@ -0,0 +1,52 @@ +# Sentry Native SDK - Project Overview + +This is the **Sentry Native SDK**, a C/C++ crash reporting and error monitoring library that serves as the foundation for other Sentry SDKs (Android, Unity, Unreal Engine). + +## Core Architecture + +The SDK is structured around multiple **backends** for crash handling: +- **crashpad**: Out-of-process handler (default on Windows, macOS, Linux desktop) +- **breakpad**: In-process handler (desktop platforms) +- **inproc**: Minimal in-process handler (default on Android, mobile) +- **none**: No crash handling (testing only) + +## Platform Support Matrix + +**Officially Supported:** +- Windows (MSVC 2019/2022, MinGW, ClangCL) +- macOS 13+ (Xcode LLVM, clang 15/18) +- Linux (GCC 9-14, clang 19, x86/x64/arm64) +- Android (API 16+ with NDK 19/27) +- iOS (Xcode builds) +- Gaming platforms (Xbox, PlayStation) + +**Transport Layer:** +- **curl**: Linux, macOS (requires libcurl) +- **winhttp**: Windows system library +- **none**: Custom user implementations + +## SDK Foundation Role + +This SDK is **NOT** meant to be used standalone - it's designed as a robust foundation for higher-level SDKs: +- [sentry-java](mdc:ndk/README.md) (Android integration) +- [sentry-unreal](mdc:README.md) (Unreal Engine) +- Unity SDK integration +- Direct C/C++ usage (experimental) + +## Key Design Principles + +1. **Thread Safety**: All public APIs are thread-safe +2. **Signal Safety**: Core functionality works in signal handlers +3. **Minimal Dependencies**: Self-contained with vendored dependencies +4. **Cross-Platform**: Consistent API across all platforms +5. **Performance**: Minimal overhead in normal operation +6. **Robustness**: Must not crash the host application + +## Build System + +- **CMake-based** with extensive configuration options +- **Static/Shared** library support +- **Cross-compilation** support for all platforms +- **Submodule dependencies** in [external/](mdc:external) + +Reference the [README.md](mdc:README.md) for detailed platform support and [CONTRIBUTING.md](mdc:CONTRIBUTING.md) for development setup. diff --git a/.cursor/rules/02-api-design-conventions.mdc b/.cursor/rules/02-api-design-conventions.mdc new file mode 100644 index 000000000..72e04e6cc --- /dev/null +++ b/.cursor/rules/02-api-design-conventions.mdc @@ -0,0 +1,150 @@ +# API Design & Coding Conventions + +## Naming Conventions + +**Public API Functions:** +- All public functions start with `sentry_` +- Use snake_case: `sentry_capture_event()`, `sentry_options_new()` +- Experimental APIs: `SENTRY_EXPERIMENTAL_API` attribute +- Deprecated APIs: `SENTRY_DEPRECATED("message")` attribute + +**Internal Functions:** +- Private functions use `sentry__` (double underscore): `sentry__scope_lock()` +- Static functions can omit prefix within compilation units +- Platform-specific suffixes: `_windows`, `_unix`, `_darwin` + +**Types and Constants:** +- Types: `sentry_value_t`, `sentry_options_t` (typedef structs) +- Enums: `SENTRY_LEVEL_ERROR`, `SENTRY_VALUE_TYPE_STRING` +- Constants: `SENTRY_SDK_VERSION`, `SENTRY_BREADCRUMBS_MAX` + +## Memory Management Patterns + +**Reference Counting:** +```c +// Values use ref counting - always match incref/decref +sentry_value_t val = sentry_value_new_string("hello"); // starts at ref count 1 +sentry_value_incref(val); // Optional increment +sentry_value_decref(val); // Required decrement + +// Ownership transfer functions consume references +sentry_value_append(list, val); // `val` is consumed, don't decref +``` + +**Allocation Helpers:** +```c +// Use typed allocation macro +my_struct_t *obj = SENTRY_MAKE(my_struct_t); +if (!obj) return NULL; // Always check allocation failures + +// Custom allocator for signal safety +#ifdef WITH_PAGE_ALLOCATOR + if (sentry__page_allocator_enabled()) { + return sentry__page_allocator_alloc(size); + } +#endif +``` + +## Error Handling Patterns + +**Return Value Conventions:** +- `0` = success, non-zero = failure (for int returns) +- `NULL` = failure for pointer returns +- `sentry_value_new_null()` = failure for sentry_value_t returns + +**Error Propagation:** +```c +static int +example_function(void) { + thing_t *thing = allocate_thing(); + if (!thing) { + goto fail; // Use goto for cleanup + } + + if (setup_thing(thing) != 0) { + goto fail; + } + + return 0; // Success + +fail: + cleanup_thing(thing); // Clean up on failure + return 1; // Failure +} +``` + +## Thread Safety Requirements + +**Mutex Usage:** +```c +// Use dynamic mutex initialization for cross-platform support +#ifdef SENTRY__MUTEX_INIT_DYN +SENTRY__MUTEX_INIT_DYN(g_lock) +#else +static sentry_mutex_t g_lock = SENTRY__MUTEX_INIT; +#endif + +// Always use RAII-style locking +sentry_scope_t *scope = sentry__scope_lock(); +// ... use scope +sentry__scope_unlock(); +``` + +**Atomic Operations:** +```c +// Use atomic operations for lock-free operations +sentry__atomic_fetch_and_add(&thing->refcount, 1); +``` + +## Platform Abstraction + +**Conditional Compilation:** +```c +#ifdef SENTRY_PLATFORM_WINDOWS + // Windows-specific code +#elif defined(SENTRY_PLATFORM_DARWIN) + // macOS/iOS-specific code +#elif defined(SENTRY_PLATFORM_UNIX) + // Unix-like systems +#endif +``` + +**Feature Detection:** +```c +#ifdef SENTRY_BACKEND_CRASHPAD +# define SENTRY_BACKEND "crashpad" +#elif defined(SENTRY_BACKEND_BREAKPAD) +# define SENTRY_BACKEND "breakpad" +#endif +``` + +## Value System Architecture + +The core `sentry_value_t` uses **tagged pointers** for efficient storage: +- Supports: null, bool, int32, double, string, list, object +- Automatic memory management with reference counting +- Immutable when frozen (thread-safe sharing) + +**Value Creation Patterns:** +```c +// Prefer stack allocation for simple values +sentry_value_t obj = sentry_value_new_object(); +sentry_value_set_by_key(obj, "key", sentry_value_new_string("value")); + +// Use size hints for performance +sentry_value_t list = sentry__value_new_list_with_size(expected_size); +``` + +## String Handling + +**Safe String Operations:** +```c +// Always use length-aware functions +char *str = sentry__string_clone_n(input, input_len); + +// Use slices for borrowed strings +sentry_slice_t slice = sentry__slice_from_str(str); +char *owned = sentry__slice_to_owned(slice); +``` + +See [sentry.h](mdc:include/sentry.h) for the complete public API and [src/sentry_value.h](mdc:src/sentry_value.h) for internal patterns. diff --git a/.cursor/rules/03-testing-practices.mdc b/.cursor/rules/03-testing-practices.mdc new file mode 100644 index 000000000..d4c0a402e --- /dev/null +++ b/.cursor/rules/03-testing-practices.mdc @@ -0,0 +1,137 @@ +# Testing Practices + +## Test Structure + +**Unit Tests** ([tests/unit/](mdc:tests/unit/)): +- Located in `tests/unit/test_*.c` +- Registered in [tests/unit/tests.inc](mdc:tests/unit/tests.inc) using `XX(test_name)` macro +- Built into single `sentry_test_unit` executable + +**Integration Tests** ([tests/](mdc:tests/)): +- Python-based using pytest framework +- Test real SDK behavior with `sentry_example` binary +- HTTP server mocking with `pytest-httpserver` +- Cross-platform CI execution + +## Unit Test Conventions + +**Test Function Structure:** +```c +SENTRY_TEST(descriptive_test_name) +{ + // Setup phase + SENTRY_TEST_OPTIONS_NEW(options); + sentry_options_set_dsn(options, "https://key@example.com/42"); + sentry_init(options); + + // Test phase + sentry_value_t event = sentry_value_new_event(); + sentry_uuid_t event_id = sentry_capture_event(event); + + // Verification phase + TEST_CHECK(!sentry_uuid_is_nil(&event_id)); + TEST_CHECK_STRING_EQUAL(actual_str, "expected"); + + // Cleanup phase + sentry_close(); +} +``` + +**Assertion Macros:** +- `TEST_CHECK(condition)` - Basic condition check +- `TEST_CHECK_STRING_EQUAL(actual, expected)` - String comparison +- `TEST_CHECK_INT_EQUAL(actual, expected)` - Integer comparison +- `TEST_CHECK_JSON_VALUE(value, json_string)` - JSON structure comparison + +**Memory Management in Tests:** +```c +// Always clean up test artifacts +sentry_path_t *path = sentry__path_from_str(".test-db"); +sentry__path_remove_all(path); // Clean before test +// ... test code ... +sentry__path_remove_all(path); // Clean after test +sentry__path_free(path); +``` + +## Integration Test Patterns + +**Basic HTTP Test:** +```python +def test_capture_event(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) + + # Mock HTTP endpoint + httpserver.expect_request("/api/123456/envelope/").respond_with_data("OK") + + # Run example with test DSN + run(tmp_path, "sentry_example", ["capture-event"], + env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver))) + + # Verify request received + assert len(httpserver.log) == 1 + envelope = Envelope.deserialize(httpserver.log[0].get_data()) + assert_event(envelope.get_event()) +``` + +**Platform-Specific Tests:** +```python +# Use condition decorators for platform/feature requirements +@pytest.mark.skipif(not has_crashpad, reason="needs crashpad backend") +def test_crashpad_specific_feature(): + pass + +@pytest.mark.skipif(is_android, reason="not supported on Android") +def test_desktop_only_feature(): + pass +``` + +## Test Execution Environments + +**CI Matrix** (see [.github/workflows/ci.yml](mdc:.github/workflows/ci.yml)): +- Multiple compilers: GCC 9-14, Clang 19, MSVC 2019/2022 +- Multiple architectures: x86, x64, arm64 +- Multiple platforms: Windows, macOS, Linux, Android +- Analysis tools: ASan, Valgrind, Coverage (kcov/llvm-cov) + +**Local Development:** +```bash +# Run all tests +make test + +# Run specific unit test +./build/sentry_test_unit test_name + +# Run integration tests with filters +pytest tests/ -k "test_pattern" --verbose +``` + +## Example Program Usage + +The [sentry_example](mdc:examples/example.c) binary supports extensive test scenarios: + +**Common Test Commands:** +- `capture-event` - Basic event capture +- `crash` - Trigger segmentation fault +- `attachment` - Test file attachments +- `before-send` - Test filtering hooks +- `capture-transaction` - Performance monitoring +- `start-session` - Release health sessions + +**Platform-Specific Commands:** +- `fastfail` (Windows) - Test Control Flow Guard crashes +- `stack-overflow` - Test stack overflow handling +- `http-proxy` - Test proxy configuration + +## Test Data Management + +**Fixtures:** [tests/fixtures/](mdc:tests/fixtures/) +- Minidump samples for crash processing tests +- OS release files for environment detection tests +- View hierarchy JSON for UI attachment tests + +**Test Isolation:** +- Each test gets isolated temporary directory +- Database paths use test-specific prefixes +- HTTP servers use random available ports + +Reference [tests/README.md](mdc:tests) for detailed testing setup and [CONTRIBUTING.md](mdc:CONTRIBUTING.md) for development workflow. diff --git a/.cursor/rules/04-backend-architecture.mdc b/.cursor/rules/04-backend-architecture.mdc new file mode 100644 index 000000000..c5fa1e659 --- /dev/null +++ b/.cursor/rules/04-backend-architecture.mdc @@ -0,0 +1,172 @@ +# Backend Architecture & Crash Handling + +## Backend Selection Strategy + +The SDK chooses backends based on platform and requirements: + +**crashpad** (Out-of-process handler): +- **Default:** Windows, macOS, Linux desktop +- **Pros:** Robust crash handling, can survive application crashes +- **Cons:** Larger memory footprint, requires separate handler process +- **Files:** [src/backends/sentry_backend_crashpad.cpp](mdc:src/backends/sentry_backend_crashpad.cpp) + +**breakpad** (In-process handler): +- **Default:** None (optional alternative) +- **Pros:** Self-contained, smaller footprint +- **Cons:** May not survive all crash scenarios +- **Files:** [src/backends/sentry_backend_breakpad.cpp](mdc:src/backends/sentry_backend_breakpad.cpp) + +**inproc** (Minimal in-process): +- **Default:** Android, embedded systems +- **Pros:** Minimal dependencies, signal-safe +- **Cons:** Limited crash information capture +- **Files:** [src/backends/sentry_backend_inproc.c](mdc:src/backends/sentry_backend_inproc.c) + +## Backend Interface Pattern + +All backends implement the same interface defined in [src/sentry_backend.h](mdc:src/sentry_backend.h): + +```c +typedef struct sentry_backend_s { + int (*startup_func)(struct sentry_backend_s *, const sentry_options_t *); + int (*shutdown_func)(struct sentry_backend_s *); + void (*except_func)(struct sentry_backend_s *, const sentry_ucontext_t *); + void (*prune_database_func)(struct sentry_backend_s *); + size_t (*get_minidump_count_func)(struct sentry_backend_s *); + void (*user_consent_changed_func)(struct sentry_backend_s *); + void (*free_func)(struct sentry_backend_s *); + void *data; // Backend-specific state +} sentry_backend_t; +``` + +## Signal Safety Requirements + +**Critical Constraint:** Code in crash handlers must be **async-signal-safe**. + +**Safe Operations:** +- Write to pre-allocated memory only +- Use async-signal-safe system calls +- No malloc/free (use page allocator) +- No mutex operations +- No C library functions that aren't signal-safe + +**Signal-Safe Memory Management:** +```c +// Enable page allocator for signal handlers +#ifdef WITH_PAGE_ALLOCATOR +if (sentry__page_allocator_enabled()) { + return sentry__page_allocator_alloc(size); +} +#endif + +// In signal handler context +void signal_handler(int sig, siginfo_t *info, void *ucontext) { + sentry__page_allocator_enable(); // Switch to safe allocator + // ... crash handling logic ... +} +``` + +## Crashpad Backend Details + +**Process Architecture:** +- Main process links `libsentry` +- Separate `crashpad_handler` process launched on init +- IPC communication via platform-specific mechanisms + +**Handler Management:** +```cpp +// Typical crashpad initialization +crashpad::CrashpadClient client; +crashpad::CrashReportDatabase* database = + crashpad::CrashReportDatabase::Initialize(database_path); + +client.StartHandler( + handler_path, // Path to crashpad_handler executable + database_path, // Crash report storage + metrics_path, // Usage metrics + url, // Sentry upload URL + annotations, // Process annotations + arguments, // Handler arguments + false // Restart handler on failure +); +``` + +**Platform-Specific Considerations:** +- **Linux:** Signal stack setup required for stack overflow crashes +- **macOS:** Limited before_send/on_crash support due to out-of-process nature +- **Windows:** WER integration for fast-fail crashes, thread stack guarantees + +## Breakpad Backend Details + +**In-Process Architecture:** +- Crash handling happens within the crashed process +- Minidump generation via breakpad libraries +- Signal handlers for POSIX, exception handlers for Windows + +**Limitations:** +- Cannot handle all crash types (stack overflow may corrupt handler) +- Memory corruption may prevent proper crash handling +- Less reliable than out-of-process solutions + +## InProc Backend Details + +**Minimal Signal Handler:** +```c +static void +handle_signal(int signum, siginfo_t *info, void *user_context) { + sentry_ucontext_t uctx; + uctx.signum = signum; + uctx.siginfo = info; + uctx.user_context = (ucontext_t *)user_context; + + // Capture event with minimal overhead + sentry_value_t event = sentry_value_new_event(); + sentry__run_before_send(event, &uctx); + + // Minimal envelope creation and storage + sentry__capture_with_scope(event, NULL); +} +``` + +**Signal Configuration:** +- Handles: SIGSEGV, SIGBUS, SIGFPE, SIGILL, SIGABRT +- Uses alternate signal stack to handle stack overflow +- Chains to previous handlers when possible + +## Backend Selection at Build Time + +**CMake Configuration:** +```cmake +# Set backend via CMake option +set(SENTRY_BACKEND "crashpad" CACHE STRING "Backend selection") + +# Conditional compilation +if(SENTRY_BACKEND STREQUAL "crashpad") + target_compile_definitions(sentry PRIVATE SENTRY_BACKEND_CRASHPAD) + target_sources(sentry PRIVATE src/backends/sentry_backend_crashpad.cpp) +endif() +``` + +**Runtime Detection:** +```c +#ifdef SENTRY_BACKEND_CRASHPAD +# define SENTRY_BACKEND "crashpad" +#elif defined(SENTRY_BACKEND_BREAKPAD) +# define SENTRY_BACKEND "breakpad" +#elif defined(SENTRY_BACKEND_INPROC) +# define SENTRY_BACKEND "inproc" +#endif +``` + +## Performance Considerations + +**Initialization Overhead:** +- **crashpad:** ~50-100ms (process startup) +- **breakpad:** ~10-20ms (library init) +- **inproc:** ~1-5ms (signal handler setup) + +**Runtime Overhead:** +- All backends: Minimal overhead during normal operation +- Crash handling: Varies by backend complexity + +See [docs.sentry.io/platforms/native/advanced-usage/backend-tradeoffs/](https://docs.sentry.io/platforms/native/advanced-usage/backend-tradeoffs/) for detailed backend comparison. diff --git a/.cursor/rules/05-android-ndk-integration.mdc b/.cursor/rules/05-android-ndk-integration.mdc new file mode 100644 index 000000000..2c1c547a6 --- /dev/null +++ b/.cursor/rules/05-android-ndk-integration.mdc @@ -0,0 +1,227 @@ +# Android NDK Integration + +## Overview + +The [ndk/](mdc:ndk/) directory provides Android-specific integration of the Sentry Native SDK, creating an `.aar` package that bridges native C/C++ code with Java/Kotlin Android applications. + +## Architecture Layers + +**Java Layer** ([ndk/lib/src/main/java/](mdc:ndk/lib/src/main/java/)): +- `SentryNdk` - Main initialization and lifecycle management +- `NativeScope` - JNI bindings for scope operations +- `NdkOptions` - Configuration options from Android SDK +- Public API exposed to [sentry-android](https://docs.sentry.io/platforms/android/) + +**JNI Layer** ([ndk/lib/src/main/jni/](mdc:ndk/lib/src/main/jni/)): +- `sentry.c` - Bridge between Java and native SDK +- Converts Java objects to native options +- Manages native SDK lifecycle from Android context + +**Native Layer:** +- Full Sentry Native SDK linked as native libraries +- Custom transport for envelope file writing +- Android-specific optimizations (inproc backend) + +## JNI Naming Conventions + +**Function Naming:** +```c +// Pattern: Java_package_class_methodName +JNIEXPORT void JNICALL +Java_io_sentry_ndk_SentryNdk_initSentryNative( + JNIEnv *env, jclass cls, jobject options); + +JNIEXPORT void JNICALL +Java_io_sentry_ndk_NativeScope_nativeSetTag( + JNIEnv *env, jclass cls, jstring key, jstring value); +``` + +**String Handling Pattern:** +```c +static char * +call_get_string(JNIEnv *env, jobject obj, jmethodID method) { + jstring jstr = (*env)->CallObjectMethod(env, obj, method); + if (!jstr) return NULL; + + const char *native_str = (*env)->GetStringUTFChars(env, jstr, NULL); + char *result = sentry__string_clone(native_str); + + (*env)->ReleaseStringUTFChars(env, jstr, native_str); + (*env)->DeleteLocalRef(env, jstr); + return result; +} +``` + +## Options Bridge Pattern + +**Java to Native Conversion:** +```c +// Extract configuration from Java NdkOptions object +jclass options_cls = (*env)->GetObjectClass(env, ndk_options); +jmethodID dsn_mid = (*env)->GetMethodID(env, options_cls, "getDsn", "()Ljava/lang/String;"); +jmethodID debug_mid = (*env)->GetMethodID(env, options_cls, "isDebug", "()Z"); + +// Convert to native options +sentry_options_t *options = sentry_options_new(); +char *dsn_str = call_get_string(env, ndk_options, dsn_mid); +sentry_options_set_dsn(options, dsn_str); + +jboolean debug = (*env)->CallBooleanMethod(env, ndk_options, debug_mid); +sentry_options_set_debug(options, debug); +``` + +## Transport Integration + +**File-Based Transport:** +```c +// Android uses file transport to work with sentry-android's HTTP layer +static void +send_envelope(sentry_envelope_t *envelope, void *data) { + char *outbox_path = (char *)data; + if (!outbox_path || !envelope) return; + + // Generate unique filename for envelope + sentry_uuid_t uuid = sentry_uuid_new_v4(); + char uuid_str[37]; + sentry_uuid_as_string(&uuid, uuid_str); + + // Write envelope to outbox directory + char envelope_path[512]; + snprintf(envelope_path, sizeof(envelope_path), "%s/%s.envelope", + outbox_path, uuid_str); + sentry_envelope_write_to_file(envelope, envelope_path); +} +``` + +## Build Configuration + +**CMake Integration ([ndk/lib/CMakeLists.txt](mdc:ndk/lib/CMakeLists.txt)):** +```cmake +# Link against sentry-native source +set(SENTRY_NATIVE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/../..") +add_subdirectory("${SENTRY_NATIVE_SRC}" sentry-native) + +# Create Android-specific wrapper library +add_library(sentry-android SHARED + src/main/jni/sentry.c +) +target_link_libraries(sentry-android sentry) +``` + +**Gradle Configuration ([ndk/lib/build.gradle.kts](mdc:ndk/lib/build.gradle.kts)):** +```kotlin +android { + compileSdk = 35 + + defaultConfig { + minSdk = 21 + ndk { + abiFilters.addAll(listOf("x86", "armeabi-v7a", "x86_64", "arm64-v8a")) + } + } + + // Enable prefab for native dependency consumption + buildFeatures { + prefabPublishing = true + } + + prefab { + create("sentry-android") {} // JNI wrapper + create("sentry") { // Native SDK headers + headers = "../../include" + } + } +} +``` + +## Memory Management in JNI + +**Reference Management:** +```c +// Always clean up local references +jstring jkey = (*env)->NewStringUTF(env, key); +if (!jkey) return; // OutOfMemoryError thrown + +// Call Java method +(*env)->CallStaticVoidMethod(env, cls, method_id, jkey, jvalue); + +// Clean up local references +(*env)->DeleteLocalRef(env, jkey); +(*env)->DeleteLocalRef(env, jvalue); +``` + +**Exception Handling:** +```c +// Check for Java exceptions after JNI calls +if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); // Log exception + (*env)->ExceptionClear(env); // Clear for continued execution + return; // Early return on error +} +``` + +## Library Loading Strategy + +**Native Library Loading ([SentryNdk.java](mdc:ndk/lib/src/main/java/io/sentry/ndk/SentryNdk.java)):** +```java +public static synchronized void loadNativeLibraries() { + if (!nativeLibrariesLoaded) { + // Load dependencies first (Android < 6.0 requirement) + System.loadLibrary("log"); // Android logging + System.loadLibrary("sentry"); // Core native SDK + System.loadLibrary("sentry-android"); // JNI wrapper + nativeLibrariesLoaded = true; + } +} +``` + +## API Surface Design + +**Minimal JNI Exposure:** +- Only essential functions exposed through JNI +- Complex operations handled in Java layer when possible +- Batch operations to minimize JNI overhead + +**Scope Operations ([NativeScope.java](mdc:ndk/lib/src/main/java/io/sentry/ndk/NativeScope.java)):** +```java +@Override +public void setTag(String key, String value) { + nativeSetTag(key, value); // Direct JNI call +} + +@Override +public void setUser(String id, String email, String ipAddress, String username) { + nativeSetUser(id, email, ipAddress, username); // Batch parameters +} +``` + +## Testing on Android + +**Emulator Testing:** +```bash +# Start Android emulator (see CI configuration) +scripts/start-android.sh + +# Run NDK-specific tests +cd ndk && ./gradlew check +``` + +**Integration with sentry-android:** +- NDK package consumed as dependency in main Android SDK +- Provides both native crash handling and JNI bridge +- Compatible with sentry-android's scope and configuration system + +## Performance Considerations + +**JNI Overhead:** +- Minimize JNI boundary crossings +- Batch multiple operations when possible +- Cache method IDs and class references +- Use direct buffer access for large data + +**Android-Specific Optimizations:** +- Uses `inproc` backend by default (smaller footprint) +- File-based transport (avoids HTTP in native layer) +- Integrated with Android's crash reporting pipeline + +Reference [ndk/README.md](mdc:ndk/README.md) for detailed Android integration and [sentry-android documentation](https://docs.sentry.io/platforms/android/) for usage. diff --git a/.cursor/rules/06-cmake-build-system.mdc b/.cursor/rules/06-cmake-build-system.mdc new file mode 100644 index 000000000..3be589c0e --- /dev/null +++ b/.cursor/rules/06-cmake-build-system.mdc @@ -0,0 +1,278 @@ +# CMake Build System + +## Project Structure + +The SDK uses CMake as the primary build system with the following structure: + +**Root Configuration ([CMakeLists.txt](mdc:CMakeLists.txt)):** +- Project definition and version extraction from [include/sentry.h](mdc:include/sentry.h) +- Cross-platform toolchain detection +- Top-level build options and feature detection + +**Source Configuration ([src/CMakeLists.txt](mdc:src/CMakeLists.txt)):** +- Platform-specific source file selection +- Backend and transport configuration +- Module and symbolizer selection + +**Test Configuration ([tests/unit/CMakeLists.txt](mdc:tests/unit/CMakeLists.txt)):** +- Unit test compilation with same build settings as main library +- Test source aggregation and linking + +## Configuration Options + +**Core Build Options:** +```cmake +# Library type selection +option(SENTRY_BUILD_SHARED_LIBS "Build shared library" ON) +option(SENTRY_BUILD_RUNTIMESTATIC "Link with static runtime (MSVC)" OFF) +option(SENTRY_PIC "Position independent code" ON) + +# Feature selection +set(SENTRY_BACKEND "crashpad" CACHE STRING "Backend: crashpad|breakpad|inproc|none") +set(SENTRY_TRANSPORT "curl" CACHE STRING "Transport: curl|winhttp|none") + +# Development options +option(SENTRY_BUILD_TESTS "Build test suite" ${SENTRY_MAIN_PROJECT}) +option(SENTRY_BUILD_EXAMPLES "Build examples" ${SENTRY_MAIN_PROJECT}) +``` + +**Platform-Specific Options:** +```cmake +# Windows-specific +option(SENTRY_THREAD_STACK_GUARANTEE_AUTO_INIT "Auto thread stack guarantee" ON) +set(SENTRY_HANDLER_STACK_SIZE "64" CACHE STRING "Handler stack size in KiB") + +# Cross-compilation +option(SENTRY_BUILD_FORCE32 "Force 32-bit build" OFF) +set(CMAKE_SYSTEM_VERSION "10" CACHE STRING "Minimum Windows version") +``` + +## Source File Organization + +**Conditional Source Selection Pattern:** +```cmake +# Platform-specific implementations +if(WIN32) + sentry_target_sources_cwd(sentry + sentry_windows_dbghelp.c + path/sentry_path_windows.c + symbolizer/sentry_symbolizer_windows.c + ) +elseif(APPLE) + sentry_target_sources_cwd(sentry + modulefinder/sentry_modulefinder_apple.c + ) +elseif(LINUX OR ANDROID) + sentry_target_sources_cwd(sentry + modulefinder/sentry_modulefinder_linux.c + ) +endif() + +# Backend selection +if(SENTRY_BACKEND STREQUAL "crashpad") + target_sources(sentry PRIVATE + backends/sentry_backend_crashpad.cpp + ) + target_compile_definitions(sentry PRIVATE SENTRY_BACKEND_CRASHPAD) +endif() +``` + +**Helper Macro for Source Management:** +```cmake +# Custom macro for adding sources with current directory prefix +macro(sentry_target_sources_cwd target) + foreach(source ${ARGN}) + target_sources(${target} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/${source}) + endforeach() +endmacro() +``` + +## Dependency Management + +**External Dependencies:** +```cmake +# Git submodules in external/ directory +if(SENTRY_BACKEND STREQUAL "crashpad") + add_subdirectory(external/crashpad) + target_link_libraries(sentry PRIVATE + crashpad::client + crashpad::util + ) +endif() + +# System dependencies +find_package(CURL QUIET) +if(CURL_FOUND AND SENTRY_TRANSPORT STREQUAL "curl") + target_link_libraries(sentry PRIVATE ${CURL_LIBRARIES}) + target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIRS}) +endif() +``` + +**Platform Libraries:** +```cmake +# Windows-specific libraries +if(WIN32) + target_link_libraries(sentry PRIVATE + dbghelp + winhttp + version + ) +endif() + +# Unix-specific libraries +if(UNIX AND NOT APPLE) + target_link_libraries(sentry PRIVATE + ${CMAKE_DL_LIBS} + $<$:rt> + ) +endif() +``` + +## Cross-Platform Toolchain Support + +**Compiler Detection and Configuration:** +```cmake +# C/C++ standard requirements +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 99) +endif() +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) # Required for crashpad/breakpad +endif() + +# Compiler-specific flags +if(MSVC) + target_compile_definitions(sentry PRIVATE _CRT_SECURE_NO_WARNINGS) + if(SENTRY_BUILD_RUNTIMESTATIC) + set_property(TARGET sentry PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endif() +``` + +**Cross-Compilation Support:** +```cmake +# Android NDK integration +if(ANDROID) + target_compile_definitions(sentry PRIVATE SENTRY_PLATFORM_ANDROID) + set(SENTRY_BACKEND "inproc" CACHE STRING "Android uses inproc backend" FORCE) +endif() + +# MinGW configuration +if(MINGW) + target_link_libraries(sentry PRIVATE + -static-libgcc + -static-libstdc++ + ) +endif() +``` + +## Installation Configuration + +**Install Targets:** +```cmake +include(GNUInstallDirs) +set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/sentry") + +# Library installation +install(TARGETS sentry + EXPORT sentry-targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +# Header installation +install(FILES include/sentry.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# CMake package files +install(EXPORT sentry-targets + FILE sentry-targets.cmake + NAMESPACE sentry:: + DESTINATION ${CMAKE_INSTALL_CMAKEDIR} +) +``` + +## Testing Integration + +**Test Target Configuration:** +```cmake +# Copy sentry properties to test executable +sentry_get_property(SOURCES) +sentry_get_property(COMPILE_DEFINITIONS) +sentry_get_property(LINK_LIBRARIES) + +# Create test executable with same configuration +add_executable(sentry_test_unit + ${SENTRY_SOURCES} + main.c + test_*.c +) +target_compile_definitions(sentry_test_unit PRIVATE ${SENTRY_COMPILE_DEFINITIONS}) +target_link_libraries(sentry_test_unit PRIVATE ${SENTRY_LINK_LIBRARIES}) +``` + +## Gaming Platform Support + +**Xbox/PlayStation Integration:** +```cmake +# Xbox toolchain detection +if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Gaming.Xbox.Scarlett.x64") + include("${SENTRY_TOOLCHAINS_DIR}/xbox/CMakeGDKScarlett.cmake") + set(XBOX TRUE) +endif() + +# Platform-specific definitions +if(XBOX) + target_compile_definitions(sentry PRIVATE SENTRY_PLATFORM_XBOX) +endif() +``` + +## Build Performance Optimizations + +**Parallel Build Support:** +```cmake +# Enable parallel builds where supported +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.12") + cmake_policy(SET CMP0074 NEW) # Use _ROOT variables +endif() + +# Pre-compiled headers for large codebases (optional) +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16") + target_precompile_headers(sentry PRIVATE sentry_boot.h) +endif() +``` + +**Debug Configuration:** +```cmake +# Debug symbols and optimization +if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + target_compile_options(sentry PRIVATE + $<$:-g -O2> + $<$:-g -O2> + ) +endif() +``` + +## Package Configuration + +**CMake Package Support:** +```cmake +# Generate package configuration files for downstream consumption +include(CMakePackageConfigHelpers) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/sentry-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/sentry-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_CMAKEDIR} +) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/sentry-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +``` + +Reference [CMakeLists.txt](mdc:CMakeLists.txt) for complete configuration and [README.md build section](mdc:README.md#building-and-installation) for usage examples. diff --git a/.cursor/rules/07-logging-debugging.mdc b/.cursor/rules/07-logging-debugging.mdc new file mode 100644 index 000000000..a0d1434f3 --- /dev/null +++ b/.cursor/rules/07-logging-debugging.mdc @@ -0,0 +1,272 @@ +# Logging & Debugging Practices + +## Logging System Architecture + +The SDK uses a centralized logging system defined in [src/sentry_logger.h](mdc:src/sentry_logger.h) with platform-specific implementations. + +**Log Levels:** +- `SENTRY_LEVEL_DEBUG` - Detailed diagnostic information +- `SENTRY_LEVEL_INFO` - General information messages +- `SENTRY_LEVEL_WARNING` - Warning conditions +- `SENTRY_LEVEL_ERROR` - Error conditions +- `SENTRY_LEVEL_FATAL` - Critical errors leading to termination + +## Logging Macros and Usage + +**Standard Logging Macros:** +```c +// Use appropriate level macros for consistent formatting +SENTRY_DEBUG("Simple debug message"); +SENTRY_DEBUGF("Debug with formatting: %s = %d", key, value); + +SENTRY_INFO("Initialization complete"); +SENTRY_INFOF("Processed %zu items", count); + +SENTRY_WARN("Deprecated API usage detected"); +SENTRY_WARNF("Failed to load optional component: %s", component_name); + +SENTRY_ERROR("Critical configuration error"); +SENTRY_ERRORF("Memory allocation failed: requested %zu bytes", size); +``` + +**Conditional Debug Logging:** +```c +// Debug logs only appear when debug mode is enabled +#if !defined(NDEBUG) + SENTRY_DEBUGF("Internal state: refcount=%ld", thing->refcount); +#endif +``` + +## Platform-Specific Logging + +**Android Logging ([src/sentry_logger.c](mdc:src/sentry_logger.c)):** +```c +#if defined(SENTRY_PLATFORM_ANDROID) +#include + +void sentry__logger_defaultlogger(sentry_level_t level, const char *message, + va_list args, void *data) { + android_LogPriority priority = ANDROID_LOG_DEBUG; + switch (level) { + case SENTRY_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break; + case SENTRY_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break; + case SENTRY_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break; + case SENTRY_LEVEL_FATAL: priority = ANDROID_LOG_FATAL; break; + } + __android_log_vprint(priority, "sentry-native", message, args); +} +#endif +``` + +**Standard Platform Logging:** +```c +void sentry__logger_defaultlogger(sentry_level_t level, const char *message, + va_list args, void *data) { + const char *prefix = "[sentry] "; + const char *priority = sentry__logger_describe(level); + + // Format: [sentry] LEVEL: message + vfprintf(stderr, format, args); +} +``` + +## Custom Logger Integration + +**Logger Function Type:** +```c +typedef void (*sentry_logger_function_t)( + sentry_level_t level, const char *message, va_list args, void *userdata); + +// Custom logger setup +void custom_logger(sentry_level_t level, const char *message, + va_list args, void *userdata) { + // Custom logging implementation + my_logging_system_vprintf(level, message, args); +} + +sentry_options_t *options = sentry_options_new(); +sentry_options_set_logger(options, custom_logger, userdata); +``` + +**Thread-Safe Logger Requirements:** +```c +// Note: Multiple threads may invoke your logger function +void threadsafe_logger(sentry_level_t level, const char *message, + va_list args, void *userdata) { + logger_state_t *state = (logger_state_t *)userdata; + + // Proper synchronization if modifying shared state + pthread_mutex_lock(&state->mutex); + // ... logging implementation + pthread_mutex_unlock(&state->mutex); +} +``` + +## Debug Mode Configuration + +**Enabling Debug Logging:** +```c +// Via options +sentry_options_t *options = sentry_options_new(); +sentry_options_set_debug(options, true); // Enable debug logging +sentry_options_set_logger_level(options, SENTRY_LEVEL_DEBUG); + +// Via environment variable +export SENTRY_DEBUG=1 // Enables debug logging +``` + +**Automatic Debug Mode:** +```c +// Debug mode is automatically enabled in debug builds +#if !defined(NDEBUG) + if (!opts->debug && (!debug || !sentry__string_eq(debug, "0"))) { + opts->debug = 1; // Auto-enable in debug builds + } +#endif +``` + +## Diagnostic Information + +**Memory Diagnostics:** +```c +// Log memory allocation patterns +SENTRY_DEBUGF("Allocated %zu bytes for %s", size, purpose); + +// Reference counting diagnostics +SENTRY_DEBUGF("Value refcount: %zu", sentry_value_refcount(value)); + +// Page allocator status +if (sentry__page_allocator_enabled()) { + SENTRY_DEBUG("Using signal-safe page allocator"); +} +``` + +**Backend Diagnostics:** +```c +// Backend initialization +SENTRY_INFOF("Initializing %s backend", SENTRY_BACKEND); + +// Crash handler setup +#ifdef SENTRY_PLATFORM_LINUX + SENTRY_DEBUGF("Installing signal stack (size: %d)", SIGNAL_STACK_SIZE); +#endif + +// Transport configuration +SENTRY_DEBUGF("Using %s transport", transport_name); +``` + +## Error Context Reporting + +**Error Reporting with Context:** +```c +// Include relevant context in error messages +SENTRY_ERRORF("Failed to initialize backend %s: %s", + backend_name, error_description); + +// System error integration +SENTRY_ERRORF("File operation failed: %s (errno=%d)", + strerror(errno), errno); + +// Platform-specific error codes +#ifdef SENTRY_PLATFORM_WINDOWS + DWORD error = GetLastError(); + SENTRY_ERRORF("Windows API failed: error code %lu", error); +#endif +``` + +## Performance Diagnostics + +**Timing Information:** +```c +// Benchmark critical operations +uint64_t start_time = sentry__usec_time(); +// ... operation ... +uint64_t elapsed = sentry__usec_time() - start_time; +SENTRY_DEBUGF("Operation completed in %llu microseconds", elapsed); +``` + +**Resource Usage:** +```c +// Memory usage tracking +size_t allocated_bytes = get_allocated_memory(); +SENTRY_DEBUGF("Current memory usage: %zu bytes", allocated_bytes); + +// Backend-specific metrics +SENTRY_DEBUGF("Crashpad handler PID: %d", handler_process_id); +``` + +## Signal-Safe Logging + +**Constraints in Signal Handlers:** +```c +// AVOID: Regular logging in signal handlers (not async-signal-safe) +void signal_handler(int sig) { + // DON'T DO THIS: + // SENTRY_ERRORF("Signal %d received", sig); // NOT SAFE + + // Instead, use minimal async-safe operations: + static const char msg[] = "Signal received\n"; + write(STDERR_FILENO, msg, sizeof(msg) - 1); +} +``` + +**Pre-formatted Messages:** +```c +// Pre-format critical messages for signal safety +static const char *signal_names[] = { + [SIGSEGV] = "Segmentation fault", + [SIGBUS] = "Bus error", + [SIGFPE] = "Floating point exception", +}; + +void signal_safe_log_signal(int sig) { + if (sig < sizeof(signal_names)/sizeof(signal_names[0]) && signal_names[sig]) { + write(STDERR_FILENO, signal_names[sig], strlen(signal_names[sig])); + } +} +``` + +## Test and Debug Utilities + +**Test Logger Implementation:** +```c +// Example from test suite +typedef struct { + uint64_t called; + bool assert_now; +} logger_test_t; + +static void test_logger(sentry_level_t level, const char *message, + va_list args, void *data) { + logger_test_t *test_data = data; + if (test_data->assert_now) { + test_data->called++; + TEST_CHECK(level == SENTRY_LEVEL_WARNING); + // ... test assertions + } +} +``` + +**Debug Assertions:** +```c +// Use TEST_CHECK macros in test code +TEST_CHECK_STRING_EQUAL(actual_log_output, expected_message); +TEST_CHECK_INT_EQUAL(log_call_count, expected_calls); +``` + +## Production Logging Best Practices + +**Log Level Guidelines:** +- **DEBUG**: Internal state, algorithm details, memory operations +- **INFO**: Initialization, configuration, major state changes +- **WARN**: Recoverable errors, deprecated usage, performance issues +- **ERROR**: Configuration errors, I/O failures, API misuse +- **FATAL**: Unrecoverable errors leading to termination + +**Performance Considerations:** +- Debug logging is completely disabled in release builds unless explicitly enabled +- Use format strings efficiently - avoid expensive string construction +- Consider log volume in production environments +- Custom loggers should be thread-safe and performant + +Reference [src/sentry_logger.h](mdc:src/sentry_logger.h) for the complete logging API and [tests/unit/test_logger.c](mdc:tests/unit/test_logger.c) for usage examples.