{#let metrics=info:buildMetrics.get} {#include main fluid=true} {#style} #chartjs-tooltip { color: white; border-radius: 0em 1em 1em 1em; padding: 1em; } .tooltip-steps { padding-left: 0.5em; margin-bottom: 0; font-size: 1.2em; } .tooltip-title { font-size: 1.5em; font-weight: bold; } {/style} {#title}Build Steps{/title} {#body}

Executed {metrics.records.size} build steps on {metrics.threadSlotRecords.keys.size} threads in {metrics.duration} ms.

Build Steps Concurrent Execution Chart

{#for record in metrics.records} {/for}
# Build Step Started Duration Thread Actions
{record_count} {record.stepId} {record.started} {record.duration} ms {record.thread} {#if !metrics.dependencyGraphs.get(record.stepId).links.empty} {/if}

Build Steps Concurrent Execution Chart

{#script} const labels = [ {#each metrics.slots}{it}{#if it_hasNext},{/if}{/each} ]; const nextColor = function(number) { const hue = number * 137.508; // golden angle approximation return "hsl(" + hue +",80%,65%)"; }; const threadBuildSteps = { {#for entry in metrics.threadSlotRecords.entrySet} "{entry.key}" : [ {#for data in entry.value} [ {#for stepId in data} '{stepId}',{/for} ], {/for} ], {/for} } const data = { labels: labels, datasets: [ {#for entry in metrics.threadSlotRecords.entrySet} { label: '{entry.key}', data: [{#each entry.value}{#if it.empty}0{#else}1{/if},{/each}], backgroundColor: nextColor({entry_index}), }, {/for} ] }; const externalTooltip = (context) => { const { chart, tooltip } = context; let tooltipEl = document.getElementById('chartjs-tooltip'); // Create element on first render if (!tooltipEl) { tooltipEl = document.createElement('div'); tooltipEl.id = 'chartjs-tooltip'; chart.canvas.parentNode.appendChild(tooltipEl); } // Hide if no tooltip if (tooltip.opacity === 0) { tooltipEl.style.opacity = 0; return; } // Set caret Position tooltipEl.classList.remove('above', 'below', 'no-transform'); if (tooltip.yAlign) { tooltipEl.classList.add(tooltip.yAlign); } else { tooltipEl.classList.add('no-transform'); } let innerHtml = ''; // We expect a single tooltip item const tooltipItem = context.tooltip.dataPoints[0]; const thread = tooltipItem.dataset.label; const buildStepIds = threadBuildSteps[thread][tooltipItem.dataIndex]; innerHtml += '
' + thread + '
'; innerHtml += ''; let ulRoot = tooltipEl; ulRoot.innerHTML = innerHtml; const position = context.chart.canvas.getBoundingClientRect(); const bodyFont = Chart.helpers.toFont(tooltip.options.bodyFont); // Display, position, and font tooltipEl.style.opacity = 1; tooltipEl.style.position = 'absolute'; const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas; tooltipEl.style.left = positionX + tooltip.caretX + 'px'; tooltipEl.style.top = (positionY + tooltip.caretY + 7) + 'px'; //tooltipEl.style.left = position.left + window.pageXOffset + tooltip.caretX + 'px'; //tooltipEl.style.top = position.top + window.pageYOffset + tooltip.caretY + 'px'; tooltipEl.style.font = bodyFont.string; tooltipEl.style.padding = tooltip.padding + 'px ' + tooltip.padding + 'px'; tooltipEl.style.background = 'rgba(0, 0, 0, 0.7)'; tooltipEl.style.pointerEvents = 'none'; }; const config = { type: 'bar', data: data, options: { plugins: { title: { display: true, text: 'Build Step Concurrent Execution', }, tooltip: { enabled: false, external: externalTooltip, } }, responsive: true, scales: { x: { stacked: true, title: { display: true, text: "{metrics.slots.size} time slots ({metrics.slots.get(0)} ms)", }, }, y: { stacked: true, title: { display: true, text: "Number of build threads used in a time slot", }, } } } }; const ctx = document.getElementById('buildStepsChart').getContext('2d'); const buildStepsChart = new Chart(ctx, config); {#scriptref} {/include} {/let}