Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref(stack-trace-interfaces): Add Native Stack Trace V2 - Part 2 - [INGEST-512] #29532

Merged
merged 42 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
da948ed
ref(crashContent): Move crash content related files to folder
priscilawebdev Oct 22, 2021
9d59c47
removed unused file
priscilawebdev Oct 22, 2021
29b19b5
tests
priscilawebdev Oct 22, 2021
5b71d34
tests2
priscilawebdev Oct 22, 2021
68d1ba1
ref(threads-interface): Add threads interface V2
priscilawebdev Oct 20, 2021
0509a0f
wip
priscilawebdev Oct 21, 2021
3db480f
added traceEventDataSection
priscilawebdev Oct 21, 2021
b0c2cea
revert test
priscilawebdev Oct 22, 2021
fcfb91a
removed repeat css
priscilawebdev Oct 22, 2021
aa9e41f
remove comment
priscilawebdev Oct 22, 2021
c9c75a0
remove test test file
priscilawebdev Oct 22, 2021
03e1ede
update thread selector
priscilawebdev Oct 22, 2021
5087bb2
fixed event section title for non thread
priscilawebdev Oct 22, 2021
57d2983
ref(crashContent): Move crash content related files to folder
priscilawebdev Oct 22, 2021
1466d81
ref(native-stack-trace): Add new design - part 2
priscilawebdev Oct 24, 2021
f90f098
fixed tests
priscilawebdev Oct 25, 2021
1d60855
fix css lint error
priscilawebdev Oct 25, 2021
bd300c1
fixed visual snapshot render error
priscilawebdev Oct 25, 2021
58d35e8
ref(interface-files): Move files to appropriate folder
priscilawebdev Oct 25, 2021
74b8cbc
ref(crashContent): Move crash content related files to folder
priscilawebdev Oct 22, 2021
6b8361f
removed unused file
priscilawebdev Oct 22, 2021
a9dc5f6
tests
priscilawebdev Oct 22, 2021
1710655
tests2
priscilawebdev Oct 22, 2021
2b9e6f3
ref(threads-interface): Add threads interface V2
priscilawebdev Oct 20, 2021
e65a69d
wip
priscilawebdev Oct 21, 2021
cc3e798
added traceEventDataSection
priscilawebdev Oct 21, 2021
e5f2033
revert test
priscilawebdev Oct 22, 2021
876b0f9
removed repeat css
priscilawebdev Oct 22, 2021
0bad552
remove comment
priscilawebdev Oct 22, 2021
5a06f24
remove test test file
priscilawebdev Oct 22, 2021
d32b8ed
update thread selector
priscilawebdev Oct 22, 2021
5aa142a
fixed event section title for non thread
priscilawebdev Oct 22, 2021
941fe23
Merge branch 'ref/add-threads-interface-new-version' into ref/add-thr…
priscilawebdev Oct 25, 2021
a70da97
fix lint issue
priscilawebdev Oct 25, 2021
e49fc6d
Merge branch 'ref/add-threads-interface-new-version' into ref/add-thr…
priscilawebdev Oct 25, 2021
2350935
fix import
priscilawebdev Oct 25, 2021
d731f32
fix lint error
priscilawebdev Oct 25, 2021
000d014
Merge branch 'master' into ref/add-threads-interface-new-version
priscilawebdev Oct 25, 2021
615e8c2
Merge branch 'ref/add-threads-interface-new-version' into ref/add-thr…
priscilawebdev Oct 25, 2021
2203616
Merge branch 'master' into ref/add-threads-interface-new-version
priscilawebdev Oct 27, 2021
2d1ec0d
Merge branch 'ref/add-threads-interface-new-version' into ref/add-thr…
priscilawebdev Oct 27, 2021
857320f
Merge branch 'master' into ref/add-threads-interface-new-version-part-2
priscilawebdev Oct 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion static/app/components/events/eventEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Csp from 'app/components/events/interfaces/csp';
import DebugMeta from 'app/components/events/interfaces/debugMeta';
import DebugMetaV2 from 'app/components/events/interfaces/debugMeta-v2';
import Exception from 'app/components/events/interfaces/exception';
import ExceptionV2 from 'app/components/events/interfaces/exceptionV2';
import Generic from 'app/components/events/interfaces/generic';
import Message from 'app/components/events/interfaces/message';
import Request from 'app/components/events/interfaces/request';
Expand Down Expand Up @@ -46,7 +47,16 @@ function EventEntry({
switch (entry.type) {
case EntryType.EXCEPTION: {
const {data, type} = entry;
return (
return hasNativeStackTraceV2 ? (
<ExceptionV2
type={type}
event={event}
data={data}
projectId={projectSlug}
groupingCurrentLevel={groupingCurrentLevel}
hasHierarchicalGrouping={hasHierarchicalGrouping}
/>
) : (
<Exception
type={type}
event={event}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,17 @@ class RawContent extends React.Component<Props, State> {
}

render() {
const {values} = this.props;
const {values, organization} = this.props;
const isNative = this.isNative();

if (!values) {
return null;
}

const hasNativeStackTraceV2 = !!organization?.features?.includes(
'native-stack-trace-v2'
);

return (
<React.Fragment>
{values.map((exc, excIdx) => {
Expand All @@ -168,7 +172,7 @@ class RawContent extends React.Component<Props, State> {
}
return (
<div key={excIdx}>
{downloadButton}
{!hasNativeStackTraceV2 ? downloadButton : null}
<pre className="traceback plain">{content}</pre>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {ExceptionValue, Group, PlatformType} from 'app/types';
import {Event} from 'app/types/event';
import {STACK_VIEW} from 'app/types/stacktrace';
import {defined} from 'app/utils';
import {useOrganization} from 'app/utils/useOrganization';
import EmptyMessage from 'app/views/settings/components/emptyMessage';

import StacktraceContent from '../stackTrace/content';
import StackTraceContent from '../stackTrace/content';
import StacktraceContentV2 from '../stackTrace/contentV2';
import StacktraceContentV3 from '../stackTrace/contentV3';

type Props = {
data: ExceptionValue['stacktrace'];
Expand All @@ -35,6 +37,8 @@ function StackTrace({
expandFirstFrame,
event,
}: Props) {
const organization = useOrganization();

if (!defined(stacktrace)) {
return null;
}
Expand Down Expand Up @@ -75,6 +79,20 @@ function StackTrace({
* It is easier to fix the UI logic to show a non-empty stack trace for chained exceptions
*/

if (!!organization.features?.includes('native-stack-trace-v2')) {
return (
<StacktraceContentV3
data={data}
expandFirstFrame={expandFirstFrame}
includeSystemFrames={includeSystemFrames}
groupingCurrentLevel={groupingCurrentLevel}
platform={platform}
newestFirst={newestFirst}
event={event}
/>
);
}

if (hasHierarchicalGrouping) {
return (
<StacktraceContentV2
Expand All @@ -90,7 +108,7 @@ function StackTrace({
}

return (
<StacktraceContent
<StackTraceContent
data={data}
expandFirstFrame={expandFirstFrame}
includeSystemFrames={includeSystemFrames}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import * as React from 'react';
import styled from '@emotion/styled';
import {PlatformIcon} from 'platformicons';

import Line from 'app/components/events/interfaces/frame/line';
import {
getImageRange,
parseAddress,
stackTracePlatformIcon,
} from 'app/components/events/interfaces/utils';
import {t} from 'app/locale';
import {Frame, Organization, PlatformType} from 'app/types';
import {Event} from 'app/types/event';
import {StacktraceType} from 'app/types/stacktrace';
import withOrganization from 'app/utils/withOrganization';

import Line from '../../frame/line';
import {getImageRange, parseAddress, stackTracePlatformIcon} from '../../utils';

const defaultProps = {
includeSystemFrames: true,
expandFirstFrame: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import {cloneElement, Fragment, MouseEvent, useState} from 'react';
import styled from '@emotion/styled';

import List from 'app/components/list';
import ListItem from 'app/components/list/listItem';
import {t} from 'app/locale';
import space from 'app/styles/space';
import {Frame, Group, PlatformType} from 'app/types';
import {Event} from 'app/types/event';
import {StacktraceType} from 'app/types/stacktrace';

import Line from '../../frame/lineV2';
import {getImageRange, parseAddress} from '../../utils';

type Props = {
data: StacktraceType;
platform: PlatformType;
event: Event;
groupingCurrentLevel?: Group['metadata']['current_level'];
newestFirst?: boolean;
className?: string;
isHoverPreviewed?: boolean;
includeSystemFrames?: boolean;
expandFirstFrame?: boolean;
};

function StackTraceContent({
data,
platform,
event,
newestFirst,
className,
isHoverPreviewed,
groupingCurrentLevel,
includeSystemFrames = true,
expandFirstFrame = true,
}: Props) {
const [showingAbsoluteAddresses, setShowingAbsoluteAddresses] = useState(false);
const [showCompleteFunctionName, setShowCompleteFunctionName] = useState(false);

const {frames = [], framesOmitted, registers} = data;

function findImageForAddress(
address: Frame['instructionAddr'],
addrMode: Frame['addrMode']
) {
const images = event.entries.find(entry => entry.type === 'debugmeta')?.data?.images;

return images && address
? images.find((img, idx) => {
if (!addrMode || addrMode === 'abs') {
const [startAddress, endAddress] = getImageRange(img);
return address >= (startAddress as any) && address < (endAddress as any);
}

return addrMode === `rel:${idx}`;
})
: null;
}

function getClassName() {
const classes = ['traceback'];

if (className) {
classes.push(className);
}

if (includeSystemFrames) {
return [...classes, 'full-traceback'].join(' ');
}

return [...classes, 'in-app-traceback'].join(' ');
}

function isFrameUsedForGrouping(frame: Frame) {
const {minGroupingLevel} = frame;

if (groupingCurrentLevel === undefined || minGroupingLevel === undefined) {
return false;
}

return minGroupingLevel <= groupingCurrentLevel;
}

function handleToggleAddresses(mouseEvent: MouseEvent<SVGElement>) {
mouseEvent.stopPropagation(); // to prevent collapsing if collapsible
setShowingAbsoluteAddresses(!showingAbsoluteAddresses);
}

function handleToggleFunctionName(mouseEvent: MouseEvent<SVGElement>) {
mouseEvent.stopPropagation(); // to prevent collapsing if collapsible
setShowCompleteFunctionName(!showCompleteFunctionName);
}

function getLastFrameIndex() {
const inAppFrameIndexes = frames
.map((frame, frameIndex) => {
if (frame.inApp) {
return frameIndex;
}
return undefined;
})
.filter(frame => frame !== undefined);

return !inAppFrameIndexes.length
? frames.length - 1
: inAppFrameIndexes[inAppFrameIndexes.length - 1];
}

function renderOmittedFrames(firstFrameOmitted: any, lastFrameOmitted: any) {
return (
<ListItem className="frame frames-omitted">
{t(
'Frames %d until %d were omitted and not available.',
firstFrameOmitted,
lastFrameOmitted
)}
</ListItem>
);
}

function renderConvertedFrames() {
const firstFrameOmitted = framesOmitted?.[0] ?? null;
const lastFrameOmitted = framesOmitted?.[1] ?? null;
const lastFrameIndex = getLastFrameIndex();

let nRepeats = 0;

const maxLengthOfAllRelativeAddresses = frames.reduce(
(maxLengthUntilThisPoint, frame) => {
const correspondingImage = findImageForAddress(
frame.instructionAddr,
frame.addrMode
);

try {
const relativeAddress = (
parseAddress(frame.instructionAddr) -
parseAddress(correspondingImage.image_addr)
).toString(16);

return maxLengthUntilThisPoint > relativeAddress.length
? maxLengthUntilThisPoint
: relativeAddress.length;
} catch {
return maxLengthUntilThisPoint;
}
},
0
);

const convertedFrames = frames
.map((frame, frameIndex) => {
const prevFrame = frames[frameIndex - 1];
const nextFrame = frames[frameIndex + 1];

const repeatedFrame =
nextFrame &&
frame.lineNo === nextFrame.lineNo &&
frame.instructionAddr === nextFrame.instructionAddr &&
frame.package === nextFrame.package &&
frame.module === nextFrame.module &&
frame.function === nextFrame.function;

if (repeatedFrame) {
nRepeats++;
}

const isUsedForGrouping = isFrameUsedForGrouping(frame);

const isVisible =
includeSystemFrames ||
frame.inApp ||
(nextFrame && nextFrame.inApp) ||
// the last non-app frame
(!frame.inApp && !nextFrame) ||
isUsedForGrouping;

if (isVisible && !repeatedFrame) {
const lineProps = {
event,
frame,
prevFrame,
nextFrame,
isExpanded: expandFirstFrame && lastFrameIndex === frameIndex,
emptySourceNotation: lastFrameIndex === frameIndex && frameIndex === 0,
platform,
timesRepeated: nRepeats,
showingAbsoluteAddress: showingAbsoluteAddresses,
onAddressToggle: handleToggleAddresses,
image: findImageForAddress(frame.instructionAddr, frame.addrMode),
maxLengthOfRelativeAddress: maxLengthOfAllRelativeAddresses,
registers: {},
includeSystemFrames,
onFunctionNameToggle: handleToggleFunctionName,
showCompleteFunctionName,
isHoverPreviewed,
isUsedForGrouping,
};

nRepeats = 0;

if (frameIndex === firstFrameOmitted) {
return (
<Fragment key={frameIndex}>
<Line {...lineProps} nativeV2 />
{renderOmittedFrames(firstFrameOmitted, lastFrameOmitted)}
</Fragment>
);
}

return <Line key={frameIndex} nativeV2 {...lineProps} />;
}

if (!repeatedFrame) {
nRepeats = 0;
}

if (frameIndex !== firstFrameOmitted) {
return null;
}

return renderOmittedFrames(firstFrameOmitted, lastFrameOmitted);
})
.filter(frame => !!frame) as React.ReactElement[];

if (convertedFrames.length > 0 && registers) {
const lastFrame = convertedFrames.length - 1;
convertedFrames[lastFrame] = cloneElement(convertedFrames[lastFrame], {
registers,
});

if (!newestFirst) {
return convertedFrames;
}

return [...convertedFrames].reverse();
}

if (!newestFirst) {
return convertedFrames;
}

return [...convertedFrames].reverse();
}

return <StyledList className={getClassName()}>{renderConvertedFrames()}</StyledList>;
}

export default StackTraceContent;

const StyledList = styled(List)`
grid-gap: 0;
position: relative;
overflow: hidden;
box-shadow: ${p => p.theme.dropShadowLight};

&& {
border-radius: ${p => p.theme.borderRadius};
border: 1px solid ${p => p.theme.gray200};
margin-bottom: ${space(3)};
}
`;
Loading