@@ -8,106 +8,134 @@ import type {
8
8
} from 'sentry/types' ;
9
9
import { EntryType } from 'sentry/types/event' ;
10
10
11
- const NATIVE_PLATFORMS : PlatformKey [ ] = [ 'cocoa' , 'native' ] ;
11
+ /** All platforms that always use Debug Files. */
12
+ const DEBUG_FILE_PLATFORMS : Set < PlatformKey > = new Set ( [
13
+ 'objc' ,
14
+ 'cocoa' ,
15
+ 'swift' ,
16
+ 'native' ,
17
+ 'c' ,
18
+ ] ) ;
19
+ /** Other platforms that may use Debug Files. */
20
+ const MAYBE_DEBUG_FILE_PLATFORMS : Set < PlatformKey > = new Set ( [ 'csharp' , 'java' ] ) ;
21
+
22
+ /**
23
+ * Returns whether to display the "Reprocess Event" action.
24
+ *
25
+ * That is the case when we have a "reprocessable" event, which is an event that needs
26
+ * Debug Files for proper processing, as those Debug Files could have been uploaded *after*
27
+ * the Event was ingested.
28
+ */
29
+ export function displayReprocessEventAction ( event ?: Event ) : boolean {
30
+ if ( ! event ) {
31
+ return false ;
32
+ }
12
33
13
- // Finds all frames in a given data blob and returns it's platforms
14
- function getPlatforms ( exceptionValue : ExceptionValue | StacktraceType | null ) {
15
- const frames = exceptionValue ?. frames ?? [ ] ;
16
- const stacktraceFrames = ( exceptionValue as ExceptionValue ) ?. stacktrace ?. frames ?? [ ] ;
34
+ const eventPlatforms = getEventPlatform ( event ) ;
35
+ // Check Events from platforms that always use Debug Files as a fast-path
36
+ if ( hasIntersection ( eventPlatforms , DEBUG_FILE_PLATFORMS ) ) {
37
+ return true ;
38
+ }
17
39
18
- if ( ! frames . length && ! stacktraceFrames . length ) {
19
- return [ ] ;
40
+ const hasDebugImages = ( event ?. entries ?? [ ] ) . some (
41
+ entry => entry . type === EntryType . DEBUGMETA && entry . data . images . length > 0
42
+ ) ;
43
+
44
+ // Otherwise, check alternative platforms if they actually have Debug Files
45
+ if ( hasIntersection ( eventPlatforms , MAYBE_DEBUG_FILE_PLATFORMS ) && hasDebugImages ) {
46
+ return true ;
20
47
}
21
48
22
- return [ ...frames , ...stacktraceFrames ]
23
- . map ( frame => frame . platform )
24
- . filter ( platform => ! ! platform ) ;
25
- }
49
+ // Finally, fall back to checking the `platform` of each frame
50
+ const exceptionEntry = event . entries . find (
51
+ entry => entry . type === EntryType . EXCEPTION
52
+ ) as EntryException | undefined ;
53
+
54
+ if ( ! exceptionEntry ) {
55
+ return false ;
56
+ }
26
57
27
- function getStackTracePlatforms ( event : Event , exceptionEntry : EntryException ) {
28
- // Fetch platforms in stack traces of an exception entry
29
- const exceptionEntryPlatforms = ( exceptionEntry . data . values ?? [ ] ) . flatMap (
30
- getPlatforms
58
+ return hasIntersection (
59
+ getStackTracePlatforms ( event , exceptionEntry ) ,
60
+ DEBUG_FILE_PLATFORMS
31
61
) ;
62
+ }
63
+
64
+ /**
65
+ * Returns whether the two Sets have intersecting elements.
66
+ */
67
+ function hasIntersection < T > ( set1 : Set < T > , set2 : Set < T > ) : boolean {
68
+ for ( const v of set1 ) {
69
+ if ( set2 . has ( v ) ) {
70
+ return true ;
71
+ }
72
+ }
73
+ return false ;
74
+ }
75
+
76
+ /**
77
+ * Returns the event platform as a Set.
78
+ */
79
+ function getEventPlatform ( event : Event ) : Set < PlatformKey > {
80
+ const platforms = new Set < PlatformKey > ( ) ;
81
+ addPlatforms ( platforms , [ event ] ) ;
82
+ return platforms ;
83
+ }
84
+
85
+ /**
86
+ * Returns a Set of all platforms found in the `event` and `exceptionEntry`.
87
+ */
88
+ function getStackTracePlatforms (
89
+ event : Event ,
90
+ exceptionEntry : EntryException
91
+ ) : Set < PlatformKey > {
92
+ const platforms = new Set < PlatformKey > ( ) ;
93
+
94
+ // Add platforms in stack traces of an exception entry
95
+ ( exceptionEntry . data . values ?? [ ] ) . forEach ( exc => addFramePlatforms ( platforms , exc ) ) ;
32
96
33
- // Fetch platforms in an exception entry
97
+ // Add platforms in a stack trace entry
34
98
const stackTraceEntry = ( event . entries . find (
35
99
entry => entry . type === EntryType . STACKTRACE
36
100
) ?. data ?? { } ) as StacktraceType ;
37
101
38
- // Fetch platforms in an exception entry
39
- const stackTraceEntryPlatforms = Object . keys ( stackTraceEntry ) . flatMap ( key =>
40
- getPlatforms ( stackTraceEntry [ key ] )
102
+ Object . keys ( stackTraceEntry ) . forEach ( key =>
103
+ addFramePlatforms ( platforms , stackTraceEntry [ key ] )
41
104
) ;
42
105
43
- // Fetch platforms in an thread entry
106
+ // Add platforms in a thread entry
44
107
const threadEntry = ( event . entries . find ( entry => entry . type === EntryType . THREADS ) ?. data
45
108
. values ?? [ ] ) as Array < Thread > ;
46
109
47
- // Fetch platforms in a thread entry
48
- const threadEntryPlatforms = threadEntry . flatMap ( ( { stacktrace} ) =>
49
- getPlatforms ( stacktrace )
50
- ) ;
110
+ threadEntry . forEach ( ( { stacktrace} ) => addFramePlatforms ( platforms , stacktrace ) ) ;
51
111
52
- return new Set ( [
53
- ...exceptionEntryPlatforms ,
54
- ...stackTraceEntryPlatforms ,
55
- ...threadEntryPlatforms ,
56
- ] ) ;
112
+ return platforms ;
57
113
}
58
114
59
- // Checks whether an event indicates that it is a native event.
60
- function isNativeEvent ( event : Event , exceptionEntry : EntryException ) {
61
- const { platform} = event ;
62
-
63
- if ( platform && NATIVE_PLATFORMS . includes ( platform ) ) {
64
- return true ;
65
- }
66
-
67
- const stackTracePlatforms = getStackTracePlatforms ( event , exceptionEntry ) ;
68
-
69
- return NATIVE_PLATFORMS . some ( nativePlatform => stackTracePlatforms . has ( nativePlatform ) ) ;
70
- }
71
-
72
- // Checks whether an event indicates that it has an associated minidump.
73
- function isMinidumpEvent ( exceptionEntry : EntryException ) {
74
- const { data} = exceptionEntry ;
75
- return ( data . values ?? [ ] ) . some ( value => value . mechanism ?. type === 'minidump' ) ;
76
- }
115
+ /**
116
+ * Adds all the platforms in the frames of `exceptionValue` to the `platforms` Set.
117
+ */
118
+ function addFramePlatforms (
119
+ platforms : Set < PlatformKey > ,
120
+ exceptionValue : ExceptionValue | StacktraceType | null
121
+ ) {
122
+ const frames = exceptionValue ?. frames ?? [ ] ;
123
+ const stacktraceFrames = ( exceptionValue as ExceptionValue ) ?. stacktrace ?. frames ?? [ ] ;
77
124
78
- // Checks whether an event indicates that it has an apple crash report.
79
- function isAppleCrashReportEvent ( exceptionEntry : EntryException ) {
80
- const { data} = exceptionEntry ;
81
- return ( data . values ?? [ ] ) . some ( value => value . mechanism ?. type === 'applecrashreport' ) ;
125
+ addPlatforms ( platforms , frames ) ;
126
+ addPlatforms ( platforms , stacktraceFrames ) ;
82
127
}
83
128
84
- export function displayReprocessEventAction ( orgFeatures : Array < string > , event ?: Event ) {
85
- if ( ! event || ! orgFeatures . includes ( 'reprocessing-v2' ) ) {
86
- return false ;
87
- }
88
-
89
- const { entries} = event ;
90
- const exceptionEntry = entries . find ( entry => entry . type === EntryType . EXCEPTION ) as
91
- | EntryException
92
- | undefined ;
93
-
94
- if ( ! exceptionEntry ) {
95
- return false ;
129
+ /**
130
+ * Adds all the `platform` properties found in `iter` to the `platforms` Set.
131
+ */
132
+ function addPlatforms (
133
+ platforms : Set < PlatformKey > ,
134
+ iter : Array < { platform ?: PlatformKey | null } >
135
+ ) {
136
+ for ( const o of iter ) {
137
+ if ( o . platform ) {
138
+ platforms . add ( o . platform ) ;
139
+ }
96
140
}
97
-
98
- // We want to show the reprocessing button if the issue in question is native or contains native frames.
99
- // The logic is taken from the symbolication pipeline in Python, where it is used to determine whether reprocessing
100
- // payloads should be stored:
101
- // https://github.com/getsentry/sentry/blob/cb7baef414890336881d67b7a8433ee47198c701/src/sentry/lang/native/processing.py#L425-L426
102
- // It is still not ideal as one can always merge native and non-native events together into one issue,
103
- // but it's the best approximation we have.
104
- if (
105
- ! isMinidumpEvent ( exceptionEntry ) &&
106
- ! isAppleCrashReportEvent ( exceptionEntry ) &&
107
- ! isNativeEvent ( event , exceptionEntry )
108
- ) {
109
- return false ;
110
- }
111
-
112
- return true ;
113
141
}
0 commit comments