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();
}
}