private static void PerformEdgeHeatRanking(ProtoGraph graph) { if (graph.EdgeCount < 10) { return; } var edgeList = graph.GetEdgeObjListCopy(); var allEdgeExecutions = edgeList.Select(e => e.ExecutionCount).Distinct().ToList(); allEdgeExecutions.Sort(); float decile = allEdgeExecutions.Count / 10f; float current = decile; ulong[] EdgeHeatThresholds = new ulong[9]; for (var i = 0; i < 9; i++) { EdgeHeatThresholds[i] = allEdgeExecutions.ElementAt((int)current); current += decile; } foreach (EdgeData e in edgeList) { ulong execCount = e.ExecutionCount; int threshold = 0; while (threshold < EdgeHeatThresholds.Length && execCount > EdgeHeatThresholds[threshold]) { threshold++; } e.heatRank = threshold; } }
/// <summary> /// Create a timeline event /// </summary> /// <param name="timelineEventType">The base category</param> /// <param name="item">The event data</param> public TIMELINE_EVENT(eTimelineEvent timelineEventType, object item) : base(eLogFilterBaseType.TimeLine) { _eventType = timelineEventType; _item = item; switch (timelineEventType) { case eTimelineEvent.ProcessStart: case eTimelineEvent.ProcessEnd: { TraceRecord process = (TraceRecord)item; SetIDs(ID: process.PID); break; } case eTimelineEvent.ThreadStart: case eTimelineEvent.ThreadEnd: { ProtoGraph thread = (ProtoGraph)item; SetIDs(ID: thread.ThreadID); break; } case eTimelineEvent.APICall: { TraceRecord process = ((APICALL)item).Graph !.TraceData; SetIDs(process.PID); break; } default: Debug.Assert(false, "Bad timeline event"); break; } Inited = true; }
private static void PerformNodeHeatRanking(ProtoGraph graph) { if (graph.NodeList.Count < 10) { return; } var nodeSpan = graph.GetNodeObjlistSpan(); var allNodeExecutions = nodeSpan.ToArray().Select(n => n.ExecutionCount).ToList(); allNodeExecutions.Sort(); System.Collections.Generic.List <ulong> NodeHeatThresholds = Enumerable.Repeat((ulong)0, 9).ToList(); int decile = allNodeExecutions.Count / 10; int current = decile; for (var i = 0; i < 9; i++) { NodeHeatThresholds[i] = allNodeExecutions.ElementAt(current); current += decile; } foreach (NodeData n in nodeSpan) { ulong execCount = n.ExecutionCount; int threshold = 0; while (threshold < NodeHeatThresholds.Count && execCount > NodeHeatThresholds[threshold]) { threshold++; } n.HeatRank = threshold; } }
/// <summary> /// Get the label of the timeline event /// </summary> /// <returns>The list of strings and colours which need to be joined to make a label</returns> public List <Tuple <string, WritableRgbaFloat> > Label() { Debug.Assert(_item is not null); if (_cachedLabel != null && _cachedLabelTheme == Themes.ThemeVariant) { return(_cachedLabel); } _cachedLabel = new List <Tuple <string, WritableRgbaFloat> >(); _cachedLabelTheme = Themes.ThemeVariant; WritableRgbaFloat textColour = Themes.GetThemeColourWRF(Themes.eThemeColour.WindowText); switch (_eventType) { case eTimelineEvent.ProcessStart: { TraceRecord trace = (TraceRecord)_item; _cachedLabel.Add(new Tuple <string, WritableRgbaFloat>($"Process ({trace.PID}) Started", textColour)); break; } case eTimelineEvent.ProcessEnd: { TraceRecord trace = (TraceRecord)_item; _cachedLabel.Add(new Tuple <string, WritableRgbaFloat>($"Process ({trace.PID}) Ended", textColour)); break; } case eTimelineEvent.ThreadStart: { ProtoGraph graph = (ProtoGraph)_item; _cachedLabel.Add(new Tuple <string, WritableRgbaFloat>($"Thread ({graph.ThreadID}) Started", textColour)); break; } case eTimelineEvent.ThreadEnd: { ProtoGraph graph = (ProtoGraph)_item; _cachedLabel.Add(new Tuple <string, WritableRgbaFloat>($"Thread ({graph.ThreadID}) Ended", textColour)); break; } case eTimelineEvent.APICall: { Logging.APICALL call = (Logging.APICALL)_item; NodeData n = call.Node !; var labelitems = n.CreateColourisedSymbolCall(call.Graph, call.Index, textColour, Themes.GetThemeColourWRF(Themes.eThemeColour.Emphasis1)); _cachedLabel.AddRange(labelitems); break; } default: _cachedLabel.Add(new Tuple <string, WritableRgbaFloat>($"Bad timeline event: ", textColour)); Debug.Assert(false, $"Bad timeline event: {_eventType}"); return(_cachedLabel); } return(_cachedLabel); }
/// <summary> /// Worker for processing trace data /// </summary> /// <param name="newProtoGraph">Thread graph being processed</param> public ThreadTraceProcessingThread(ProtoGraph newProtoGraph) { protograph = newProtoGraph; if (rgatState.ConnectedToRemote && rgatState.NetworkBridge.HeadlessMode) { Logging.RecordLogEvent("Error: Trace processor created in headless mode", Logging.LogFilterType.Error); rgatState.NetworkBridge.Teardown("TraceProcessor created in wrong mode"); } }
private readonly object _lock = new object(); //functionality first, performance later public SocketTraceIngestThread(ProtoGraph newProtoGraph) { protograph = newProtoGraph; /* * _updateRates = Enumerable.Repeat(0.0f, _StatCacheSize).ToList(); * * StatsTimer = new System.Timers.Timer(1000.0 / GlobalConfig.IngestStatsPerSecond); * StatsTimer.Elapsed += StatsTimerFired; * StatsTimer.AutoReset = true; * StatsTimer.Start(); */ }
private void update_rendering(PlottedGraph graph) { ProtoGraph protoGraph = graph.InternalProtoGraph; if (protoGraph == null || protoGraph.EdgeCount == 0) { return; } //if (graph.NodesDisplayData == null)// || !graph.setGraphBusy(true, 2)) // return; if (graph.ReplayState == PlottedGraph.REPLAY_STATE.Ended && protoGraph.Terminated) { graph.ResetAnimation(); } //update the render if there are more verts/edges or graph is being resized graph.RenderGraph(); if (!protoGraph.Terminated) { if (graph.IsAnimated) { graph.ProcessLiveAnimationUpdates(out int doneCount); } } else { if (graph.InternalProtoGraph.TraceData.DiscardTraceData is false && (graph.ReplayState == PlottedGraph.REPLAY_STATE.Playing || graph._userSelectedAnimPosition != -1)) { if (--_nextReplayStep <= 0) { graph.ProcessReplayUpdates(); _nextReplayStep = _FramesBetweenAnimationUpdates; } } } }
private static bool EvaluateProcessTestResults(TraceRequirements requirements, TRACE_TEST_RESULTS results, int depth) { //need to ensure each set of thread requirements can be satisfied by at least one unique thread Dictionary <REQUIREMENTS_LIST, List <ProtoGraph> > reqSatisfyGraphs = new Dictionary <REQUIREMENTS_LIST, List <ProtoGraph> >(); foreach (REQUIREMENTS_LIST reqlist in requirements.ThreadRequirements) { reqSatisfyGraphs[reqlist] = new List <ProtoGraph>(); } foreach (var threadReqList in requirements.ThreadRequirements) { Dictionary <ProtoGraph, REQUIREMENT_TEST_RESULTS> allThreadResults = results.ThreadResults[threadReqList]; foreach (var graph_results in allThreadResults) { ProtoGraph graph = graph_results.Key; if (graph_results.Value.Failed.Count == 0) { reqSatisfyGraphs[threadReqList].Add(graph); } } } bool threadsVerified = VerifyAllThreadRequirements(reqSatisfyGraphs, out string?threadVerifyError); bool processesVerified = results.ProcessResults.Failed.Count == 0; //if(!threadsVerified) FailedTestRequirements.Add(null, $"Thread traces didn't satisfy conditions list: "+threadVerifyError); //if(!processesVerified) FailedTestRequirements.Add(null, $"{results.ProcessResults.Failed.Count} requirements failed"); return(threadsVerified && processesVerified); //todo - multi process tests /* * foreach(var x in results.ChildResults) * { * TRACE_TEST_RESULTS processThreadResults = childtrace.EvaluateProcessTestRequirement(processThreadReqs); * EvaluateProcessTestResults(processThreadReqs, processThreadResults); * } */ }
private void DrawEventListTable(TraceRecord trace, SandboxChart.ItemNode?selectedNode) { if (chart is null) { return; } ImGui.PushStyleColor(ImGuiCol.Header, Themes.GetThemeColourUINT(Themes.eThemeColour.Emphasis2, 40)); TIMELINE_EVENT[] events = trace.GetTimeLineEntries(); if (ImGui.BeginTable("#TaTTFullList", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.Resizable | ImGuiTableFlags.RowBg)) { ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableSetupColumn("#", ImGuiTableColumnFlags.WidthFixed, 50); ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, 70); ImGui.TableSetupColumn("Module", ImGuiTableColumnFlags.WidthFixed, 70); ImGui.TableSetupColumn("Details", ImGuiTableColumnFlags.None); ImGui.TableHeadersRow(); var SelectedEntity = chart.SelectedEntity; var SelectedAPIEvent = chart.SelectedAPIEvent; bool ThreadNodeSelected = selectedNode is not null && Equals(selectedNode.reference.GetType(), typeof(ProtoGraph)); bool ProcessNodeSelected = selectedNode is not null && Equals(selectedNode.reference.GetType(), typeof(TraceRecord)); int i = 0; foreach (TIMELINE_EVENT TLevent in events) { i += 1; ImGui.TableNextRow(); if (TLevent.MetaError != null) { ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, Themes.GetThemeColourUINT(Themes.eThemeColour.BadStateColour)); ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, Themes.GetThemeColourUINT(Themes.eThemeColour.BadStateColour)); } bool selected = false; string eventType = ""; string module = ""; if (ImGui.TableNextColumn()) { switch (TLevent.TimelineEventType) { case eTimelineEvent.ProcessStart: case eTimelineEvent.ProcessEnd: eventType = "Process"; if (selectedNode != null) { selected = (ProcessNodeSelected && TLevent.ID == ((TraceRecord)selectedNode.reference).PID); } break; case eTimelineEvent.ThreadStart: case eTimelineEvent.ThreadEnd: eventType = "Thread"; if (selectedNode != null) { ProtoGraph currentEntryGraph = (ProtoGraph)TLevent.Item; selected = (ThreadNodeSelected && currentEntryGraph.ThreadID == ((ProtoGraph)selectedNode.reference).ThreadID); selected = selected || (ProcessNodeSelected && currentEntryGraph.TraceData.PID == ((TraceRecord)(selectedNode.reference)).PID); } break; case eTimelineEvent.APICall: { Logging.APICALL call = (Logging.APICALL)(TLevent.Item); selected = TLevent == SelectedAPIEvent; if (call.Node !.IsExternal) { eventType = "API - " + call.APIType(); module = Path.GetFileNameWithoutExtension(trace.DisassemblyData.GetModulePath(call.Node.GlobalModuleID)); //api call is selected if it is either directly activated, or interacts with a reference to the active entity //eg: if the file.txt node is selected, writefile to the relevant handle will also be selected selected = selected || (SelectedEntity != null && SelectedEntity == chart.GetInteractedEntity(TLevent)); if (!selected && selectedNode != null) { //select all apis called by selected thread node selected = selected || (ThreadNodeSelected && call.Graph !.ThreadID == ((ProtoGraph)selectedNode.reference).ThreadID); //select all apis called by selected process node selected = selected || (ProcessNodeSelected && call.Graph !.TraceData.PID == ((TraceRecord)selectedNode.reference).PID); } //WinAPIDetails.API_ENTRY = call.APIEntry; } else { eventType = "Internal"; } } break; } if (ImGui.Selectable(i.ToString(), selected, ImGuiSelectableFlags.SpanAllColumns) && !selected) { chart.SelectAPIEvent(TLevent); } }
/// <summary> /// Create an APi call /// </summary> /// <param name="graph">The graph of the thread that called it</param> public APICALL(ProtoGraph graph) { Graph = graph; }
/// <summary> /// Set the graph (ie: thread) that generated this log /// </summary> /// <param name="graph">A ProtoGraph</param> public void SetAssociatedGraph(ProtoGraph graph) { Graph = graph; Trace = graph.TraceData; }
/// <summary> /// Draw a chart node /// </summary> /// <param name="node">The node</param> /// <param name="position">Where on the chart to draw it</param> /// <param name="font">The font to use for text</param> /// <returns>If the node was clicked</returns> private bool DrawNode(ItemNode node, Vector2 position, ImFontPtr font) { Vector2 cursor = ImGui.GetCursorScreenPos(); if (!InFrame(position - cursor)) { return(false); } var DrawList = ImGui.GetWindowDrawList(); bool isSelected = node == _selectedNode; switch (node.TLtype) { case Logging.eTimelineEvent.ProcessStart: case Logging.eTimelineEvent.ProcessEnd: { TraceRecord trace = (TraceRecord)node.reference; switch (trace.TraceState) { case TraceRecord.ProcessState.eTerminated: DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff0000ff, $"{ImGuiController.FA_ICON_COGS}"); DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Exited)"); break; case TraceRecord.ProcessState.eRunning: DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ff00, $"{ImGuiController.FA_ICON_COGS}"); DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Running)"); break; case TraceRecord.ProcessState.eSuspended: DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ffff, $"{ImGuiController.FA_ICON_COGS}"); DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Suspended)"); break; default: Debug.Assert(false, "Bad trace state"); break; } } break; case Logging.eTimelineEvent.ThreadStart: case Logging.eTimelineEvent.ThreadEnd: { ProtoGraph graph = (ProtoGraph)node.reference; if (graph.Terminated) { DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff0000ff, $"{ImGuiController.FA_ICON_COG}"); DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Thread {graph.ThreadID} (Exited)"); } else { DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ff00, $"{ImGuiController.FA_ICON_COG}"); DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Thread {graph.ThreadID} (Active)"); } } break; case Logging.eTimelineEvent.APICall: Logging.TIMELINE_EVENT apiEvent = (Logging.TIMELINE_EVENT)node.reference; Logging.APICALL apicall = (Logging.APICALL)apiEvent.Item; if (!apicall.APIDetails.HasValue) { return(false); } APIDetailsWin.API_ENTRY details = apicall.APIDetails.Value; DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF); switch (details.FilterType) { case "File": DrawList.AddText(font, 20, position - new Vector2(10f, 10f), 0xff000000, $"{ImGuiController.FA_ICON_FILECODE}"); DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "File Interaction"); DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label); break; case "Registry": DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_SQUAREGRID}"); DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Registry Interaction"); DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label); break; case "Process": DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_COGS}"); DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Process Interaction"); DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label); break; case "Network": DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_NETWORK}"); DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Network Interaction"); DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label); break; default: DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_UP}"); DrawList.AddText(position + new Vector2(20, -15), 0xff000000, details.FilterType); DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label); break; } break; default: DrawList.AddCircleFilled(position, nodeSize, 0xff000000); break; } if (node == _selectedNode) { DrawList.AddCircle(position, 18, 0xff222222); } ImGui.SetCursorScreenPos(position - new Vector2(12, 12)); ImGui.InvisibleButton($"##{position.X}-{position.Y}", new Vector2(25, 25)); bool clicked = false; if (ImGui.IsItemClicked()) { clicked = true; _selectedNode = node; if (_selectedNode.TLtype == Logging.eTimelineEvent.APICall) { SelectedEntity = node; SelectedAPIEvent = (Logging.TIMELINE_EVENT)node.reference; } else { SelectedEntity = null; SelectedAPIEvent = null; } } //Vector2 labelSize = ImGui.CalcTextSize(node.label); //DrawList.AddRectFilled(position, position + labelSize, 0xddffffff); ImGui.SetCursorScreenPos(cursor); return(clicked); }
private static bool VerifyAllThreadRequirements(Dictionary <REQUIREMENTS_LIST, List <ProtoGraph> > reqSatisfyGraphs, out string?error) { error = ""; int reqListCount = reqSatisfyGraphs.Count; List <REQUIREMENTS_LIST> unsatisfied = new List <REQUIREMENTS_LIST>(); List <Tuple <REQUIREMENTS_LIST, ProtoGraph> > candidates = new List <Tuple <REQUIREMENTS_LIST, ProtoGraph> >(); List <ProtoGraph> uniqueGraphs = new List <ProtoGraph>(); //now we have a list of graphs that satisfy each set of requirements //list any requirements that no graphs satisfy foreach (var req_graplist in reqSatisfyGraphs) { REQUIREMENTS_LIST reqs = req_graplist.Key; if (req_graplist.Value.Count == 0) { unsatisfied.Add(reqs); } foreach (var validGraph in req_graplist.Value) { candidates.Add(new Tuple <REQUIREMENTS_LIST, ProtoGraph>(reqs, validGraph)); if (!uniqueGraphs.Contains(validGraph)) { uniqueGraphs.Add(validGraph); } } } if (unsatisfied.Count > 0) { return(false); } //now pair each requirementlist up to a single graph //this might be better solved with a theorem prover, but for now just terribly brute force it //might stipulate that each set of requirements needs to be sufficiently specific it will only match 1 graph List <Tuple <REQUIREMENTS_LIST, ProtoGraph> > bestList = new List <Tuple <REQUIREMENTS_LIST, ProtoGraph> >(); List <Tuple <REQUIREMENTS_LIST, ProtoGraph> > currentList = new List <Tuple <REQUIREMENTS_LIST, ProtoGraph> >(); List <REQUIREMENTS_LIST> usedReqs = new List <REQUIREMENTS_LIST>(); List <ProtoGraph> usedGraphs = new List <ProtoGraph>(); foreach (var permutation in GetPermutationsWithRept <Tuple <REQUIREMENTS_LIST, ProtoGraph> >(candidates, candidates.Count)) { usedGraphs.Clear(); usedReqs.Clear(); currentList.Clear(); foreach (var req_graph in permutation) { ProtoGraph graph = req_graph.Item2; REQUIREMENTS_LIST reqs = req_graph.Item1; if (usedReqs.Contains(reqs)) { continue; } if (usedGraphs.Contains(graph)) { continue; } currentList.Add(req_graph); usedGraphs.Add(graph); usedReqs.Add(reqs); if (usedReqs.Count == reqListCount) { return(true); } } if (currentList.Count > bestList.Count) { bestList = currentList; } } error = $"Failed to satisfy all thread requirements, best attempt was {bestList.Count}/{reqListCount}"; return(false); }
/// <summary> /// Creates an array of metadata for basic blocks used for basic-block-centric graph layout /// item[0] = blockID /// item[1] = offsetFromCenter; number of nodes ahead the center node is /// item[2] = centerPseudoBlockTopID; top of the block this node is in /// item[3] = centerPseudoBlockBaseID; base of the block this node is in /// </summary> /// <param name="plot">The graph being plotted</param> /// <param name="blockData">Output description of basic block information for each node</param> /// <param name="blockMiddles">Output List of basic block middle nodes</param> private static bool CreateBlockMetadataBuf(PlottedGraph plot, out NODE_BLOCK_METADATA_COMPUTEBUFFER[] blockData, out int[] blockMiddles) { ProtoGraph graph = plot.InternalProtoGraph; List <int>[] nodeNeighboursArray = plot.GetNodeNeighboursArray(); int nodeCount = nodeNeighboursArray.Length; NODE_BLOCK_METADATA_COMPUTEBUFFER[] blockDataInts = new NODE_BLOCK_METADATA_COMPUTEBUFFER[nodeCount]; Dictionary <int, int> blockMiddlesDict = new Dictionary <int, int>(); List <int> blockMiddleNodesList = new List <int>(); Dictionary <int, NodeData> exceptionBlocks = new(); /* * Step 1: Build a list of active blocks (ie: blocks which currently have instructions in, * as opposed to blocks which have been split into new ones by control flow */ List <int> activeBlockIDs = new(); Dictionary <int, int> NodeBlockToBlockMetaIndex = new(); Dictionary <int, int> BlockMetaToBlockFirstLastIndex = new(); for (var i = 0; i < nodeCount; i++) { NodeData n = graph.NodeList[i]; int nodeBlockID = (int)n.BlockID; if (NodeBlockToBlockMetaIndex.TryGetValue(nodeBlockID, out int metaBlockID) is false || activeBlockIDs.Contains(metaBlockID) is false) { metaBlockID = activeBlockIDs.Count; NodeBlockToBlockMetaIndex[nodeBlockID] = metaBlockID; BlockMetaToBlockFirstLastIndex[metaBlockID] = nodeBlockID; activeBlockIDs.Add(metaBlockID); if (n.CausedException) { exceptionBlocks[metaBlockID] = n; } } } //Step 2: Build the list of block center nodes that the block velocity shader will run over blockMiddleNodesList.Capacity = activeBlockIDs.Count; foreach (int blockIdx in activeBlockIDs) { if (blockIdx == -1) { blockMiddleNodesList.Add(-1); } int originalBlockIndex = BlockMetaToBlockFirstLastIndex[blockIdx]; if (originalBlockIndex < 0 || originalBlockIndex >= graph.BlocksFirstLastNodeList.Count) { blockMiddlesDict[blockIdx] = -1; //instructions sent and not executed? why? blockMiddleNodesList.Add((int)-1); continue; } var firstIdx_LastIdx = graph.BlocksFirstLastNodeList[originalBlockIndex]; if (firstIdx_LastIdx == null) { continue; } if (firstIdx_LastIdx.Item1 == firstIdx_LastIdx.Item2) { if (blockMiddleNodesList.Contains((int)firstIdx_LastIdx.Item1)) { continue; } blockMiddlesDict[blockIdx] = (int)firstIdx_LastIdx.Item1; //1 node block, top/mid/base is the same blockMiddleNodesList.Add((int)firstIdx_LastIdx.Item1); //Debug.Assert(blockIdx == (blockMiddleNodesList.Count-1)); } else { var block = graph.ProcessData.BasicBlocksList[originalBlockIndex]?.Item2; Debug.Assert(block is not null); int midIdx = (int)Math.Ceiling((block.Count - 1.0) / 2.0); var middleIns = block[midIdx]; if (!middleIns.GetThreadVert(graph.ThreadID, out uint centerNodeID)) { blockMiddlesDict[blockIdx] = -1; //instructions sent and not executed? why? //Debug.Assert(false, $"Instruction 0x{middleIns.address:X} not found in thread {tid}"); } else { //if (blockMiddleNodesList.Contains((int)centerNodeID)) //{ // continue; //} blockMiddlesDict[blockIdx] = (int)centerNodeID; blockMiddleNodesList.Add((int)centerNodeID); //Debug.Assert(blockIdx == (blockMiddleNodesList.Count - 1)); } } } /* * Step 3: Build the block metadata buffer which allows the position and velocity shaders to process each * node in the context of the block it is in */ int externals = 0; for (uint nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) { NodeData?n = graph.GetNode(nodeIdx); Debug.Assert(n is not null); int blockID; int offsetFromCenter; Tuple <uint, uint>?FirstLastIdx; if (!n.IsExternal) { if (n.BlockID >= graph.BlocksFirstLastNodeList.Count) { continue; } FirstLastIdx = graph.BlocksFirstLastNodeList[(int)n.BlockID]; //bug: this can happen before bflnl is filled if (FirstLastIdx == null) { continue; } blockID = NodeBlockToBlockMetaIndex[(int)n.BlockID]; if (!blockMiddlesDict.ContainsKey(blockID)) { continue; } var blockEntry = graph.ProcessData.BasicBlocksList[(int)n.BlockID]; Debug.Assert(blockEntry is not null); int blockNodeCount = blockEntry.Item2.Count; if (exceptionBlocks.TryGetValue(blockID, out NodeData? exceptionNode) && exceptionNode is not null) { for (int bIdx = 0; bIdx < blockNodeCount; bIdx++) { if (blockEntry.Item2[bIdx].Address == exceptionNode.Address) { blockNodeCount = bIdx + 1; break; } } } int midIdx = (int)Math.Ceiling((blockNodeCount - 1.0) / 2.0); offsetFromCenter = n.BlockIndex - midIdx; } else { externals += 1; FirstLastIdx = new Tuple <uint, uint>(n.Index, n.Index); offsetFromCenter = 0; blockMiddleNodesList.Add((int)n.Index); //external nodes dont have a block id so just give them a unique one //all that matters in the shader is it's unique blockID = blockMiddleNodesList.Count; blockMiddlesDict[blockID] = (int)n.Index; } int blockTopNodeIndex = -1; int blockBaseNodeIndex = -1; if (offsetFromCenter is 0) { if (graph.GetNode(FirstLastIdx.Item1)?.IncomingNeighboursSet.Count > 0) { blockTopNodeIndex = (int)FirstLastIdx.Item1; } else { //these are all back edges, which have 0 force /* * //the top of the block wasnt connected to anything * //there might be a connection below though * List<InstructionData>? blockInslist = graph.ProcessData.BasicBlocksList[(int)n.BlockID]?.Item2; * if (blockInslist is not null) * { * for (var i = 1; i < blockInslist.Count; i++) * { * uint n2Idx = (uint)(FirstLastIdx.Item1 + i); * NodeData? n2 = graph.GetNode(n2Idx); * if (n2 is not null && n2.IncomingNeighboursSet.Count > 0) * { * blockTopNodeIndex = (int)n2Idx; * break; * } * } * } */ } if (graph.GetNode(FirstLastIdx.Item2)?.OutgoingNeighboursSet.Count > 0) { blockBaseNodeIndex = (int)FirstLastIdx.Item2; } } blockDataInts[nodeIdx].MetaBlockIndex = blockID; blockDataInts[nodeIdx].OffsetFromCenter = offsetFromCenter; blockDataInts[nodeIdx].BlockTopEdgeList = blockTopNodeIndex; blockDataInts[nodeIdx].BlockBaseEdgeList = blockBaseNodeIndex != blockTopNodeIndex ? blockBaseNodeIndex : -1; //Debug.Assert(offsetFromCenter is not 0 || n.IsExternal || (blockMiddleNodesList[blockID] == (int)nodeIdx)); } blockMiddles = blockMiddleNodesList.ToArray(); blockData = blockDataInts; return(true); }