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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ jobs:
MINGW_PKG_PREFIX: x86_64-w64-mingw32
MINGW_ASM_MASM_COMPILER: llvm-ml
MINGW_ASM_MASM_FLAGS: -m64
- name: Wine (Clang 21 + MinGW-w64)
os: ubuntu-26.04
CC: clang-21
CXX: clang++-21
TEST_WINE: 1
- name: Android (API 21, NDK 23)
os: ubuntu-latest
ANDROID_API: 21
Expand Down Expand Up @@ -237,6 +242,7 @@ jobs:
CXX: ${{ matrix.CXX }}
TEST_X86: ${{ matrix.TEST_X86 }}
TEST_MINGW: ${{ matrix.TEST_MINGW }}
TEST_WINE: ${{ matrix.TEST_WINE }}
TEST_ARM32: ${{ matrix.TEST_ARM32 }}
TEST_QEMU: ${{ matrix.TEST_QEMU }}
USE_SCCACHE: ${{ matrix.USE_SCCACHE }}
Expand Down Expand Up @@ -295,6 +301,17 @@ jobs:
exit 1
fi

- name: Installing Wine Dependencies
if: ${{ env['TEST_WINE'] }}
run: |
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL -o /etc/apt/keyrings/winehq-archive.asc https://dl.winehq.org/wine-builds/winehq.key
sudo curl -fsSL -o /etc/apt/sources.list.d/winehq-resolute.sources https://dl.winehq.org/wine-builds/ubuntu/dists/resolute/winehq-resolute.sources
sudo sed -i 's|winehq-archive\.key|winehq-archive.asc|' /etc/apt/sources.list.d/winehq-resolute.sources
sudo apt-get update
sudo apt-get install --install-recommends winehq-stable mingw-w64 lld
wine --version
Comment thread
jpnurmi marked this conversation as resolved.

- name: Installing kcov dependencies
if: ${{ contains(env['RUN_ANALYZER'], 'kcov') }}
run: |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

- Native: store daemon logs, minidumps, crash envelopes, and scratch files in `.run` directories so they are cleaned up with the run instead of accumulating in the database root. Minidumps can still be retained with `cache_keep`, which stores `.dmp` sidecars alongside cached envelopes. ([#1976](https://github.com/getsentry/sentry-native/pull/1976))
- Linux/ARM32: prevent recursive crashes when libunwind receives an unmapped initial instruction pointer during crash handling. ([#1977](https://github.com/getsentry/sentry-native/pull/1977))
- Wine: fix OS version detection and cross-compiling Windows builds from Linux. ([#2001](https://github.com/getsentry/sentry-native/pull/2001))

## 0.16.3

Expand Down
2 changes: 1 addition & 1 deletion examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,7 @@ main(int argc, char **argv)
assert(0);
}
if (has_arg(argc, argv, "abort")) {
#ifdef _WIN32
#if defined(_WIN32) && (defined(_MSC_VER) || defined(_UCRT))
// Suppress the Windows abort dialog that would block CI
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
Expand Down
4 changes: 2 additions & 2 deletions src/modulefinder/sentry_modulefinder_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
# include <dbghelp.h>
#endif
#define PSAPI_VERSION 2
#include <Psapi.h>
#include <TlHelp32.h>
#include <psapi.h>
#include <tlhelp32.h>

static bool g_initialized = false;
static sentry_mutex_t g_mutex = SENTRY__MUTEX_INIT;
Expand Down
9 changes: 4 additions & 5 deletions src/sentry_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,11 @@ sentry__get_windows_version(windows_version_t *win_ver)
}
win_ver->build = strtoul(buf, NULL, 10);
Comment thread
jpnurmi marked this conversation as resolved.

// UBR (Update Build Revision) is optional (not present on Wine)
reg_version = 0;
buf_size = sizeof(uint32_t);
if (RegGetValueA(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "UBR",
RRF_RT_REG_DWORD, NULL, &reg_version, &buf_size)
!= ERROR_SUCCESS) {
return 0;
}
RegGetValueA(HKEY_LOCAL_MACHINE, CURRENT_VERSION, "UBR", RRF_RT_REG_DWORD,
NULL, &reg_version, &buf_size);
win_ver->ubr = reg_version;

return 1;
Expand Down
20 changes: 17 additions & 3 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ def adb(*args, **kwargs):
)


def exe_name(name):
if sys.platform == "win32" or os.environ.get("TEST_WINE"):
return f"{name}.exe"
return name


def run_command(name):
name = exe_name(name)
if os.environ.get("TEST_WINE"):
return ["wine", name]
return [name]


# https://docs.pytest.org/en/latest/assert.html#assert-details
pytest.register_assert_rewrite("tests.assertions")

Expand Down Expand Up @@ -191,9 +204,10 @@ def run(
)
return child

cmd = [
"./{}".format(exe) if sys.platform != "win32" else "{}\\{}.exe".format(cwd, exe)
]
executable = (
"./{}".format(exe) if sys.platform != "win32" else "{}\\{}".format(cwd, exe)
)
cmd = run_command(executable)
if "asan" in os.environ.get("RUN_ANALYZER", ""):
asan_options = env.get("ASAN_OPTIONS", "")
if "detect_leaks" not in asan_options:
Expand Down
13 changes: 7 additions & 6 deletions tests/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import msgpack

from . import REPLAY_ID, SENTRY_VERSION
from .conditions import is_android, is_asan, is_tsan
from .conditions import is_android, is_asan, is_tsan, is_wine

VERSION_RE = re.compile(r"(\d+\.\d+\.\d+)[-.]?(.*)")

Expand Down Expand Up @@ -131,11 +131,12 @@ def assert_event_meta(
if is_android:
expected_sdk["name"] = "sentry.native.android"
else:
if sys.platform == "win32":
assert_matches(
event["contexts"]["os"],
{"name": "Windows", "version": platform.version()},
)
if sys.platform == "win32" or is_wine:
expected_os_context = {"name": "Windows"}
if not is_wine:
expected_os_context["version"] = platform.version()
assert_matches(event["contexts"]["os"], expected_os_context)
assert event["contexts"]["os"]["version"] is not None
assert event["contexts"]["os"]["build"] is not None
elif sys.platform == "linux":
version = platform.release()
Expand Down
19 changes: 19 additions & 0 deletions tests/build_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def get_platform_cmake_args():

This handles:
- 32-bit builds (TEST_X86)
- Wine cross-builds (TEST_WINE)
- ASAN/TSAN sanitizers (RUN_ANALYZER)
- Additional CMAKE_DEFINES from environment
"""
Expand All @@ -61,6 +62,24 @@ def get_platform_cmake_args():
"-DCMAKE_ASM_COMPILER=arm-linux-gnueabihf-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++"
linker_flags = "-fuse-ld=lld -static-libgcc -static-libstdc++"
args.extend(
[
"-DCMAKE_SYSTEM_NAME=Windows",
"-DCMAKE_SYSTEM_VERSION=10.0",
f"-DCMAKE_C_COMPILER={cc}",
"-DCMAKE_C_COMPILER_TARGET=x86_64-w64-windows-gnu",
f"-DCMAKE_CXX_COMPILER={cxx}",
"-DCMAKE_CXX_COMPILER_TARGET=x86_64-w64-windows-gnu",
"-DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres",
f"-DCMAKE_EXE_LINKER_FLAGS={linker_flags}",
f"-DCMAKE_SHARED_LINKER_FLAGS={linker_flags}",
f"-DCMAKE_MODULE_LINKER_FLAGS={linker_flags}",
]
)

if "asan" in os.environ.get("RUN_ANALYZER", ""):
args.append("-DWITH_ASAN_OPTION=ON")
Expand Down
5 changes: 1 addition & 4 deletions tests/cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import pytest

from . import adb
from . import adb, exe_name
from .conditions import has_sccache
from .build_config import (
get_android_config,
Expand Down Expand Up @@ -73,9 +73,6 @@ def destroy(self):

if "llvm-cov" in os.environ.get("RUN_ANALYZER", ""):

def exe_name(name):
return name + ".exe" if sys.platform == "win32" else name

def lib_name(name):
if sys.platform == "win32":
return name + ".dll"
Expand Down
4 changes: 3 additions & 1 deletion tests/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

is_aix = sys.platform == "aix" or sys.platform == "os400"
is_android = os.environ.get("ANDROID_API")
is_wine = bool(os.environ.get("TEST_WINE"))
is_x86 = os.environ.get("TEST_X86")
is_arm32 = bool(os.environ.get("TEST_ARM32"))
is_qemu = bool(os.environ.get("TEST_QEMU"))
Expand All @@ -29,14 +30,15 @@
# breakpad accesses thread state registers directly, which doesn't work on arm64e
and not (is_arm64e and sys.platform == "darwin")
)
# crashpad requires http, needs porting to AIX, and doesn’t work with kcov/valgrind/tsan either
# crashpad requires http, needs porting to AIX, and doesn’t work with kcov/valgrind/tsan or Wine cross-builds
has_crashpad = (
has_http
and not is_valgrind
and not is_kcov
and not is_android
and not is_aix
and not is_tsan
and not is_wine
)
# android has no local filesystem
has_files = not is_android
Expand Down
8 changes: 7 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from datetime import datetime, timedelta, UTC
from . import adb, run
from .cmake import CMake
from .conditions import has_sccache, is_android
from .conditions import has_sccache, is_android, is_wine
import tests

LABEL = "label"
Expand Down Expand Up @@ -211,6 +211,12 @@ def pytest_sessionfinish(session, exitstatus):


def pytest_sessionstart(session):
if is_wine:
# Keep Wine diagnostics from obscuring test output.
os.environ.setdefault("WINEDEBUG", "-all")
# Prevent intentional crashes from opening WineDbg and fresh prefixes
# from prompting to install Wine Mono.
os.environ.setdefault("WINEDLLOVERRIDES", "winedbg.exe=d;mscoree=d")
if has_sccache:
subprocess.run(["sccache", "--zero-stats"], capture_output=True)

Expand Down
2 changes: 1 addition & 1 deletion tests/proxy_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def _forward_http(client, proxy_auth):
lines = header.decode("iso-8859-1").split("\r\n")
method, target, version = lines[0].split(" ", 2)
headers = _parse_headers(lines[1:])
body = _read_http_body(client, headers, initial_body)

if not _auth_matches(headers, proxy_auth):
_send_proxy_auth_required(client, method)
Expand All @@ -188,7 +189,6 @@ def _forward_http(client, proxy_auth):
if url.query:
path += "?" + url.query

body = _read_http_body(client, headers, initial_body)
outbound_headers = [
(name, value)
for name, value in headers
Expand Down
15 changes: 11 additions & 4 deletions tests/test_build_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
import sys
import os
import pytest
from .conditions import has_breakpad, has_crashpad, has_native, is_android, is_qemu
from .conditions import (
has_breakpad,
has_crashpad,
has_native,
is_android,
is_qemu,
is_wine,
)


def test_static_lib(cmake):
Expand All @@ -16,12 +23,12 @@ def test_static_lib(cmake):
)

# on linux we can use `ldd` to check that we don’t link to `libsentry.so`
if sys.platform == "linux" and not is_android and not is_qemu:
if sys.platform == "linux" and not is_android and not is_qemu and not is_wine:
output = subprocess.check_output("ldd sentry_example", cwd=tmp_path, shell=True)
assert b"libsentry.so" not in output

# on windows, we read the file header to check that the exe is compiled correctly
if sys.platform == "win32":
if sys.platform == "win32" or is_wine:
with open(os.path.join(tmp_path, "sentry_example.exe"), "rb") as binary:
binary.seek(0x3C, 0)
offset = int.from_bytes(binary.read(4), byteorder="little")
Expand All @@ -36,7 +43,7 @@ def test_static_lib(cmake):
expected = b"PE\x00\x00\x64\x86" # IMAGE_FILE_MACHINE_AMD64
assert magic == expected
# similarly, we use `file` on linux
if sys.platform == "linux":
if sys.platform == "linux" and not is_wine:
Comment thread
cursor[bot] marked this conversation as resolved.
output = subprocess.check_output(
"file sentry_example", cwd=tmp_path, shell=True
)
Expand Down
14 changes: 13 additions & 1 deletion tests/test_dotnet_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,22 @@

from tests import adb
from tests.assertions import wait_for
from tests.conditions import has_native, is_android, is_arm32, is_tsan, is_x86, is_asan
from tests.conditions import (
has_native,
is_android,
is_arm32,
is_tsan,
is_wine,
is_x86,
is_asan,
)

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"
)


def assert_empty_run_dir(database_path):
run_dirs = [d for d in database_path.glob("*.run") if d.is_dir()]
Expand Down
8 changes: 7 additions & 1 deletion tests/test_integration_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
has_files,
is_asan,
is_qemu,
is_wine,
)


Expand Down Expand Up @@ -360,7 +361,12 @@ def test_external_crash_reporter_http(cmake, httpserver, build_args):
crash = crash_request.get_data()

envelope = Envelope.deserialize(crash)
assert envelope.headers["cache_dir"] == str(cache_dir)
actual_cache_dir = envelope.headers["cache_dir"]
if is_wine:
actual_cache_dir = subprocess.check_output(
["winepath", "-u", actual_cache_dir], text=True
).strip()
assert actual_cache_dir == str(cache_dir)
assert_meta(envelope, integration=build_args.get("SENTRY_BACKEND", ""))

envelope = Envelope.deserialize(feedback)
Expand Down
Loading
Loading