Skip to content

Commit ef67ba7

Browse files
authored
feat(symbolication): Rewrite Electron minidump modules (#87176)
Electron minidumps frequently contain root modules with nonsensical debug file names, which results from packagers like electron-forge. Consequently, the module won't be found on the official Electron symbol source. To fix this, we rewrite the debug file name of the first module according to these rules for Electron minidumps before doing the lookup. The rules are tried in order and only the first that matches is applied. The rewrites we perform are as follows: * /PATH/TO/FILE Framework -> /PATH/TO/Electron Framework * /PATH/TO/FILE Helper (FOOBAR) -> /PATH/TO/Electron Helper (FOOBAR) * /PATH/TO/FILE.exe.pdb -> /PATH/TO/electron.exe.pdb * /PATH/TO/FILE -> /PATH/TO/electron These were determined by examining logs of successful downloads from the Electron symbol source. The rewriting itself is performed by Symbolicator. We can't do it earlier because we only gain access to the modules contained in the minidump during stackwalking. The Symbolicator end of this change is getsentry/symbolicator#1650.
1 parent 934c5a1 commit ef67ba7

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

src/sentry/lang/native/processing.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,39 @@
4242
# Attachment type used for Apple Crash Reports
4343
APPLECRASHREPORT_ATTACHMENT_TYPE = "event.applecrashreport"
4444

45+
# Rules for rewriting the debug file of the first module
46+
# in an Electron minidump.
47+
#
48+
# Such minidumps frequently contain root modules
49+
# with nonsensical debug file names, which results from packagers like
50+
# electron-forge. Consequently, the module won't be found on the official
51+
# Electron symbol source.
52+
#
53+
# To fix this, we rewrite the debug file name of the first module
54+
# according to these rules for Electron minidumps before doing the lookup.
55+
# The rules are tried in order and only the first that matches is applied.
56+
#
57+
# The rewrites we perform are as follows:
58+
#
59+
# * /PATH/TO/FILE Framework -> /PATH/TO/Electron Framework
60+
# * /PATH/TO/FILE Helper (FOOBAR) -> /PATH/TO/Electron Helper (FOOBAR)
61+
# * /PATH/TO/FILE.exe.pdb -> /PATH/TO/electron.exe.pdb
62+
# * /PATH/TO/FILE -> /PATH/TO/electron
63+
#
64+
# These were determined by examining logs of successful downloads from the Electron symbol
65+
# source.
66+
#
67+
# The rewriting itself is performed by Symbolicator. We can't do it before
68+
# because we only gain access to the modules contained in the minidump
69+
# during stackwalking.
70+
#
71+
# NOTE: These regexes and replacement strings are written in the syntax the Rust regex crate accepts!
72+
ELECTRON_FIRST_MODULE_REWRITE_RULES = [
73+
{"from": "[^/\\\\]+ (?<suffix>Framework|Helper( \\(.+\\))?)$", "to": "Electron $suffix"},
74+
{"from": "[^/\\\\]+\\.exe\\.pdb$", "to": "electron.exe.pdb"},
75+
{"from": "[^/\\\\]+$", "to": "electron"},
76+
]
77+
4578

4679
def _merge_frame(new_frame, symbolicated, platform="native"):
4780
# il2cpp events which have the "csharp" platform have good (C#) names
@@ -288,8 +321,17 @@ def process_minidump(symbolicator: Symbolicator, data: Any) -> Any:
288321
logger.error("Missing minidump for minidump event")
289322
return
290323

324+
# We do module rewriting only for Electron minidumps.
325+
# See the documentation of `ELECTRON_FIRST_MODULE_REWRITE_RULES`.
326+
if get_path(data, "sdk", "name") == "sentry.javascript.electron":
327+
rewrite_first_module = ELECTRON_FIRST_MODULE_REWRITE_RULES
328+
else:
329+
rewrite_first_module = []
330+
291331
metrics.incr("process.native.symbolicate.request")
292-
response = symbolicator.process_minidump(data.get("platform"), minidump.data)
332+
response = symbolicator.process_minidump(
333+
data.get("platform"), minidump.data, rewrite_first_module
334+
)
293335

294336
if _handle_response_status(data, response):
295337
_merge_full_response(data, response)

src/sentry/lang/native/symbolicator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,14 @@ def _process(self, task_name: str, path: str, **kwargs):
166166
# Otherwise, we are done processing, yay
167167
return json_response
168168

169-
def process_minidump(self, platform, minidump):
169+
def process_minidump(self, platform, minidump, rewrite_first_module):
170170
(sources, process_response) = sources_for_symbolication(self.project)
171171
scraping_config = get_scraping_config(self.project)
172172
data = {
173173
"platform": orjson.dumps(platform).decode(),
174174
"sources": orjson.dumps(sources).decode(),
175175
"scraping": orjson.dumps(scraping_config).decode(),
176+
"rewrite_first_module": orjson.dumps(rewrite_first_module).decode(),
176177
"options": '{"dif_candidates": true}',
177178
}
178179

tests/sentry/lang/native/test_processing.py

+40
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55

66
from __future__ import annotations
77

8+
import re
89
from typing import Any
910
from unittest import mock
1011

1112
import pytest
1213

1314
from sentry.lang.native.processing import (
15+
ELECTRON_FIRST_MODULE_REWRITE_RULES,
1416
_merge_image,
1517
get_frames_for_symbolication,
1618
process_native_stacktraces,
@@ -229,6 +231,44 @@ def test_instruction_addr_adjustment_none():
229231
assert not processed_frames[1]["adjust_instruction_addr"]
230232

231233

234+
def test_rewrite_electron_debug_file():
235+
def rewrite(debug_file):
236+
for rule in ELECTRON_FIRST_MODULE_REWRITE_RULES:
237+
# Need to patch the regexes and replacement strings here
238+
# from Rust to Python syntax.
239+
# In regex: ?<group> -> ?P<group>
240+
# In replacement: $group -> \g<group>
241+
from_patched = re.sub("\\?<", "?P<", rule["from"])
242+
to_patched = re.sub("\\$(\\w+)", "\\\\g<\\1>", rule["to"])
243+
replaced = re.sub(from_patched, to_patched, debug_file)
244+
if replaced != debug_file:
245+
return replaced
246+
247+
return debug_file
248+
249+
assert rewrite("/home/My Awesome Crasher") == "/home/electron"
250+
assert (
251+
rewrite("/home/My Awesome Crasher Helper (Renderer)") == "/home/Electron Helper (Renderer)"
252+
)
253+
assert rewrite("/home/My Awesome Crasher Helper") == "/home/Electron Helper"
254+
assert (
255+
rewrite("C:/projects/src/out/Default/myapp.exe.pdb")
256+
== "C:/projects/src/out/Default/electron.exe.pdb"
257+
)
258+
assert (
259+
rewrite("C:\\projects\\src\\out\\Default\\myapp.exe.pdb")
260+
== "C:\\projects\\src\\out\\Default\\electron.exe.pdb"
261+
)
262+
assert (
263+
rewrite("C:\\projects\\src\\out\\Default\\myapp-exe-pdb")
264+
== "C:\\projects\\src\\out\\Default\\electron"
265+
)
266+
assert (
267+
rewrite("/home/************/usr/lib/slack/slack")
268+
== "/home/************/usr/lib/slack/electron"
269+
)
270+
271+
232272
@django_db_all
233273
@mock.patch("sentry.lang.native.processing.Symbolicator")
234274
def test_il2cpp_symbolication(mock_symbolicator, default_project):

0 commit comments

Comments
 (0)