|
@@ -8,106 +8,134 @@ import type {
|
|
|
} from 'sentry/types';
|
|
|
import {EntryType} from 'sentry/types/event';
|
|
|
|
|
|
-const NATIVE_PLATFORMS: PlatformKey[] = ['cocoa', 'native'];
|
|
|
+/** All platforms that always use Debug Files. */
|
|
|
+const DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set([
|
|
|
+ 'objc',
|
|
|
+ 'cocoa',
|
|
|
+ 'swift',
|
|
|
+ 'native',
|
|
|
+ 'c',
|
|
|
+]);
|
|
|
+/** Other platforms that may use Debug Files. */
|
|
|
+const MAYBE_DEBUG_FILE_PLATFORMS: Set<PlatformKey> = new Set(['csharp', 'java']);
|
|
|
+
|
|
|
+/**
|
|
|
+ * Returns whether to display the "Reprocess Event" action.
|
|
|
+ *
|
|
|
+ * That is the case when we have a "reprocessable" event, which is an event that needs
|
|
|
+ * Debug Files for proper processing, as those Debug Files could have been uploaded *after*
|
|
|
+ * the Event was ingested.
|
|
|
+ */
|
|
|
+export function displayReprocessEventAction(event?: Event): boolean {
|
|
|
+ if (!event) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
-// Finds all frames in a given data blob and returns it's platforms
|
|
|
-function getPlatforms(exceptionValue: ExceptionValue | StacktraceType | null) {
|
|
|
- const frames = exceptionValue?.frames ?? [];
|
|
|
- const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
|
|
|
+ const eventPlatforms = getEventPlatform(event);
|
|
|
+ // Check Events from platforms that always use Debug Files as a fast-path
|
|
|
+ if (hasIntersection(eventPlatforms, DEBUG_FILE_PLATFORMS)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- if (!frames.length && !stacktraceFrames.length) {
|
|
|
- return [];
|
|
|
+ const hasDebugImages = (event?.entries ?? []).some(
|
|
|
+ entry => entry.type === EntryType.DEBUGMETA && entry.data.images.length > 0
|
|
|
+ );
|
|
|
+
|
|
|
+ // Otherwise, check alternative platforms if they actually have Debug Files
|
|
|
+ if (hasIntersection(eventPlatforms, MAYBE_DEBUG_FILE_PLATFORMS) && hasDebugImages) {
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- return [...frames, ...stacktraceFrames]
|
|
|
- .map(frame => frame.platform)
|
|
|
- .filter(platform => !!platform);
|
|
|
-}
|
|
|
+ // Finally, fall back to checking the `platform` of each frame
|
|
|
+ const exceptionEntry = event.entries.find(
|
|
|
+ entry => entry.type === EntryType.EXCEPTION
|
|
|
+ ) as EntryException | undefined;
|
|
|
+
|
|
|
+ if (!exceptionEntry) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
-function getStackTracePlatforms(event: Event, exceptionEntry: EntryException) {
|
|
|
- // Fetch platforms in stack traces of an exception entry
|
|
|
- const exceptionEntryPlatforms = (exceptionEntry.data.values ?? []).flatMap(
|
|
|
- getPlatforms
|
|
|
+ return hasIntersection(
|
|
|
+ getStackTracePlatforms(event, exceptionEntry),
|
|
|
+ DEBUG_FILE_PLATFORMS
|
|
|
);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Returns whether the two Sets have intersecting elements.
|
|
|
+ */
|
|
|
+function hasIntersection<T>(set1: Set<T>, set2: Set<T>): boolean {
|
|
|
+ for (const v of set1) {
|
|
|
+ if (set2.has(v)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Returns the event platform as a Set.
|
|
|
+ */
|
|
|
+function getEventPlatform(event: Event): Set<PlatformKey> {
|
|
|
+ const platforms = new Set<PlatformKey>();
|
|
|
+ addPlatforms(platforms, [event]);
|
|
|
+ return platforms;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Returns a Set of all platforms found in the `event` and `exceptionEntry`.
|
|
|
+ */
|
|
|
+function getStackTracePlatforms(
|
|
|
+ event: Event,
|
|
|
+ exceptionEntry: EntryException
|
|
|
+): Set<PlatformKey> {
|
|
|
+ const platforms = new Set<PlatformKey>();
|
|
|
+
|
|
|
+ // Add platforms in stack traces of an exception entry
|
|
|
+ (exceptionEntry.data.values ?? []).forEach(exc => addFramePlatforms(platforms, exc));
|
|
|
|
|
|
- // Fetch platforms in an exception entry
|
|
|
+ // Add platforms in a stack trace entry
|
|
|
const stackTraceEntry = (event.entries.find(
|
|
|
entry => entry.type === EntryType.STACKTRACE
|
|
|
)?.data ?? {}) as StacktraceType;
|
|
|
|
|
|
- // Fetch platforms in an exception entry
|
|
|
- const stackTraceEntryPlatforms = Object.keys(stackTraceEntry).flatMap(key =>
|
|
|
- getPlatforms(stackTraceEntry[key])
|
|
|
+ Object.keys(stackTraceEntry).forEach(key =>
|
|
|
+ addFramePlatforms(platforms, stackTraceEntry[key])
|
|
|
);
|
|
|
|
|
|
- // Fetch platforms in an thread entry
|
|
|
+ // Add platforms in a thread entry
|
|
|
const threadEntry = (event.entries.find(entry => entry.type === EntryType.THREADS)?.data
|
|
|
.values ?? []) as Array<Thread>;
|
|
|
|
|
|
- // Fetch platforms in a thread entry
|
|
|
- const threadEntryPlatforms = threadEntry.flatMap(({stacktrace}) =>
|
|
|
- getPlatforms(stacktrace)
|
|
|
- );
|
|
|
+ threadEntry.forEach(({stacktrace}) => addFramePlatforms(platforms, stacktrace));
|
|
|
|
|
|
- return new Set([
|
|
|
- ...exceptionEntryPlatforms,
|
|
|
- ...stackTraceEntryPlatforms,
|
|
|
- ...threadEntryPlatforms,
|
|
|
- ]);
|
|
|
+ return platforms;
|
|
|
}
|
|
|
|
|
|
-// Checks whether an event indicates that it is a native event.
|
|
|
-function isNativeEvent(event: Event, exceptionEntry: EntryException) {
|
|
|
- const {platform} = event;
|
|
|
-
|
|
|
- if (platform && NATIVE_PLATFORMS.includes(platform)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- const stackTracePlatforms = getStackTracePlatforms(event, exceptionEntry);
|
|
|
-
|
|
|
- return NATIVE_PLATFORMS.some(nativePlatform => stackTracePlatforms.has(nativePlatform));
|
|
|
-}
|
|
|
-
|
|
|
-// Checks whether an event indicates that it has an associated minidump.
|
|
|
-function isMinidumpEvent(exceptionEntry: EntryException) {
|
|
|
- const {data} = exceptionEntry;
|
|
|
- return (data.values ?? []).some(value => value.mechanism?.type === 'minidump');
|
|
|
-}
|
|
|
+/**
|
|
|
+ * Adds all the platforms in the frames of `exceptionValue` to the `platforms` Set.
|
|
|
+ */
|
|
|
+function addFramePlatforms(
|
|
|
+ platforms: Set<PlatformKey>,
|
|
|
+ exceptionValue: ExceptionValue | StacktraceType | null
|
|
|
+) {
|
|
|
+ const frames = exceptionValue?.frames ?? [];
|
|
|
+ const stacktraceFrames = (exceptionValue as ExceptionValue)?.stacktrace?.frames ?? [];
|
|
|
|
|
|
-// Checks whether an event indicates that it has an apple crash report.
|
|
|
-function isAppleCrashReportEvent(exceptionEntry: EntryException) {
|
|
|
- const {data} = exceptionEntry;
|
|
|
- return (data.values ?? []).some(value => value.mechanism?.type === 'applecrashreport');
|
|
|
+ addPlatforms(platforms, frames);
|
|
|
+ addPlatforms(platforms, stacktraceFrames);
|
|
|
}
|
|
|
|
|
|
-export function displayReprocessEventAction(orgFeatures: Array<string>, event?: Event) {
|
|
|
- if (!event || !orgFeatures.includes('reprocessing-v2')) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- const {entries} = event;
|
|
|
- const exceptionEntry = entries.find(entry => entry.type === EntryType.EXCEPTION) as
|
|
|
- | EntryException
|
|
|
- | undefined;
|
|
|
-
|
|
|
- if (!exceptionEntry) {
|
|
|
- return false;
|
|
|
+/**
|
|
|
+ * Adds all the `platform` properties found in `iter` to the `platforms` Set.
|
|
|
+ */
|
|
|
+function addPlatforms(
|
|
|
+ platforms: Set<PlatformKey>,
|
|
|
+ iter: Array<{platform?: PlatformKey | null}>
|
|
|
+) {
|
|
|
+ for (const o of iter) {
|
|
|
+ if (o.platform) {
|
|
|
+ platforms.add(o.platform);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- // We want to show the reprocessing button if the issue in question is native or contains native frames.
|
|
|
- // The logic is taken from the symbolication pipeline in Python, where it is used to determine whether reprocessing
|
|
|
- // payloads should be stored:
|
|
|
- // https://github.com/getsentry/sentry/blob/cb7baef414890336881d67b7a8433ee47198c701/src/sentry/lang/native/processing.py#L425-L426
|
|
|
- // It is still not ideal as one can always merge native and non-native events together into one issue,
|
|
|
- // but it's the best approximation we have.
|
|
|
- if (
|
|
|
- !isMinidumpEvent(exceptionEntry) &&
|
|
|
- !isAppleCrashReportEvent(exceptionEntry) &&
|
|
|
- !isNativeEvent(event, exceptionEntry)
|
|
|
- ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
}
|