|
@@ -18,14 +18,30 @@ type Props = {
|
|
platform: PlatformType;
|
|
platform: PlatformType;
|
|
expandFirstFrame?: boolean;
|
|
expandFirstFrame?: boolean;
|
|
groupingCurrentLevel?: Group['metadata']['current_level'];
|
|
groupingCurrentLevel?: Group['metadata']['current_level'];
|
|
|
|
+ hiddenFrameCount?: number;
|
|
includeSystemFrames?: boolean;
|
|
includeSystemFrames?: boolean;
|
|
inlined?: boolean;
|
|
inlined?: boolean;
|
|
isHoverPreviewed?: boolean;
|
|
isHoverPreviewed?: boolean;
|
|
|
|
+ isShowFramesToggleExpanded?: boolean;
|
|
|
|
+ isSubFrame?: boolean;
|
|
maxDepth?: number;
|
|
maxDepth?: number;
|
|
meta?: Record<any, any>;
|
|
meta?: Record<any, any>;
|
|
newestFirst?: boolean;
|
|
newestFirst?: boolean;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+function isRepeatedFrame(frame: Frame, nextFrame?: Frame) {
|
|
|
|
+ if (!nextFrame) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return (
|
|
|
|
+ frame.lineNo === nextFrame.lineNo &&
|
|
|
|
+ frame.instructionAddr === nextFrame.instructionAddr &&
|
|
|
|
+ frame.package === nextFrame.package &&
|
|
|
|
+ frame.module === nextFrame.module &&
|
|
|
|
+ frame.function === nextFrame.function
|
|
|
|
+ );
|
|
|
|
+}
|
|
|
|
+
|
|
export function NativeContent({
|
|
export function NativeContent({
|
|
data,
|
|
data,
|
|
platform,
|
|
platform,
|
|
@@ -41,9 +57,86 @@ export function NativeContent({
|
|
}: Props) {
|
|
}: Props) {
|
|
const [showingAbsoluteAddresses, setShowingAbsoluteAddresses] = useState(false);
|
|
const [showingAbsoluteAddresses, setShowingAbsoluteAddresses] = useState(false);
|
|
const [showCompleteFunctionName, setShowCompleteFunctionName] = useState(false);
|
|
const [showCompleteFunctionName, setShowCompleteFunctionName] = useState(false);
|
|
|
|
+ const [toggleFrameMap, setToggleFrameMap] = useState(setInitialFrameMap());
|
|
|
|
|
|
const {frames = [], framesOmitted, registers} = data;
|
|
const {frames = [], framesOmitted, registers} = data;
|
|
|
|
|
|
|
|
+ function frameIsVisible(frame: Frame, nextFrame: Frame) {
|
|
|
|
+ return (
|
|
|
|
+ includeSystemFrames ||
|
|
|
|
+ frame.inApp ||
|
|
|
|
+ (nextFrame && nextFrame.inApp) ||
|
|
|
|
+ // the last non-app frame
|
|
|
|
+ (!frame.inApp && !nextFrame) ||
|
|
|
|
+ isFrameUsedForGrouping(frame)
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function setInitialFrameMap(): {[frameIndex: number]: boolean} {
|
|
|
|
+ const indexMap = {};
|
|
|
|
+ (data.frames ?? []).forEach((frame, frameIdx) => {
|
|
|
|
+ const nextFrame = (data.frames ?? [])[frameIdx + 1];
|
|
|
|
+ const repeatedFrame = isRepeatedFrame(frame, nextFrame);
|
|
|
|
+ if (frameIsVisible(frame, nextFrame) && !repeatedFrame && !frame.inApp) {
|
|
|
|
+ indexMap[frameIdx] = false;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return indexMap;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getInitialFrameCounts(): {[frameIndex: number]: number} {
|
|
|
|
+ let count = 0;
|
|
|
|
+ const countMap = {};
|
|
|
|
+ (data.frames ?? []).forEach((frame, frameIdx) => {
|
|
|
|
+ const nextFrame = (data.frames ?? [])[frameIdx + 1];
|
|
|
|
+ const repeatedFrame = isRepeatedFrame(frame, nextFrame);
|
|
|
|
+ if (frameIsVisible(frame, nextFrame) && !repeatedFrame && !frame.inApp) {
|
|
|
|
+ countMap[frameIdx] = count;
|
|
|
|
+ count = 0;
|
|
|
|
+ } else {
|
|
|
|
+ if (!repeatedFrame && !frame.inApp) {
|
|
|
|
+ count += 1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return countMap;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getRepeatedFrameIndices() {
|
|
|
|
+ const repeats: number[] = [];
|
|
|
|
+ (data.frames ?? []).forEach((frame, frameIdx) => {
|
|
|
|
+ const nextFrame = (data.frames ?? [])[frameIdx + 1];
|
|
|
|
+ const repeatedFrame = isRepeatedFrame(frame, nextFrame);
|
|
|
|
+
|
|
|
|
+ if (repeatedFrame) {
|
|
|
|
+ repeats.push(frameIdx);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return repeats;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getHiddenFrameIndices(frameCountMap: {[frameIndex: number]: number}) {
|
|
|
|
+ const repeatedIndeces = getRepeatedFrameIndices();
|
|
|
|
+ let hiddenFrameIndices: number[] = [];
|
|
|
|
+ Object.keys(toggleFrameMap)
|
|
|
|
+ .filter(frameIndex => toggleFrameMap[frameIndex] === true)
|
|
|
|
+ .forEach(indexString => {
|
|
|
|
+ const index = parseInt(indexString, 10);
|
|
|
|
+ const indicesToBeAdded: number[] = [];
|
|
|
|
+ let i = 1;
|
|
|
|
+ let numHidden = frameCountMap[index];
|
|
|
|
+ while (numHidden > 0) {
|
|
|
|
+ if (!repeatedIndeces.includes(index - i)) {
|
|
|
|
+ indicesToBeAdded.push(index - i);
|
|
|
|
+ numHidden -= 1;
|
|
|
|
+ }
|
|
|
|
+ i += 1;
|
|
|
|
+ }
|
|
|
|
+ hiddenFrameIndices = [...hiddenFrameIndices, ...indicesToBeAdded];
|
|
|
|
+ });
|
|
|
|
+ return hiddenFrameIndices;
|
|
|
|
+ }
|
|
|
|
+
|
|
function findImageForAddress(
|
|
function findImageForAddress(
|
|
address: Frame['instructionAddr'],
|
|
address: Frame['instructionAddr'],
|
|
addrMode: Frame['addrMode']
|
|
addrMode: Frame['addrMode']
|
|
@@ -86,6 +179,18 @@ export function NativeContent({
|
|
setShowCompleteFunctionName(!showCompleteFunctionName);
|
|
setShowCompleteFunctionName(!showCompleteFunctionName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ const handleToggleFrames = (
|
|
|
|
+ mouseEvent: React.MouseEvent<HTMLElement>,
|
|
|
|
+ frameIndex: number
|
|
|
|
+ ) => {
|
|
|
|
+ mouseEvent.stopPropagation(); // to prevent toggling frame context
|
|
|
|
+
|
|
|
|
+ setToggleFrameMap(prevState => ({
|
|
|
|
+ ...prevState,
|
|
|
|
+ [frameIndex]: !prevState[frameIndex],
|
|
|
|
+ }));
|
|
|
|
+ };
|
|
|
|
+
|
|
function getLastFrameIndex() {
|
|
function getLastFrameIndex() {
|
|
const inAppFrameIndexes = frames
|
|
const inAppFrameIndexes = frames
|
|
.map((frame, frameIndex) => {
|
|
.map((frame, frameIndex) => {
|
|
@@ -112,6 +217,8 @@ export function NativeContent({
|
|
const firstFrameOmitted = framesOmitted?.[0] ?? null;
|
|
const firstFrameOmitted = framesOmitted?.[0] ?? null;
|
|
const lastFrameOmitted = framesOmitted?.[1] ?? null;
|
|
const lastFrameOmitted = framesOmitted?.[1] ?? null;
|
|
const lastFrameIndex = getLastFrameIndex();
|
|
const lastFrameIndex = getLastFrameIndex();
|
|
|
|
+ const frameCountMap = getInitialFrameCounts();
|
|
|
|
+ const hiddenFrameIndices: number[] = getHiddenFrameIndices(frameCountMap);
|
|
|
|
|
|
let nRepeats = 0;
|
|
let nRepeats = 0;
|
|
|
|
|
|
@@ -142,14 +249,7 @@ export function NativeContent({
|
|
.map((frame, frameIndex) => {
|
|
.map((frame, frameIndex) => {
|
|
const prevFrame = frames[frameIndex - 1];
|
|
const prevFrame = frames[frameIndex - 1];
|
|
const nextFrame = 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;
|
|
|
|
|
|
+ const repeatedFrame = isRepeatedFrame(frame, nextFrame);
|
|
|
|
|
|
if (repeatedFrame) {
|
|
if (repeatedFrame) {
|
|
nRepeats++;
|
|
nRepeats++;
|
|
@@ -157,15 +257,10 @@ export function NativeContent({
|
|
|
|
|
|
const isUsedForGrouping = isFrameUsedForGrouping(frame);
|
|
const isUsedForGrouping = isFrameUsedForGrouping(frame);
|
|
|
|
|
|
- const isVisible =
|
|
|
|
- includeSystemFrames ||
|
|
|
|
- frame.inApp ||
|
|
|
|
- (nextFrame && nextFrame.inApp) ||
|
|
|
|
- // the last non-app frame
|
|
|
|
- (!frame.inApp && !nextFrame) ||
|
|
|
|
- isUsedForGrouping;
|
|
|
|
-
|
|
|
|
- if (isVisible && !repeatedFrame) {
|
|
|
|
|
|
+ if (
|
|
|
|
+ (frameIsVisible(frame, nextFrame) && !repeatedFrame) ||
|
|
|
|
+ hiddenFrameIndices.includes(frameIndex)
|
|
|
|
+ ) {
|
|
const frameProps = {
|
|
const frameProps = {
|
|
event,
|
|
event,
|
|
frame,
|
|
frame,
|
|
@@ -179,13 +274,19 @@ export function NativeContent({
|
|
timesRepeated: nRepeats,
|
|
timesRepeated: nRepeats,
|
|
showingAbsoluteAddress: showingAbsoluteAddresses,
|
|
showingAbsoluteAddress: showingAbsoluteAddresses,
|
|
onAddressToggle: handleToggleAddresses,
|
|
onAddressToggle: handleToggleAddresses,
|
|
|
|
+ onShowFramesToggle: (e: React.MouseEvent<HTMLElement>) => {
|
|
|
|
+ handleToggleFrames(e, frameIndex);
|
|
|
|
+ },
|
|
image: findImageForAddress(frame.instructionAddr, frame.addrMode),
|
|
image: findImageForAddress(frame.instructionAddr, frame.addrMode),
|
|
maxLengthOfRelativeAddress: maxLengthOfAllRelativeAddresses,
|
|
maxLengthOfRelativeAddress: maxLengthOfAllRelativeAddresses,
|
|
registers: {},
|
|
registers: {},
|
|
includeSystemFrames,
|
|
includeSystemFrames,
|
|
onFunctionNameToggle: handleToggleFunctionName,
|
|
onFunctionNameToggle: handleToggleFunctionName,
|
|
showCompleteFunctionName,
|
|
showCompleteFunctionName,
|
|
|
|
+ hiddenFrameCount: frameCountMap[frameIndex],
|
|
isHoverPreviewed,
|
|
isHoverPreviewed,
|
|
|
|
+ isShowFramesToggleExpanded: toggleFrameMap[frameIndex],
|
|
|
|
+ isSubFrame: hiddenFrameIndices.includes(frameIndex),
|
|
isUsedForGrouping,
|
|
isUsedForGrouping,
|
|
frameMeta: meta?.frames?.[frameIndex],
|
|
frameMeta: meta?.frames?.[frameIndex],
|
|
registersMeta: meta?.registers,
|
|
registersMeta: meta?.registers,
|