vis stacks

This commit is contained in:
Qiuxia Fan 2022-04-22 11:00:00 +08:00
parent 68122c0a28
commit cf46164337
7 changed files with 2654 additions and 24 deletions

31
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"axios": "^0.24.0", "axios": "^0.24.0",
"d3": "^7.3.0", "d3": "^7.3.0",
"d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1", "d3-tip": "^0.9.1",
"echarts": "^5.2.2", "echarts": "^5.2.2",
"element-plus": "^2.0.2", "element-plus": "^2.0.2",
@ -9340,6 +9341,21 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/d3-flame-graph": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/d3-flame-graph/-/d3-flame-graph-4.1.3.tgz",
"integrity": "sha512-NijuhJZhaTMwobVgwGQ67x9PovqMMHXBbs0FMHEGJvsWZGuL4M7OsB03v8mHdyVyHhnQYGsYnb5w021e9+R+RQ==",
"dependencies": {
"d3-array": "^3.1.1",
"d3-dispatch": "^3.0.1",
"d3-ease": "^3.0.1",
"d3-format": "^3.0.1",
"d3-hierarchy": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-transition": "^3.0.1"
}
},
"node_modules/d3-force": { "node_modules/d3-force": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@ -36407,6 +36423,21 @@
"d3-dsv": "1 - 3" "d3-dsv": "1 - 3"
} }
}, },
"d3-flame-graph": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/d3-flame-graph/-/d3-flame-graph-4.1.3.tgz",
"integrity": "sha512-NijuhJZhaTMwobVgwGQ67x9PovqMMHXBbs0FMHEGJvsWZGuL4M7OsB03v8mHdyVyHhnQYGsYnb5w021e9+R+RQ==",
"requires": {
"d3-array": "^3.1.1",
"d3-dispatch": "^3.0.1",
"d3-ease": "^3.0.1",
"d3-format": "^3.0.1",
"d3-hierarchy": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-transition": "^3.0.1"
}
},
"d3-force": { "d3-force": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",

View File

@ -12,6 +12,7 @@
"dependencies": { "dependencies": {
"axios": "^0.24.0", "axios": "^0.24.0",
"d3": "^7.3.0", "d3": "^7.3.0",
"d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1", "d3-tip": "^0.9.1",
"echarts": "^5.2.2", "echarts": "^5.2.2",
"element-plus": "^2.0.2", "element-plus": "^2.0.2",

11
src/types/ebpf.d.ts vendored
View File

@ -56,3 +56,14 @@ export type Process = {
attributes: { name: string; value: string }; attributes: { name: string; value: string };
labels: string[]; labels: string[];
}; };
export type StackElement = {
id: string;
originId: string;
name: string;
parentId: string;
symbol: string;
dumpCount: number;
stackType: string;
value: number;
children?: StackElement[];
};

View File

@ -82,13 +82,6 @@ const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
function changeLabels(opt: any[]) { function changeLabels(opt: any[]) {
const arr = opt.map((d) => d.value); const arr = opt.map((d) => d.value);
// if (arr.includes("0")) {
// selectedLabels.value = labels.value.map((d: Option) => d.value);
// return;
// }
// selectedLabels.value = Array.from(new Set(arr));
selectedLabels.value = arr; selectedLabels.value = arr;
} }

View File

@ -13,31 +13,95 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div>tree</div> <div id="graph-stack" ref="graph"></div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from "vue"; import { ref, watch } from "vue";
import * as d3 from "d3";
import { flamegraph } from "d3-flame-graph";
import { useEbpfStore } from "@/store/modules/ebpf"; import { useEbpfStore } from "@/store/modules/ebpf";
import { StackElement } from "@/types/ebpf";
import { json } from "./json";
import "d3-flame-graph/dist/d3-flamegraph.css";
/*global Nullable*/
const ebpfStore = useEbpfStore(); const ebpfStore = useEbpfStore();
const stackTree = ref<Nullable<StackElement>>(null);
const graph = ref<Nullable<HTMLDivElement>>(null);
const flameChart = ref<any>(null);
function sortArr(arr: any[]) { function drawGraph() {
if (flameChart.value) {
flameChart.value.destroy();
}
if (!ebpfStore.analyzeTrees.length) {
return (stackTree.value = null);
}
stackTree.value = processTree(ebpfStore.analyzeTrees);
const w = (graph.value && graph.value.getBoundingClientRect().width) || 10;
flameChart.value = flamegraph()
.width(w - 10)
.cellHeight(18)
.transitionDuration(750)
.minFrameSize(5)
.transitionEase(d3.easeCubic as any)
.sort(true)
.title("")
.onClick(onClick)
.selfValue(false)
.setColorMapper((d, originalColor) =>
d.highlight ? "#6aff8f" : originalColor
);
// stackTree.value = (json as any);
console.log(stackTree.value);
d3.select("#graph-stack")
.datum(stackTree.value)
.call(flameChart.value)
.call(invokeFind);
}
function onClick(d: any) {
console.info(`Clicked on ${d.data.name}, id: "${d.data.value}"`);
}
function resetZoom() {
if (!flameChart.value) {
return;
}
flameChart.value.resetZoom();
}
function invokeFind() {
const searchId = parseInt(location.hash.substring(1), 10);
if (searchId) {
find(searchId);
}
}
function find(id: number) {
var elem = flameChart.value.findById(id);
if (elem) {
flameChart.value.zoomTo(elem);
}
}
function processTree(arr: StackElement[]) {
const copyArr = JSON.parse(JSON.stringify(arr)); const copyArr = JSON.parse(JSON.stringify(arr));
const obj: any = {}; const obj: any = {};
const res = []; let res = null;
for (const item of copyArr) { for (const item of copyArr) {
obj[item.id] = item; item.originId = item.id;
item.value = item.dumpCount;
item.name = item.symbol;
delete item.id;
obj[item.originId] = item;
} }
for (const item of copyArr) { for (const item of copyArr) {
if (item.parentId === "0") { if (item.parentId === "0") {
item.name = item.symbol; res = item;
item.value = item.dumpCount;
res.push(item);
} }
for (const key in obj) { for (const key in obj) {
if (item.id === obj[key].parentId) { if (item.originId === obj[key].parentId) {
obj[key].name = obj[key].symbol;
obj[key].value = obj[key].dumpCount;
if (item.children) { if (item.children) {
item.children.push(obj[key]); item.children.push(obj[key]);
} else { } else {
@ -46,13 +110,19 @@ function sortArr(arr: any[]) {
} }
} }
} }
console.log(res);
return res; return res;
} }
watch( watch(
() => ebpfStore.analyzeTrees, () => ebpfStore.analyzeTrees,
() => { () => {
sortArr(ebpfStore.analyzeTrees); drawGraph();
} }
); );
</script> </script>
<style>
#graph-stack {
width: 100%;
height: 350px;
cursor: pointer;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,7 @@ limitations under the License. -->
</div> </div>
<div> <div>
<el-button <el-button
class="grey" class="grey small"
:class="{ ghost: displayMode !== 'List' }" :class="{ ghost: displayMode !== 'List' }"
@click="displayMode = 'List'" @click="displayMode = 'List'"
> >
@ -96,7 +96,7 @@ limitations under the License. -->
{{ t("list") }} {{ t("list") }}
</el-button> </el-button>
<el-button <el-button
class="grey" class="grey small"
:class="{ ghost: displayMode !== 'Tree' }" :class="{ ghost: displayMode !== 'Tree' }"
@click="displayMode = 'Tree'" @click="displayMode = 'Tree'"
> >
@ -104,7 +104,7 @@ limitations under the License. -->
{{ t("tree") }} {{ t("tree") }}
</el-button> </el-button>
<el-button <el-button
class="grey" class="grey small"
:class="{ ghost: displayMode !== 'Table' }" :class="{ ghost: displayMode !== 'Table' }"
@click="displayMode = 'Table'" @click="displayMode = 'Table'"
> >
@ -112,7 +112,7 @@ limitations under the License. -->
{{ t("table") }} {{ t("table") }}
</el-button> </el-button>
<el-button <el-button
class="grey" class="grey small"
:class="{ ghost: displayMode !== 'Statistics' }" :class="{ ghost: displayMode !== 'Statistics' }"
@click="displayMode = 'Statistics'" @click="displayMode = 'Statistics'"
> >