mesytec-mnode/external/taskflow-3.8.0/tfprof/js/index.js
2025-01-04 01:25:05 +01:00

995 lines
27 KiB
JavaScript

// Program: tfprof
// Author: Dr. Tsung-Wei Huang
'use strict';
const tfp = {
// main timeline svg
dom : null,
svg : null,
width: null,
height: null,
topMargin: 30,
bottomMargin: 40,
leftMargin: 100,
rightMargin: 40,
innerH: 30,
innerW: 20,
segMaxH: 18,
segH: null,
segHOfst: null,
// timeline chart
tlG: null,
tlW: null,
tlH: null,
tlXScale: d3.scaleLinear(),
tlWScale: d3.scaleBand(),
tlEScale: d3.scaleOrdinal(),
tlZScale: d3.scaleOrdinal(),
tlXAxis: d3.axisBottom(),
tlWAxis: d3.axisLeft(),
tlEAxis: d3.axisLeft(),
tlBrush: d3.brushX(),
tlBrushTimeout: null,
// load chart
loadG: null,
loadW: null,
loadH: null,
loadXScale: d3.scaleLinear(),
loadEScale: d3.scaleOrdinal(),
loadXAxis: d3.axisBottom(),
loadEAxis: d3.axisLeft(),
// overview chart
ovG: null,
ovXScale: d3.scaleLinear(),
ovXSel: null,
ovXDomain: null,
ovXAxis: d3.axisBottom(),
ovBrush: d3.brushX(),
ovH: 20,
// rank chart
rankG: null,
rankH: 300,
rankW: null,
rankXScale: d3.scaleBand(),
rankYScale: d3.scaleLinear(),
rankXAxis: d3.axisBottom(),
rankYAxis: d3.axisLeft(),
// legend
zColorMap: new Map([
['static', '#4682b4'],
['subflow', '#F1A42B'],
['condition', '#42B649'],
['module', '#0000FF'],
['async', '#292b2c'],
['clustered', '#999DA0']
]),
zScale: null,
// segmenet
disableHover: false,
// transition
transDuration: 700,
// data field
zoomXs: [], // scoped time data
zoomY : null, // scoped worker
view : "Cluster", // default view type
limit : 256,
maxLimit: 512,
data: null,
tfpFile: null,
numTasks: null,
numExecutors: null,
numWorkers: null,
timeFormat : function(d) {
if(d >= 1e9) return `${(d/1e9).toFixed(2)}G`;
if(d >= 1e6) return `${(d/1e6).toFixed(1)}M`;
if(d >= 1e3) return `${(d/1e3).toFixed(0)}K`;
return d;
}
};
async function fetchTFPData(file) {
const response = await fetch(file);
const json = await response.json();
return json;
}
async function queryInfo() {
const response = await fetch(`/queryInfo`, {
method: 'put'
});
let info = await response.json();
tfp.tfpFile = info.tfpFile;
tfp.numTasks = info.numTasks;
tfp.numExecutors = info.numExecutors;
tfp.numWorkers = info.numWorkers;
tfp.maxLimit = Math.max(1, Math.min(tfp.maxLimit, tfp.numTasks));
tfp.limit = Math.min(tfp.limit, tfp.maxLimit)
}
async function queryData(zoomX, zoomY, view, limit) {
//console.log(zoomY);
//$('#tfp_tb_loader').css("display", "block");
const response = await fetch(`/queryData`, {
method: 'put',
body: JSON.stringify({
zoomX: zoomX,
zoomY: zoomY,
view : view,
limit: limit
})
});
tfp.data = await response.json();
//console.log(tfp.data);
let eMeta = tfp.data.reduce((res, item) => {
res[item.executor] = [...res[item.executor] || [], item.worker];
return res;
}, {});
tfp.eMeta = Object.keys(eMeta).map(e => {
return {executor: e, workers: eMeta[e]}
});
//$('#tfp_tb_loader').css("display", "none");
}
function _adjustDim() {
tfp.width = tfp.dom.style('width').slice(0, -2)
- tfp.dom.style('padding-right').slice(0, -2)
- tfp.dom.style('padding-left').slice(0, -2);
tfp.segH = tfp.segMaxH * 0.8;
tfp.segHOfst = (tfp.segMaxH - tfp.segH) / 2;
const w = tfp.width - tfp.leftMargin - tfp.rightMargin - tfp.innerW;
tfp.tlW = w*0.80;
tfp.tlH = tfp.data.length * tfp.segMaxH;
tfp.loadW = w - tfp.tlW;
tfp.loadH = tfp.tlH;
tfp.ovW = tfp.tlW;
tfp.rankW = tfp.width - tfp.leftMargin - tfp.rightMargin;
tfp.height = tfp.topMargin + tfp.tlH + 2*tfp.innerH + tfp.ovH + tfp.rankH + tfp.bottomMargin;
tfp.svg.attr('width', tfp.width).attr('height', tfp.height);
}
function _render_tlXAxis() {
tfp.tlXScale
.domain(tfp.zoomXs[tfp.zoomXs.length - 1])
.range([0, tfp.tlW]).clamp(true);
tfp.tlXAxis.scale(tfp.tlXScale)
.tickSizeOuter(0)
.tickSize(-tfp.tlH)
.tickFormat(tfp.timeFormat)
//.tickFormat(d3.format('.2s'))
.ticks(numXTicks(tfp.tlW));
tfp.tlG.select('g.tfp-tl-x-axis')
.attr('transform', `translate(0, ${tfp.tlH})`)
.transition().duration(tfp.transDuration)
.call(tfp.tlXAxis)
.attr('font-size', 16);
}
function _render_tlWAxis() {
tfp.tlWScale
.domain(tfp.data.map(d=>d.worker))
.range([0, tfp.tlH]);
tfp.tlWAxis.scale(tfp.tlWScale)
.tickSizeOuter(0)
//.tickFormat(d => {
// const wl = d.split('.L');
// return +wl[1] == 0 ? wl[0] : `↳L${wl[1]}`;
//});
.tickFormat(d => d);
tfp.tlG.select('g.tfp-tl-w-axis')
//.attr('transform', `translate(${tfp.tlW}, 0)`)
.attr('transform', `translate(0, 0)`)
.transition().duration(tfp.transDuration)
.call(tfp.tlWAxis)
.attr('font-size', 14);
//tfp.tlG.select('g.tfp-tl-w-axis').selectAll('text')
// .on('click', d => console.log(d));
}
function _render_tlEAxis() {
tfp.tlEScale.domain(tfp.eMeta.map(d=>d.executor));
let cntLines = 0;
tfp.tlEScale.range(tfp.eMeta.map(d => {
const pos = (cntLines + d.workers.length/2)*tfp.segMaxH;
cntLines += d.workers.length;
return pos;
}));
//tfp.tlEAxis.scale(tfp.tlEScale).tickSizeOuter(0);
//tfp.tlG.select('g.tfp-tl-e-axis')
// .transition().duration(tfp.transDuration)
// .call(tfp.tlEAxis)
// .attr('font-size', 14)
//
//tfp.tlG.select('g.tfp-tl-e-axis').selectAll('text')
// .on('click', d => console.log(d));
// rect
var ed1 = tfp.tlG.select('g.tfp-tl-e-rect').selectAll('rect').data(tfp.eMeta);
ed1.exit().transition().duration(tfp.transDuration)
.style('stroke-opacity', 0)
.style('fill-opacity', 0)
.remove();
ed1 = ed1.merge(ed1.enter().append('rect')
.attr('x', 0).attr('y', 0).attr('height', 0).attr('width', 0)
//.on('mouseover', tfp.executorTooltip.show)
//.on('mouseout', tfp.executorTooltip.hide);
);
ed1.transition().duration(tfp.transDuration)
.attr('width', tfp.tlW)
.attr('height', d => tfp.segMaxH * d.workers.length)
.attr('y', d => tfp.tlEScale(d.executor) - d.workers.length/2*tfp.segMaxH);
}
function _render_tlZAxis() {
let zGroup = tfp.tlG.select('g.tfp-tl-legend');
zGroup.attr('transform', `translate(0, ${-tfp.segMaxH})`);
tfp.tlZScale.domain(Array.from(tfp.zColorMap.keys()));
tfp.tlZScale.range(Array.from(tfp.zColorMap.values()));
const zW = tfp.width - tfp.leftMargin - tfp.rightMargin;
const zH = tfp.segMaxH;
const binW = zW / tfp.tlZScale.domain().length;
let slot = zGroup.selectAll('g').data(tfp.tlZScale.domain());
slot.exit().remove();
const newslot = slot.enter().append('g')
.attr('transform', (d, i) => `translate(${binW * i}, -5)`);
newslot.append('rect');
newslot.append('text');
// .on('click', d => console.log("click legend", d));
slot = slot.merge(newslot);
slot.select('rect')
.attr('width', binW)
.attr('height', zH)
.attr('fill', d => tfp.tlZScale(d));
slot.select('text')
.text(d => d).attr('x', binW*0.5).attr('y', zH*0.5)
.style('font-size', 14);
}
function _render_tlSegs() {
var sd1 = tfp.tlG.select('g.tfp-tl-graph').selectAll('g').data(tfp.data)
sd1.exit().remove();
sd1 = sd1.merge(sd1.enter().append('g'));
sd1.attr('transform', (d, i) => `translate(0, ${i*tfp.segMaxH})`);
var sd2 = sd1.selectAll('rect').data(d => d.segs);
sd2.exit().remove();
sd2 = sd2.merge(sd2.enter().append('rect')
.attr('rx', 1)
.attr('ry', 1)
.attr('x', tfp.tlW/2) // here we initialize the rect to avoid
.attr('y', tfp.tlH/2) // NaN y error during transition
.attr('width', 0)
.attr('height', 0)
.style('fill-opacity', 0)
.on('mouseover.tlTooltip', tfp.tlTooltip.show)
.on('mouseout.tlTooltip', tfp.tlTooltip.hide)
.on("mouseover", function(d) {
if (tfp.disableHover) return;
const r = tfp.segHOfst; // enlarge ratio
d3.select(this).transition().duration(250).style('fill-opacity', 1)
.attr("width", d => d3.max([1, tfp.tlXScale(d.span[1])-tfp.tlXScale(d.span[0])]) + r)
.attr('height', tfp.segH+r)
.attr('x', d => tfp.tlXScale(d.span[0])-r/2)
.attr('y', d => tfp.segHOfst-r/2);
})
.on("mouseout", function(d) {
d3.select(this).transition().duration(250).style('fill-opacity', .8)
.attr("width", d => d3.max([1, tfp.tlXScale(d.span[1])-tfp.tlXScale(d.span[0])]))
.attr('height', tfp.segH)
.attr('x', d => tfp.tlXScale(d.span[0]))
.attr('y', d => tfp.segHOfst)
.style('fill', d => tfp.zColorMap.get(d.type));
})
.on('click', async function(d) {
const zoomX = d.span;
//console.log("zoom to ", zoomX);
tfp.zoomXs.push(zoomX);
await _onZoomX(zoomX, true);
})
);
sd2.transition().duration(tfp.transDuration)
.attr('x', d => tfp.tlXScale(d.span[0]))
.attr('width', d => d3.max([1, tfp.tlXScale(d.span[1])-tfp.tlXScale(d.span[0])]))
.attr('y', tfp.segHOfst)
.attr('height', tfp.segH)
.style('fill-opacity', .8)
.style('fill', d => tfp.zColorMap.get(d.type));
}
function _render_tlBrush() {
tfp.tlG.select('g.tfp-tl-brush')
.call(tfp.tlBrush.extent([[0, 0], [tfp.tlW, tfp.tlH]]));
}
function _render_tl() {
tfp.tlG.attr('transform', `translate(${tfp.leftMargin}, ${tfp.topMargin})`);
_render_tlXAxis(); // x-axis
_render_tlWAxis(); // w-axis
_render_tlEAxis(); // e-axis
_render_tlZAxis(); // z-axis
_render_tlSegs(); // s-rect
_render_tlBrush(); // brush
}
function _render_ovXAxis() {
tfp.ovXScale.domain(tfp.ovXDomain).range([0, tfp.ovW]).clamp(true);
tfp.ovXAxis.scale(tfp.ovXScale)
.tickSizeOuter(0)
.tickSize(-tfp.ovH)
.tickFormat(tfp.timeFormat)
.ticks(numXTicks(tfp.tlW));
tfp.ovG.select('g.tfp-ov-x-axis')
.attr('transform', `translate(0, ${tfp.ovH})`)
.transition().duration(tfp.transDuration)
.call(tfp.ovXAxis)
.attr('font-size', 16);
tfp.ovG.select('.tfp-ov-bg').attr('width', tfp.ovW).attr('height', tfp.ovH);
}
function _render_ovBrush() {
tfp.ovG.select('g.tfp-ov-brush')
.call(tfp.ovBrush.extent([[0, 0], [tfp.ovW, tfp.ovH]]))
.call(tfp.ovBrush.move, tfp.ovXSel.map(tfp.ovXScale))
}
function _render_ovInfo() {
tfp.ovG.select('.tfp-ov-info')
.attr('x', tfp.ovW + tfp.innerW)
.attr('y', tfp.ovH/2)
.attr('font-size', 16)
.html(`${d3.sum(tfp.data, d=>d.tasks)} tasks`);
}
function _render_ov() {
tfp.ovG.attr('transform',
`translate(${tfp.leftMargin}, ${tfp.topMargin + tfp.tlH + tfp.innerH})`
);
console.assert(tfp.ovXDomain && tfp.ovXSel);
_render_ovXAxis();
_render_ovBrush();
_render_ovInfo();
}
function _render_loadXAxis() {
tfp.loadXScale
.domain([0, d3.max(tfp.data, d=>d.totalTime)])
.range([0, tfp.loadW]).clamp(true);
tfp.loadXAxis.scale(tfp.loadXScale)
.tickSizeOuter(0)
.tickSize(-tfp.loadH)
.tickFormat(tfp.timeFormat)
.ticks(numXTicks(tfp.loadW));
tfp.loadG.select('g.tfp-load-x-axis')
.attr('transform', `translate(0, ${tfp.loadH})`)
.transition().duration(tfp.transDuration)
.call(tfp.loadXAxis)
.attr('font-size', 16);
}
function _render_loadEAxis() {
tfp.loadEScale.domain(tfp.eMeta.map(d=>d.executor));
let cntLines = 0;
tfp.loadEScale.range(tfp.eMeta.map(d => {
const pos = (cntLines + d.workers.length/2)*tfp.segMaxH;
cntLines += d.workers.length;
return pos;
}));
//tfp.loadEAxis.scale(tfp.loadEScale).tickSizeOuter(0);
//
//tfp.loadG.select('g.tfp-load-e-axis')
// .transition().duration(tfp.transDuration)
// .call(tfp.loadEAxis)
// .attr('font-size', 14)
//
//tfp.loadG.select('g.tfp-load-e-axis').selectAll('text')
// .on('click', d => console.log(d));
// rect
var ed1 = tfp.loadG.select('g.tfp-load-e-rect').selectAll('rect').data(tfp.eMeta);
ed1.exit().transition().duration(tfp.transDuration)
.style('stroke-opacity', 0)
.style('fill-opacity', 0)
.remove();
ed1 = ed1.merge(ed1.enter().append('rect')
.attr('x', 0).attr('y', 0).attr('height', 0).attr('width', 0)
//.on('mouseover', tfp.executorTooltip.show)
//.on('mouseout', tfp.executorTooltip.hide);
);
ed1.transition().duration(tfp.transDuration)
.attr('width', tfp.loadW)
.attr('height', d => tfp.segMaxH * d.workers.length)
.attr('y', d => tfp.loadEScale(d.executor) - d.workers.length/2*tfp.segMaxH);
}
function _render_loadGraph() {
var sd1 = tfp.loadG.select('g.tfp-load-graph').selectAll('g').data(tfp.data)
sd1.exit().remove();
sd1 = sd1.merge(sd1.enter().append('g'));
sd1.attr('transform', (d, i) => `translate(0, ${i*tfp.segMaxH})`);
var sd2 = sd1.selectAll('rect').data(d => d.load);
sd2.exit().remove();
sd2 = sd2.merge(sd2.enter().append('rect')
.attr('x', 0) // here we initialize the rect to avoid
.attr('y', 0) // NaN y error during transition
.attr('width', 0)
.attr('height', 0)
.style('fill-opacity', 0)
.on('mouseover.loadTooltip', tfp.loadTooltip.show)
.on('mouseout.loadTooltip', tfp.loadTooltip.hide)
.on("mouseover", function(d) {
if (tfp.disableHover) return;
const r = tfp.segHOfst; // enlarge ratio
d3.select(this).transition().duration(250).style('fill-opacity', 1)
.attr("width", d => d3.max([1, tfp.loadXScale(d.span[1])-tfp.loadXScale(d.span[0])]) + r)
.attr('height', tfp.segH+r)
.attr('x', d => tfp.loadXScale(d.span[0])-r/2)
.attr('y', d => tfp.segHOfst-r/2);
})
.on("mouseout", function(d) {
d3.select(this).transition().duration(250).style('fill-opacity', .8)
.attr("width", d => d3.max([1, tfp.loadXScale(d.span[1])-tfp.loadXScale(d.span[0])]))
.attr('height', tfp.segH)
.attr('x', d => tfp.loadXScale(d.span[0]))
.attr('y', d => tfp.segHOfst)
.style('fill', d => tfp.zColorMap.get(d.type));
})
);
sd2.transition().duration(tfp.transDuration)
.attr('x', d => tfp.loadXScale(d.span[0]))
.attr('width', d => d3.max([1, tfp.loadXScale(d.span[1])-tfp.loadXScale(d.span[0])]))
.attr('y', tfp.segHOfst)
.attr('height', tfp.segH)
.style('fill-opacity', .8)
.style('fill', d => tfp.zColorMap.get(d.type));
}
function _render_load() {
tfp.loadG.attr('transform',
`translate(${tfp.width-tfp.rightMargin-tfp.loadW}, ${tfp.topMargin})`
);
_render_loadXAxis();
_render_loadEAxis();
_render_loadGraph();
}
function _render_rankGraph() {
//var from = Math.max(0, Math.round($('#tfp_tb_rank_from').val()) - 1);
//var to = Math.round($('#tfp_tb_rank_to').val());
//var limit = (from >= to) ? [0, 50] : [from, to]
// process data
let rank = [];
for(let w=0; w<tfp.data.length; w++) {
for(let s=0; s<tfp.data[w].segs.length; s++) {
rank.push([w, s, tfp.data[w].segs[s].span[1] - tfp.data[w].segs[s].span[0]]);
//for(let c=tfp.data[w].segs[s].cluster[0]; c<=tfp.data[w].segs[s].cluster[1]; c++) {
// rank.push([w, c, tfp.db.data[w].segs[c].span[1] - tfp.db.data[w].segs[c].span[0]]);
//}
}
}
//rank = rank.sort((a, b) => b[2] - a[2]).slice(limit[0], limit[1]);
rank = rank.sort((a, b) => b[2] - a[2]);
// x-axis
tfp.rankXScale.domain(rank).range([0, tfp.rankW]).padding(0.2);
tfp.rankXAxis.scale(tfp.rankXScale).tickFormat('');
tfp.rankG.select('g.tfp-rank-x-axis')
.attr('transform', `translate(0, ${tfp.rankH})`)
.transition().duration(tfp.transDuration)
.call(tfp.rankXAxis);
// y-axis
const maxY = rank.length ? rank[0][2] : 0;
tfp.rankYScale.domain([0, maxY]).range([tfp.rankH, 0]);
tfp.rankYAxis.scale(tfp.rankYScale)
.tickFormat(tfp.timeFormat)
.tickSize(-tfp.rankW);
tfp.rankG.select('g.tfp-rank-y-axis')
//.transition().duration(tfp.transDuration)
.call(tfp.rankYAxis)
.attr('font-size', 14);
var bars = tfp.rankG.select('g.tfp-rank-graph').selectAll('rect').data(rank);
bars.exit().remove();
let newBars = bars.enter().append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', 0)
.attr('height', 0)
.style('fill-opacity', 0)
.on('mouseover.rankTooltip', tfp.rankTooltip.show)
.on('mouseout.rankTooltip', tfp.rankTooltip.hide)
.on("mouseover", function(d) {
if (tfp.disableHover) return;
d3.select(this).transition().duration(250).style('fill-opacity', 1)
.attr('x', tfp.rankXScale)
.attr('y', d=>tfp.rankYScale(d[2]))
.attr('width', tfp.rankXScale.bandwidth())
.attr('height', d=>tfp.rankH-tfp.rankYScale(d[2]))
.style('fill', d => tfp.zColorMap.get(tfp.data[d[0]].segs[d[1]].type));
})
.on("mouseout", function(d) {
d3.select(this).transition().duration(250).style('fill-opacity', .8)
.attr('x', tfp.rankXScale)
.attr('y', d=>tfp.rankYScale(d[2]))
.attr('width', tfp.rankXScale.bandwidth())
.attr('height', d=>tfp.rankH-tfp.rankYScale(d[2]))
.style('fill', d => tfp.zColorMap.get(tfp.data[d[0]].segs[d[1]].type));
});
bars = bars.merge(newBars);
bars.transition().duration(tfp.transDuration)
.attr('x', tfp.rankXScale)
.attr('y', d=>tfp.rankYScale(d[2]))
.attr('width', tfp.rankXScale.bandwidth())
.attr('height', d=>tfp.rankH-tfp.rankYScale(d[2]))
.style('fill-opacity', .8)
.style('fill', d => tfp.zColorMap.get(tfp.data[d[0]].segs[d[1]].type));
// xlabel
tfp.rankG.select('text.tfp-rank-label')
.html(`Top-${rank.length} Critical Tasks`);
}
function _render_rank() {
tfp.rankG.attr('transform',
`translate(${tfp.leftMargin}, ${tfp.topMargin + tfp.tlH + 2*tfp.innerH + tfp.ovH})`
);
tfp.rankG.select('text.tfp-rank-label')
.attr('transform', `translate(${tfp.rankW/2}, ${tfp.rankH + tfp.innerH/2})`);
_render_rankGraph();
}
async function _onZoomX(zoomX, refreshBrush) {
await queryData(zoomX, tfp.zoomY, tfp.view, tfp.limit);
_render_tlXAxis();
_render_tlSegs();
_render_loadXAxis();
_render_loadGraph();
_render_ovInfo();
_render_rankGraph();
if(refreshBrush) {
tfp.ovXSel = zoomX;
_render_ovBrush();
}
}
function numXTicks(W) {
return Math.max(2, Math.min(12, Math.round(W * 0.012)));
}
function _adjust_tb() {
// flattern the executors and workers
let flat = [];
for(let l=0; l<tfp.eMeta.length; l++) {
flat.push(`E${tfp.eMeta[l].executor}`)
flat.push(...tfp.eMeta[l].workers)
}
// worker tb
var wtb = d3.select('#tfp_tb_workers').selectAll('a').data(flat);
wtb.selectAll('input').remove();
wtb.selectAll('label').remove();
wtb.exit().remove();
wtb = wtb.merge(wtb.enter().append('a')
.attr('class', 'dropdown-item')
);
wtb.append('input')
.attr('type', 'checkbox')
.attr('class', d=>{ return d.split('.').length == 1 ? 'mr-2' : 'mr-2 ml-4'})
.attr('value', d=>d)
.attr('id', d=>d)
.property('checked', true)
.attr('name', d=>{ return d.split('.').length == 1 ? 'executor' : 'worker'});
wtb.append('label').attr('for', d=>d).text(d => {
const wl = d.split('.');
if(wl.length == 1) {
return `Executor ${wl[0]}`;
}
else {
return `${d} (Worker ${wl[1]} @ Level ${wl[2]})`;
}
});
// limit tb
let numTasks = d3.sum(tfp.data, d=>d.segs.length);
let limit = document.getElementById('tfp_tb_limit');
limit.min = 1;
limit.max = tfp.maxLimit;
limit.value = tfp.limit;
// rank tb
//document.getElementById('tfp_tb_rank_from').value = 1;
//document.getElementById('tfp_tb_rank_to').value = 50;
}
function _render(adjustDim) {
if(adjustDim) {
_adjustDim();
}
_render_tl();
_render_load();
_render_ov();
_render_rank();
}
async function main() {
await queryInfo();
$('#tfp_tb_finfo').text(tfp.tfpFile.split(/(\\|\/)/g).pop()); // filename
$('#tfp_tb_tinfo').text(`${tfp.numTasks} tasks`);
$('#tfp_tb_einfo').text(`${tfp.numExecutors} executors`);
$('#tfp_tb_winfo').text(`${tfp.numWorkers} workers`);
await queryData(null, null, tfp.view, tfp.limit);
let minX = null, maxX = null;
for(let i=0; i<tfp.data.length; i++) {
let l = tfp.data[i].segs.length;
if(l > 0) {
if(minX == null || tfp.data[i].segs[0].span[0] < minX) {
minX = tfp.data[i].segs[0].span[0];
}
if(maxX == null || tfp.data[i].segs[l-1].span[1] > maxX) {
maxX = tfp.data[i].segs[l-1].span[1];
}
}
}
tfp.ovXDomain = [minX, maxX];
tfp.ovXSel = [minX, maxX];
tfp.zoomXs = [[minX, maxX]];
tfp.zoomY = tfp.data.map(d=>d.worker);
// adjust the margin to the current nav
tfp.topMargin += $('nav').outerHeight(true);
// clean-up the loader
$('#tfp_loader').css("display", "none");
//$('#tfp_toolbar').css("display", "block")
$('#tfp').css("display", "block");
// initialize static field
tfp.dom = d3.select('#tfp');
tfp.svg = tfp.dom.append('svg').attr('width', 0).attr('height', 0);
tfp.tlG = tfp.svg.append('g').attr('class', 'tfp-tl-group');
tfp.tlG.append('g').attr('class', 'tfp-tl-legend');
tfp.tlG.append('g').attr('class', 'tfp-tl-x-axis');
tfp.tlG.append('g').attr('class', 'tfp-tl-w-axis');
tfp.tlG.append('g').attr('class', 'tfp-tl-e-axis');
tfp.tlG.append('g').attr('class', 'tfp-tl-e-rect'); // layer 1
tfp.tlG.append('g').attr('class', 'tfp-tl-brush'); // layer 2
tfp.tlG.append('g').attr('class', 'tfp-tl-graph'); // layer 3
tfp.ovG = tfp.svg.append('g').attr('class', 'tfp-ov-group');
tfp.ovG.append('rect').attr('class', 'tfp-ov-bg');
tfp.ovG.append('g').attr('class', 'tfp-ov-x-axis');
tfp.ovG.append('g').attr('class', 'tfp-ov-brush');
tfp.ovG.append('text').attr('class', 'tfp-ov-info');
tfp.loadG = tfp.svg.append('g').attr('class', 'tfp-load-group');
tfp.loadG.append('g').attr('class', 'tfp-load-x-axis');
tfp.loadG.append('g').attr('class', 'tfp-load-e-rect'); // layer 1
tfp.loadG.append('g').attr('class', 'tfp-load-graph'); // layer 2
tfp.rankG = tfp.svg.append('g').attr('class', 'tfp-rank-group');
tfp.rankG.append('g').attr('class', 'tfp-rank-x-axis');
tfp.rankG.append('g').attr('class', 'tfp-rank-y-axis');
tfp.rankG.append('g').attr('class', 'tfp-rank-graph');
tfp.rankG.append('text').attr('class', 'tfp-rank-label');
// tl brush event
tfp.tlBrush.on('brush', function() {
const s = d3.event.selection;
if(s) {
tfp.ovXSel = s.map(x=>tfp.tlXScale.invert(x));
_render_ovBrush();
}
})
.on('end', async function() {
if(!d3.event.sourceEvent) return;
const s = d3.event.selection;
// Consume the brush action
if (s) {
const zoomX = s.map(tfp.tlXScale.invert);
//console.log("zoom to ", zoomX);
tfp.zoomXs.push(zoomX);
await _onZoomX(zoomX, false);
tfp.tlG.select("g.tfp-tl-brush").call(tfp.tlBrush.move, null);
}
// double-click to the previous cache
else {
if (!tfp.tlBrushTimeout) {
return (tfp.tlBrushTimeout = setTimeout(() => {
tfp.tlBrushTimeout = null;
}, 350));
}
if(tfp.zoomXs.length > 1) {
let zoomX = tfp.zoomXs.pop();
while(tfp.zoomXs.length > 1) {
if(zoomX[0] == tfp.zoomXs[tfp.zoomXs.length-1][0] &&
zoomX[1] == tfp.zoomXs[tfp.zoomXs.length-1][1]) {
tfp.zoomXs.pop();
}
else break;
}
await _onZoomX(tfp.zoomXs[tfp.zoomXs.length-1], true);
}
}
});
// ov brush event
tfp.ovBrush.on('end', async function() {
if(d3.event.sourceEvent && d3.event.selection) {
if(d3.event.sourceEvent.type === "mouseup") {
const zoomX = d3.event.selection.map(tfp.ovXScale.invert);
//console.log("ovBrush fires zoomX", zoomX);
tfp.zoomXs.push(zoomX);
await _onZoomX(zoomX, false);
}
}
});
// tl tooltips
tfp.tlTooltip = d3.tip()
.attr('class', 'tfp-tooltip')
.direction('s')
.offset([10, 0])
.html(d => {
return `Type: ${d.type}<br>
Name: ${d.name}<br>
Span: [${d.span}]<br>
Time: ${d.span[1]-d.span[0]}`;
});
tfp.svg.call(tfp.tlTooltip);
// load tooltip
tfp.loadTooltip = d3.tip()
.attr('class', 'tfp-tooltip')
.direction('s')
.offset([10, 0])
.html(d => {
//const p = ((d[1]-d[0]) * 100 / (d.data.totalTime)).toFixed(2);
return `Type: ${d.type}<br>
Total Time: ${d.span[1]-d.span[0]}<br>
Ratio: ${d.ratio}%`;
});
tfp.svg.call(tfp.loadTooltip);
// rank tooltip
tfp.rankTooltip = d3.tip()
.attr('class', 'tfp-tooltip')
.direction('s')
.offset([10, 0])
.html(d=> {
return `Worker: ${tfp.data[d[0]].worker}<br>
Type: ${tfp.data[d[0]].segs[d[1]].type}<br>
Name: ${tfp.data[d[0]].segs[d[1]].name}<br>
Time: ${d[2]}`
});
tfp.svg.call(tfp.rankTooltip);
_adjust_tb();
_render(true)
// set up the jquery
$("#tfp_tb_workers").on('click', function(event){
event.stopPropagation();
});
$("#tfp_tb_workers input[name='executor']").on('click', async function(event) {
event.stopPropagation();
//console.log("executor click!!", $(this).val(), $(this).is(':checked'));
let v = $(this).is(':checked');
let e = $(this).val();
$.each($("#tfp_tb_workers input[name='worker']"), function(){
if($(this).val().split('.')[0] == e) {
$(this).prop('checked', v);
}
});
var zoomY = [];
$.each($("input[name='worker']:checked"), function(){
zoomY.push($(this).val());
});
tfp.ovXSel = tfp.zoomXs[tfp.zoomXs.length-1];
tfp.zoomY = zoomY;
await queryData(tfp.zoomXs[tfp.zoomXs.length-1], tfp.zoomY, tfp.view, tfp.limit);
_render(true);
});
$("#tfp_tb_workers input[name='worker']").on('click', async function(event) {
event.stopPropagation();
//console.log("worker click!!", $(this).val(), $(this).is(':checked'));
let v = $(this).is(':checked');
let e = $(this).val().split('.')[0];
// check or uncheck the executor
let m = true;
$.each($("#tfp_tb_workers input[name='worker']"), function(){
if($(this).val().split('.')[0] == e && !$(this).is(':checked')) {
m = false;
}
});
$(`#${e}`).prop('checked', m);
var zoomY = [];
$.each($("input[name='worker']:checked"), function(){
zoomY.push($(this).val());
});
tfp.ovXSel = tfp.zoomXs[tfp.zoomXs.length-1];
tfp.zoomY = zoomY;
await queryData(tfp.zoomXs[tfp.zoomXs.length-1], tfp.zoomY, tfp.view, tfp.limit);
_render(true);
});
$('#tfp_tb_view a').on('click', async function() {
//event.stopPropagation(); // keep dropdown alive
tfp.view = $(this).text();
//console.log($(this).siblings('.hidden').text());
$(this).parent().siblings('.btn').text($(this).text());
tfp.ovXSel = tfp.zoomXs[tfp.zoomXs.length-1];
await queryData(tfp.zoomXs[tfp.zoomXs.length-1], tfp.zoomY, tfp.view, tfp.limit);
_render(false);
});
$('#tfp_tb_reset_zoom').on('click', async function() {
tfp.zoomXs = [tfp.ovXDomain]; // clear cached data
await _onZoomX(tfp.zoomXs[tfp.zoomXs.length-1], true);
});
$('#tfp_tb_limit').on('input change', function (){
$(this).siblings('.btn').text($(this).val());
if($(this).data('timeout')) {
clearTimeout($(this).data('timeout'));
}
// get selected option and change background
$(this).data('timeout', setTimeout(async ()=>{
tfp.limit = +$(this).val();
tfp.ovXSel = tfp.zoomXs[tfp.zoomXs.length-1];
await queryData(tfp.zoomXs[tfp.zoomXs.length-1], tfp.zoomY, tfp.view, tfp.limit);
_render(false);
}, 1000));
});
}
main();