import {Fragment, useState} from 'react'; import styled from '@emotion/styled'; import isNil from 'lodash/isNil'; import Pill from 'sentry/components/pill'; import Pills from 'sentry/components/pills'; import {t} from 'sentry/locale'; import { Event, Frame, PlatformType, Project, STACK_TYPE, STACK_VIEW, Thread, } from 'sentry/types'; import {TraceEventDataSection} from '../traceEventDataSection'; import Exception from './crashContent/exception'; import StackTrace from './crashContent/stackTrace'; import ThreadSelector from './threads/threadSelector'; import findBestThread from './threads/threadSelector/findBestThread'; import getThreadException from './threads/threadSelector/getThreadException'; import getThreadStacktrace from './threads/threadSelector/getThreadStacktrace'; import NoStackTraceMessage from './noStackTraceMessage'; import {isStacktraceNewestFirst} from './utils'; type ExceptionProps = React.ComponentProps; type Props = Pick & { data: { values?: Array; }; event: Event; projectId: Project['id']; type: string; }; type State = { activeThread?: Thread; }; function getIntendedStackView(thread: Thread, event: Event) { const exception = getThreadException(event, thread); if (exception) { return !!exception.values.find(value => !!value.stacktrace?.hasSystemFrames) ? STACK_VIEW.APP : STACK_VIEW.FULL; } const stacktrace = getThreadStacktrace(false, thread); return stacktrace?.hasSystemFrames ? STACK_VIEW.APP : STACK_VIEW.FULL; } function Threads({ data, event, projectId, type, hasHierarchicalGrouping, groupingCurrentLevel, }: Props) { const threads = data.values ?? []; const [state, setState] = useState(() => { const thread = !!threads.length ? findBestThread(threads) : undefined; return {activeThread: thread}; }); const stackTraceNotFound = !threads.length; const {activeThread} = state; const hasMoreThanOneThread = threads.length > 1; const exception = getThreadException(event, activeThread); const stackView = activeThread ? getIntendedStackView(activeThread, event) : undefined; function getPlatform(): PlatformType { let exceptionFramePlatform: Frame | undefined = undefined; for (const value of exception?.values ?? []) { exceptionFramePlatform = value.stacktrace?.frames?.find(frame => !!frame.platform); if (exceptionFramePlatform) { break; } } if (exceptionFramePlatform?.platform) { return exceptionFramePlatform.platform; } const threadFramePlatform = activeThread?.stacktrace?.frames?.find( frame => !!frame.platform ); if (threadFramePlatform?.platform) { return threadFramePlatform.platform; } return event.platform ?? 'other'; } function renderPills() { const {id, name, current, crashed} = activeThread ?? {}; if (isNil(id) || !name) { return null; } return ( {!isNil(id) && } {!!name?.trim() && } {current !== undefined && } {crashed !== undefined && ( {crashed ? t('yes') : t('no')} )} ); } function renderContent({ display, recentFirst, fullStackTrace, }: Parameters['children']>[0]) { const stackType = display.includes('minified') ? STACK_TYPE.MINIFIED : STACK_TYPE.ORIGINAL; if (exception) { return ( ); } const stackTrace = getThreadStacktrace( stackType !== STACK_TYPE.ORIGINAL, activeThread ); if (stackTrace) { return ( ); } return ( ); } function getTitle() { if (hasMoreThanOneThread && activeThread) { return ( { setState({ ...state, activeThread: thread, }); }} exception={exception} fullWidth /> ); } return {t('Stack Trace')}; } const platform = getPlatform(); return ( value.rawStacktrace) || !!activeThread?.rawStacktrace } hasVerboseFunctionNames={ !!exception?.values?.some( value => !!value.stacktrace?.frames?.some( frame => !!frame.rawFunction && !!frame.function && frame.rawFunction !== frame.function ) ) || !!activeThread?.stacktrace?.frames?.some( frame => !!frame.rawFunction && !!frame.function && frame.rawFunction !== frame.function ) } hasAbsoluteFilePaths={ !!exception?.values?.some( value => !!value.stacktrace?.frames?.some(frame => !!frame.filename) ) || !!activeThread?.stacktrace?.frames?.some(frame => !!frame.filename) } hasAbsoluteAddresses={ !!exception?.values?.some( value => !!value.stacktrace?.frames?.some(frame => !!frame.instructionAddr) ) || !!activeThread?.stacktrace?.frames?.some(frame => !!frame.instructionAddr) } hasAppOnlyFrames={ !!exception?.values?.some( value => !!value.stacktrace?.frames?.some(frame => frame.inApp !== true) ) || !!activeThread?.stacktrace?.frames?.some(frame => frame.inApp !== true) } hasNewestFirst={ !!exception?.values?.some(value => (value.stacktrace?.frames ?? []).length > 1) || (activeThread?.stacktrace?.frames ?? []).length > 1 } stackTraceNotFound={stackTraceNotFound} wrapTitle={false} > {childrenProps => ( {renderPills()} {renderContent(childrenProps)} )} ); } export default Threads; const Title = styled('h3')` margin-bottom: 0; `;