feat: add ebpf

This commit is contained in:
Fine 2023-05-29 17:56:08 +08:00
parent 34ff6d9e9c
commit cb62b19eb6
7 changed files with 134 additions and 94 deletions

View File

@ -16,15 +16,13 @@
*/ */
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import type { StrategyItem, CheckItems } from "@/types/continous-profiling"; import type { StrategyItem, CheckItems } from "@/types/continous-profiling";
import type { ProcessNode, EBPFTaskList } from "@/types/ebpf"; import type { EBPFTaskList, EBPFProfilingSchedule, AnalyzationTrees } from "@/types/ebpf";
import type { Instance, Process } from "@/types/selector"; import type { Instance, Process } from "@/types/selector";
import { store } from "@/store"; import { store } from "@/store";
import graphql from "@/graphql"; import graphql from "@/graphql";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
import type { Call } from "@/types/topology";
import type { LayoutConfig } from "@/types/dashboard";
import type { DurationTime } from "@/types/app";
import { EBPFProfilingTriggerType } from "../data"; import { EBPFProfilingTriggerType } from "../data";
interface ContinousProfilingState { interface ContinousProfilingState {
@ -36,15 +34,11 @@ interface ContinousProfilingState {
errorReason: string; errorReason: string;
processes: Process[]; processes: Process[];
instances: Instance[]; instances: Instance[];
nodes: ProcessNode[]; eBPFSchedules: EBPFProfilingSchedule[];
calls: Call[]; currentSchedule: EBPFProfilingSchedule | Record<string, never>;
node: Nullable<ProcessNode>; analyzeTrees: AnalyzationTrees[];
call: Nullable<Call>; ebpfTips: string;
metricsLayout: LayoutConfig[]; aggregateType: string;
selectedMetric: Nullable<LayoutConfig>;
activeMetricIndex: string;
aliveNetwork: boolean;
loadNodes: boolean;
} }
export const continousProfilingStore = defineStore({ export const continousProfilingStore = defineStore({
@ -56,17 +50,13 @@ export const continousProfilingStore = defineStore({
selectedContinousTask: {}, selectedContinousTask: {},
errorReason: "", errorReason: "",
errorTip: "", errorTip: "",
ebpfTips: "",
processes: [], processes: [],
instances: [], instances: [],
nodes: [], eBPFSchedules: [],
calls: [], currentSchedule: {},
node: null, analyzeTrees: [],
call: null, aggregateType: "COUNT",
metricsLayout: [],
selectedMetric: null,
activeMetricIndex: "",
aliveNetwork: false,
loadNodes: false,
}), }),
actions: { actions: {
setSelectedStrategy(task: Recordable<StrategyItem>) { setSelectedStrategy(task: Recordable<StrategyItem>) {
@ -75,57 +65,11 @@ export const continousProfilingStore = defineStore({
setSelectedContinousTask(task: Recordable<EBPFTaskList>) { setSelectedContinousTask(task: Recordable<EBPFTaskList>) {
this.selectedContinousTask = task || {}; this.selectedContinousTask = task || {};
}, },
setNode(node: Nullable<ProcessNode>) { setCurrentSchedule(s: EBPFProfilingSchedule) {
this.node = node; this.currentSchedule = s;
}, },
setLink(link: Call) { setAnalyzeTrees(tree: AnalyzationTrees[]) {
this.call = link; this.analyzeTrees = tree;
},
setMetricsLayout(layout: LayoutConfig[]) {
this.metricsLayout = layout;
},
setSelectedMetric(item: LayoutConfig) {
this.selectedMetric = item;
},
setActiveItem(index: string) {
this.activeMetricIndex = index;
},
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
const obj = {} as Recordable;
let calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
for (const node of data.nodes) {
if (next.source === node.id) {
next.sourceObj = node;
}
if (next.target === node.id) {
next.targetObj = node;
}
}
next.value = next.value || 1;
prev.push(next);
}
return prev;
}, []);
const param = {} as Recordable;
calls = data.calls.reduce((prev: (Call & Recordable)[], next: Call & Recordable) => {
if (param[next.targetId + next.sourceId]) {
next.lowerArc = true;
}
param[next.sourceId + next.targetId] = true;
next.sourceId = next.source;
next.targetId = next.target;
next.source = next.sourceObj;
next.target = next.targetObj;
delete next.sourceObj;
delete next.targetObj;
prev.push(next);
return prev;
}, []);
this.calls = calls;
this.nodes = data.nodes;
}, },
async setContinuousProfilingPolicy( async setContinuousProfilingPolicy(
serviceId: string, serviceId: string,
@ -193,8 +137,11 @@ export const continousProfilingStore = defineStore({
this.selectedContinousTask = this.taskList[0] || {}; this.selectedContinousTask = this.taskList[0] || {};
this.setSelectedContinousTask(this.selectedContinousTask); this.setSelectedContinousTask(this.selectedContinousTask);
if (!this.taskList.length) { if (!this.taskList.length) {
this.nodes = []; const networkProfilingStore = useNetworkProfilingStore();
this.calls = []; networkProfilingStore.seNodes([]);
networkProfilingStore.setLinks([]);
this.eBPFSchedules = [];
this.analyzeTrees = [];
} }
return res.data; return res.data;
}, },
@ -224,18 +171,55 @@ export const continousProfilingStore = defineStore({
} }
return res.data; return res.data;
}, },
async getProcessTopology(params: { duration: DurationTime; serviceInstanceId: string }) { async getEBPFSchedules(params: { taskId: string }) {
this.loadNodes = true; if (!params.taskId) {
const res: AxiosResponse = await graphql.query("getProcessTopology").params(params); return new Promise((resolve) => resolve({}));
this.loadNodes = false; }
const res: AxiosResponse = await graphql.query("getEBPFSchedules").params({ ...params });
if (res.data.errors) { if (res.data.errors) {
this.nodes = []; this.eBPFSchedules = [];
this.calls = [];
return res.data; return res.data;
} }
const { topology } = res.data.data; this.ebpftTips = "";
const { eBPFSchedules } = res.data.data;
this.setTopology(topology); this.eBPFSchedules = eBPFSchedules;
if (!eBPFSchedules.length) {
this.eBPFSchedules = [];
this.analyzeTrees = [];
}
return res.data;
},
async getEBPFAnalyze(params: {
scheduleIdList: string[];
timeRanges: Array<{ start: number; end: number }>;
aggregateType: string;
}) {
this.aggregateType = params.aggregateType;
if (!params.scheduleIdList.length) {
return new Promise((resolve) => resolve({}));
}
if (!params.timeRanges.length) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql.query("getEBPFResult").params(params);
if (res.data.errors) {
this.analyzeTrees = [];
return res.data;
}
const { analysisEBPFResult } = res.data.data;
this.ebpftTips = analysisEBPFResult.tip;
if (!analysisEBPFResult) {
this.analyzeTrees = [];
return res.data;
}
if (analysisEBPFResult.tip) {
this.analyzeTrees = [];
return res.data;
}
this.analyzeTrees = analysisEBPFResult.trees;
return res.data; return res.data;
}, },
}, },

View File

@ -28,7 +28,7 @@ interface EbpfState {
analyzeTrees: AnalyzationTrees[]; analyzeTrees: AnalyzationTrees[];
labels: Option[]; labels: Option[];
couldProfiling: boolean; couldProfiling: boolean;
tip: string; ebpfTips: string;
selectedTask: Recordable<EBPFTaskList>; selectedTask: Recordable<EBPFTaskList>;
aggregateType: string; aggregateType: string;
} }
@ -42,7 +42,7 @@ export const ebpfStore = defineStore({
analyzeTrees: [], analyzeTrees: [],
labels: [{ value: "", label: "" }], labels: [{ value: "", label: "" }],
couldProfiling: false, couldProfiling: false,
tip: "", ebpfTips: "",
selectedTask: {}, selectedTask: {},
aggregateType: "COUNT", aggregateType: "COUNT",
}), }),
@ -88,7 +88,7 @@ export const ebpfStore = defineStore({
} }
const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params); const res: AxiosResponse = await graphql.query("getEBPFTasks").params(params);
this.tip = ""; this.ebpfTips = "";
if (res.data.errors) { if (res.data.errors) {
return res.data; return res.data;
} }
@ -111,7 +111,7 @@ export const ebpfStore = defineStore({
this.eBPFSchedules = []; this.eBPFSchedules = [];
return res.data; return res.data;
} }
this.tip = ""; this.ebpfTips = "";
const { eBPFSchedules } = res.data.data; const { eBPFSchedules } = res.data.data;
this.eBPFSchedules = eBPFSchedules; this.eBPFSchedules = eBPFSchedules;
@ -140,7 +140,7 @@ export const ebpfStore = defineStore({
return res.data; return res.data;
} }
const { analysisEBPFResult } = res.data.data; const { analysisEBPFResult } = res.data.data;
this.tip = analysisEBPFResult.tip; this.ebpfTips = analysisEBPFResult.tip;
if (!analysisEBPFResult) { if (!analysisEBPFResult) {
this.analyzeTrees = []; this.analyzeTrees = [];
return res.data; return res.data;

View File

@ -65,6 +65,12 @@ export const networkProfilingStore = defineStore({
setLink(link: Call) { setLink(link: Call) {
this.call = link; this.call = link;
}, },
seNodes(nodes: Node[]) {
this.nodes = nodes;
},
setLinks(links: Call[]) {
this.calls = links;
},
setMetricsLayout(layout: LayoutConfig[]) { setMetricsLayout(layout: LayoutConfig[]) {
this.metricsLayout = layout; this.metricsLayout = layout;
}, },

View File

@ -39,7 +39,7 @@ limitations under the License. -->
</el-button> </el-button>
</div> </div>
<div <div
class="vis-graph ml-5" class="vis-graph-topology ml-5"
v-loading="networkProfilingStore.loadNodes" v-loading="networkProfilingStore.loadNodes"
v-if="continousProfilingStore.selectedContinousTask.type === TargetTypes[2].value" v-if="continousProfilingStore.selectedContinousTask.type === TargetTypes[2].value"
> >
@ -48,7 +48,14 @@ limitations under the License. -->
{{ t("noData") }} {{ t("noData") }}
</div> </div>
</div> </div>
<div v-else class="vis-graph ml-5"> ebpf </div> <div class="vis-graph ml-5" v-else>
<div class="schedules">
<EBPFSchedules :type="ComponentType" />
</div>
<div class="item">
<EBPFStack :type="ComponentType" />
</div>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -61,7 +68,9 @@ limitations under the License. -->
import { useNetworkProfilingStore } from "@/store/modules/network-profiling"; import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useAppStoreWithOut } from "@/store/modules/app"; import { useAppStoreWithOut } from "@/store/modules/app";
import ProcessTopology from "@/views/dashboard/related/network-profiling/components/ProcessTopology.vue"; import ProcessTopology from "@/views/dashboard/related/network-profiling/components/ProcessTopology.vue";
import { TargetTypes } from "../data"; import EBPFSchedules from "@/views/dashboard/related/ebpf/components/EBPFSchedules.vue";
import EBPFStack from "@/views/dashboard/related/ebpf/components/EBPFStack.vue";
import { TargetTypes, ComponentType } from "../data";
import dateFormatStep from "@/utils/dateFormat"; import dateFormatStep from "@/utils/dateFormat";
import getLocalTime from "@/utils/localtime"; import getLocalTime from "@/utils/localtime";
@ -95,6 +104,12 @@ limitations under the License. -->
return; return;
} }
const res = await continousProfilingStore.getEBPFSchedules({
taskId: continousProfilingStore.selectedContinousTask.taskId,
});
if (res.errors) {
ElMessage.error(res.errors);
}
} }
async function getTopology() { async function getTopology() {
@ -148,4 +163,20 @@ limitations under the License. -->
.selector { .selector {
width: 220px; width: 220px;
} }
.vis-graph {
height: 100%;
flex-grow: 2;
min-width: 700px;
overflow: auto;
}
.vis-graph-topology {
height: 100%;
flex-grow: 2;
min-width: 700px;
overflow: hidden;
position: relative;
width: calc(100% - 330px);
}
</style> </style>

View File

@ -28,3 +28,5 @@ export const TargetTypes = [
{ label: "OFF_CPU", value: "OFF_CPU" }, { label: "OFF_CPU", value: "OFF_CPU" },
{ label: "NETWORK", value: "NETWORK" }, { label: "NETWORK", value: "NETWORK" },
]; ];
export const ComponentType = "CONTINOUS_PROFILING";

View File

@ -98,12 +98,21 @@ limitations under the License. -->
import type { Option } from "@/types/app"; import type { Option } from "@/types/app";
import { TableHeader, AggregateTypes } from "./data"; import { TableHeader, AggregateTypes } from "./data";
import { useEbpfStore } from "@/store/modules/ebpf"; import { useEbpfStore } from "@/store/modules/ebpf";
import { useContinousProfilingStore } from "@/store/modules/continous-profiling";
import type { EBPFProfilingSchedule, Process } from "@/types/ebpf"; import type { EBPFProfilingSchedule, Process } from "@/types/ebpf";
import { ElMessage, ElTable } from "element-plus"; import { ElMessage, ElTable } from "element-plus";
import { dateFormat } from "@/utils/dateFormat"; import { dateFormat } from "@/utils/dateFormat";
import { ComponentType } from "@/views/dashboard/related/continuous-profiling/data";
const { t } = useI18n(); const { t } = useI18n();
const ebpfStore = useEbpfStore(); /*global defineProps*/
const props = defineProps({
type: {
type: String,
default: "",
},
});
const ebpfStore = props.type === ComponentType ? useContinousProfilingStore() : useEbpfStore();
const pageSize = 5; const pageSize = 5;
const multipleTableRef = ref<InstanceType<typeof ElTable>>(); const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const selectedProcesses = ref<string[]>([]); const selectedProcesses = ref<string[]>([]);

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div id="graph-stack" ref="graph"> <div id="graph-stack" ref="graph">
<span class="tip" v-show="ebpfStore.tip">{{ ebpfStore.tip }}</span> <span class="tip" v-show="ebpfStore.ebpfTips">{{ ebpfStore.ebpfTips }}</span>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -23,12 +23,20 @@ limitations under the License. -->
import d3tip from "d3-tip"; import d3tip from "d3-tip";
import { flamegraph } from "d3-flame-graph"; import { flamegraph } from "d3-flame-graph";
import { useEbpfStore } from "@/store/modules/ebpf"; import { useEbpfStore } from "@/store/modules/ebpf";
import { useContinousProfilingStore } from "@/store/modules/continous-profiling";
import { ComponentType } from "@/views/dashboard/related/continuous-profiling/data";
import type { StackElement } from "@/types/ebpf"; import type { StackElement } from "@/types/ebpf";
import { AggregateTypes } from "./data"; import { AggregateTypes } from "./data";
import "d3-flame-graph/dist/d3-flamegraph.css"; import "d3-flame-graph/dist/d3-flamegraph.css";
/*global Nullable*/ /*global Nullable, defineProps*/
const ebpfStore = useEbpfStore(); const props = defineProps({
type: {
type: String,
default: "",
},
});
const ebpfStore = props.type === ComponentType ? useContinousProfilingStore() : useEbpfStore();
const stackTree = ref<Nullable<StackElement>>(null); const stackTree = ref<Nullable<StackElement>>(null);
const selectStack = ref<Nullable<StackElement>>(null); const selectStack = ref<Nullable<StackElement>>(null);
const graph = ref<Nullable<HTMLDivElement>>(null); const graph = ref<Nullable<HTMLDivElement>>(null);