Browse Source

feat(profiling): add frame-level platform info (#68776)

Way back we added
[frame-level](https://github.com/getsentry/vroom/blob/main/internal/frame/frame.go#L42)
platform information in `vroom`, but we're not passing this information
down from `sentry`.

This will also help us to distinguish between `js` and `cocoa` frames in
mixed stack traces scenarios (such as for react-native).
Francesco Vigliaturo 11 months ago
parent
commit
10bf7e266d
2 changed files with 53 additions and 1 deletions
  1. 17 1
      src/sentry/profiles/task.py
  2. 36 0
      tests/sentry/profiles/test_task.py

+ 17 - 1
src/sentry/profiles/task.py

@@ -148,6 +148,10 @@ def process_profile_task(
     if not _normalize_profile(profile, organization, project):
         return
 
+    # set platform information at frame-level
+    # only for those platforms that didn't go through symbolication
+    _set_frames_platform(profile)
+
     if "version" in profile:
         set_measurement("profile.samples.processed", len(profile["profile"]["samples"]))
         set_measurement("profile.stacks.processed", len(profile["profile"]["stacks"]))
@@ -637,7 +641,9 @@ def _process_symbolicator_results_for_sample(
             # This works since symbolicated_frames are in the same order
             # as raw_frames (except some frames are not sent).
             for frame_idx in symbolicated_frames_dict[symbolicated_frame_idx]:
-                new_frames.append(symbolicated_frames[frame_idx])
+                f = symbolicated_frames[frame_idx]
+                f["platform"] = platform
+                new_frames.append(f)
 
             # go to the next symbolicated frame result
             symbolicated_frame_idx += 1
@@ -1115,3 +1121,13 @@ def _calculate_duration_for_sample_format_v2(profile: Profile) -> int:
 
 def _calculate_duration_for_android_format(profile: Profile) -> int:
     return int(profile["duration_ns"] * 1e-6)
+
+
+def _set_frames_platform(profile: Profile):
+    platform = profile["platform"]
+    frames = (
+        profile["profile"]["methods"] if platform == "android" else profile["profile"]["frames"]
+    )
+    for f in frames:
+        if "platform" not in f:
+            f["platform"] = platform

+ 36 - 0
tests/sentry/profiles/test_task.py

@@ -16,12 +16,14 @@ from sentry.models.project import Project
 from sentry.models.release import Release
 from sentry.models.releasefile import ReleaseFile
 from sentry.profiles.task import (
+    Profile,
     _calculate_profile_duration_ms,
     _deobfuscate,
     _deobfuscate_locally,
     _deobfuscate_using_symbolicator,
     _normalize,
     _process_symbolicator_results_for_sample,
+    _set_frames_platform,
     _symbolicate_profile,
 )
 from sentry.testutils.cases import TransactionTestCase
@@ -865,3 +867,37 @@ class DeobfuscationViaSymbolicator(TransactionTestCase):
 
         _symbolicate_profile(js_profile, self.project)
         assert js_profile["profile"]["frames"][0].get("data", {}).get("symbolicated", False)
+
+
+def test_set_frames_platform_sample():
+    js_prof: Profile = {
+        "version": "1",
+        "platform": "javascript",
+        "profile": {
+            "frames": [
+                {"function": "a"},
+                {"function": "b", "platform": "cocoa"},
+                {"function": "c"},
+            ]
+        },
+    }
+    _set_frames_platform(js_prof)
+
+    platforms = [f["platform"] for f in js_prof["profile"]["frames"]]
+    assert platforms == ["javascript", "cocoa", "javascript"]
+
+
+def test_set_frames_platform_android():
+    android_prof: Profile = {
+        "platform": "android",
+        "profile": {
+            "methods": [
+                {"name": "a"},
+                {"name": "b"},
+            ]
+        },
+    }
+    _set_frames_platform(android_prof)
+
+    platforms = [m["platform"] for m in android_prof["profile"]["methods"]]
+    assert platforms == ["android", "android"]