diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java
index 4adf8ef7f499d..a54d6132bd852 100644
--- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java
+++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/devui/deployment/BuildTimeContentProcessor.java
@@ -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/");
diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-bar-stack.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-bar-stack.js
new file mode 100644
index 0000000000000..9b5ce4fd85d6e
--- /dev/null
+++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-bar-stack.js
@@ -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 = "
";
+ for (let i = 0; i < params.data.name.length; i++) {
+ namesUL = namesUL + "- " + params.data.name[i] + "
";
+ }
+ namesUL = namesUL + "
";
+ return `
+ ${params.seriesName}
+ ${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);
\ No newline at end of file
diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-items.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-items.js
index 9e2fddcbcdab6..5c8b7806fca03 100644
--- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-items.js
+++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-items.js
@@ -42,7 +42,8 @@ export class QwcBuildItems extends QwcHotReloadElement {
}`;
static properties = {
- _buildStepsMetrics: { state: true },
+ _buildItems: { state: true },
+ _count: { state: false },
_filtered: {state: true, type: Array}
};
@@ -52,14 +53,15 @@ export class QwcBuildItems extends QwcHotReloadElement {
}
hotReload(){
- this.jsonRpc.getBuildStepsMetrics().then(e => {
- this._buildStepsMetrics = e.result;
- this._filtered = this._buildStepsMetrics.items;
+ this.jsonRpc.getBuildItems().then(e => {
+ this._buildItems = e.result;
+ this._count = this._buildItems.length;
+ this._filtered = this._buildItems;
});
}
render() {
- if (this._buildStepsMetrics && this._filtered) {
+ if (this._buildItems && this._filtered) {
return this._render();
}else {
return html`
@@ -81,18 +83,18 @@ export class QwcBuildItems extends QwcHotReloadElement {
_filter(e) {
const searchTerm = (e.detail.value || '').trim();
if (searchTerm === '') {
- this._filtered = this._buildStepsMetrics.items;
+ this._filtered = this._buildItems;
return;
}
- this._filtered = this._buildStepsMetrics.items.filter((item) => {
+ this._filtered = this._buildItems.filter((item) => {
return this._match(item.class, searchTerm);
});
}
_render() {
return html`
-
Produced ${this._buildStepsMetrics.itemsCount} build items.
+
Produced ${this._count} build items.
{
+ 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()}
+
+
+ `;
+ }else{
+ return html`
+
+
Loading Build Steps Execution Graph...
+
+
+ `;
+ }
+
+
+ }
+
+ _renderTopBar(){
+ return html`
+
+
+
+ Back
+
+
Build Steps Concurrent Execution Chart
+ `;
+ }
+
+ _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);
\ No newline at end of file
diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-steps.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-steps.js
index 77c6faa9c6e04..77a1365849ff6 100644
--- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-steps.js
+++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-build-steps.js
@@ -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
@@ -57,6 +58,7 @@ export class QwcBuildSteps extends QwcHotReloadElement {
static properties = {
_buildMetrics: { state: true },
_selectedBuildStep: {state: true},
+ _showBuildStepsExecutionGraph: {state: true},
_filtered: {state: true, type: Array}
};
@@ -64,6 +66,7 @@ export class QwcBuildSteps extends QwcHotReloadElement {
super();
this._buildMetrics = null;
this._selectedBuildStep = null;
+ this._showBuildStepsExecutionGraph = false;
this.hotReload();
}
@@ -109,7 +112,9 @@ export class QwcBuildSteps extends QwcHotReloadElement {
_render() {
if(this._selectedBuildStep){
return this._renderBuildStepGraph();
- }else{
+ }else if(this._showBuildStepsExecutionGraph){
+ return this._renderBuildStepsExecutionGraph();
+ }else{
return this._renderBuildStepList();
}
}
@@ -117,7 +122,13 @@ export class QwcBuildSteps extends QwcHotReloadElement {
_renderBuildStepList(){
return html`
-
Executed ${this._buildMetrics.records.length} build steps on ${this._buildMetrics.numberOfThreads} threads in ${this._buildMetrics.duration} ms.
+
+ Executed ${this._buildMetrics.records.length} build steps on ${this._buildMetrics.numberOfThreads} threads in ${this._buildMetrics.duration} ms.
+
+
+ Build Steps Concurrent Execution Chart
+
+
`;
+ }
+
_stepIdRenderer(record) {
return html`${record.stepId}
`;
}
@@ -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);
\ No newline at end of file
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/build/BuildMetricsJsonRPCService.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/build/BuildMetricsJsonRPCService.java
index b8d410bc66543..14be5c34a0b7b 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/build/BuildMetricsJsonRPCService.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/devui/runtime/build/BuildMetricsJsonRPCService.java
@@ -1,5 +1,6 @@
package io.quarkus.devui.runtime.build;
+import java.util.List;
import java.util.Map;
import jakarta.enterprise.context.ApplicationScoped;
@@ -10,6 +11,19 @@
@ApplicationScoped
public class BuildMetricsJsonRPCService {
+ public BuildExecutionMetrics getThreadSlotRecords() {
+ BuildExecutionMetrics buildExecutionMetrics = new BuildExecutionMetrics();
+ Map buildStepMetrics = buildStepMetrics();
+ buildExecutionMetrics.threadSlotRecords = (Map) buildStepMetrics.get("threadSlotRecords");
+ buildExecutionMetrics.slots = (List) buildStepMetrics.get("slots");
+ return buildExecutionMetrics;
+ }
+
+ public JsonArray getBuildItems() {
+ Map buildStepMetrics = buildStepMetrics();
+ return (JsonArray) buildStepMetrics.get("items");
+ }
+
public BuildMetrics getBuildMetrics() {
BuildMetrics buildMetrics = new BuildMetrics();
Map buildStepMetrics = buildStepMetrics();
@@ -45,4 +59,9 @@ static class BuildMetrics {
public Long duration;
public JsonArray records;
}
+
+ static class BuildExecutionMetrics {
+ public List slots;
+ public Map threadSlotRecords;
+ }
}