diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abbd51da4b..e7b482e737 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,12 +69,18 @@ jobs: CC: gcc-9 CXX: g++-9 TEST_X86: 1 - - name: Linux Arm32 (gcc-arm-linux-gnueabihf + qemu-user) + - name: Linux Arm32 (cross-GCC + QEMU) os: ubuntu-24.04-arm CC: arm-linux-gnueabihf-gcc CXX: arm-linux-gnueabihf-g++ TEST_ARM32: 1 TEST_QEMU: 1 + - name: Linux RISC-V (cross-GCC + QEMU) + os: ubuntu-24.04-arm + CC: riscv64-linux-gnu-gcc + CXX: riscv64-linux-gnu-g++ + TEST_RISCV: 1 + TEST_QEMU: 1 - name: Linux (GCC 9.5.0) os: ubuntu-24.04 CC: gcc-9 @@ -260,6 +266,7 @@ jobs: TEST_MINGW: ${{ matrix.TEST_MINGW }} TEST_WINE: ${{ matrix.TEST_WINE }} TEST_ARM32: ${{ matrix.TEST_ARM32 }} + TEST_RISCV: ${{ matrix.TEST_RISCV }} TEST_QEMU: ${{ matrix.TEST_QEMU }} TEST_SYSTEM_PROXY: ${{ matrix.TEST_SYSTEM_PROXY }} USE_SCCACHE: ${{ matrix.USE_SCCACHE }} @@ -297,7 +304,7 @@ jobs: [ -n "$CC" ] && [ -n "$CXX" ] || { echo "Ubuntu runner configurations require toolchain selection via CC and CXX" >&2; exit 1; } - name: Installing Linux Dependencies - if: ${{ runner.os == 'Linux' && !env['TEST_X86'] && !env['TEST_ARM32'] && !env['ANDROID_API'] && !matrix.container }} + if: ${{ runner.os == 'Linux' && !env['TEST_X86'] && !env['TEST_ARM32'] && !env['TEST_RISCV'] && !env['ANDROID_API'] && !matrix.container }} run: | sudo apt update # Install common dependencies @@ -368,11 +375,29 @@ jobs: libstdc++6:armhf - name: Installing qemu-user - if: ${{ runner.os == 'Linux' && env['TEST_QEMU'] && !matrix.container }} + if: ${{ runner.os == 'Linux' && env['TEST_ARM32'] && !matrix.container }} run: | sudo apt update sudo apt install -y qemu-user-static binfmt-support + - name: Installing Linux RISC-V Dependencies + if: ${{ runner.os == 'Linux' && env['TEST_RISCV'] && !matrix.container }} + run: | + sudo dpkg --add-architecture riscv64 + sudo apt-get update + sudo apt-get install -y \ + cmake \ + gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \ + zlib1g-dev:riscv64 libssl-dev:riscv64 \ + libcurl4-openssl-dev:riscv64 liblzma-dev:riscv64 \ + libstdc++6:riscv64 + echo "PKG_CONFIG_LIBDIR=/usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig" >> "$GITHUB_ENV" + + - uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + if: ${{ runner.os == 'Linux' && env['TEST_RISCV'] && !matrix.container }} + with: + platforms: riscv64 + - name: Installing Alpine Linux Dependencies if: ${{ contains(matrix.container, 'alpine') }} run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a8a6409c6..aa8f7ea84a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Native: Provide exception metadata, registers, and a basic crashing-thread stack to `on_crash` for crash identification. ([#2174](https://github.com/getsentry/sentry-native/pull/2174)) - Native: reduce disk I/O on scope changes by sending incremental updates to the crash daemon over IPC. ([#2155](https://github.com/getsentry/sentry-native/pull/2155), [#2157](https://github.com/getsentry/sentry-native/pull/2157)) - Native: install handlers for the daemon's own fatal signals and unhandled exceptions, allowing best-effort recovery actions such as dumping pending envelopes before termination. ([#2220](https://github.com/getsentry/sentry-native/pull/2220)) +- Linux: add RISC-V 64 support. ([#2232](https://github.com/getsentry/sentry-native/pull/2232)) - Report the host OS in the OS context under Wine and Proton, instead of the Windows version that Wine emulates. On Linux, prefer the host `os-release` inside Steam Runtime and Flatpak containers, and report SteamOS and Bazzite as the OS name. ([#2241](https://github.com/getsentry/sentry-native/pull/2241)) - Add `sentry_scope_set_trace` for per-scope traces. ([#2236](https://github.com/getsentry/sentry-native/pull/2236)) - Add `sentry_continue_trace` to continue incoming traces without a transaction. ([#2237](https://github.com/getsentry/sentry-native/pull/2237)) diff --git a/README.md b/README.md index 798b60c8a9..3ad98fa43a 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ The SDK bundle contains the following folders: The SDK currently supports and is tested on the following OS/Compiler variations: -- Linux (x86, x64, arm32 and arm64) with GCC 9+ and Clang 11+ +- Linux (x86, x64 and arm64) with GCC 9+ and Clang 11+ - Windows (x86, x64 and arm64) with MSVC (Visual Studio 2022+) and LLVM-MinGW - macOS 15+ with Apple Clang and LLVM Clang 15+ - Android API 21+ with NDK 23+ @@ -80,9 +80,11 @@ The SDK currently supports and is tested on the following OS/Compiler variations - Nintendo Switch via [sentry-switch](https://github.com/getsentry/sentry-switch). See [Nintendo Switch documentation](https://docs.sentry.io/platforms/nintendo-switch/) to get access. Additionally, the SDK should support the following platforms, although they are -not automatically tested, so breakage may occur: +not fully tested, so breakage may occur: -- Windows Versions lower than Windows 10 / Windows Server 2016 +- Windows versions lower than Windows 10 / Windows Server 2016: not tested in CI. +- Linux arm32 and riscv64: cross-compiled in CI, with only a subset of non-crash + tests run under QEMU. The SDK supports different features on the target platform: diff --git a/src/backends/native/minidump/sentry_minidump_common.c b/src/backends/native/minidump/sentry_minidump_common.c index 84fb4d0bc6..fa8b507699 100644 --- a/src/backends/native/minidump/sentry_minidump_common.c +++ b/src/backends/native/minidump/sentry_minidump_common.c @@ -213,6 +213,8 @@ sentry__minidump_get_context_size(void) return sizeof(minidump_context_x86_t); # elif defined(__arm__) return sizeof(minidump_context_arm_t); +# elif defined(__riscv) && __riscv_xlen == 64 + return sizeof(minidump_context_riscv64_t); # else # error "Unsupported architecture" # endif diff --git a/src/backends/native/minidump/sentry_minidump_format.h b/src/backends/native/minidump/sentry_minidump_format.h index 6daf02f360..9770c5b7e8 100644 --- a/src/backends/native/minidump/sentry_minidump_format.h +++ b/src/backends/native/minidump/sentry_minidump_format.h @@ -50,6 +50,7 @@ typedef enum { MINIDUMP_CPU_ARM = 5, // PROCESSOR_ARCHITECTURE_ARM MINIDUMP_CPU_X86_64 = 9, // PROCESSOR_ARCHITECTURE_AMD64 MINIDUMP_CPU_ARM64 = 12, // PROCESSOR_ARCHITECTURE_ARM64 + MINIDUMP_CPU_RISCV64 = 0x8006, // MD_CPU_ARCHITECTURE_RISCV64 (Breakpad) } minidump_cpu_type_t; // OS types (must match Breakpad MDOSPlatform values) @@ -318,6 +319,18 @@ typedef struct { uint32_t fpextra[8]; } PACKED_ATTR minidump_context_arm_t; PACKED_STRUCT_END + +#elif defined(__riscv) && __riscv_xlen == 64 +PACKED_STRUCT_BEGIN +typedef struct { + uint32_t context_flags; + uint32_t version; + uint64_t regs[32]; // PC, X1-X31 + uint64_t fpregs[32]; // F0-F31 + uint32_t fcsr; + uint32_t reserved; +} PACKED_ATTR minidump_context_riscv64_t; +PACKED_STRUCT_END #endif /** diff --git a/src/backends/native/minidump/sentry_minidump_linux.c b/src/backends/native/minidump/sentry_minidump_linux.c index 25c314ea16..42ce54f368 100644 --- a/src/backends/native/minidump/sentry_minidump_linux.c +++ b/src/backends/native/minidump/sentry_minidump_linux.c @@ -259,6 +259,18 @@ ptrace_get_thread_registers(pid_t tid, ucontext_t *uctx) SENTRY_DEBUGF("ptrace(PTRACE_GETREGSET) failed for thread %d: %s", tid, strerror(errno)); } +# elif defined(__riscv) && __riscv_xlen == 64 + struct iovec iov + = { uctx->uc_mcontext.__gregs, sizeof(uctx->uc_mcontext.__gregs) }; + if (ptrace(PTRACE_GETREGSET, tid, (void *)NT_PRSTATUS, &iov) == 0) { + success = true; + struct iovec fp_iov = { &uctx->uc_mcontext.__fpregs.__d, + sizeof(uctx->uc_mcontext.__fpregs.__d) }; + ptrace(PTRACE_GETREGSET, tid, (void *)NT_FPREGSET, &fp_iov); + } else { + SENTRY_DEBUGF("ptrace(PTRACE_GETREGSET) failed for thread %d: %s", tid, + strerror(errno)); + } # elif defined(__i386__) struct user_regs_struct regs; if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) { @@ -548,6 +560,8 @@ write_system_info_stream(minidump_writer_t *writer, minidump_directory_t *dir) sysinfo.processor_architecture = MINIDUMP_CPU_X86; # elif defined(__arm__) sysinfo.processor_architecture = MINIDUMP_CPU_ARM; +# elif defined(__riscv) && __riscv_xlen == 64 + sysinfo.processor_architecture = MINIDUMP_CPU_RISCV64; # endif # if defined(SENTRY_PLATFORM_ANDROID) @@ -856,6 +870,19 @@ write_thread_context( return write_data(writer, &context, sizeof(context)); +# elif defined(__riscv) && __riscv_xlen == 64 + (void)tid; + + minidump_context_riscv64_t context = { 0 }; + context.context_flags = 0x08000003; // RISCV64 | Integer | Floating point + context.version = 1; + memcpy(context.regs, uctx->uc_mcontext.__gregs, sizeof(context.regs)); + memcpy(context.fpregs, uctx->uc_mcontext.__fpregs.__d.__f, + sizeof(context.fpregs)); + context.fcsr = uctx->uc_mcontext.__fpregs.__d.__fcsr; + + return write_data(writer, &context, sizeof(context)); + # else # error "Unsupported architecture for Linux" # endif @@ -875,7 +902,8 @@ extract_elf_build_id(const char *elf_path, uint64_t elf_offset, } // Read ELF header -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Ehdr ehdr; # else Elf32_Ehdr ehdr; @@ -915,7 +943,8 @@ extract_elf_build_id(const char *elf_path, uint64_t elf_offset, return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Shdr *sections = (Elf64_Shdr *)shdr_buf; # else Elf32_Shdr *sections = (Elf32_Shdr *)shdr_buf; @@ -976,7 +1005,8 @@ read_elf_soname(const char *elf_path, uint64_t elf_offset, char *soname_buf, return false; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Ehdr ehdr; typedef Elf64_Dyn DynT; typedef Elf64_Shdr ShdrT; @@ -1313,6 +1343,8 @@ ptrace_capture_thread( ptrace_sp = (uint32_t)ptrace_ctx.uc_mcontext.gregs[REG_ESP]; # elif defined(__arm__) ptrace_sp = ptrace_ctx.uc_mcontext.arm_sp; +# elif defined(__riscv) && __riscv_xlen == 64 + ptrace_sp = ptrace_ctx.uc_mcontext.__gregs[REG_SP]; # endif if (ptrace_sp != 0) { @@ -1400,6 +1432,8 @@ write_thread_list_stream(minidump_writer_t *writer, minidump_directory_t *dir) sp = (uint32_t)uctx->uc_mcontext.gregs[REG_ESP]; # elif defined(__arm__) sp = uctx->uc_mcontext.arm_sp; +# elif defined(__riscv) && __riscv_xlen == 64 + sp = uctx->uc_mcontext.__gregs[REG_SP]; # endif SENTRY_DEBUGF("Thread %u: has context, SP=0x%llx", @@ -2089,6 +2123,11 @@ walk_indirect_registers_for_tid(minidump_writer_t *writer, pid_t tid, sentry__indirect_consider(acc, base, (uint64_t)(uint32_t)uctx->uc_mcontext.gregs[x86_gprs[i]], ops); } +# elif defined(__riscv) && __riscv_xlen == 64 + for (int r = 1; r < 32; r++) { + sentry__indirect_consider( + acc, base, (uint64_t)uctx->uc_mcontext.__gregs[r], ops); + } # elif defined(__arm__) // ARMv7's mcontext fields are individual struct members rather than // an array; walk them explicitly. Most heap pointers we care about diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 118b8fa9a7..779844df57 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -778,6 +778,10 @@ build_stacktrace_for_thread( ip = (uint64_t)thread_context->uc_mcontext.arm_pc; fp = (uint64_t)thread_context->uc_mcontext.arm_fp; sp = (uint64_t)thread_context->uc_mcontext.arm_sp; +# elif defined(__riscv) && __riscv_xlen == 64 + ip = (uint64_t)thread_context->uc_mcontext.__gregs[REG_PC]; + fp = (uint64_t)thread_context->uc_mcontext.__gregs[REG_S0]; + sp = (uint64_t)thread_context->uc_mcontext.__gregs[REG_SP]; # endif #elif defined(SENTRY_PLATFORM_MACOS) # if defined(__x86_64__) @@ -1178,6 +1182,21 @@ build_stacktrace_for_thread( break; } } +#elif defined(__riscv) && __riscv_xlen == 64 + // RISC-V frame records precede the CFA held in s0 + if (!read_stack_value(stack_buf, stack_start, stack_size, + current_fp - 2 * sizeof(uintptr_t), &saved_fp) + || !read_stack_value(stack_buf, stack_start, stack_size, + current_fp - sizeof(uintptr_t), &return_addr)) { + break; + } + // GCC < 14 leaf frames save only s0, so the caller is in ra + if (walk_count == 0 && return_addr >= stack_start + && return_addr < stack_end) { + saved_fp = return_addr; + return_addr + = (uint64_t)thread_context->uc_mcontext.__gregs[REG_RA]; + } #else // Read saved frame pointer and return address // Frame layout: [FP] = saved FP, [FP + pointer size] = return addr @@ -1294,7 +1313,8 @@ extract_elf_build_id_for_module(const char *elf_path, uint64_t elf_offset, } // Read ELF header -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Ehdr ehdr; # else Elf32_Ehdr ehdr; @@ -1332,7 +1352,8 @@ extract_elf_build_id_for_module(const char *elf_path, uint64_t elf_offset, return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Shdr *sections = (Elf64_Shdr *)shdr_buf; # else Elf32_Shdr *sections = (Elf32_Shdr *)shdr_buf; @@ -1428,7 +1449,8 @@ elf_locate_symtab(const char *path, int allow_dynsym, sym_source_t *src, return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Ehdr ehdr; # else Elf32_Ehdr ehdr; @@ -1459,7 +1481,8 @@ elf_locate_symtab(const char *path, int allow_dynsym, sym_source_t *src, return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Shdr *sections = (Elf64_Shdr *)shdr_buf; # else Elf32_Shdr *sections = (Elf32_Shdr *)shdr_buf; @@ -1662,7 +1685,8 @@ sym_source_lookup(const sym_source_t *src, uint64_t sym_target, char *name_out, return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Sym chunk[256]; # else Elf32_Sym chunk[256]; @@ -1686,7 +1710,8 @@ sym_source_lookup(const sym_source_t *src, uint64_t sym_target, char *name_out, } for (size_t k = 0; k < n; k++) { -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) unsigned char sym_type = ELF64_ST_TYPE(chunk[k].st_info); # else unsigned char sym_type = ELF32_ST_TYPE(chunk[k].st_info); diff --git a/src/backends/native/sentry_crash_handler.c b/src/backends/native/sentry_crash_handler.c index db8ce22919..68d0d87d77 100644 --- a/src/backends/native/sentry_crash_handler.c +++ b/src/backends/native/sentry_crash_handler.c @@ -324,6 +324,8 @@ capture_frame_pointer_stack( fp = (uintptr_t)uctx->uc_mcontext.gregs[REG_EBP]; # elif defined(__aarch64__) fp = (uintptr_t)uctx->uc_mcontext.regs[29]; +# elif defined(__riscv) && __riscv_xlen == 64 + fp = (uintptr_t)uctx->uc_mcontext.__gregs[REG_S0]; # endif capture->frame_pointer = true; while (fp && fp >= sp && fp - sp <= SENTRY_CRASH_MAX_STACK_CAPTURE @@ -331,7 +333,14 @@ capture_frame_pointer_stack( && capture->count < SENTRY_CRASH_MAX_BACKTRACE_FRAMES) { uintptr_t pair[2]; struct iovec local = { pair, sizeof(pair) }; - struct iovec remote = { (void *)fp, sizeof(pair) }; + uintptr_t record = fp; +# if defined(__riscv) && __riscv_xlen == 64 + record -= sizeof(pair); + if (record < sp) { + break; + } +# endif + struct iovec remote = { (void *)record, sizeof(pair) }; if (syscall(SYS_process_vm_readv, getpid(), &local, 1, &remote, 1, 0) != (ssize_t)sizeof(pair)) { break; diff --git a/src/sentry_elf.h b/src/sentry_elf.h index 9c161bce0d..3e1d4176ab 100644 --- a/src/sentry_elf.h +++ b/src/sentry_elf.h @@ -64,7 +64,8 @@ sentry__elf_is_file(const char *path) static inline bool sentry__elf_is_native_class(const unsigned char e_ident[EI_NIDENT]) { -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) return e_ident[EI_CLASS] == ELFCLASS64; # else return e_ident[EI_CLASS] == ELFCLASS32; @@ -79,7 +80,8 @@ sentry__elf_has_shdr_size( return false; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) return e_shentsize == sizeof(Elf64_Shdr); # else return e_shentsize == sizeof(Elf32_Shdr); @@ -94,7 +96,8 @@ sentry__elf_has_sym_entsize( return false; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) return sh_entsize == sizeof(Elf64_Sym); # else return sh_entsize == sizeof(Elf32_Sym); @@ -109,7 +112,8 @@ sentry__elf_has_phdr_size( return false; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) return e_phentsize == sizeof(Elf64_Phdr); # else return e_phentsize == sizeof(Elf32_Phdr); @@ -227,7 +231,8 @@ sentry__elf_compute_size_from_phdrs(const char *elf_path, uint64_t elf_offset) return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Ehdr ehdr; # else Elf32_Ehdr ehdr; @@ -260,7 +265,8 @@ sentry__elf_compute_size_from_phdrs(const char *elf_path, uint64_t elf_offset) return 0; } -# if defined(__x86_64__) || defined(__aarch64__) +# if defined(__x86_64__) || defined(__aarch64__) \ + || (defined(__riscv) && __riscv_xlen == 64) Elf64_Phdr *phdrs = (Elf64_Phdr *)phdr_buf; # else Elf32_Phdr *phdrs = (Elf32_Phdr *)phdr_buf; diff --git a/src/sentry_os.c b/src/sentry_os.c index 5248d42ec6..231540283a 100644 --- a/src/sentry_os.c +++ b/src/sentry_os.c @@ -1187,6 +1187,17 @@ sentry__build_registers(const sentry_ucontext_t *uctx) sentry_value_set_by_key(registers, "cpsr", sentry__value_new_addr(uctx->user_context->uc_mcontext.arm_cpsr)); +# elif defined(__riscv) && __riscv_xlen == 64 + + static const char *names[] + = { "pc", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", + "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", + "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" }; + for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + sentry_value_set_by_key(registers, names[i], + sentry__value_new_addr(uctx->user_context->uc_mcontext.__gregs[i])); + } + # endif # undef SET_REG diff --git a/src/sentry_os.h b/src/sentry_os.h index 90bce1c847..aa5fe55fe0 100644 --- a/src/sentry_os.h +++ b/src/sentry_os.h @@ -26,6 +26,8 @@ sentry__ucontext_get_ip(const ucontext_t *uctx) return (uintptr_t)uctx->uc_mcontext.arm_pc; # elif defined(__aarch64__) return (uintptr_t)uctx->uc_mcontext.pc; +# elif defined(__riscv) && __riscv_xlen == 64 + return (uintptr_t)uctx->uc_mcontext.__gregs[REG_PC]; # else (void)uctx; return 0; @@ -43,6 +45,8 @@ sentry__ucontext_get_sp(const ucontext_t *uctx) return (uintptr_t)uctx->uc_mcontext.arm_sp; # elif defined(__aarch64__) return (uintptr_t)uctx->uc_mcontext.sp; +# elif defined(__riscv) && __riscv_xlen == 64 + return (uintptr_t)uctx->uc_mcontext.__gregs[REG_SP]; # else (void)uctx; return 0; diff --git a/tests/build_config.py b/tests/build_config.py index b0ecec3f3f..727e459a0b 100644 --- a/tests/build_config.py +++ b/tests/build_config.py @@ -62,6 +62,16 @@ def get_platform_cmake_args(): "-DCMAKE_ASM_COMPILER=arm-linux-gnueabihf-gcc", ] ) + elif sys.platform == "linux" and os.environ.get("TEST_RISCV"): + args.extend( + [ + "-DCMAKE_SYSTEM_NAME=Linux", + "-DCMAKE_SYSTEM_PROCESSOR=riscv64", + "-DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc", + "-DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++", + "-DCMAKE_ASM_COMPILER=riscv64-linux-gnu-gcc", + ] + ) elif sys.platform == "linux" and os.environ.get("TEST_WINE"): cc = os.environ.get("CC") or "clang" cxx = os.environ.get("CXX") or "clang++" diff --git a/tests/conditions.py b/tests/conditions.py index 04e21514fc..2c0bd567ee 100644 --- a/tests/conditions.py +++ b/tests/conditions.py @@ -7,6 +7,7 @@ is_wine = bool(os.environ.get("TEST_WINE")) is_x86 = os.environ.get("TEST_X86") is_arm32 = bool(os.environ.get("TEST_ARM32")) +is_riscv = bool(os.environ.get("TEST_RISCV")) is_qemu = bool(os.environ.get("TEST_QEMU")) # opt-in because the tests change the user's system proxy settings (with sudo on macOS) has_system_proxy = bool(os.environ.get("TEST_SYSTEM_PROXY")) diff --git a/tests/requirements.txt b/tests/requirements.txt index 4559a2527b..7e0e5ddc44 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -4,7 +4,7 @@ pytest-httpserver==1.0.10 flaky==3.8.1 msgpack==1.2.3 pytest-xdist==3.8.0 -clang-format==20.1.5 +clang-format==20.1.5; platform_machine != "riscv64" pywin32==311; sys_platform == "win32" psutil==7.1.1 # For E2E tests that call Sentry API diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 196dfcec77..0de2ae33c9 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -13,6 +13,8 @@ has_native, is_android, is_arm32, + is_qemu, + is_riscv, is_tsan, is_wine, is_x86, @@ -22,7 +24,8 @@ project_fixture_path = pathlib.Path("tests/fixtures/dotnet_signal") pytestmark = pytest.mark.skipif( - is_wine, reason=".NET signal tests do not support Windows targets under Wine" + is_wine or (is_riscv and is_qemu), + reason=".NET signal tests do not support Wine or emulated RISC-V targets", ) diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index 0af07c0f6d..805177b795 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -24,7 +24,14 @@ REPLAY_ID, SENTRY_VERSION, ) -from .conditions import has_crashpad, has_files, has_oom, has_system_proxy +from .conditions import ( + has_crashpad, + has_files, + has_oom, + has_system_proxy, + is_qemu, + is_riscv, +) from .proxy import ( setup_proxy_env_vars, cleanup_proxy_env_vars, @@ -48,8 +55,9 @@ wait_for_file, ) +# ptrace is unavailable under RISC-V user-mode emulation pytestmark = pytest.mark.skipif( - not has_crashpad, + not has_crashpad or (is_riscv and is_qemu), reason="Tests need a crashpad backend and a valid environment for it", ) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 8ff820d2e4..2aa36c8ab0 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -773,6 +773,7 @@ def _parse_minidump(path): is_arm64 = cpu_arch in (0x8003, 0xC) is_amd64 = cpu_arch == 9 is_x86 = cpu_arch == 0 + is_riscv = cpu_arch == 0x8006 threads = [] if 3 in streams: # ThreadList @@ -799,6 +800,9 @@ def _parse_minidump(path): elif is_x86: # CONTEXT_X86: esp at offset 0xC4. sp = struct.unpack_from("/` and `include/tdep-/` directories from the diff --git a/vendor/stb_sprintf.c b/vendor/stb_sprintf.c index d60a91bae9..6cbd0f5ec1 100644 --- a/vendor/stb_sprintf.c +++ b/vendor/stb_sprintf.c @@ -1,2 +1,5 @@ +#include + +#define stbsp__uintptr uintptr_t #define STB_SPRINTF_IMPLEMENTATION #include "stb_sprintf.h"