From 47b0a0ba43b1bb50521ed88dc97da9d112d82f60 Mon Sep 17 00:00:00 2001 From: Shreyan Gupta Date: Mon, 3 Mar 2025 20:49:37 -0800 Subject: [PATCH] [testloop] Fix broken visualizer (#13033) The recent change in https://github.com/near/nearcore/pull/13016 to shift from index to identifier broke the testloop visualizer. We now have the visualizer showing the identifier instead of just index. ![image](https://github.com/user-attachments/assets/9f062810-8737-4e15-a96d-65e1bf83a484) --- core/async/src/test_loop.rs | 6 ++-- .../src/log_visualizer/LogVisualizer.tsx | 16 +++++----- tools/debug-ui/src/log_visualizer/arrows.ts | 26 +++++++-------- tools/debug-ui/src/log_visualizer/events.ts | 22 ++++++------- tools/debug-ui/src/log_visualizer/layout.ts | 32 ++++++++++++------- 5 files changed, 55 insertions(+), 47 deletions(-) diff --git a/core/async/src/test_loop.rs b/core/async/src/test_loop.rs index cd3a954e1fb..89b0a3b829e 100644 --- a/core/async/src/test_loop.rs +++ b/core/async/src/test_loop.rs @@ -171,6 +171,8 @@ struct EventStartLogOutput { current_index: usize, /// See `EventEndLogOutput::total_events`. total_events: usize, + /// The identifier of the event, usually the node_id. + identifier: String, /// The Debug representation of the event payload. current_event: String, /// The current virtual time. @@ -333,11 +335,11 @@ impl TestLoopV2 { /// Processes the given event, by logging a line first and then finding a handler to run it. fn process_event(&mut self, event: EventInHeap) { - let description = format!("({},{})", event.event.identifier, event.event.description); let start_json = serde_json::to_string(&EventStartLogOutput { current_index: event.id, total_events: self.next_event_index, - current_event: description, + identifier: event.event.identifier, + current_event: event.event.description, current_time_ms: event.due.whole_milliseconds() as u64, }) .unwrap(); diff --git a/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx b/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx index 765474e9457..7bd838b938a 100644 --- a/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx +++ b/tools/debug-ui/src/log_visualizer/LogVisualizer.tsx @@ -73,7 +73,7 @@ export const LogVisualizer = () => { })} {/* Render the column headers. */} - {layouts.columns.map((_, i) => { + {layouts.columns.map((col, i) => { return (
{ left: layouts.getGridColumnXOffset(i), width: layouts.getGridColumnWidth(i), }}> - Node {i} + {col.identifier}
); })} @@ -157,17 +157,17 @@ export const LogVisualizer = () => { className={ 'attached-event' + (event.id == selectedEventId || - parent.id == selectedEventId + parent.id == selectedEventId ? ' selected' : '') } style={{ left: layouts.getArrowColumnXOffset( - new ArrowColumn(parent.column, 'middle'), + new ArrowColumn(parent.columnNumber, 'middle'), arrowGroupId ), top: - layouts.getItemYOffset(parent.row) + + layouts.getItemYOffset(parent.rowNumber) + layouts.sizes.itemHeight, }} onClick={(e) => { @@ -187,9 +187,9 @@ export const LogVisualizer = () => { key={`event ${event.id}`} className={'event' + (event.id == selectedEventId ? ' selected' : '')} style={{ - left: layouts.getItemXOffset(event.column), - top: layouts.getItemYOffset(event.row), - width: layouts.getItemWidth(event.column), + left: layouts.getItemXOffset(event.columnNumber), + top: layouts.getItemYOffset(event.rowNumber), + width: layouts.getItemWidth(event.columnNumber), height: layouts.sizes.itemHeight, }} onClick={(e) => { diff --git a/tools/debug-ui/src/log_visualizer/arrows.ts b/tools/debug-ui/src/log_visualizer/arrows.ts index fd42601d4dc..c03ff7e8904 100644 --- a/tools/debug-ui/src/log_visualizer/arrows.ts +++ b/tools/debug-ui/src/log_visualizer/arrows.ts @@ -90,7 +90,7 @@ export type ArrowRowPosition = 'above' | 'inbound' | 'outbound'; export const ARROW_ROW_POSITIONS: ArrowRowPosition[] = ['above', 'inbound', 'outbound']; export class ArrowRow { - constructor(public readonly gridRow: number, public readonly positioning: ArrowRowPosition) {} + constructor(public readonly gridRow: number, public readonly positioning: ArrowRowPosition) { } // Gets a unique key for this ArrowRow that is larger if it's more // vertically down. @@ -120,7 +120,7 @@ export class ArrowColumn { constructor( public readonly gridColumn: number, public readonly positioning: ArrowColumnPosition - ) {} + ) { } get key(): number { return this.gridColumn * 2 + (this.positioning == 'left' ? 0 : 1); @@ -265,8 +265,8 @@ export function makeOutgoingArrowsForItem( for (const targetId of targetIds) { const child = items.get(targetId)!; columnToMaxRow.set( - child.column, - Math.max(columnToMaxRow.get(child.column) ?? 0, child.row) + child.columnNumber, + Math.max(columnToMaxRow.get(child.columnNumber) ?? 0, child.rowNumber) ); } let minColumn = 100000, @@ -279,22 +279,22 @@ export function makeOutgoingArrowsForItem( const verticalParts = [] as ArrowPartVertical[]; // If we only have children to the same instance, then draw them in the // first fashion (see comment at beginning of file). - if (minColumn == maxColumn && minColumn == item.column && attachmentId === null) { - horizontalParts.push(ArrowPartHorizontal.outOfItem(item.row, item.column)); + if (minColumn == maxColumn && minColumn == item.columnNumber && attachmentId === null) { + horizontalParts.push(ArrowPartHorizontal.outOfItem(item.rowNumber, item.columnNumber)); verticalParts.push( ArrowPartVertical.acrossRows( - new ArrowRow(item.row, 'outbound'), - new ArrowRow(columnToMaxRow.get(item.column)!, 'inbound'), - item.column + new ArrowRow(item.rowNumber, 'outbound'), + new ArrowRow(columnToMaxRow.get(item.columnNumber)!, 'inbound'), + item.columnNumber ) ); } else { // Otherwise we draw them in the second fashion. - const verticalOut = ArrowPartVertical.outOfItem(item.row, item.column); + const verticalOut = ArrowPartVertical.outOfItem(item.rowNumber, item.columnNumber); verticalParts.push(verticalOut); horizontalParts.push( ArrowPartHorizontal.acrossColumns( - item.row + 1, + item.rowNumber + 1, verticalOut.column.min(new ArrowColumn(minColumn, 'left')), verticalOut.column.max(new ArrowColumn(maxColumn, 'left')) ) @@ -302,7 +302,7 @@ export function makeOutgoingArrowsForItem( for (const column of columnToMaxRow.keys()) { verticalParts.push( ArrowPartVertical.acrossRows( - new ArrowRow(item.row + 1, 'above'), + new ArrowRow(item.rowNumber + 1, 'above'), new ArrowRow(columnToMaxRow.get(column)!, 'inbound'), column ) @@ -311,7 +311,7 @@ export function makeOutgoingArrowsForItem( } for (const targetId of targetIds) { const child = items.get(targetId)!; - horizontalParts.push(ArrowPartHorizontal.intoItem(child.row, child.column)); + horizontalParts.push(ArrowPartHorizontal.intoItem(child.rowNumber, child.columnNumber)); } arrowGroups.push({ horizontalParts, diff --git a/tools/debug-ui/src/log_visualizer/events.ts b/tools/debug-ui/src/log_visualizer/events.ts index 4229f8b6b54..ce6407828ba 100644 --- a/tools/debug-ui/src/log_visualizer/events.ts +++ b/tools/debug-ui/src/log_visualizer/events.ts @@ -12,7 +12,7 @@ export class EventItem { // is zero; in a multi-instance test, this is the instance number. // Emitted from the Rust side as part of the event dump (like "(0, Event)") // and parsed here. - public readonly column: number; + public readonly identifier: string; // The title we display for the event. Parsed from the event dump after // extracting column number; this is usually the enum variant name. public readonly title: string; @@ -25,27 +25,21 @@ export class EventItem { // for how this is derived. public readonly parentId: number | null = null; - // The row of the event in the log visualizer. This is set during layout. - public row = 0; + // The row and column of the event in the log visualizer. This is set during layout. + public rowNumber = 0; + public columnNumber = 0; // The children IDs of this event. Derived from parentId. public readonly childIds: number[] = []; // The log lines emitted by the Rust program while handling this event. public readonly logRows: string[] = []; - constructor(id: number, parentId: number | null, time: number, eventDump: string) { + constructor(id: number, identifier: string, parentId: number | null, time: number, eventDump: string) { this.id = id; this.time = time; this.parentId = parentId; + this.identifier = identifier; + this.data = eventDump; - // If the event dump is a tuple, the first element is the instance ID. - if (eventDump.startsWith('(')) { - const split = eventDump.indexOf(','); - this.column = parseInt(eventDump.substring(1, split)); - this.data = eventDump.substring(split + 1, eventDump.length - 1).trim(); - } else { - this.column = 0; - this.data = eventDump; - } // Parse the title and subtitle; for example, if the event dump is // "OutboundNetwork(NetworkRequests(...))"" // then the title is "OutboundNetwork" and the subtitle is @@ -200,6 +194,7 @@ export class EventItemCollection { type EventStartLogLineData = { current_index: number; total_events: number; + identifier: string; current_event: string; current_time_ms: number; }; @@ -209,6 +204,7 @@ export class EventItemCollection { totalEventCount = startData.total_events; const event = new EventItem( startData.current_index, + startData.identifier, parentIds[startData.current_index] ?? null, startData.current_time_ms, startData.current_event diff --git a/tools/debug-ui/src/log_visualizer/layout.ts b/tools/debug-ui/src/log_visualizer/layout.ts index 5ee37103cb2..1fce381f629 100644 --- a/tools/debug-ui/src/log_visualizer/layout.ts +++ b/tools/debug-ui/src/log_visualizer/layout.ts @@ -44,7 +44,7 @@ class OneRowLayout { public arrowLayout = new Map(); - constructor(public readonly time: number) {} + constructor(public readonly time: number) { } } // The layout parameters for a single column of items. @@ -74,8 +74,9 @@ class OneColumnLayout { public startOffset = 0; public itemOffset = 0; public itemEndOffset = 0; - public arrowLayout = new Map(); + + constructor(public identifier: string) { } } // The computed layout parameters for a unique vertical position of horizontal @@ -125,12 +126,20 @@ export class Layouts { const sortedItems = [...items.getAllNonAttachedItems()]; const nextRowForColumn: number[] = []; let currentTime = 0; + + const columnIdentifiers = new Set(); for (const item of sortedItems) { - while (this.columns.length <= item.column) { - this.columns.push(new OneColumnLayout()); - nextRowForColumn.push(0); - } + columnIdentifiers.add(item.identifier); } + + // Convert the set to an sorted mapping + const columnIdentifierMap = new Map(); + Array.from(columnIdentifiers).sort().forEach((identifier, index) => { + columnIdentifierMap.set(identifier, index); + this.columns.push(new OneColumnLayout(identifier)); + nextRowForColumn.push(0); + }); + sortedItems.sort((a, b) => { if (a.time < b.time) { return -1; @@ -142,6 +151,7 @@ export class Layouts { return a.id - b.id; }); for (const item of sortedItems) { + item.columnNumber = columnIdentifierMap.get(item.identifier)!; if (item.time > currentTime) { currentTime = item.time; for (let i = 0; i < this.columns.length; i++) { @@ -159,13 +169,13 @@ export class Layouts { if (parentItem !== null) { // Make sure that the child item is always placed below the parent; otherwise the arrows would // point backwards. - nextRowForColumn[item.column] = Math.max( - nextRowForColumn[item.column], - parentItem.row + 1 + nextRowForColumn[item.columnNumber] = Math.max( + nextRowForColumn[item.columnNumber], + parentItem.rowNumber + 1 ); } - item.row = nextRowForColumn[item.column]++; - if (item.row >= this.rows.length) { + item.rowNumber = nextRowForColumn[item.columnNumber]++; + if (item.rowNumber >= this.rows.length) { this.rows.push(new OneRowLayout(item.time)); } }