utils.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import {IconQuestion, IconWarning} from 'sentry/icons';
  2. import {t} from 'sentry/locale';
  3. import type {Event, Frame, PlatformKey} from 'sentry/types';
  4. import {EventOrGroupType} from 'sentry/types';
  5. import {defined, objectIsEmpty} from 'sentry/utils';
  6. import {SymbolicatorStatus} from '../types';
  7. export function trimPackage(pkg: string) {
  8. const pieces = pkg.split(/^([a-z]:\\|\\\\)/i.test(pkg) ? '\\' : '/');
  9. const filename = pieces[pieces.length - 1] || pieces[pieces.length - 2] || pkg;
  10. return filename.replace(/\.(dylib|so|a|dll|exe)$/, '');
  11. }
  12. export function getPlatform(dataPlatform: PlatformKey | null, platform: string) {
  13. // prioritize the frame platform but fall back to the platform
  14. // of the stack trace / exception
  15. return dataPlatform || platform;
  16. }
  17. export function getFrameHint(frame: Frame) {
  18. // returning [hintText, hintIcon]
  19. const {symbolicatorStatus} = frame;
  20. const func = frame.function || '<unknown>';
  21. // Custom color used to match adjacent text.
  22. const warningIcon = <IconQuestion size="xs" color={'#2c45a8' as any} />;
  23. const errorIcon = <IconWarning size="xs" color="red300" />;
  24. if (func.match(/^@objc\s/)) {
  25. return [t('Objective-C -> Swift shim frame'), warningIcon];
  26. }
  27. if (func.match(/^__?hidden#\d+/)) {
  28. return [t('Hidden function from bitcode build'), errorIcon];
  29. }
  30. if (!symbolicatorStatus && func === '<unknown>') {
  31. // Only render this if the event was not symbolicated.
  32. return [t('No function name was supplied by the client SDK.'), warningIcon];
  33. }
  34. if (
  35. func === '<unknown>' ||
  36. (func === '<redacted>' && symbolicatorStatus === SymbolicatorStatus.MISSING_SYMBOL)
  37. ) {
  38. switch (symbolicatorStatus) {
  39. case SymbolicatorStatus.MISSING_SYMBOL:
  40. return [t('The symbol was not found within the debug file.'), warningIcon];
  41. case SymbolicatorStatus.UNKNOWN_IMAGE:
  42. return [t('No image is specified for the address of the frame.'), warningIcon];
  43. case SymbolicatorStatus.MISSING:
  44. return [
  45. t('The debug file could not be retrieved from any of the sources.'),
  46. errorIcon,
  47. ];
  48. case SymbolicatorStatus.MALFORMED:
  49. return [t('The retrieved debug file could not be processed.'), errorIcon];
  50. default:
  51. }
  52. }
  53. if (func === '<redacted>') {
  54. return [t('Unknown system frame. Usually from beta SDKs'), warningIcon];
  55. }
  56. return [null, null];
  57. }
  58. export function isDotnet(platform: string) {
  59. // csharp platform represents .NET and can be F#, VB or any language targeting CLS (the Common Language Specification)
  60. return platform === 'csharp';
  61. }
  62. export function hasContextSource(frame: Frame) {
  63. return defined(frame.context) && !!frame.context.length;
  64. }
  65. export function hasContextVars(frame: Frame) {
  66. return !objectIsEmpty(frame.vars || {});
  67. }
  68. export function hasContextRegisters(registers: Record<string, string>) {
  69. return !objectIsEmpty(registers);
  70. }
  71. export function hasAssembly(frame: Frame, platform?: string) {
  72. return (
  73. isDotnet(getPlatform(frame.platform, platform ?? 'other')) && defined(frame.package)
  74. );
  75. }
  76. export function isExpandable({
  77. frame,
  78. registers,
  79. emptySourceNotation,
  80. platform,
  81. isOnlyFrame,
  82. }: {
  83. frame: Frame;
  84. registers: Record<string, string>;
  85. emptySourceNotation?: boolean;
  86. isOnlyFrame?: boolean;
  87. platform?: string;
  88. }) {
  89. return (
  90. (!isOnlyFrame && emptySourceNotation) ||
  91. hasContextSource(frame) ||
  92. hasContextVars(frame) ||
  93. hasContextRegisters(registers) ||
  94. hasAssembly(frame, platform)
  95. );
  96. }
  97. export function getLeadHint({
  98. event,
  99. hasNextFrame,
  100. }: {
  101. event: Event;
  102. hasNextFrame: boolean;
  103. }) {
  104. if (hasNextFrame) {
  105. return t('Called from');
  106. }
  107. switch (event.type) {
  108. case EventOrGroupType.ERROR:
  109. // ANRs/AppHangs are errors, but not crashes, so "Crashed in non-app" might be confusing as if
  110. // there was a crash prior to ANR, hence special-casing them
  111. return isAnrEvent(event) ? t('Occurred in non-app') : t('Crashed in non-app');
  112. default:
  113. return t('Occurred in non-app');
  114. }
  115. }
  116. function isAnrEvent(event: Event) {
  117. const mechanismTag = event.tags?.find(({key}) => key === 'mechanism')?.value;
  118. const isANR =
  119. mechanismTag === 'ANR' ||
  120. mechanismTag === 'AppExitInfo' ||
  121. mechanismTag === 'AppHang' ||
  122. mechanismTag === 'mx_hang_diagnostic';
  123. return isANR;
  124. }
  125. export function hasFileExtension(filepath: string) {
  126. // Regular expression to match a file extension
  127. const fileExtensionPattern = /\.[0-9a-z]+$/i;
  128. // Check if the filepath matches the pattern
  129. return fileExtensionPattern.test(filepath);
  130. }