Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
22 changes: 16 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,7 @@ 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.startswith(b"MDMP")
Comment thread
sentry[bot] marked this conversation as resolved.
Outdated
return attachments


Expand Down
54 changes: 54 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 Down Expand Up @@ -49,6 +50,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 +87,37 @@ 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)
}
module_name = {"win32": "sentry.dll", "darwin": "libsentry.dylib"}.get(
sys.platform, "libsentry.so"
)
codeview = codeviews[module_name]
signature = codeview[:4]
identifier = codeview[4:20] if signature == b"RSDS" else codeview[4:]
assert signature in (b"RSDS", b"LEpB")
assert any(identifier)


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