Skip to content

Commit 4a79ad5

Browse files
authored
fix(java): Allow frame removals during symbolication (#104319)
Since the introduction of getsentry/symbolicator#1817 Symbolicator can actually remove frames, not only expand/remap them, so this change is necessary to not display synthetic frames generated by R8/proguard. NOTE: We only apply this logic for `java` frames to account for mixed stacktraces. ## Before <img width="1474" height="396" alt="Google Chrome 2025-12-03 13 03 20" src="https://github.com/user-attachments/assets/ca6b2326-03d1-420c-8bc0-6ed6777f290d" /> ## After <img width="1107" height="357" alt="Finder 2025-12-03 14 17 53" src="https://github.com/user-attachments/assets/be5df668-eae8-4f5a-9be6-188b7f4ebc49" />
1 parent 043f1c7 commit 4a79ad5

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

src/sentry/lang/java/processing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def process_jvm_stacktraces(symbolicator: Symbolicator, data: Any) -> Any:
246246
new_frame = dict(raw_frame)
247247
_merge_frame(new_frame, returned)
248248
new_frames.append(new_frame)
249-
else:
249+
elif not _handles_frame(raw_frame, data.get("platform", "unknown")):
250250
new_frames.append(raw_frame)
251251

252252
sinfo.stacktrace["frames"] = new_frames

tests/relay_integration/lang/java/test_plugin.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@
5454
PROGUARD_BUG_UUID = "071207ac-b491-4a74-957c-2c94fd9594f2"
5555
PROGUARD_BUG_SOURCE = b"x"
5656

57+
PROGUARD_OUTLINE_UUID = "d9a82b1e-7a8e-4c4b-9c7a-8b5e3f2a1d6c"
58+
PROGUARD_OUTLINE_SOURCE = b"""\
59+
# compiler: R8
60+
# compiler_version: 2.0
61+
# min_api: 15
62+
outline.OutlineClass -> o.a:
63+
1:2:int outline() -> a
64+
# {"id":"com.android.tools.r8.outline"}
65+
com.example.RealClass -> com.example.b:
66+
4:4:int realMethod(int):98:98 -> s
67+
5:5:int realMethod(int):100:100 -> s
68+
27:27:int realMethod(int):0:0 -> s
69+
# {"id":"com.android.tools.r8.outlineCallsite","positions":{"1":4,"2":5},"outline":"Lo/a;a()I"}
70+
"""
71+
5772
JVM_DEBUG_ID = "6dc7fdb0-d2fb-4c8e-9d6b-bb1aa98929b1"
5873
JVM_SOURCE = b"""\
5974
package io.sentry.samples
@@ -662,6 +677,87 @@ def test_resolving_does_not_fail_when_no_module_or_function(self) -> None:
662677
metrics = event.data["_metrics"]
663678
assert not metrics.get("flag.processing.error")
664679

680+
@requires_symbolicator
681+
@pytest.mark.symbolicator
682+
def test_removes_frames_not_found_in_mapping_but_preserves_native_frames(self) -> None:
683+
"""Test that outline frames are removed while native frames are preserved."""
684+
self.upload_proguard_mapping(PROGUARD_OUTLINE_UUID, PROGUARD_OUTLINE_SOURCE)
685+
686+
event_data = {
687+
"user": {"ip_address": "31.172.207.97"},
688+
"extra": {},
689+
"project": self.project.id,
690+
"platform": "java",
691+
"debug_meta": {"images": [{"type": "proguard", "uuid": PROGUARD_OUTLINE_UUID}]},
692+
"exception": {
693+
"values": [
694+
{
695+
"stacktrace": {
696+
"frames": [
697+
{
698+
"function": "s",
699+
"abs_path": None,
700+
"module": "com.example.b",
701+
"filename": None,
702+
"lineno": 27,
703+
},
704+
{
705+
"package": "/system/lib64/libc.so",
706+
"instruction_addr": "0x00000000000a9170",
707+
"addr_mode": "rel:0",
708+
"symbol": "__pthread_start(void*)",
709+
"symbol_addr": "0x00000000000a9130",
710+
},
711+
{
712+
"function": "a",
713+
"abs_path": None,
714+
"module": "o.a",
715+
"filename": None,
716+
"lineno": 1,
717+
},
718+
{
719+
"platform": "native",
720+
"instruction_addr": "0x79571a8000",
721+
"addr_mode": "abs",
722+
},
723+
]
724+
},
725+
"module": "com.example",
726+
"type": "Exception",
727+
"value": "Something went wrong",
728+
}
729+
]
730+
},
731+
"timestamp": before_now(seconds=1).isoformat(),
732+
}
733+
734+
event = self.post_and_retrieve_event(event_data)
735+
736+
exc = event.interfaces["exception"].values[0]
737+
bt = exc.stacktrace
738+
frames = bt.frames
739+
740+
# Should only have 3 frames:
741+
# - First deobfuscated Java frame (realMethod)
742+
# - Native frame (preserved)
743+
# - Native frame (preserved)
744+
# The outline frame should be removed by symbolicator
745+
assert len(frames) == 3
746+
747+
# First frame should be deobfuscated and inlined
748+
assert frames[0].function == "realMethod"
749+
assert frames[0].module == "com.example.RealClass"
750+
assert frames[0].lineno == 98
751+
752+
# Second frame (native, no function/module) should be preserved as-is
753+
assert frames[1].package == "/system/lib64/libc.so"
754+
assert frames[1].instruction_addr == "0xa9170"
755+
assert frames[1].symbol == "__pthread_start(void*)"
756+
757+
# Third frame (native platform) should be preserved as-is
758+
assert frames[2].platform == "native"
759+
assert frames[2].instruction_addr == "0x79571a8000"
760+
665761
@requires_symbolicator
666762
@pytest.mark.symbolicator
667763
def test_sets_inapp_after_resolving(self) -> None:

0 commit comments

Comments
 (0)