Skip to content
Merged
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
31 changes: 28 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved

- name: Installing Alpine Linux Dependencies
if: ${{ contains(matrix.container, 'alpine') }}
run: |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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+
Expand All @@ -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:

Expand Down
2 changes: 2 additions & 0 deletions src/backends/native/minidump/sentry_minidump_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/backends/native/minidump/sentry_minidump_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment thread
jpnurmi marked this conversation as resolved.
Resolved
} minidump_cpu_type_t;

// OS types (must match Breakpad MDOSPlatform values)
Expand Down Expand Up @@ -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

/**
Expand Down
45 changes: 42 additions & 3 deletions src/backends/native/minidump/sentry_minidump_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, &regs) == 0) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
Comment thread
jpnurmi marked this conversation as resolved.
Resolved
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
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down
37 changes: 31 additions & 6 deletions src/backends/native/sentry_crash_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand All @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion src/backends/native/sentry_crash_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,23 @@ 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
&& fp % sizeof(uintptr_t) == 0
&& 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;
Expand Down
Loading
Loading