Skip to content

Commit

Permalink
Dev UI: Migrate Build Concurrent Execution Graph
Browse files Browse the repository at this point in the history
Signed-off-by: Phillip Kruger <[email protected]>
  • Loading branch information
phillip-kruger committed Aug 1, 2023
1 parent c30e9c1 commit 1956ac7
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ InternalImportMapBuildItem createKnownInternalImportMap(NonApplicationRootPathBu
contextRoot + "echarts/echarts-horizontal-stacked-bar.js");
internalImportMapBuildItem.add("echarts-force-graph",
contextRoot + "echarts/echarts-force-graph.js");
internalImportMapBuildItem.add("echarts-bar-stack",
contextRoot + "echarts/echarts-bar-stack.js");

// Other assets
internalImportMapBuildItem.add("icon/", contextRoot + "icon/");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { EchartsAbstractCanvas } from './echarts-abstract-canvas.js';

/**
* This wraps the Bar Stack echart into a component
* see https://echarts.apache.org/examples/en/editor.html?c=bar-stack
*/
class EchartsBarStack extends EchartsAbstractCanvas {

static get properties() {
return {
xdata:{type: String},
xdataName: {type: String},
series: { type: String},
ydataName: {type: String}
};
}

constructor() {
super();

this.xdata = null;
this.xdataName = null;
this.series = null;
this.primaryTextColor = "--lumo-body-text-color";
}

getOption(){

let textColor = this.primaryTextColor;
if(textColor.startsWith('--')){
textColor = getComputedStyle(this.shadowRoot.host).getPropertyValue(textColor);
}
const barStackOption = new Object();

barStackOption.tooltip = new Object();
barStackOption.tooltip.trigger = "item";
barStackOption.tooltip.axisPointer= new Object();
barStackOption.tooltip.axisPointer.type = "shadow";
barStackOption.tooltip.formatter = function (params) {
let namesUL = "<ul>";
for (let i = 0; i < params.data.name.length; i++) {
namesUL = namesUL + "<li>" + params.data.name[i] + "</li>";
}
namesUL = namesUL + "</ul>";
return `
<b>${params.seriesName}</b></br>
${namesUL}`;
};
barStackOption.legend = new Object();
barStackOption.legend.textStyle = new Object();
barStackOption.legend.textStyle.color = textColor;

barStackOption.grid = new Object();
barStackOption.grid.top = "20%";
barStackOption.grid.left = "3%";
barStackOption.grid.right = "4%";
barStackOption.grid.bottom = "3%";
barStackOption.grid.containLabel = true;

let xAxis = new Object();
xAxis.type = "category";

xAxis.data = this.xdata.split(',');
xAxis.axisLine = new Object();
xAxis.axisLine.lineStyle = new Object();
xAxis.axisLine.lineStyle.color = textColor;
if(this.xdataName){
xAxis.name = this.xdataName;
xAxis.nameLocation = "center";
xAxis.nameGap = 30;
}
barStackOption.xAxis = [];
barStackOption.xAxis.push(xAxis);

let yAxis = new Object();
yAxis.type = "value";
yAxis.axisLine = new Object();
yAxis.axisLine.lineStyle = new Object();
yAxis.axisLine.lineStyle.color = textColor;
if(this.ydataName){
yAxis.name = this.ydataName;
yAxis.nameLocation = "center";
yAxis.nameGap = 30;
}

barStackOption.yAxis = [];
barStackOption.yAxis.push(yAxis);

barStackOption.series = [];
let seriesMap = new Map(Object.entries(JSON.parse(this.series)));


for (let [key, value] of seriesMap) {
let arr = [];

for (let i = 0; i < value.length; i++) {
let val = value[i];


let dataItem = new Object();
dataItem.name = val;
dataItem.label = new Object();
if(val.length > 0){
dataItem.value = 1;
}
arr.push(dataItem);
}

const serie = new Object();
serie.name = key;
serie.type = "bar";
serie.stack = "stack";
serie.emphasis = new Object();
serie.emphasis.focus = "series";

serie.data = arr;
barStackOption.series.push(serie);
}

return barStackOption;

}

}
customElements.define('echarts-bar-stack', EchartsBarStack);
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { LitElement, html, css} from 'lit';
import { JsonRpc } from 'jsonrpc';
import 'echarts-bar-stack';
import '@vaadin/button';
import '@vaadin/checkbox';
import '@vaadin/checkbox-group';
import '@vaadin/progress-bar';

/**
* This component shows the Build Step Execution Graph
*/
export class QwcBuildStepsExecutionGraph extends LitElement {

static styles = css`
.top-bar {
display: flex;
align-items: baseline;
gap: 20px;
padding-left: 20px;
padding-right: 20px;
}
.top-bar h4 {
color: var(--lumo-contrast-60pct);
}
`;

static properties = {
extensionName: {type: String}, // TODO: Add 'pane' concept in router to register internal extension pages.
_threadSlotRecords: {state: true},
_slots: {state: true}
};

constructor() {
super();
this._threadSlotRecords = null;
this._slots = null;
}

connectedCallback() {
super.connectedCallback();
this.jsonRpc = new JsonRpc(this.extensionName);
this._fetchBuildStepsExecutionData();
}

_fetchBuildStepsExecutionData(){
this.jsonRpc.getThreadSlotRecords().then(jsonRpcResponse => {
this._slots = jsonRpcResponse.result.slots;
this._threadSlotRecords = jsonRpcResponse.result.threadSlotRecords;
});
}

render() {

if(this._threadSlotRecords){
let xdata = this._slots.toString();
let xname = this._slots.length + " time slots (" + this._slots[0] +" ms)";
let yname = "Number of build threads used in a time slot";
return html`${this._renderTopBar()}
<echarts-bar-stack width="400px" height="400px"
xdata="${xdata}"
xdataName="${xname}"
ydataName="${yname}"
series="${JSON.stringify(this._threadSlotRecords)}">
</echarts-bar-stack>
`;
}else{
return html`
<div style="color: var(--lumo-secondary-text-color);width: 95%;" >
<div>Loading Build Steps Execution Graph...</div>
<vaadin-progress-bar indeterminate></vaadin-progress-bar>
</div>
`;
}


}

_renderTopBar(){
return html`
<div class="top-bar">
<vaadin-button @click="${this._backAction}">
<vaadin-icon icon="font-awesome-solid:caret-left" slot="prefix"></vaadin-icon>
Back
</vaadin-button>
<h4>Build Steps Concurrent Execution Chart</h4>
</div>`;
}

_backAction(){
const back = new CustomEvent("build-steps-graph-back", {
detail: {},
bubbles: true,
cancelable: true,
composed: false,
});
this.dispatchEvent(back);
}
}
customElements.define('qwc-build-steps-execution-graph', QwcBuildStepsExecutionGraph);
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import '@vaadin/text-field';
import '@vaadin/vertical-layout';
import '@vaadin/horizontal-layout';
import '@vaadin/progress-bar';
import '@vaadin/button';
import './qwc-build-step-graph.js';

import './qwc-build-steps-execution-graph.js';

/**
* This component shows the Build Steps
Expand Down Expand Up @@ -57,13 +58,15 @@ export class QwcBuildSteps extends QwcHotReloadElement {
static properties = {
_buildMetrics: { state: true },
_selectedBuildStep: {state: true},
_showBuildStepsExecutionGraph: {state: true},
_filtered: {state: true, type: Array}
};

constructor() {
super();
this._buildMetrics = null;
this._selectedBuildStep = null;
this._showBuildStepsExecutionGraph = false;
this.hotReload();
}

Expand Down Expand Up @@ -109,15 +112,23 @@ export class QwcBuildSteps extends QwcHotReloadElement {
_render() {
if(this._selectedBuildStep){
return this._renderBuildStepGraph();
}else{
}else if(this._showBuildStepsExecutionGraph){
return this._renderBuildStepsExecutionGraph();
}else{
return this._renderBuildStepList();
}
}

_renderBuildStepList(){

return html`<div class="build-steps">
<div class="summary">Executed <strong>${this._buildMetrics.records.length}</strong> build steps on <strong>${this._buildMetrics.numberOfThreads}</strong> threads in <strong>${this._buildMetrics.duration} ms</strong>.</div>
<div class="summary">
Executed <strong>${this._buildMetrics.records.length}</strong> build steps on <strong>${this._buildMetrics.numberOfThreads}</strong> threads in <strong>${this._buildMetrics.duration} ms</strong>.
<vaadin-button theme="tertiary" @click="${this._showBuildStepsChart}">
<vaadin-icon icon="font-awesome-solid:chart-simple" slot="prefix"></vaadin-icon>
Build Steps Concurrent Execution Chart
</vaadin-button>
</div>
<vaadin-text-field
placeholder="Filter"
style="width: 100%;"
Expand Down Expand Up @@ -157,14 +168,19 @@ export class QwcBuildSteps extends QwcHotReloadElement {
}

_renderBuildStepGraph(){

return html`<qwc-build-step-graph class="graph"
stepId="${this._selectedBuildStep.stepId}"
extensionName="${this.jsonRpc.getExtensionName()}"
@build-steps-graph-back=${this._showBuildStepsList}></qwc-build-step-graph>`;

}

_renderBuildStepsExecutionGraph(){
return html`<qwc-build-steps-execution-graph class="graph"
extensionName="${this.jsonRpc.getExtensionName()}"
@build-steps-graph-back=${this._showBuildStepsList}></qwc-build-steps-execution-graph>`;
}

_stepIdRenderer(record) {
return html`<code>${record.stepId}</code>`;
}
Expand All @@ -175,11 +191,17 @@ export class QwcBuildSteps extends QwcHotReloadElement {

_showGraph(buildStep){
this._selectedBuildStep = buildStep;
this._showBuildStepsExecutionGraph = false;
}

_showBuildStepsList(){
this._selectedBuildStep = null;
this._showBuildStepsExecutionGraph = false;
}

_showBuildStepsChart(){
this._selectedBuildStep = null;
this._showBuildStepsExecutionGraph = true;
}
}
customElements.define('qwc-build-steps', QwcBuildSteps);
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.devui.runtime.build;

import java.util.List;
import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;
Expand All @@ -10,6 +11,14 @@
@ApplicationScoped
public class BuildMetricsJsonRPCService {

public BuildExecutionMetrics getThreadSlotRecords() {
BuildExecutionMetrics buildExecutionMetrics = new BuildExecutionMetrics();
Map<String, Object> buildStepMetrics = buildStepMetrics();
buildExecutionMetrics.threadSlotRecords = (Map<String, JsonArray>) buildStepMetrics.get("threadSlotRecords");
buildExecutionMetrics.slots = (List) buildStepMetrics.get("slots");
return buildExecutionMetrics;
}

public JsonArray getBuildItems() {
Map<String, Object> buildStepMetrics = buildStepMetrics();
return (JsonArray) buildStepMetrics.get("items");
Expand Down Expand Up @@ -50,4 +59,9 @@ static class BuildMetrics {
public Long duration;
public JsonArray records;
}

static class BuildExecutionMetrics {
public List<Long> slots;
public Map<String, JsonArray> threadSlotRecords;
}
}

0 comments on commit 1956ac7

Please sign in to comment.