blob: 82b4ed23acb3b41ed3b497498378faf22439f5f7 [file] [log] [blame]
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="filters">
<div class="mb-10 flex-h">
<Selector
:value="selectedLabels"
:options="labels"
size="small"
placeholder="Please select labels"
@change="changeLabels"
class="inputs mr-10"
:multiple="true"
/>
<div class="mr-5 duration" v-if="duration.length">
<span>{{ duration[0] }}</span>
<span> ~ </span>
<span>{{ duration[1] }}</span>
</div>
</div>
<div class="flex-h">
<Selector
v-if="ebpfStore.selectedTask.targetType === 'OFF_CPU'"
:value="aggregateType"
:options="AggregateTypes"
size="small"
placeholder="Please select a type"
@change="changeAggregateType"
class="selector mr-10"
/>
<el-popover placement="bottom" :width="680" trigger="click" :persistent="false">
<template #reference>
<el-button size="small">
{{ t("processSelect") }}
</el-button>
</template>
<el-input
v-model="searchText"
placeholder="Please input name"
class="input-with-search"
size="small"
@change="searchProcesses(0)"
>
<template #append>
<el-button size="small">
<Icon size="sm" iconName="search" />
</el-button>
</template>
</el-input>
<el-table :data="currentProcesses" ref="multipleTableRef" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column
v-for="(h, index) of TableHeader"
:property="h.property"
:label="h.label"
:key="index"
width="150"
/>
<el-table-column width="300" label="Attributes">
<template #default="scope">
{{ attributes(scope.row.attributes) }}
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
small
layout="prev, pager, next"
:page-size="pageSize"
:total="processes.length"
@current-change="changePage"
@prev-click="changePage"
@next-click="changePage"
/>
</el-popover>
<el-button type="primary" size="small" @click="analyzeEBPF">
{{ t("analyze") }}
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import type { Option } from "@/types/app";
import { TableHeader, AggregateTypes } from "./data";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useContinousProfilingStore } from "@/store/modules/continous-profiling";
import type { EBPFProfilingSchedule, Process } from "@/types/ebpf";
import { ElMessage, ElTable } from "element-plus";
import { dateFormat } from "@/utils/dateFormat";
import { ComponentType } from "@/views/dashboard/related/continuous-profiling/data";
const { t } = useI18n();
/*global defineProps*/
const props = defineProps({
type: {
type: String,
default: "",
},
});
const ebpfStore = props.type === ComponentType ? useContinousProfilingStore() : useEbpfStore();
const pageSize = 5;
const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const selectedProcesses = ref<string[]>([]);
const labels = ref<Option[]>([{ label: "All", value: "0" }]);
const processes = ref<Process[]>([]);
const currentProcesses = ref<Process[]>([]);
const selectedLabels = ref<string[]>(["0"]);
const searchText = ref<string>("");
const aggregateType = ref<string>(AggregateTypes[0].value);
const duration = ref<string[]>([]);
const attributes = (attr: { name: string; value: string }[]) => {
return attr.map((d: { name: string; value: string }) => `${d.name}=${d.value}`).join("; ");
};
function changeLabels(opt: any[]) {
const arr = opt.map((d) => d.value);
selectedLabels.value = arr;
}
function changeAggregateType(opt: any[]) {
aggregateType.value = opt[0].value;
ebpfStore.setAnalyzeTrees([]);
}
const handleSelectionChange = (arr: Process[]) => {
selectedProcesses.value = arr.map((d: Process) => d.id);
};
async function analyzeEBPF() {
let arr: string[] = selectedLabels.value;
if (selectedLabels.value.includes("0")) {
arr = labels.value.map((d: Option) => d.value);
}
const ranges: { start: number; end: number }[] = [];
const scheduleIdList = ebpfStore.eBPFSchedules.flatMap((d: EBPFProfilingSchedule) => {
const l = d.process.labels.find((d: string) => arr.includes(d));
const i = selectedProcesses.value.includes(d.process.id);
if (l || i) {
ranges.push({
start: d.startTime,
end: d.endTime,
});
return d.scheduleId;
}
});
let timeRanges: { start: number; end: number }[] = [];
for (const r of ranges) {
if (timeRanges.length) {
for (const t of timeRanges) {
if (r.start > t.start && r.start < t.end) {
if (r.end > t.end) {
t.end = r.end;
}
}
}
} else {
timeRanges.push(r);
}
}
const res = await ebpfStore.getEBPFAnalyze({
scheduleIdList,
timeRanges,
aggregateType: aggregateType.value,
});
if (res.data && res.data.errors) {
ElMessage.error(res.data.errors);
return;
}
}
function getSchedules() {
labels.value = [{ label: "All", value: "0" }];
selectedLabels.value = ["0"];
processes.value = [];
const ranges = ebpfStore.eBPFSchedules.map((d: EBPFProfilingSchedule) => {
for (const l of d.process.labels) {
labels.value.push({ label: l, value: l });
}
processes.value.push(d.process);
return [d.startTime / 10000, d.endTime / 10000];
});
if (ranges.length) {
const arr = ranges.flat(1);
const min = Math.min(...arr);
const max = Math.max(...arr);
duration.value = [dateFormat(min * 10000), dateFormat(max * 10000)];
} else {
duration.value = [];
}
searchProcesses(0);
analyzeEBPF();
}
function changePage(pageIndex: number) {
searchProcesses(pageIndex);
}
function searchProcesses(pageIndex: number) {
const arr = processes.value.filter(
(d: { name: string; instanceName: string; attributes: { name: string; value: string }[] }) =>
d.name.includes(searchText.value) ||
d.instanceName.includes(searchText.value) ||
searchAttribute(d.attributes, searchText.value),
);
currentProcesses.value = arr.filter(
(d, index: number) => (pageIndex - 1 || 0) * pageSize <= index && pageSize * (pageIndex || 1) > index,
);
}
function searchAttribute(attributes: { name: string; value: string }[], text: string) {
const item = attributes.find((d: { name: string; value: string }) => d.name === "command_line");
if (!item) {
return false;
}
return item.value.includes(text);
}
watch(
() => ebpfStore.eBPFSchedules,
() => {
getSchedules();
},
);
</script>
<style lang="scss" scoped>
.filters {
margin: 5px 0;
width: 100%;
min-width: 560px;
}
.inputs {
width: 400px;
}
.input-with-search {
width: 650px;
margin-bottom: 5px;
}
.pagination {
margin-top: 10px;
}
.selector {
width: 120px;
}
.duration {
line-height: 30px;
}
</style>