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
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))
- Crashpad/Windows: preserve module CodeView UUIDs for minimal PDB70 records with empty PDB filenames. ([#2003](https://github.com/getsentry/sentry-native/pull/2003))

## 0.16.3

Expand Down
2 changes: 1 addition & 1 deletion external/crashpad
9 changes: 9 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
sourcedir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))


def lib_name(name):
if sys.platform == "win32":
prefix = "lib" if os.environ.get("TEST_MINGW") else ""
return prefix + name + ".dll"
elif sys.platform == "darwin":
return "lib" + name + ".dylib"
return "lib" + name + ".so"


def adb(*args, **kwargs):
return subprocess.run(
["{}/platform-tools/adb".format(os.environ["ANDROID_HOME"]), *args], **kwargs
Expand Down
23 changes: 17 additions & 6 deletions tests/assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ class CrashpadAttachments:
view_hierarchy: dict
cmake_cache: int
bytes_bin: bytes = None
minidump: bytes = None


def _unpack_breadcrumbs(payload):
Expand All @@ -530,6 +531,7 @@ def _load_crashpad_attachments(msg):
view_hierarchy = {}
cmake_cache = -1
bytes_bin = None
minidump = None
for part in msg.walk():
if part.get_filename() is not None:
assert part.get("Content-Type") is None
Expand All @@ -548,8 +550,20 @@ def _load_crashpad_attachments(msg):
case "bytes.bin":
bytes_bin = part.get_payload(decode=True)

if (
part.get_param("name", header="content-disposition")
== "upload_file_minidump"
):
minidump = part.get_payload(decode=True)

return CrashpadAttachments(
event, breadcrumb1, breadcrumb2, view_hierarchy, cmake_cache, bytes_bin
event,
breadcrumb1,
breadcrumb2,
view_hierarchy,
cmake_cache,
bytes_bin,
minidump,
)


Expand Down Expand Up @@ -593,11 +607,8 @@ def assert_crashpad_upload(req, expect_attachment=False, expect_view_hierarchy=F
assert attachments.bytes_bin == None
if expect_view_hierarchy:
assert_attachment_content_view_hierarchy(attachments.view_hierarchy)
assert any(
b'name="upload_file_minidump"' in part.as_bytes()
and b"\n\nMDMP" in part.as_bytes()
for part in msg.walk()
)
assert attachments.minidump is not None, "minidump attachment missing"
assert attachments.minidump.startswith(b"MDMP"), "invalid minidump signature"
return attachments


Expand Down
9 changes: 1 addition & 8 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, lib_name
from .conditions import has_sccache
from .build_config import (
get_android_config,
Expand Down Expand Up @@ -76,13 +76,6 @@ def destroy(self):
def exe_name(name):
return name + ".exe" if sys.platform == "win32" else name

def lib_name(name):
if sys.platform == "win32":
return name + ".dll"
elif sys.platform == "darwin":
return "lib" + name + ".dylib"
return "lib" + name + ".so"

for i, (d, _) in enumerate(self.runs.values()):
# first merge the raw profiling runs
files = [f for f in os.listdir(d) if f.endswith(".profraw")]
Expand Down
57 changes: 57 additions & 0 deletions tests/test_integration_crashpad.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import struct
import subprocess
import sys
import time
Expand All @@ -17,6 +18,7 @@
is_logs_envelope,
is_feedback_envelope,
is_replay_envelope,
lib_name,
REPLAY_ID,
)
from .conditions import has_crashpad, has_oom
Expand Down Expand Up @@ -49,6 +51,28 @@
flushes_state = sys.platform != "darwin"


def _minidump_stream(minidump, stream_type):
stream_count, directory_rva = struct.unpack_from("<II", minidump, 8)
for stream in range(stream_count):
stream_offset = directory_rva + stream * 12
current_type, size, rva = struct.unpack_from("<III", minidump, stream_offset)
if current_type == stream_type:
return minidump[rva : rva + size]
raise AssertionError(f"stream {stream_type} not found in minidump")


def _minidump_modules(minidump):
modules = _minidump_stream(minidump, 4)
module_count = struct.unpack_from("<I", modules)[0]
for module in range(module_count):
offset = 4 + module * 108
name_rva = struct.unpack_from("<I", modules, offset + 20)[0]
name_size = struct.unpack_from("<I", minidump, name_rva)[0]
name = minidump[name_rva + 4 : name_rva + 4 + name_size].decode("utf-16-le")
record_size, record_rva = struct.unpack_from("<II", modules, offset + 76)
yield name, minidump[record_rva : record_rva + record_size]


def test_crashpad_capture(cmake, httpserver):
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})

Expand All @@ -64,6 +88,39 @@ def test_crashpad_capture(cmake, httpserver):
assert len(httpserver.log) == 2


def test_crashpad_codeview(cmake, httpserver):
tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"})

httpserver.expect_oneshot_request("/api/123456/minidump/").respond_with_data("OK")

with httpserver.wait(timeout=10) as waiting:
run(
tmp_path,
"sentry_example",
["log", "crashpad-wait-for-upload", "crash"],
expect_failure=True,
env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)),
)

assert waiting.result
assert len(httpserver.log) == 1
attachments = assert_crashpad_upload(httpserver.log[0][0])
codeviews = {
name.replace("\\", "/").rsplit("/", 1)[-1]: codeview
for name, codeview in _minidump_modules(attachments.minidump)
}
codeview = codeviews[lib_name("sentry")]
signature = codeview[:4]
if sys.platform == "linux":
assert signature == b"LEpB"
identifier = codeview[4:]
else:
assert signature == b"RSDS"
identifier = codeview[4:20]

assert any(identifier)


def _setup_crashpad_proxy_test(cmake, httpserver, proxy):
if proxy:
proxy_process, port = start_proxy(proxy)
Expand Down
Loading