let executionGraph; let eventStructureGraph; let relationOptions = { rf: { label: "rf", optional: true, visible: true, edgeConfig: { color: { color: 'red', highlight: 'red' }, label: 'rf' } }, dep: { label: "sdep", optional: true, visible: true, edgeConfig: { color: { color: 'orange', highlight: 'orange' }, label: 'sdep', dashes: true, smooth: { enabled: true, type: 'curvedCW' } } }, lck: { label: "lck", optional: true, visible: true, edgeConfig: { color: { color: 'grey', highlight: 'grey' }, label: 'lck', dashes: true, smooth: { enabled: true, type: 'curvedCCW' } } }, co: { label: "co", optional: true, visible: true, edgeConfig: { color: { color: 'blue', highlight: 'blue' }, label: 'co' } }, hb: { label: "hb", optional: true, visible: false, edgeConfig: { color: { color: 'green', highlight: 'green' }, label: 'hb' } }, po: { label: "po", optional: true, visible: true, edgeConfig: { color: { color: 'black', highlight: 'black' }, label: 'po' } }, sw: { label: "sw", optional: true, visible: true, edgeConfig: { color: { color: '#8B0A50', highlight: '#8B0A50' }, label: 'sw' } }, rs: { label: "rs", optional: true, visible: true, edgeConfig: { color: { color: 'black', highlight: 'black' }, label: 'rs' } }, psc: { label: "psc", optional: true, visible: true, edgeConfig: { color: { color: 'black', highlight: 'black' }, label: 'psc' } }, race: { label: "race", optional: true, visible: true, edgeConfig: { color: { color: 'black', highlight: 'black' }, label: 'race' } } } let memoryOrderAnnotations = { NonAtomic: "na", Relaxed: "rlx", Release: "rel", Acquire: "acq", ReleaseAcquire: "a/r", SC: "sc" } function eventToExecutionGraphLabel(eventArray) { let eventArrayFlat = eventArray.flat(4); let eventType = eventArrayFlat[1]; let label = `${eventArrayFlat[0]}:${eventType}`; switch (eventType) { case "R": label += `${memoryOrderAnnotations[eventArrayFlat[5]]} ${eventArrayFlat[2]} = ${eventArrayFlat[3]}`; break; case "W": label += `${memoryOrderAnnotations[eventArrayFlat[4]]} ${eventArrayFlat[2]} = ${eventArrayFlat[3]}`; break; case "F": break; case "U": break; case "L": break; } return label; } function eventToEventStructureLabel(eventArray) { let eventArrayFlat = eventArray.flat(4); let eventType = eventArrayFlat[1]; let label = `${eventArrayFlat[0]}\n${eventType}`; switch (eventType) { case "R": label += ` ${eventArrayFlat[2]} ${eventArrayFlat[3]}`; break; case "W": label += ` ${eventArrayFlat[2]} ${eventArrayFlat[3]}`; break; case "F": break; case "U": break; case "L": break; } return label; } function filterRelationForExecutionEvents(eventIdList, relation) { return relation.filter((x) => eventIdList.includes(x[0]) && eventIdList.includes(x[1])); } function clusterPositionEventsByAcyclicRelation(eventIdList, relation, verticalNodeDistance = 100) { let yVal = 0; let nodeObjectList = []; let numberOfClusters = 0; let clusterId; let filteredRelation = filterRelationForExecutionEvents(eventIdList, relation); //Root node, e, is not a descendent in any of the ancestor-descendent pairs in the relation let rootNodes = eventIdList.filter((e) => filteredRelation.every((x) => x[1] !== e)); let collator = new Intl.Collator('en', {numeric: true, sensitivity: 'base'}); // collator used later to sort nodes by id for cluster display purposes let childNodes = rootNodes.slice().sort(collator.compare); while (childNodes.length > 0) { for (node of childNodes) { let parents = filteredRelation.filter((x) => x[1] == node).map((x) => x[0]); let numberofSiblings = parents.length > 0 ? filteredRelation.filter((x) => x[0] == parents[0]).length - 1 : 0; if (parents.length == 1 && numberofSiblings == 0) { clusterId = nodeObjectList.find(x => x.id == parents[0]).clusterId; } else { clusterId = numberOfClusters; numberOfClusters += 1; } nodeObjectList.push({ id: node, y: yVal, clusterId }); } //Map current childNodes to their children in relation childNodes = childNodes.map((n) => filteredRelation.filter((x) => x[0] === n).map((x) => x[1])).flat().sort(collator.compare); yVal += verticalNodeDistance; } return nodeObjectList; } function toggleEdge(value) { let visible = relationOptions[value].visible; relationOptions[value].visible = !visible; renderExecutionGraph(); } function redrawGraphs() { renderExecutionGraph(); if (document.getElementById("event-structure-checkbox").checked) { renderEventStructureGraph(); } } function displayGraphRenderingOptions() { document.getElementById("execution-graph-options-render").innerHTML = ""; let element = document.getElementById("execution-graph-options-edges"); element.innerHTML = ""; Object.entries(relationOptions).forEach((entry => { if (entry[1].optional) { element.innerHTML += ""; } })); Object.entries(relationOptions).forEach((entry => { if (!entry[1].optional) { element.innerHTML += ""; } })); } function clearExecutionGraphView() { document.getElementById("execution-graph-heading").setAttribute("hidden", true); document.getElementById('execution-graph').innerHTML = ""; document.getElementById("execution-graph-options-render").innerHTML = ""; document.getElementById("execution-graph-options-edges").innerHTML = ""; } function clearEventStructureGraphView() { document.getElementById("event-structure-graph-heading").setAttribute("hidden", true); document.getElementById('event-structure-graph').innerHTML = ""; } function renderExecutionGraph() { if (numberOfViewableExecutions > 0) { document.getElementById("execution-graph-heading").removeAttribute("hidden"); let labelledEvents = clusterPositionEventsByAcyclicRelation(currentExecution.event_id_list, model_denotation.les.po_relation); let nodes = labelledEvents.map((obj) => { let eventArray = model_denotation.les.event_list.find((arr) => arr[0] === obj.id); obj['label'] = eventToExecutionGraphLabel(eventArray); obj['x'] = obj.clusterId * 150; return obj; }); let allRelations = currentExecution.relations.concat([["po", model_denotation.les.po_relation]]); let edges = allRelations.map( (entry) => { let relation_name = entry[0]; let relation = entry[1]; return relation.map((pair) => { let obj = { ...relationOptions[relation_name].edgeConfig }; obj['from'] = pair[0]; obj['to'] = pair[1]; if ('color' in obj) { let fontObj = {}; fontObj['color'] = obj.color.color; obj['font'] = fontObj; } obj['hidden'] = !relationOptions[relation_name].visible; return obj; }) } ).flat(); let compoundedEdges = edges.reduce((acc, edge) => { if (!edge.hidden) { if (acc.some((e) => e.to == edge.to && e.from == edge.from)) { let collidingEdge = acc.find((e) => e.to == edge.to && e.from == edge.from) collidingEdge.label += `, ${edge.label}`; if (!('compoundEdge' in collidingEdge)) { collidingEdge['compoundEdge'] = true; collidingEdge['color'] = { color: 'black', highlight: 'black' }; collidingEdge['dashes'] = false; collidingEdge['smooth'] = { enabled: false }; collidingEdge['font'] = { color: 'black' }; } } else { acc.push(edge); } } return acc; }, []); let data = { nodes: new vis.DataSet(nodes), edges: new vis.DataSet(compoundedEdges) }; let options = { nodes: { color: 'white', borderWidth: 0, physics: false }, edges: { arrows: 'to', font: { background: 'white' } }, physics: { enabled: false }, interaction: { zoomView: false } }; let container = document.getElementById('execution-graph'); executionGraph = new vis.Network(container, data, options); displayGraphRenderingOptions(); } else { clearExecutionGraphView(); } } function renderEventStructureGraph() { document.getElementById("event-structure-graph-heading").removeAttribute("hidden"); let labelledEvents = clusterPositionEventsByAcyclicRelation(model_denotation.les.event_list.map((x) => parseInt(x[0])).sort(), model_denotation.les.po_relation, 50); let nodes = new vis.DataSet(model_denotation.les.event_list.map((event) => { let obj = labelledEvents.find((x) => x.id == event[0]); obj['label'] = eventToEventStructureLabel(event); return obj; })); let edges = new vis.DataSet(model_denotation.les.po_relation.map((pair) => { return { to: pair[1], from: pair[0], arrows: { to: { enabled: true, type: 'vee', scaleFactor: 0.5 } } }; }).sort((a, b) => a.to > b.to ? 1 : a.to < b.to ? -1 : 0)); let dep_relation = []; let data = { nodes: nodes, edges: edges }; let options = { nodes: { shape: 'box', color: { background: 'EBEBFD', border: 'black' }, borderWidth: 0.5, font: { face: 'Computer Modern', multi: 'html', align: 'right' }, physics: false, margin: { top: -12.5 } }, layout: { hierarchical: { enabled: true, direction: 'UD', sortMethod: 'directed', nodeSpacing: 100, levelSeparation: 75, shakeTowards: 'roots' } }, physics: { enabled: false } }; let container = document.getElementById('event-structure-graph'); eventStructureGraph = new vis.Network(container, data, options); eventStructureGraph.on('afterDrawing', () => { eventStructureGraph.off('afterDrawing'); options.layout = { hierarchical: { enabled: false } } eventStructureGraph.setOptions(options); edges.add(model_denotation.les.conflict_relation.map((pair) => { return { to: pair[1], from: pair[0], dashes: true, color: 'white', width: 2, label: 'conflict', font: { vadjust: 10 }, background: { enabled: true, color: "A42015", size: 5 } }; }).concat(dep_relation.map((pair) => { return obj = { to: pair[1], from: pair[0], color: { color: 'orange', highlight: 'orange' }, label: 'sdep', dashes: true, smooth: { enabled: true, type: 'curvedCCW' }, arrows: { to: { enabled: true, scaleFactor: 0.5 } }, font: { color: 'orange' } }; }))); }); } function setEventStructurevisibility(show) { if (show) { renderEventStructureGraph(); } else { clearEventStructureGraphView(); } }