You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.9 KiB
96 lines
3.9 KiB
const colors = ["7aa2f7", "ff9e64", "f7768e", "2ac3de", "7dcfff", "1abc9c", "9ece6a", "e0af68", "bb9af7", "9d7cd8", "ff007c"];
|
|
|
|
const formatTime = (ms) => {
|
|
if (ms<=1e3) return `${ms}us`;
|
|
if (ms<=1e6) return `${(ms*1e-3).toFixed(2)}ms`;
|
|
return `${(ms*1e-6).toFixed(2)}s`;
|
|
}
|
|
|
|
async function main() {
|
|
const { traceEvents } = await (await fetch("/get_profile")).json();
|
|
const root = createChild("div.root", document.querySelector("body"));
|
|
const list = createChild("div.list", root);
|
|
const data = [];
|
|
const nameColors = {}; // event names get a unique color
|
|
const procNames = {};
|
|
for (const e of traceEvents) {
|
|
if (e.name === "process_name") {
|
|
const proc = createChild(`div.proc-${e.pid}`, list);
|
|
createChild("p.process-name", proc).textContent = e.args.name;
|
|
procNames[e.pid] = e.args.name;
|
|
}
|
|
else if (e.name === "thread_name") {
|
|
const thread = createChild(`div.thread-${e.pid}-${e.tid}`, `proc-${e.pid}`);
|
|
createChild("p.thread-name", thread).textContent = e.args.name;
|
|
}
|
|
else if (e.ph === "X") {
|
|
const thread = document.getElementById(`thread-${e.pid}-${e.tid}`);
|
|
if (!(e.name in nameColors)) nameColors[e.name] = colors[data.length%(colors.length-1)];
|
|
data.push({ ...e, y:rect(thread).y, color:`#${nameColors[e.name]}`, proc:procNames[e.pid] });
|
|
}
|
|
}
|
|
// render graph
|
|
const svg = d3.select(root).append("svg").attr("width", "100%");
|
|
const { y, width } = rect(svg.node()); // global coordinates
|
|
const render = svg.append("g").attr("transform", `translate(0, ${y})`);
|
|
const timestamps = data.map(t => t.ts);
|
|
const st = Math.min(...timestamps);
|
|
const timeScale = d3.scaleLinear().domain([0, Math.max(...timestamps)-st]).range([y, width]);
|
|
const timeAxis = render.append("g").call(d3.axisTop(timeScale).tickFormat(formatTime));
|
|
list.style = `margin-top: ${rect(timeAxis.node()).bottom}px;`;
|
|
// rescale time based coordinates to fit screen
|
|
for (e of data) {
|
|
e.st = e.ts-st;
|
|
e.x = timeScale(e.st);
|
|
e.width = timeScale(e.dur);
|
|
}
|
|
render.selectAll("rect").data(data).join("rect").attr("fill", d => d.color).attr("x", d => d.x).attr("y", d => d.y).attr("width", d => d.width)
|
|
.attr("height", 20);
|
|
render.call(d3.brush().on("end", (e) => {
|
|
if (!e.selection) return renderTable({ data });
|
|
const [[x0, y0], [x1, y1]] = e.selection;
|
|
const newData = data.filter(d => d.x>=x0 && d.x<=x1 && d.y>=y0 && d.y<=y1);
|
|
renderTable({ data: newData });
|
|
}));
|
|
createChild("div.table-root", root);
|
|
renderTable({ data });
|
|
}
|
|
|
|
const rect = (e) => e.getBoundingClientRect();
|
|
|
|
const createChild = (es, p) => {
|
|
const parts = es.split(".", 2);
|
|
if (typeof p === "string") p = document.getElementById(p);
|
|
const ret = p.appendChild(document.createElement(parts[0]));
|
|
if (parts.length !== 1) ret.id = parts[1];
|
|
return ret;
|
|
}
|
|
|
|
const columnNames = {"name":"Name", "st":"Start Time", "dur":"Duration", "proc":"Process"};
|
|
const tableState = {data:null, sortBy:null, asc:true};
|
|
function renderTable(newState) {
|
|
const { data, sortBy, asc } = Object.assign(tableState, newState);
|
|
const root = document.getElementById("table-root");
|
|
root.innerHTML = "";
|
|
const table = createChild("table", root);
|
|
const thead = createChild("tr", createChild("thead", table));
|
|
for (const [k,v] of Object.entries(columnNames)) {
|
|
const th = createChild(`th.${k}`, thead);
|
|
th.innerText = v;
|
|
th.onclick = (e) => renderTable(k === sortBy ? { asc:!asc } : { sortBy:k, asc:true });
|
|
}
|
|
if (sortBy != null) {
|
|
data.sort((a, b) => asc ? a[sortBy]-b[sortBy] : b[sortBy]-a[sortBy]); // inplace sort
|
|
document.getElementById(sortBy).className = asc ? "sorted-asc" : "sorted-desc";
|
|
}
|
|
const tbody = createChild("tbody", table);
|
|
for (const d of data) {
|
|
const row = createChild("tr", tbody);
|
|
for (const k of Object.keys(columnNames)) {
|
|
let formatted = typeof d[k] === "string" ? d[k] : formatTime(d[k]);
|
|
createChild("td", row).innerText = formatted;
|
|
}
|
|
}
|
|
}
|
|
|
|
main()
|
|
|