Improve table view
Summary: added component name, root component name, duration, event type and better names changelog: UIDebugger - added event debugger table view and side panel views Reviewed By: lblasa Differential Revision: D48559367 fbshipit-source-id: d357ecf654b4e443eac7673731a8be542e76dd48
This commit is contained in:
committed by
Facebook GitHub Bot
parent
5f9000aa82
commit
206ef79cf9
@@ -35,6 +35,7 @@ export type SubtreeUpdateEvent = {
|
|||||||
frameworkEvents?: FrameworkEvent[];
|
frameworkEvents?: FrameworkEvent[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NodeMap = Map<Id, ClientNode>;
|
||||||
export type FrameworkEventType = string;
|
export type FrameworkEventType = string;
|
||||||
|
|
||||||
export type FrameworkEventMetadata = {
|
export type FrameworkEventMetadata = {
|
||||||
@@ -59,7 +60,7 @@ export type FrameworkEvent = {
|
|||||||
nodeId: Id;
|
nodeId: Id;
|
||||||
type: FrameworkEventType;
|
type: FrameworkEventType;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
payload?: JSON;
|
payload?: JsonObject;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
attribution?: FrameworkEventAttribution;
|
attribution?: FrameworkEventAttribution;
|
||||||
thread?: 'main' | string;
|
thread?: 'main' | string;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {Atom, _ReadOnlyAtom} from 'flipper-plugin';
|
|||||||
import {
|
import {
|
||||||
Id,
|
Id,
|
||||||
FrameworkEventType,
|
FrameworkEventType,
|
||||||
|
FrameworkEvent,
|
||||||
Inspectable,
|
Inspectable,
|
||||||
Bounds,
|
Bounds,
|
||||||
Tag,
|
Tag,
|
||||||
@@ -73,6 +74,11 @@ export type NodeSelection = {
|
|||||||
source: SelectionSource;
|
source: SelectionSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AugmentedFrameworkEvent = FrameworkEvent & {
|
||||||
|
nodeName?: string;
|
||||||
|
rootComponentName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type OnSelectNode = (
|
export type OnSelectNode = (
|
||||||
node: Id | undefined,
|
node: Id | undefined,
|
||||||
source: SelectionSource,
|
source: SelectionSource,
|
||||||
|
|||||||
@@ -16,14 +16,20 @@ import {
|
|||||||
usePlugin,
|
usePlugin,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import React, {useEffect, useRef} from 'react';
|
import React, {useEffect, useRef} from 'react';
|
||||||
import {FrameworkEvent, Id} from '../ClientTypes';
|
import {FrameworkEvent, Id, NodeMap} from '../ClientTypes';
|
||||||
import {plugin} from '../index';
|
import {plugin} from '../index';
|
||||||
import {Button, Tooltip} from 'antd';
|
import {Button, Tooltip} from 'antd';
|
||||||
|
import {AugmentedFrameworkEvent} from '../DesktopTypes';
|
||||||
|
import {formatDuration, formatTimestampMillis} from '../utils/timeUtils';
|
||||||
|
import {eventTypeToName} from './sidebar/inspector/FrameworkEventsInspector';
|
||||||
|
import {startCase} from 'lodash';
|
||||||
|
|
||||||
export function FrameworkEventsTable({nodeId}: {nodeId: Id}) {
|
export function FrameworkEventsTable({nodeId}: {nodeId: Id; nodes: NodeMap}) {
|
||||||
const instance = usePlugin(plugin);
|
const instance = usePlugin(plugin);
|
||||||
|
|
||||||
const managerRef = useRef<DataTableManager<FrameworkEvent> | null>(null);
|
const managerRef = useRef<DataTableManager<AugmentedFrameworkEvent> | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (nodeId != null) {
|
if (nodeId != null) {
|
||||||
@@ -52,23 +58,50 @@ export function FrameworkEventsTable({nodeId}: {nodeId: Id}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns: DataTableColumn<FrameworkEvent>[] = [
|
const columns: DataTableColumn<AugmentedFrameworkEvent>[] = [
|
||||||
{
|
{
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
onRender: (row: FrameworkEvent) => {
|
onRender: (row: FrameworkEvent) => formatTimestampMillis(row.timestamp),
|
||||||
return new Date(row.timestamp).toLocaleTimeString();
|
title: 'Timestamp',
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'treeId',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'nodeId',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'type',
|
key: 'type',
|
||||||
|
title: 'Event type',
|
||||||
|
onRender: (row: FrameworkEvent) => eventTypeToName(row.type),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'duration',
|
||||||
|
title: 'Duration',
|
||||||
|
onRender: (row: FrameworkEvent) =>
|
||||||
|
row.duration != null ? formatDuration(row.duration) : null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'treeId',
|
||||||
|
title: 'TreeId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rootComponentName',
|
||||||
|
title: 'Root component name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nodeId',
|
||||||
|
title: 'Component ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'nodeName',
|
||||||
|
title: 'Component name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'thread',
|
key: 'thread',
|
||||||
|
title: 'Thread',
|
||||||
|
onRender: (row: FrameworkEvent) => startCase(row.thread),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'payload',
|
||||||
|
title: 'Payload',
|
||||||
|
onRender: (row: FrameworkEvent) =>
|
||||||
|
Object.keys(row.payload ?? {}).length > 0
|
||||||
|
? JSON.stringify(row.payload)
|
||||||
|
: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export function Component() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (viewMode.mode === 'frameworkEventsTable') {
|
if (viewMode.mode === 'frameworkEventsTable') {
|
||||||
return <FrameworkEventsTable nodeId={viewMode.nodeId} />;
|
return <FrameworkEventsTable nodeId={viewMode.nodeId} nodes={nodes} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {last, startCase, uniqBy} from 'lodash';
|
|||||||
import {FilterOutlined, TableOutlined} from '@ant-design/icons';
|
import {FilterOutlined, TableOutlined} from '@ant-design/icons';
|
||||||
import {ViewMode} from '../../../DesktopTypes';
|
import {ViewMode} from '../../../DesktopTypes';
|
||||||
import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownItem';
|
import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownItem';
|
||||||
|
import {formatDuration, formatTimestampMillis} from '../../../utils/timeUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
node: ClientNode;
|
node: ClientNode;
|
||||||
@@ -233,7 +234,7 @@ function EventDetails({
|
|||||||
<Tag color={threadToColor(event.thread)}>{event.thread}</Tag>
|
<Tag color={threadToColor(event.thread)}>{event.thread}</Tag>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="Timestamp">
|
<Descriptions.Item label="Timestamp">
|
||||||
{formatTimestamp(event.timestamp)}
|
{formatTimestampMillis(event.timestamp)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
{event.duration && (
|
{event.duration && (
|
||||||
<Descriptions.Item label="Duration">
|
<Descriptions.Item label="Duration">
|
||||||
@@ -257,35 +258,14 @@ function EventDetails({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const options: Intl.DateTimeFormatOptions = {
|
export const options: Intl.DateTimeFormatOptions = {
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
second: 'numeric',
|
second: 'numeric',
|
||||||
hour12: false,
|
hour12: false,
|
||||||
};
|
};
|
||||||
function formatTimestamp(timestamp: number): string {
|
|
||||||
const date = new Date(timestamp);
|
|
||||||
|
|
||||||
const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date);
|
export function eventTypeToName(eventType: string) {
|
||||||
const milliseconds = date.getMilliseconds();
|
|
||||||
|
|
||||||
return `${formattedDate}.${milliseconds.toString().padStart(3, '0')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDuration(nanoseconds: number): string {
|
|
||||||
if (nanoseconds < 1_000) {
|
|
||||||
return `${nanoseconds} nanoseconds`;
|
|
||||||
} else if (nanoseconds < 1_000_000) {
|
|
||||||
return `${(nanoseconds / 1_000).toFixed(2)} microseconds`;
|
|
||||||
} else if (nanoseconds < 1_000_000_000) {
|
|
||||||
return `${(nanoseconds / 1_000_000).toFixed(2)} milliseconds`;
|
|
||||||
} else if (nanoseconds < 1_000_000_000_000) {
|
|
||||||
return `${(nanoseconds / 1_000_000_000).toFixed(2)} seconds`;
|
|
||||||
} else {
|
|
||||||
return `${(nanoseconds / (1_000_000_000 * 60)).toFixed(2)} minutes`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function eventTypeToName(eventType: string) {
|
|
||||||
return eventType.slice(eventType.lastIndexOf(frameworkEventSeparator) + 1);
|
return eventType.slice(eventType.lastIndexOf(frameworkEventSeparator) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {createDataSource, createState, PluginClient} from 'flipper-plugin';
|
|||||||
import {
|
import {
|
||||||
Events,
|
Events,
|
||||||
FrameScanEvent,
|
FrameScanEvent,
|
||||||
FrameworkEvent,
|
|
||||||
FrameworkEventType,
|
FrameworkEventType,
|
||||||
Id,
|
Id,
|
||||||
Metadata,
|
Metadata,
|
||||||
@@ -29,11 +28,14 @@ import {
|
|||||||
ReadOnlyUIState,
|
ReadOnlyUIState,
|
||||||
LiveClientState,
|
LiveClientState,
|
||||||
WireFrameMode,
|
WireFrameMode,
|
||||||
|
AugmentedFrameworkEvent,
|
||||||
} from './DesktopTypes';
|
} from './DesktopTypes';
|
||||||
import {getStreamInterceptor} from './fb-stubs/StreamInterceptor';
|
import {getStreamInterceptor} from './fb-stubs/StreamInterceptor';
|
||||||
import {prefetchSourceFileLocation} from './components/fb-stubs/IDEContextMenu';
|
import {prefetchSourceFileLocation} from './components/fb-stubs/IDEContextMenu';
|
||||||
import {checkFocusedNodeStillActive} from './plugin/ClientDataUtils';
|
import {checkFocusedNodeStillActive} from './plugin/ClientDataUtils';
|
||||||
import {uiActions} from './plugin/uiActions';
|
import {uiActions} from './plugin/uiActions';
|
||||||
|
import {first} from 'lodash';
|
||||||
|
import {getNode} from './utils/map';
|
||||||
|
|
||||||
type PendingData = {
|
type PendingData = {
|
||||||
metadata: Record<MetadataId, Metadata>;
|
metadata: Record<MetadataId, Metadata>;
|
||||||
@@ -46,7 +48,7 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
const streamInterceptor = getStreamInterceptor(client.device.os);
|
const streamInterceptor = getStreamInterceptor(client.device.os);
|
||||||
const snapshot = createState<SnapshotInfo | null>(null);
|
const snapshot = createState<SnapshotInfo | null>(null);
|
||||||
const nodesAtom = createState<Map<Id, ClientNode>>(new Map());
|
const nodesAtom = createState<Map<Id, ClientNode>>(new Map());
|
||||||
const frameworkEvents = createDataSource<FrameworkEvent>([], {
|
const frameworkEvents = createDataSource<AugmentedFrameworkEvent>([], {
|
||||||
indices: [['nodeId']],
|
indices: [['nodeId']],
|
||||||
limit: 10000,
|
limit: 10000,
|
||||||
});
|
});
|
||||||
@@ -223,7 +225,7 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
lastFrameTime = frameScan.frameTime;
|
lastFrameTime = frameScan.frameTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFrameworkEvents(frameScan);
|
applyFrameworkEvents(frameScan, processedNodes);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -233,9 +235,18 @@ export function plugin(client: PluginClient<Events>) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function applyFrameworkEvents(frameScan: FrameScanEvent) {
|
function applyFrameworkEvents(
|
||||||
|
frameScan: FrameScanEvent,
|
||||||
|
nodes: Map<Id, ClientNode>,
|
||||||
|
) {
|
||||||
for (const frameworkEvent of frameScan.frameworkEvents ?? []) {
|
for (const frameworkEvent of frameScan.frameworkEvents ?? []) {
|
||||||
frameworkEvents.append(frameworkEvent);
|
const treeRoot = getNode(frameworkEvent.treeId, nodes);
|
||||||
|
const treeRootFirstChild = getNode(first(treeRoot?.children), nodes);
|
||||||
|
frameworkEvents.append({
|
||||||
|
...frameworkEvent,
|
||||||
|
nodeName: nodes.get(frameworkEvent.nodeId)?.name,
|
||||||
|
rootComponentName: treeRootFirstChild?.name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiState.isPaused.get() === true) {
|
if (uiState.isPaused.get() === true) {
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
|
|
||||||
import {ClientNode, Id} from '../ClientTypes';
|
import {ClientNode, Id} from '../ClientTypes';
|
||||||
|
|
||||||
export function getNode(id: Id | undefined, nodes: Map<Id, ClientNode>) {
|
export function getNode(
|
||||||
|
id: Id | undefined,
|
||||||
|
nodes: Map<Id, ClientNode>,
|
||||||
|
): ClientNode | undefined {
|
||||||
//map just returns undefined when you pass null or undefined as a key
|
//map just returns undefined when you pass null or undefined as a key
|
||||||
return nodes.get(id!);
|
return nodes.get(id!);
|
||||||
}
|
}
|
||||||
|
|||||||
33
desktop/plugins/public/ui-debugger/utils/timeUtils.tsx
Normal file
33
desktop/plugins/public/ui-debugger/utils/timeUtils.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {options} from '../components/sidebar/inspector/FrameworkEventsInspector';
|
||||||
|
|
||||||
|
export function formatTimestampMillis(timestamp: number): string {
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
|
||||||
|
const formattedDate = new Intl.DateTimeFormat('en-US', options).format(date);
|
||||||
|
const milliseconds = date.getMilliseconds();
|
||||||
|
|
||||||
|
return `${formattedDate}.${milliseconds.toString().padStart(3, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDuration(nanoseconds: number): string {
|
||||||
|
if (nanoseconds < 1_000) {
|
||||||
|
return `${nanoseconds} nanoseconds`;
|
||||||
|
} else if (nanoseconds < 1_000_000) {
|
||||||
|
return `${(nanoseconds / 1_000).toFixed(2)} microseconds`;
|
||||||
|
} else if (nanoseconds < 1_000_000_000) {
|
||||||
|
return `${(nanoseconds / 1_000_000).toFixed(2)} milliseconds`;
|
||||||
|
} else if (nanoseconds < 1_000_000_000_000) {
|
||||||
|
return `${(nanoseconds / 1_000_000_000).toFixed(2)} seconds`;
|
||||||
|
} else {
|
||||||
|
return `${(nanoseconds / (1_000_000_000 * 60)).toFixed(2)} minutes`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user