Browse Source

feat(dart): add dart raw stacktrace representation (#74715)

For raw stacktraces we don't have a Dart stacktrace representation and
we currently fall back to using `getPythonFrame`.

Related issue: https://github.com/getsentry/sentry-dart/issues/2040
Giancarlo Buenaflor 7 months ago
parent
commit
6de2850d94

+ 39 - 0
static/app/components/events/interfaces/crashContent/stackTrace/rawContent.spec.tsx

@@ -122,6 +122,45 @@ describe('RawStacktraceContent', () => {
       );
     });
 
+    it('renders dart example', () => {
+      const dartData: StacktraceType = {
+        hasSystemFrames: false,
+        framesOmitted: null,
+        registers: {},
+        frames: [
+          FrameFixture({
+            function: 'doThing',
+            package: 'flutter',
+            lineNo: 300,
+            colNo: 2,
+            filename: 'ink_well.dart',
+            absPath: 'package:flutter/src/material/ink_well.dart',
+            platform: undefined,
+          }),
+          FrameFixture({
+            function: '<asynchronous suspension>',
+            package: '<asynchronous suspension>',
+            platform: undefined,
+          }),
+          FrameFixture({
+            function: 'main',
+            package: 'sentry_flutter',
+            lineNo: 778,
+            colNo: 5,
+            filename: 'main.dart',
+            absPath: 'package:sentry_flutter/main.dart',
+            platform: undefined,
+          }),
+        ],
+      };
+      expect(displayRawContent(dartData, 'dart', exception)).toEqual(
+        `Error: an error occurred
+  #0      main (package:sentry_flutter/main.dart:778:5)
+  #1      <asynchronous suspension>
+  #2      doThing (package:flutter/src/material/ink_well.dart:300:2)`
+      );
+    });
+
     const inAppFrame = (fnName, line) =>
       FrameFixture({
         function: fnName,

+ 36 - 2
static/app/components/events/interfaces/crashContent/stackTrace/rawContent.tsx

@@ -98,6 +98,33 @@ export function getJavaFrame(frame: Frame): string {
   return result;
 }
 
+export function getDartFrame(frame: Frame, frameIdxFromEnd: number): string {
+  let result = `  #${frameIdxFromEnd}`;
+
+  if (frame.function === '<asynchronous suspension>') {
+    return `${result}      ${frame.function}`;
+  }
+
+  if (defined(frame.function)) {
+    result += '      ' + frame.function;
+  }
+  if (defined(frame.absPath)) {
+    result += ' (';
+
+    result += frame.absPath;
+    if (defined(frame.lineNo) && frame.lineNo >= 0) {
+      result += ':' + frame.lineNo;
+    }
+    if (defined(frame.colNo) && frame.colNo >= 0) {
+      result += ':' + frame.colNo;
+    }
+
+    result += ')';
+  }
+
+  return result;
+}
+
 function ljust(str: string, len: number) {
   return str + Array(Math.max(0, len - str.length) + 1).join(' ');
 }
@@ -138,7 +165,12 @@ function getPreamble(exception: ExceptionValue, platform: string | undefined): s
   }
 }
 
-function getFrame(frame: Frame, frameIdx: number, platform: string | undefined): string {
+function getFrame(
+  frame: Frame,
+  frameIdx: number,
+  frameIdxFromEnd: number,
+  platform: string | undefined
+): string {
   if (frame.platform) {
     platform = frame.platform;
   }
@@ -153,6 +185,8 @@ function getFrame(frame: Frame, frameIdx: number, platform: string | undefined):
       return getPythonFrame(frame);
     case 'java':
       return getJavaFrame(frame);
+    case 'dart':
+      return getDartFrame(frame, frameIdxFromEnd);
     case 'objc':
     // fallthrough
     case 'cocoa':
@@ -180,7 +214,7 @@ export default function displayRawContent(
     : rawFrames;
 
   const frames = framesToUse.map((frame, frameIdx) =>
-    getFrame(frame, frameIdx, platform)
+    getFrame(frame, frameIdx, framesToUse.length - frameIdx - 1, platform)
   );
 
   if (platform !== 'python') {