Migrate framework events backing data structure to datasource

Summary: This will allow us to build a nice table easily.

Reviewed By: lblasa

Differential Revision: D47520029

fbshipit-source-id: 3cdd776533b66688329171eb29b892e0b9153540
This commit is contained in:
Luke De Feo
2023-07-19 08:58:20 -07:00
committed by Facebook GitHub Bot
parent 16480a95f3
commit 7812dae764
6 changed files with 46 additions and 53 deletions

View File

@@ -17,14 +17,12 @@ import {
} from '../types'; } from '../types';
import React, {useMemo} from 'react'; import React, {useMemo} from 'react';
import { import {
Atom,
DataInspector, DataInspector,
DataSource, DataSource,
DataTable, DataTable,
DataTableColumn, DataTableColumn,
DetailSidebar, DetailSidebar,
Layout, Layout,
useValue,
} from 'flipper-plugin'; } from 'flipper-plugin';
export function PerfStats(props: { export function PerfStats(props: {
@@ -32,15 +30,13 @@ export function PerfStats(props: {
nodes: Map<Id, UINode>; nodes: Map<Id, UINode>;
rootId?: Id; rootId?: Id;
events: DataSource<DynamicPerformanceStatsEvent, number>; events: DataSource<DynamicPerformanceStatsEvent, number>;
frameworkEvents: Atom<Map<Id, FrameworkEvent[]>>; frameworkEvents: DataSource<FrameworkEvent>;
}) { }) {
const uiStateValues = Object.entries(props.uiState).map(([key, value]) => [ const uiStateValues = Object.entries(props.uiState).map(([key, value]) => [
key, key,
value.get(), value.get(),
]); ]);
const frameworkEventsValue = useValue(props.frameworkEvents);
const allColumns = useMemo(() => { const allColumns = useMemo(() => {
if (props.events.size > 0) { if (props.events.size > 0) {
const row = props.events.get(0); const row = props.events.get(0);
@@ -65,7 +61,6 @@ export function PerfStats(props: {
return columns; return columns;
}, [props.events]); }, [props.events]);
const newLocal = [...frameworkEventsValue.entries()];
return ( return (
<Layout.Container grow> <Layout.Container grow>
<DataTable<PerformanceStatsEvent> <DataTable<PerformanceStatsEvent>
@@ -79,11 +74,7 @@ export function PerfStats(props: {
rootId: props.rootId, rootId: props.rootId,
nodesCount: props.nodes.size, nodesCount: props.nodes.size,
rootNode: props.nodes.get(props.rootId ?? 'noroot'), rootNode: props.nodes.get(props.rootId ?? 'noroot'),
frameworkEvents: newLocal, frameworkEventsSize: props.frameworkEvents.size,
frameworkEventsSize: newLocal.reduce(
(acc, value) => acc + value[1].length,
0,
),
}}></DataInspector> }}></DataInspector>
</DetailSidebar> </DetailSidebar>
</Layout.Container> </Layout.Container>

View File

@@ -24,6 +24,7 @@ import React, {
useRef, useRef,
} from 'react'; } from 'react';
import { import {
DataSource,
getFlipperLib, getFlipperLib,
HighlightManager, HighlightManager,
HighlightProvider, HighlightProvider,
@@ -69,6 +70,7 @@ export type TreeNode = UINode & {
idx: number; idx: number;
isExpanded: boolean; isExpanded: boolean;
indentGuide: NodeIndentGuide | null; indentGuide: NodeIndentGuide | null;
frameworkEvents: number | null;
}; };
export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) { export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
@@ -79,7 +81,6 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
const isContextMenuOpen = useValue(instance.uiState.isContextMenuOpen); const isContextMenuOpen = useValue(instance.uiState.isContextMenuOpen);
const hoveredNode = head(useValue(instance.uiState.hoveredNodes)); const hoveredNode = head(useValue(instance.uiState.hoveredNodes));
const frameworkEvents = useValue(instance.frameworkEvents);
const filterMainThreadMonitoring = useValue( const filterMainThreadMonitoring = useValue(
instance.uiState.filterMainThreadMonitoring, instance.uiState.filterMainThreadMonitoring,
); );
@@ -94,6 +95,9 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
focusedNode || rootId, focusedNode || rootId,
expandedNodes, expandedNodes,
selectedNode?.id, selectedNode?.id,
instance.frameworkEvents,
frameworkEventsMonitoring,
filterMainThreadMonitoring,
); );
const refs: React.RefObject<HTMLLIElement>[] = treeNodes.map(() => const refs: React.RefObject<HTMLLIElement>[] = treeNodes.map(() =>
@@ -101,7 +105,16 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
); );
return {treeNodes, refs}; return {treeNodes, refs};
}, [expandedNodes, focusedNode, nodes, rootId, selectedNode]); }, [
expandedNodes,
filterMainThreadMonitoring,
focusedNode,
frameworkEventsMonitoring,
instance.frameworkEvents,
nodes,
rootId,
selectedNode?.id,
]);
const isUsingKBToScrollUtill = useRef<MillisSinceEpoch>(0); const isUsingKBToScrollUtill = useRef<MillisSinceEpoch>(0);
@@ -240,9 +253,6 @@ export function Tree2({nodes, rootId}: {nodes: Map<Id, UINode>; rootId: Id}) {
innerRef={refs[virtualRow.index]} innerRef={refs[virtualRow.index]}
key={virtualRow.index} key={virtualRow.index}
treeNode={treeNodes[virtualRow.index]} treeNode={treeNodes[virtualRow.index]}
frameworkEvents={frameworkEvents}
frameworkEventsMonitoring={frameworkEventsMonitoring}
filterMainThreadMonitoring={filterMainThreadMonitoring}
highlightedNodes={highlightedNodes} highlightedNodes={highlightedNodes}
selectedNode={selectedNode?.id} selectedNode={selectedNode?.id}
hoveredNode={hoveredNode} hoveredNode={hoveredNode}
@@ -292,9 +302,6 @@ function TreeItemContainer({
transform, transform,
innerRef, innerRef,
treeNode, treeNode,
frameworkEvents,
frameworkEventsMonitoring,
filterMainThreadMonitoring,
highlightedNodes, highlightedNodes,
selectedNode, selectedNode,
hoveredNode, hoveredNode,
@@ -308,10 +315,7 @@ function TreeItemContainer({
transform: string; transform: string;
innerRef: Ref<any>; innerRef: Ref<any>;
treeNode: TreeNode; treeNode: TreeNode;
frameworkEvents: Map<Id, FrameworkEvent[]>;
highlightedNodes: Set<Id>; highlightedNodes: Set<Id>;
frameworkEventsMonitoring: Map<FrameworkEventType, boolean>;
filterMainThreadMonitoring: boolean;
selectedNode?: Id; selectedNode?: Id;
hoveredNode?: Id; hoveredNode?: Id;
isUsingKBToScroll: RefObject<MillisSinceEpoch>; isUsingKBToScroll: RefObject<MillisSinceEpoch>;
@@ -321,15 +325,6 @@ function TreeItemContainer({
onCollapseNode: (node: Id) => void; onCollapseNode: (node: Id) => void;
onHoverNode: (node: Id) => void; onHoverNode: (node: Id) => void;
}) { }) {
let events = frameworkEvents.get(treeNode.id);
if (events) {
events = events
.filter((e) => frameworkEventsMonitoring.get(e.type))
.filter(
(e) => filterMainThreadMonitoring === false || e.thread === 'main',
);
}
return ( return (
<div <div
ref={innerRef} ref={innerRef}
@@ -375,9 +370,9 @@ function TreeItemContainer({
{nodeIcon(treeNode)} {nodeIcon(treeNode)}
<TreeItemRowContent treeNode={treeNode} /> <TreeItemRowContent treeNode={treeNode} />
{events && ( {treeNode.frameworkEvents && (
<Badge <Badge
count={events.length} count={treeNode.frameworkEvents}
style={{ style={{
backgroundColor: theme.primaryColor, backgroundColor: theme.primaryColor,
marginLeft: theme.space.small, marginLeft: theme.space.small,
@@ -616,6 +611,9 @@ function toTreeNodes(
rootId: Id, rootId: Id,
expandedNodes: Set<Id>, expandedNodes: Set<Id>,
selectedNode: Id | undefined, selectedNode: Id | undefined,
frameworkEvents: DataSource<FrameworkEvent>,
frameworkEventsMonitoring: Map<FrameworkEventType, boolean>,
filterMainThreadMonitoring: boolean,
): TreeNode[] { ): TreeNode[] {
const root = nodes.get(rootId); const root = nodes.get(rootId);
if (root == null) { if (root == null) {
@@ -643,11 +641,21 @@ function toTreeNodes(
const isExpanded = expandedNodes.has(node.id); const isExpanded = expandedNodes.has(node.id);
const isSelected = node.id === selectedNode; const isSelected = node.id === selectedNode;
let events = frameworkEvents.getAllRecordsByIndex({nodeId: node.id});
if (events) {
events = events
.filter((e) => frameworkEventsMonitoring.get(e.type))
.filter(
(e) => filterMainThreadMonitoring === false || e.thread === 'main',
);
}
treeNodes.push({ treeNodes.push({
...node, ...node,
idx: i, idx: i,
depth, depth,
isExpanded, isExpanded,
frameworkEvents: events.length > 0 ? events.length : null,
indentGuide: stackItem.isChildOfSelectedNode indentGuide: stackItem.isChildOfSelectedNode
? { ? {
depth: stackItem.selectedNodeDepth, depth: stackItem.selectedNodeDepth,

View File

@@ -42,7 +42,6 @@ export const Inspector: React.FC<Props> = ({
}) => { }) => {
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
const selectedNodeId = useValue(instance.uiState.selectedNode)?.id; const selectedNodeId = useValue(instance.uiState.selectedNode)?.id;
const frameworkEvents = useValue(instance.frameworkEvents);
const selectedNode = selectedNodeId ? nodes.get(selectedNodeId) : undefined; const selectedNode = selectedNodeId ? nodes.get(selectedNodeId) : undefined;
if (!selectedNode) { if (!selectedNode) {
@@ -50,8 +49,8 @@ export const Inspector: React.FC<Props> = ({
} }
const selectedFrameworkEvents = selectedNodeId const selectedFrameworkEvents = selectedNodeId
? frameworkEvents?.get(selectedNodeId) ? instance.frameworkEvents.getAllRecordsByIndex({nodeId: selectedNodeId})
: undefined; : [];
return ( return (
<Layout.Container gap pad> <Layout.Container gap pad>
@@ -108,7 +107,7 @@ export const Inspector: React.FC<Props> = ({
/> />
</Tab> </Tab>
)} )}
{selectedFrameworkEvents && ( {selectedFrameworkEvents?.length > 0 && (
<Tab <Tab
key={'events'} key={'events'}
tab={ tab={

View File

@@ -15,7 +15,7 @@ import {StackTraceInspector} from './StackTraceInspector';
type Props = { type Props = {
node: UINode; node: UINode;
events: FrameworkEvent[]; events: readonly FrameworkEvent[];
showExtra?: (element: ReactNode) => void; showExtra?: (element: ReactNode) => void;
}; };
export const FrameworkEventsInspector: React.FC<Props> = ({ export const FrameworkEventsInspector: React.FC<Props> = ({
@@ -41,7 +41,7 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
}; };
const hasStacktrace = (event: FrameworkEvent) => { const hasStacktrace = (event: FrameworkEvent) => {
return event.attribution?.type === 'stacktrace'; return event?.attribution?.type === 'stacktrace';
}; };
return ( return (

View File

@@ -143,7 +143,7 @@ export function plugin(client: PluginClient<Events>) {
clearCallBack: async () => { clearCallBack: async () => {
uiState.streamState.set({state: 'Ok'}); uiState.streamState.set({state: 'Ok'});
nodesAtom.set(new Map()); nodesAtom.set(new Map());
frameworkEvents.set(new Map()); frameworkEvents.clear();
snapshot.set(null); snapshot.set(null);
}, },
}); });
@@ -191,7 +191,10 @@ export function plugin(client: PluginClient<Events>) {
}); });
const nodesAtom = createState<Map<Id, UINode>>(new Map()); const nodesAtom = createState<Map<Id, UINode>>(new Map());
const frameworkEvents = createState<Map<Id, FrameworkEvent[]>>(new Map()); const frameworkEvents = createDataSource<FrameworkEvent>([], {
indices: [['nodeId']],
limit: 10000,
});
const highlightedNodes = createState(new Set<Id>()); const highlightedNodes = createState(new Set<Id>());
const snapshot = createState<SnapshotInfo | null>(null); const snapshot = createState<SnapshotInfo | null>(null);
@@ -279,18 +282,9 @@ export function plugin(client: PluginClient<Events>) {
}; };
function applyFrameworkEvents(frameScan: FrameScanEvent) { function applyFrameworkEvents(frameScan: FrameScanEvent) {
frameworkEvents.update((draft) => { for (const frameworkEvent of frameScan.frameworkEvents ?? []) {
if (frameScan?.frameworkEvents) { frameworkEvents.append(frameworkEvent);
frameScan.frameworkEvents.forEach((frameworkEvent) => { }
const frameworkEventsForNode = draft.get(frameworkEvent.nodeId);
if (frameworkEventsForNode) {
frameworkEventsForNode.push(frameworkEvent);
} else {
draft.set(frameworkEvent.nodeId, [frameworkEvent]);
}
});
}
});
if (uiState.isPaused.get() === true) { if (uiState.isPaused.get() === true) {
return; return;

View File

@@ -110,6 +110,7 @@ type UpstreamEvent = {type: 'upstreamEvent'; eventId: Id};
type FrameworkEventAttribution = Stacktrace | Reason | UpstreamEvent; type FrameworkEventAttribution = Stacktrace | Reason | UpstreamEvent;
export type FrameworkEvent = { export type FrameworkEvent = {
id: number;
nodeId: Id; nodeId: Id;
type: FrameworkEventType; type: FrameworkEventType;
timestamp: number; timestamp: number;