threadsV2.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import {Fragment, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import isNil from 'lodash/isNil';
  4. import Pill from 'sentry/components/pill';
  5. import Pills from 'sentry/components/pills';
  6. import {t} from 'sentry/locale';
  7. import {
  8. Event,
  9. Frame,
  10. PlatformType,
  11. Project,
  12. STACK_TYPE,
  13. STACK_VIEW,
  14. Thread,
  15. } from 'sentry/types';
  16. import {TraceEventDataSection} from '../traceEventDataSection';
  17. import Exception from './crashContent/exception';
  18. import StackTrace from './crashContent/stackTrace';
  19. import ThreadSelector from './threads/threadSelector';
  20. import findBestThread from './threads/threadSelector/findBestThread';
  21. import getThreadException from './threads/threadSelector/getThreadException';
  22. import getThreadStacktrace from './threads/threadSelector/getThreadStacktrace';
  23. import NoStackTraceMessage from './noStackTraceMessage';
  24. import {isStacktraceNewestFirst} from './utils';
  25. type ExceptionProps = React.ComponentProps<typeof Exception>;
  26. type Props = Pick<ExceptionProps, 'groupingCurrentLevel' | 'hasHierarchicalGrouping'> & {
  27. data: {
  28. values?: Array<Thread>;
  29. };
  30. event: Event;
  31. projectId: Project['id'];
  32. type: string;
  33. };
  34. type State = {
  35. activeThread?: Thread;
  36. };
  37. function getIntendedStackView(thread: Thread, event: Event) {
  38. const exception = getThreadException(event, thread);
  39. if (exception) {
  40. return !!exception.values.find(value => !!value.stacktrace?.hasSystemFrames)
  41. ? STACK_VIEW.APP
  42. : STACK_VIEW.FULL;
  43. }
  44. const stacktrace = getThreadStacktrace(false, thread);
  45. return stacktrace?.hasSystemFrames ? STACK_VIEW.APP : STACK_VIEW.FULL;
  46. }
  47. function Threads({
  48. data,
  49. event,
  50. projectId,
  51. type,
  52. hasHierarchicalGrouping,
  53. groupingCurrentLevel,
  54. }: Props) {
  55. const threads = data.values ?? [];
  56. const [state, setState] = useState<State>(() => {
  57. const thread = !!threads.length ? findBestThread(threads) : undefined;
  58. return {activeThread: thread};
  59. });
  60. const stackTraceNotFound = !threads.length;
  61. const {activeThread} = state;
  62. const hasMoreThanOneThread = threads.length > 1;
  63. const exception = getThreadException(event, activeThread);
  64. const stackView = activeThread ? getIntendedStackView(activeThread, event) : undefined;
  65. function getPlatform(): PlatformType {
  66. let exceptionFramePlatform: Frame | undefined = undefined;
  67. for (const value of exception?.values ?? []) {
  68. exceptionFramePlatform = value.stacktrace?.frames?.find(frame => !!frame.platform);
  69. if (exceptionFramePlatform) {
  70. break;
  71. }
  72. }
  73. if (exceptionFramePlatform?.platform) {
  74. return exceptionFramePlatform.platform;
  75. }
  76. const threadFramePlatform = activeThread?.stacktrace?.frames?.find(
  77. frame => !!frame.platform
  78. );
  79. if (threadFramePlatform?.platform) {
  80. return threadFramePlatform.platform;
  81. }
  82. return event.platform ?? 'other';
  83. }
  84. function renderPills() {
  85. const {id, name, current, crashed} = activeThread ?? {};
  86. if (isNil(id) || !name) {
  87. return null;
  88. }
  89. return (
  90. <Pills>
  91. {!isNil(id) && <Pill name={t('id')} value={String(id)} />}
  92. {!!name?.trim() && <Pill name={t('name')} value={name} />}
  93. {current !== undefined && <Pill name={t('was active')} value={current} />}
  94. {crashed !== undefined && (
  95. <Pill name={t('errored')} className={crashed ? 'false' : 'true'}>
  96. {crashed ? t('yes') : t('no')}
  97. </Pill>
  98. )}
  99. </Pills>
  100. );
  101. }
  102. function renderContent({
  103. display,
  104. recentFirst,
  105. fullStackTrace,
  106. }: Parameters<React.ComponentProps<typeof TraceEventDataSection>['children']>[0]) {
  107. const stackType = display.includes('minified')
  108. ? STACK_TYPE.MINIFIED
  109. : STACK_TYPE.ORIGINAL;
  110. if (exception) {
  111. return (
  112. <Exception
  113. stackType={stackType}
  114. stackView={
  115. display.includes('raw-stack-trace')
  116. ? STACK_VIEW.RAW
  117. : fullStackTrace
  118. ? STACK_VIEW.FULL
  119. : STACK_VIEW.APP
  120. }
  121. projectId={projectId}
  122. newestFirst={recentFirst}
  123. event={event}
  124. platform={platform}
  125. values={exception.values}
  126. groupingCurrentLevel={groupingCurrentLevel}
  127. hasHierarchicalGrouping={hasHierarchicalGrouping}
  128. />
  129. );
  130. }
  131. const stackTrace = getThreadStacktrace(
  132. stackType !== STACK_TYPE.ORIGINAL,
  133. activeThread
  134. );
  135. if (stackTrace) {
  136. return (
  137. <StackTrace
  138. stacktrace={stackTrace}
  139. stackView={
  140. display.includes('raw-stack-trace')
  141. ? STACK_VIEW.RAW
  142. : fullStackTrace
  143. ? STACK_VIEW.FULL
  144. : STACK_VIEW.APP
  145. }
  146. newestFirst={recentFirst}
  147. event={event}
  148. platform={platform}
  149. groupingCurrentLevel={groupingCurrentLevel}
  150. hasHierarchicalGrouping={hasHierarchicalGrouping}
  151. nativeV2
  152. />
  153. );
  154. }
  155. return (
  156. <NoStackTraceMessage
  157. message={activeThread?.crashed ? t('Thread Errored') : undefined}
  158. />
  159. );
  160. }
  161. function getTitle() {
  162. if (hasMoreThanOneThread && activeThread) {
  163. return (
  164. <ThreadSelector
  165. threads={threads}
  166. activeThread={activeThread}
  167. event={event}
  168. onChange={thread => {
  169. setState({
  170. ...state,
  171. activeThread: thread,
  172. });
  173. }}
  174. exception={exception}
  175. fullWidth
  176. />
  177. );
  178. }
  179. return <Title>{t('Stack Trace')}</Title>;
  180. }
  181. const platform = getPlatform();
  182. return (
  183. <TraceEventDataSection
  184. type={type}
  185. stackType={STACK_TYPE.ORIGINAL}
  186. projectId={projectId}
  187. eventId={event.id}
  188. recentFirst={isStacktraceNewestFirst()}
  189. fullStackTrace={stackView === STACK_VIEW.FULL}
  190. title={getTitle()}
  191. platform={platform}
  192. showPermalink={!hasMoreThanOneThread}
  193. hasMinified={
  194. !!exception?.values?.find(value => value.rawStacktrace) ||
  195. !!activeThread?.rawStacktrace
  196. }
  197. hasVerboseFunctionNames={
  198. !!exception?.values?.some(
  199. value =>
  200. !!value.stacktrace?.frames?.some(
  201. frame =>
  202. !!frame.rawFunction &&
  203. !!frame.function &&
  204. frame.rawFunction !== frame.function
  205. )
  206. ) ||
  207. !!activeThread?.stacktrace?.frames?.some(
  208. frame =>
  209. !!frame.rawFunction &&
  210. !!frame.function &&
  211. frame.rawFunction !== frame.function
  212. )
  213. }
  214. hasAbsoluteFilePaths={
  215. !!exception?.values?.some(
  216. value => !!value.stacktrace?.frames?.some(frame => !!frame.filename)
  217. ) || !!activeThread?.stacktrace?.frames?.some(frame => !!frame.filename)
  218. }
  219. hasAbsoluteAddresses={
  220. !!exception?.values?.some(
  221. value => !!value.stacktrace?.frames?.some(frame => !!frame.instructionAddr)
  222. ) || !!activeThread?.stacktrace?.frames?.some(frame => !!frame.instructionAddr)
  223. }
  224. hasAppOnlyFrames={
  225. !!exception?.values?.some(
  226. value => !!value.stacktrace?.frames?.some(frame => frame.inApp !== true)
  227. ) || !!activeThread?.stacktrace?.frames?.some(frame => frame.inApp !== true)
  228. }
  229. hasNewestFirst={
  230. !!exception?.values?.some(value => (value.stacktrace?.frames ?? []).length > 1) ||
  231. (activeThread?.stacktrace?.frames ?? []).length > 1
  232. }
  233. stackTraceNotFound={stackTraceNotFound}
  234. wrapTitle={false}
  235. >
  236. {childrenProps => (
  237. <Fragment>
  238. {renderPills()}
  239. {renderContent(childrenProps)}
  240. </Fragment>
  241. )}
  242. </TraceEventDataSection>
  243. );
  244. }
  245. export default Threads;
  246. const Title = styled('h3')`
  247. margin-bottom: 0;
  248. `;