You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by qi...@apache.org on 2022/04/24 12:24:29 UTC
[skywalking-booster-ui] branch main updated: feat: Implement the eBPF profile widget on dashboard (#72)
This is an automated email from the ASF dual-hosted git repository.
qiuxiafan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git
The following commit(s) were added to refs/heads/main by this push:
new 8a07b1d feat: Implement the eBPF profile widget on dashboard (#72)
8a07b1d is described below
commit 8a07b1d804973d7f1ad0916302652892ee9bfcdf
Author: Fine0830 <fi...@outlook.com>
AuthorDate: Sun Apr 24 20:24:23 2022 +0800
feat: Implement the eBPF profile widget on dashboard (#72)
---
package-lock.json | 314 ++++++++++++++++++---
package.json | 2 +
src/assets/icons/insert_chart.svg | 17 ++
src/assets/icons/view.svg | 15 +
src/components/Radio.vue | 4 +-
src/components/Selector.vue | 2 +-
src/components/index.ts | 2 +-
src/graphql/fragments/ebpf.ts | 93 ++++++
src/graphql/index.ts | 2 +
src/{components/index.ts => graphql/query/ebpf.ts} | 42 ++-
src/locales/lang/en.ts | 5 +
src/locales/lang/zh.ts | 5 +
src/main.ts | 2 +-
src/store/modules/dashboard.ts | 7 +-
src/store/modules/ebpf.ts | 153 ++++++++++
src/store/modules/profile.ts | 30 +-
src/store/modules/trace.ts | 10 +-
src/styles/{index.scss => index.ts} | 9 +-
src/types/{dashboard.ts => dashboard.d.ts} | 0
src/types/ebpf.d.ts | 76 +++++
src/views/dashboard/List.vue | 2 +-
src/views/dashboard/controls/Ebpf.vue | 95 +++++++
src/views/dashboard/controls/Tab.vue | 7 +-
src/views/dashboard/controls/index.ts | 3 +-
src/views/dashboard/data.ts | 5 +-
src/views/dashboard/panel/Layout.vue | 4 +-
src/views/dashboard/panel/Tool.vue | 56 +++-
.../dashboard/related/ebpf/Content.vue} | 58 ++--
.../dashboard/related/{profile => ebpf}/Header.vue | 73 ++---
.../related/ebpf/components/EBPFSchedules.vue | 244 ++++++++++++++++
.../related/ebpf/components/EBPFStack.vue | 148 ++++++++++
.../{profile => ebpf}/components/NewTask.vue | 121 ++++----
.../{profile => ebpf}/components/TaskList.vue | 137 +++------
.../dashboard/related/ebpf/components/data.ts | 51 ++++
src/views/dashboard/related/profile/Header.vue | 50 ++--
.../related/profile/components/NewTask.vue | 34 ++-
.../related/profile/components/TaskList.vue | 2 +-
src/views/dashboard/related/trace/Detail.vue | 8 +-
38 files changed, 1511 insertions(+), 377 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 416405a..9993f98 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,11 +10,13 @@
"dependencies": {
"axios": "^0.24.0",
"d3": "^7.3.0",
+ "d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1",
"echarts": "^5.2.2",
"element-plus": "^2.0.2",
"lodash": "^4.17.21",
"pinia": "^2.0.5",
+ "vis-timeline": "^7.5.1",
"vue": "^3.0.0",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.9",
@@ -1810,6 +1812,18 @@
"ms": "^2.1.1"
}
},
+ "node_modules/@egjs/hammerjs": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
+ "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
+ "peer": true,
+ "dependencies": {
+ "@types/hammerjs": "^2.0.36"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/@element-plus/icons-vue": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
@@ -3299,6 +3313,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hammerjs": {
+ "version": "2.0.41",
+ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
+ "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==",
+ "peer": true
+ },
"node_modules/@types/http-proxy": {
"version": "1.17.8",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
@@ -7881,8 +7901,7 @@
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
- "dev": true
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"node_modules/compressible": {
"version": "2.0.18",
@@ -8694,6 +8713,12 @@
"node": ">=4"
}
},
+ "node_modules/cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=",
+ "peer": true
+ },
"node_modules/cssnano": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz",
@@ -9104,6 +9129,16 @@
"node": ">=0.8"
}
},
+ "node_modules/cypress/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
"node_modules/d3": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz",
@@ -9306,6 +9341,21 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@@ -16412,6 +16462,12 @@
"node": ">=0.6.0"
}
},
+ "node_modules/keycharm": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz",
+ "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==",
+ "peer": true
+ },
"node_modules/killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -18203,7 +18259,6 @@
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
- "dev": true,
"engines": {
"node": "*"
}
@@ -21672,6 +21727,15 @@
"node": ">= 6"
}
},
+ "node_modules/propagating-hammerjs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-2.0.1.tgz",
+ "integrity": "sha512-PH3zG5whbSxMocphXJzVtvKr+vWAgfkqVvtuwjSJ/apmEACUoiw6auBAT5HYXpZOR0eGcTAfYG5Yl8h91O5Elg==",
+ "peer": true,
+ "peerDependencies": {
+ "@egjs/hammerjs": "^2.0.17"
+ }
+ },
"node_modules/proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@@ -22411,6 +22475,16 @@
"request": "^2.34"
}
},
+ "node_modules/request/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -23746,15 +23820,6 @@
"ms": "^2.1.1"
}
},
- "node_modules/sockjs/node_modules/uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "dev": true,
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/sort-keys": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
@@ -26807,13 +26872,11 @@
}
},
"node_modules/uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
- "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
- "dev": true,
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
- "uuid": "bin/uuid"
+ "uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache": {
@@ -26871,6 +26934,59 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
+ "node_modules/vis-data": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.4.tgz",
+ "integrity": "sha512-usy+ePX1XnArNvJ5BavQod7YRuGQE1pjFl+pu7IS6rCom2EBoG0o1ZzCqf3l5US6MW51kYkLR+efxRbnjxNl7w==",
+ "hasInstallScript": true,
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/visjs"
+ },
+ "peerDependencies": {
+ "uuid": "^7.0.0 || ^8.0.0",
+ "vis-util": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/vis-timeline": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/vis-timeline/-/vis-timeline-7.5.1.tgz",
+ "integrity": "sha512-XZMHHbA8xm9/Y/iu3mE9MT7J5tfWgbdsW+PmqrgINU2QRX24AiqifNHZHV4YYzeJstiTSOg9Gs5qRkxQ0BvZJw==",
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/visjs"
+ },
+ "peerDependencies": {
+ "@egjs/hammerjs": "^2.0.0",
+ "component-emitter": "^1.3.0",
+ "keycharm": "^0.3.0 || ^0.4.0",
+ "moment": "^2.24.0",
+ "propagating-hammerjs": "^1.4.0 || ^2.0.0",
+ "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0",
+ "vis-data": "^6.3.0 || ^7.0.0",
+ "vis-util": "^3.0.0 || ^4.0.0 || ^5.0.0",
+ "xss": "^1.0.0"
+ }
+ },
+ "node_modules/vis-util": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz",
+ "integrity": "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g==",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/visjs"
+ },
+ "peerDependencies": {
+ "@egjs/hammerjs": "^2.0.0",
+ "component-emitter": "^1.3.0"
+ }
+ },
"node_modules/vm-browserify": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
@@ -28266,6 +28382,16 @@
"node": ">= 6"
}
},
+ "node_modules/webpack-log/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
"node_modules/webpack-merge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
@@ -28709,6 +28835,28 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
+ "node_modules/xss": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz",
+ "integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==",
+ "peer": true,
+ "dependencies": {
+ "commander": "^2.20.3",
+ "cssfilter": "0.0.10"
+ },
+ "bin": {
+ "xss": "bin/xss"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/xss/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "peer": true
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -30163,6 +30311,15 @@
}
}
},
+ "@egjs/hammerjs": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
+ "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
+ "peer": true,
+ "requires": {
+ "@types/hammerjs": "^2.0.36"
+ }
+ },
"@element-plus/icons-vue": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-0.2.7.tgz",
@@ -31426,6 +31583,12 @@
"@types/node": "*"
}
},
+ "@types/hammerjs": {
+ "version": "2.0.41",
+ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
+ "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==",
+ "peer": true
+ },
"@types/http-proxy": {
"version": "1.17.8",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
@@ -35117,8 +35280,7 @@
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
- "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
- "dev": true
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"compressible": {
"version": "2.0.18",
@@ -35759,6 +35921,12 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
+ "cssfilter": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz",
+ "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=",
+ "peer": true
+ },
"cssnano": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz",
@@ -36098,6 +36266,12 @@
"psl": "^1.1.24",
"punycode": "^1.4.1"
}
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
}
}
},
@@ -36249,6 +36423,21 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@@ -41825,6 +42014,12 @@
"verror": "1.10.0"
}
},
+ "keycharm": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz",
+ "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==",
+ "peer": true
+ },
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -43211,8 +43406,7 @@
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
- "dev": true
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"move-concurrently": {
"version": "1.0.1",
@@ -45877,6 +46071,13 @@
"sisteransi": "^1.0.5"
}
},
+ "propagating-hammerjs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-2.0.1.tgz",
+ "integrity": "sha512-PH3zG5whbSxMocphXJzVtvKr+vWAgfkqVvtuwjSJ/apmEACUoiw6auBAT5HYXpZOR0eGcTAfYG5Yl8h91O5Elg==",
+ "peer": true,
+ "requires": {}
+ },
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@@ -46435,6 +46636,14 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
}
},
"request-progress": {
@@ -47531,14 +47740,6 @@
"faye-websocket": "^0.11.3",
"uuid": "^8.3.2",
"websocket-driver": "^0.7.4"
- },
- "dependencies": {
- "uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "dev": true
- }
}
},
"sockjs-client": {
@@ -49976,10 +50177,9 @@
"dev": true
},
"uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
- "dev": true
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache": {
"version": "2.3.0",
@@ -50028,6 +50228,26 @@
}
}
},
+ "vis-data": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.4.tgz",
+ "integrity": "sha512-usy+ePX1XnArNvJ5BavQod7YRuGQE1pjFl+pu7IS6rCom2EBoG0o1ZzCqf3l5US6MW51kYkLR+efxRbnjxNl7w==",
+ "peer": true,
+ "requires": {}
+ },
+ "vis-timeline": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/vis-timeline/-/vis-timeline-7.5.1.tgz",
+ "integrity": "sha512-XZMHHbA8xm9/Y/iu3mE9MT7J5tfWgbdsW+PmqrgINU2QRX24AiqifNHZHV4YYzeJstiTSOg9Gs5qRkxQ0BvZJw==",
+ "requires": {}
+ },
+ "vis-util": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz",
+ "integrity": "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g==",
+ "peer": true,
+ "requires": {}
+ },
"vm-browserify": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
@@ -51307,6 +51527,14 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
}
},
"webpack-merge": {
@@ -51531,6 +51759,24 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
+ "xss": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.11.tgz",
+ "integrity": "sha512-EimjrjThZeK2MO7WKR9mN5ZC1CSqivSl55wvUK5EtU6acf0rzEE1pN+9ZDrFXJ82BRp3JL38pPE6S4o/rpp1zQ==",
+ "peer": true,
+ "requires": {
+ "commander": "^2.20.3",
+ "cssfilter": "0.0.10"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "peer": true
+ }
+ }
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index ba4a893..66a6188 100644
--- a/package.json
+++ b/package.json
@@ -12,11 +12,13 @@
"dependencies": {
"axios": "^0.24.0",
"d3": "^7.3.0",
+ "d3-flame-graph": "^4.1.3",
"d3-tip": "^0.9.1",
"echarts": "^5.2.2",
"element-plus": "^2.0.2",
"lodash": "^4.17.21",
"pinia": "^2.0.5",
+ "vis-timeline": "^7.5.1",
"vue": "^3.0.0",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.9",
diff --git a/src/assets/icons/insert_chart.svg b/src/assets/icons/insert_chart.svg
new file mode 100644
index 0000000..4ab3063
--- /dev/null
+++ b/src/assets/icons/insert_chart.svg
@@ -0,0 +1,17 @@
+<!-- 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. -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+<path d="M17.016 17.016v-4.031h-2.016v4.031h2.016zM12.984 17.016v-10.031h-1.969v10.031h1.969zM9 17.016v-7.031h-2.016v7.031h2.016zM18.984 3q0.797 0 1.406 0.609t0.609 1.406v13.969q0 0.797-0.609 1.406t-1.406 0.609h-13.969q-0.797 0-1.406-0.609t-0.609-1.406v-13.969q0-0.797 0.609-1.406t1.406-0.609h13.969z"></path>
+</svg>
diff --git a/src/assets/icons/view.svg b/src/assets/icons/view.svg
new file mode 100644
index 0000000..4122084
--- /dev/null
+++ b/src/assets/icons/view.svg
@@ -0,0 +1,15 @@
+<!-- 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. -->
+<svg t="1650287922642" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3055" width="48" height="48"><path d="M277.333333 325.333333m5.333334 0l325.333333 0q5.333333 0 5.333333 5.333334l0 64q0 5.333333-5.333333 5.333333l-325.333333 0q-5.333333 0-5.333334-5.333333l0-64q0-5.333333 5.333334-5.333334Z" p-id="3056" fill="#707070"></path><path d="M277.333333 474.666667m5.333334 0l325.333333 0q5.333333 0 5.333333 5.333333l0 64q0 5.333333-5.333333 5.333 [...]
diff --git a/src/components/Radio.vue b/src/components/Radio.vue
index f5aee03..7eafaa4 100644
--- a/src/components/Radio.vue
+++ b/src/components/Radio.vue
@@ -32,7 +32,7 @@ interface Option {
const emit = defineEmits(["change"]);
const props = defineProps({
options: {
- type: Array as PropType<(Option & { disabled: boolean })[]>,
+ type: Array as PropType<Option[]>,
default: () => [],
},
value: {
@@ -44,7 +44,7 @@ const props = defineProps({
const selected = ref<string>(props.value);
-function checked(opt: string) {
+function checked(opt: unknown) {
emit("change", opt);
}
</script>
diff --git a/src/components/Selector.vue b/src/components/Selector.vue
index 2501195..515a222 100644
--- a/src/components/Selector.vue
+++ b/src/components/Selector.vue
@@ -58,7 +58,7 @@ const props = defineProps({
},
size: { type: null, default: "default" },
placeholder: {
- type: [String, Number] as PropType<string | number>,
+ type: [String, undefined] as PropType<string>,
default: "Select a option",
},
borderRadius: { type: Number, default: 3 },
diff --git a/src/components/index.ts b/src/components/index.ts
index 6417e23..8d8a5a9 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -14,13 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import type { App } from "vue";
import Icon from "./Icon.vue";
import TimePicker from "./TimePicker.vue";
import Selector from "./Selector.vue";
import Graph from "./Graph.vue";
import Radio from "./Radio.vue";
import SelectSingle from "./SelectSingle.vue";
-import type { App } from "vue";
import VueGridLayout from "vue-grid-layout";
const components: { [key: string]: any } = {
diff --git a/src/graphql/fragments/ebpf.ts b/src/graphql/fragments/ebpf.ts
new file mode 100644
index 0000000..662cfb3
--- /dev/null
+++ b/src/graphql/fragments/ebpf.ts
@@ -0,0 +1,93 @@
+/**
+ * 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.
+ */
+
+export const queryCreateTaskData = {
+ variable: "$serviceId: ID!",
+ query: `
+ createTaskData: queryPrepareCreateEBPFProfilingTaskData(serviceId: $serviceId) {
+ couldProfiling
+ processLabels
+ }`,
+};
+export const createEBPFTask = {
+ variable: "$request: EBPFProfilingTaskFixedTimeCreationRequest!",
+ query: `
+ createTaskData: createEBPFProfilingFixedTimeTask(request: $request) {
+ status
+ errorReason
+ id
+ }`,
+};
+export const queryEBPFTasks = {
+ variable: "$serviceId: ID!",
+ query: `
+ queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId) {
+ taskId
+ serviceName
+ serviceId
+ processLabels
+ taskStartTime
+ triggerType
+ fixedTriggerDuration
+ targetType
+ createTime
+ }`,
+};
+export const queryEBPFSchedules = {
+ variable: "$taskId: ID!, $duration: Duration!",
+ query: `
+ eBPFSchedules: queryEBPFProfilingSchedules(taskId: $taskId, duration: $duration) {
+ scheduleId
+ taskId
+ process {
+ id
+ name
+ serviceId
+ serviceName
+ instanceId
+ instanceName
+ layer
+ agentId
+ detectType
+ attributes {
+ name
+ value
+ }
+ labels
+ }
+ startTime
+ endTime
+ }`,
+};
+
+export const analysisEBPFResult = {
+ variable:
+ "$scheduleIdList: [ID!]!, $timeRanges: [EBPFProfilingAnalyzeTimeRange!]!",
+ query: `
+ analysisEBPFResult: analysisEBPFProfilingResult(scheduleIdList: $scheduleIdList, timeRanges: $timeRanges) {
+ tip
+ trees {
+ elements {
+ id
+ parentId
+ symbol
+ stackType
+ dumpCount
+ }
+ }
+ }`,
+};
diff --git a/src/graphql/index.ts b/src/graphql/index.ts
index ab6fd31..3b76c9f 100644
--- a/src/graphql/index.ts
+++ b/src/graphql/index.ts
@@ -25,6 +25,7 @@ import * as log from "./query/log";
import * as profile from "./query/profile";
import * as alarm from "./query/alarm";
import * as event from "./query/event";
+import * as ebpf from "./query/ebpf";
const query: { [key: string]: string } = {
...app,
@@ -36,6 +37,7 @@ const query: { [key: string]: string } = {
...profile,
...alarm,
...event,
+ ...ebpf,
};
class Graphql {
private queryData = "";
diff --git a/src/components/index.ts b/src/graphql/query/ebpf.ts
similarity index 52%
copy from src/components/index.ts
copy to src/graphql/query/ebpf.ts
index 6417e23..131b57a 100644
--- a/src/components/index.ts
+++ b/src/graphql/query/ebpf.ts
@@ -14,31 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import Icon from "./Icon.vue";
-import TimePicker from "./TimePicker.vue";
-import Selector from "./Selector.vue";
-import Graph from "./Graph.vue";
-import Radio from "./Radio.vue";
-import SelectSingle from "./SelectSingle.vue";
-import type { App } from "vue";
-import VueGridLayout from "vue-grid-layout";
-const components: { [key: string]: any } = {
- Icon,
- TimePicker,
- VueGridLayout,
- Selector,
- Graph,
- Radio,
- SelectSingle,
-};
-const componentsName: string[] = Object.keys(components);
+import {
+ queryCreateTaskData,
+ createEBPFTask,
+ queryEBPFTasks,
+ queryEBPFSchedules,
+ analysisEBPFResult,
+} from "../fragments/ebpf";
-export default {
- install: (vue: App): void => {
- vue.use(components["VueGridLayout"]);
- componentsName.forEach((i) => {
- vue.component(i, components[i]);
- });
- },
-};
+export const getCreateTaskData = `query queryCreateTaskData(${queryCreateTaskData.variable}) {${queryCreateTaskData.query}}`;
+
+export const saveEBPFTask = `mutation createEBPFTask(${createEBPFTask.variable}) {${createEBPFTask.query}}`;
+
+export const getEBPFTasks = `query queryEBPFTasks(${queryEBPFTasks.variable}) {${queryEBPFTasks.query}}`;
+
+export const getEBPFSchedules = `query queryEBPFSchedules(${queryEBPFSchedules.variable}) {${queryEBPFSchedules.query}}`;
+
+export const getEBPFResult = `query analysisEBPFResult(${analysisEBPFResult.variable}) {${analysisEBPFResult.query}}`;
diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index d4b0be3..7a5a350 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -131,6 +131,11 @@ const msg = {
metricLabel: "Metric Label",
showUnit: "Show Unit",
noGraph: "No Graph",
+ taskId: "Task ID",
+ triggerType: "Trigger Type",
+ targetType: "Target Type",
+ ebpfTip: "Don't have process could profiling",
+ processSelect: "Click to select processes",
hourTip: "Select Hour",
minuteTip: "Select Minute",
secondTip: "Select Second",
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index 476dd47..d14876b 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -129,6 +129,11 @@ const msg = {
metricLabel: "指标标签",
showUnit: "显示单位",
noGraph: "无图表",
+ taskId: "任务ID",
+ triggerType: "触发类型",
+ targetType: "目标类型",
+ processSelect: "点击选择进程",
+ ebpfTip: "没有进程可以分析",
hourTip: "选择小时",
minuteTip: "选择分钟",
secondTip: "选择秒数",
diff --git a/src/main.ts b/src/main.ts
index f9694ae..9e73794 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -20,7 +20,7 @@ import router from "./router";
import { store } from "./store";
import components from "@/components";
import i18n from "./locales";
-import "./styles/index.scss";
+import "./styles/index.ts";
const app = createApp(App);
diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts
index 93df9a6..17bf3fd 100644
--- a/src/store/modules/dashboard.ts
+++ b/src/store/modules/dashboard.ts
@@ -114,7 +114,12 @@ export const dashboardStore = defineStore({
: 3,
};
}
- if (type === "Trace" || type === "Profile" || type === "Log") {
+ if (
+ type === "Trace" ||
+ type === "Profile" ||
+ type === "Log" ||
+ type === "Ebpf"
+ ) {
newItem.h = 36;
}
if (type === "Text") {
diff --git a/src/store/modules/ebpf.ts b/src/store/modules/ebpf.ts
new file mode 100644
index 0000000..b03bed7
--- /dev/null
+++ b/src/store/modules/ebpf.ts
@@ -0,0 +1,153 @@
+/**
+ * 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.
+ */
+import { defineStore } from "pinia";
+import { Duration, Option } from "@/types/app";
+import {
+ EBPFTaskCreationRequest,
+ EBPFProfilingSchedule,
+ EBPFTaskList,
+ AnalyzationTrees,
+} from "@/types/ebpf";
+import { Trace, Span } from "@/types/trace";
+import { store } from "@/store";
+import graphql from "@/graphql";
+import { AxiosResponse } from "axios";
+import { useAppStoreWithOut } from "@/store/modules/app";
+
+interface EbpfStore {
+ durationTime: Duration;
+ taskList: EBPFTaskList[];
+ eBPFSchedules: EBPFProfilingSchedule[];
+ currentSchedule: EBPFProfilingSchedule | Record<string, never>;
+ analyzeTrees: AnalyzationTrees[];
+ labels: Option[];
+ couldProfiling: boolean;
+ tip: string;
+}
+
+export const ebpfStore = defineStore({
+ id: "eBPF",
+ state: (): EbpfStore => ({
+ durationTime: useAppStoreWithOut().durationTime,
+ taskList: [],
+ eBPFSchedules: [],
+ currentSchedule: {},
+ analyzeTrees: [],
+ labels: [{ value: "", label: "" }],
+ couldProfiling: false,
+ tip: "",
+ }),
+ actions: {
+ setCurrentSpan(span: Span) {
+ this.currentSpan = span;
+ },
+ setCurrentSchedule(s: Trace) {
+ this.currentSchedule = s;
+ },
+ async getCreateTaskData(serviceId: string) {
+ const res: AxiosResponse = await graphql
+ .query("getCreateTaskData")
+ .params({ serviceId });
+
+ if (res.data.errors) {
+ return res.data;
+ }
+ const json = res.data.data.createTaskData;
+ this.couldProfiling = json.couldProfiling || false;
+ this.labels = json.processLabels.map((d: string) => {
+ return { label: d, value: d };
+ });
+ return res.data;
+ },
+ async createTask(param: EBPFTaskCreationRequest) {
+ const res: AxiosResponse = await graphql
+ .query("saveEBPFTask")
+ .params({ request: param });
+
+ if (res.data.errors) {
+ return res.data;
+ }
+ this.getTaskList(param.serviceId);
+ return res.data;
+ },
+ async getTaskList(serviceId: string) {
+ const res: AxiosResponse = await graphql
+ .query("getEBPFTasks")
+ .params({ serviceId });
+
+ this.tip = "";
+ if (res.data.errors) {
+ return res.data;
+ }
+ this.taskList = res.data.data.queryEBPFTasks.reverse() || [];
+ if (!this.taskList.length) {
+ return res.data;
+ }
+ this.getEBPFSchedules({ taskId: this.taskList[0].taskId });
+ return res.data;
+ },
+ async getEBPFSchedules(params: { taskId: string; duration?: Duration }) {
+ const duration = useAppStoreWithOut().durationTime;
+ const res: AxiosResponse = await graphql
+ .query("getEBPFSchedules")
+ .params({ ...params, duration });
+
+ if (res.data.errors) {
+ this.eBPFSchedules = [];
+ return res.data;
+ }
+ this.tip = "";
+ const { eBPFSchedules } = res.data.data;
+
+ this.eBPFSchedules = eBPFSchedules;
+ if (!eBPFSchedules.length) {
+ this.eBPFSchedules = [];
+ }
+ this.analyzeTrees = [];
+ return res.data;
+ },
+ async getEBPFAnalyze(params: {
+ scheduleIdList: string[];
+ timeRanges: Array<{ start: number; end: number }>;
+ }) {
+ const res: AxiosResponse = await graphql
+ .query("getEBPFResult")
+ .params(params);
+
+ if (res.data.errors) {
+ this.analyzeTrees = [];
+ return res.data;
+ }
+ const { analysisEBPFResult } = res.data.data;
+ this.tip = analysisEBPFResult.tip;
+ if (!analysisEBPFResult) {
+ this.analyzeTrees = [];
+ return res.data;
+ }
+ if (analysisEBPFResult.tip) {
+ this.analyzeTrees = [];
+ return res.data;
+ }
+ this.analyzeTrees = analysisEBPFResult.trees[0].elements;
+ return res.data;
+ },
+ },
+});
+
+export function useEbpfStore(): any {
+ return ebpfStore(store);
+}
diff --git a/src/store/modules/profile.ts b/src/store/modules/profile.ts
index 10f9e30..9209e39 100644
--- a/src/store/modules/profile.ts
+++ b/src/store/modules/profile.ts
@@ -16,7 +16,7 @@
*/
import { defineStore } from "pinia";
import { Duration } from "@/types/app";
-import { Service } from "@/types/selector";
+import { Endpoint } from "@/types/selector";
import {
TaskListItem,
SegmentSpan,
@@ -31,7 +31,8 @@ import { AxiosResponse } from "axios";
import { useAppStoreWithOut } from "@/store/modules/app";
interface ProfileState {
- services: Service[];
+ endpoints: Endpoint[];
+ taskEndpoints: Endpoint[];
durationTime: Duration;
condition: { serviceId: string; endpointName: string };
taskList: TaskListItem[];
@@ -47,7 +48,8 @@ interface ProfileState {
export const profileStore = defineStore({
id: "profile",
state: (): ProfileState => ({
- services: [{ value: "0", label: "All" }],
+ endpoints: [{ value: "", label: "All" }],
+ taskEndpoints: [{ value: "", label: "All" }],
durationTime: useAppStoreWithOut().durationTime,
condition: { serviceId: "", endpointName: "" },
taskList: [],
@@ -75,14 +77,28 @@ export const profileStore = defineStore({
setHighlightTop() {
this.highlightTop = !this.highlightTop;
},
- async getServices(layer: string) {
- const res: AxiosResponse = await graphql.query("queryServices").params({
- layer,
+ async getEndpoints(serviceId: string, keyword?: string) {
+ const res: AxiosResponse = await graphql.query("queryEndpoints").params({
+ serviceId,
+ duration: this.durationTime,
+ keyword: keyword || "",
});
if (res.data.errors) {
return res.data;
}
- this.services = res.data.data.services;
+ this.endpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
+ return res.data;
+ },
+ async getTaskEndpoints(serviceId: string, keyword?: string) {
+ const res: AxiosResponse = await graphql.query("queryEndpoints").params({
+ serviceId,
+ duration: this.durationTime,
+ keyword: keyword || "",
+ });
+ if (res.data.errors) {
+ return res.data;
+ }
+ this.taskEndpoints = [{ value: "", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTaskList() {
diff --git a/src/store/modules/trace.ts b/src/store/modules/trace.ts
index a7178a0..794cdff 100644
--- a/src/store/modules/trace.ts
+++ b/src/store/modules/trace.ts
@@ -95,10 +95,7 @@ export const traceStore = defineStore({
if (res.data.errors) {
return res.data;
}
- this.instances = [
- { value: "0", label: "All" },
- ...res.data.data.pods,
- ] || [{ value: " 0", label: "All" }];
+ this.instances = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getEndpoints(id: string, keyword?: string) {
@@ -113,10 +110,7 @@ export const traceStore = defineStore({
if (res.data.errors) {
return res.data;
}
- this.endpoints = [
- { value: "0", label: "All" },
- ...res.data.data.pods,
- ] || [{ value: "0", label: "All" }];
+ this.endpoints = [{ value: "0", label: "All" }, ...res.data.data.pods];
return res.data;
},
async getTraces() {
diff --git a/src/styles/index.scss b/src/styles/index.ts
similarity index 77%
rename from src/styles/index.scss
rename to src/styles/index.ts
index ce0421b..4f098aa 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.ts
@@ -14,6 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@import "./grid.scss";
-@import "./lib.scss";
-@import "./reset.scss";
+import "element-plus/es/components/message/style/css";
+import "element-plus/es/components/message-box/style/css";
+import "element-plus/es/components/notification/style/css";
+import "./grid.scss";
+import "./lib.scss";
+import "./reset.scss";
diff --git a/src/types/dashboard.ts b/src/types/dashboard.d.ts
similarity index 100%
rename from src/types/dashboard.ts
rename to src/types/dashboard.d.ts
diff --git a/src/types/ebpf.d.ts b/src/types/ebpf.d.ts
new file mode 100644
index 0000000..7ca3feb
--- /dev/null
+++ b/src/types/ebpf.d.ts
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+
+export interface EBPFTaskCreationRequest {
+ serviceId: string;
+ processLabels: string[];
+ startTime: number;
+ duration: number;
+ targetType: string;
+}
+
+export interface EBPFTaskList {
+ taskId: string;
+ serviceName: string;
+ serviceId: string;
+ processLabels: string[];
+ taskStartTime: number;
+ fixedTriggerDuration: number;
+ targetType: string;
+ createTime: number;
+ triggerType: string;
+}
+
+export interface EBPFProfilingSchedule {
+ scheduleId: string;
+ taskId: string;
+ process: Process;
+ endTime: number;
+ startTime: number;
+}
+
+export type Process = {
+ id: string;
+ name: string;
+ serviceId: string;
+ serviceName: string;
+ instanceId: string;
+ instanceName: string;
+ layer: string;
+ agentId: string;
+ detectType: string;
+ attributes: { name: string; value: string };
+ labels: string[];
+};
+export type StackElement = {
+ id: string;
+ originId: string;
+ name: string;
+ parentId: string;
+ symbol: string;
+ dumpCount: number;
+ stackType: string;
+ value: number;
+ children?: StackElement[];
+};
+export type AnalyzationTrees = {
+ id: string;
+ parentId: string;
+ symbol: string;
+ dumpCount: number;
+ stackType: string;
+};
diff --git a/src/views/dashboard/List.vue b/src/views/dashboard/List.vue
index 99f62b5..5224bd3 100644
--- a/src/views/dashboard/List.vue
+++ b/src/views/dashboard/List.vue
@@ -44,7 +44,7 @@ limitations under the License. -->
:style="{ fontSize: '13px', width: '100%' }"
v-loading="loading"
ref="multipleTableRef"
- :default-sort="{ prop: 'name' }"
+ :default-sort="{ prop: 'name', order: 'ascending' }"
@selection-change="handleSelectionChange"
height="637px"
size="small"
diff --git a/src/views/dashboard/controls/Ebpf.vue b/src/views/dashboard/controls/Ebpf.vue
new file mode 100644
index 0000000..78f67ef
--- /dev/null
+++ b/src/views/dashboard/controls/Ebpf.vue
@@ -0,0 +1,95 @@
+<!-- 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="profile-wrapper flex-v">
+ <el-popover
+ placement="bottom"
+ trigger="click"
+ :width="100"
+ v-if="dashboardStore.editMode"
+ >
+ <template #reference>
+ <span class="delete cp">
+ <Icon iconName="ellipsis_v" size="middle" class="operation" />
+ </span>
+ </template>
+ <div class="tools" @click="removeWidget">
+ <span>{{ t("delete") }}</span>
+ </div>
+ </el-popover>
+ <Header />
+ <Content />
+ </div>
+</template>
+<script lang="ts" setup>
+import type { PropType } from "vue";
+import { useI18n } from "vue-i18n";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import Header from "../related/ebpf/Header.vue";
+import Content from "../related/ebpf/Content.vue";
+
+/*global defineProps */
+const props = defineProps({
+ data: {
+ type: Object as PropType<any>,
+ default: () => ({ graph: {} }),
+ },
+ activeIndex: { type: String, default: "" },
+});
+const { t } = useI18n();
+const dashboardStore = useDashboardStore();
+
+function removeWidget() {
+ dashboardStore.removeControls(props.data);
+}
+</script>
+<style lang="scss" scoped>
+.profile-wrapper {
+ width: 100%;
+ height: 100%;
+ font-size: 12px;
+ position: relative;
+}
+
+.delete {
+ position: absolute;
+ top: 5px;
+ right: 3px;
+}
+
+.header {
+ padding: 10px;
+ font-size: 12px;
+ border-bottom: 1px solid #dcdfe6;
+}
+
+.tools {
+ padding: 5px 0;
+ color: #999;
+ cursor: pointer;
+ position: relative;
+ text-align: center;
+
+ &:hover {
+ color: #409eff;
+ background-color: #eee;
+ }
+}
+
+.trace {
+ width: 100%;
+ overflow: auto;
+}
+</style>
diff --git a/src/views/dashboard/controls/Tab.vue b/src/views/dashboard/controls/Tab.vue
index 7d15396..3ce2239 100644
--- a/src/views/dashboard/controls/Tab.vue
+++ b/src/views/dashboard/controls/Tab.vue
@@ -97,7 +97,7 @@ limitations under the License. -->
:key="item.i"
@click="clickTabGrid($event, item)"
:class="{ active: activeTabWidget === item.i }"
- drag-ignore-from="svg.d3-trace-tree, .dragger, .micro-topo-chart"
+ :drag-ignore-from="dragIgnoreFrom"
>
<component
:is="item.type"
@@ -122,6 +122,8 @@ import Trace from "./Trace.vue";
import Profile from "./Profile.vue";
import Log from "./Log.vue";
import Text from "./Text.vue";
+import Ebpf from "./Ebpf.vue";
+import { dragIgnoreFrom } from "../data";
const props = {
data: {
@@ -132,7 +134,7 @@ const props = {
};
export default defineComponent({
name: "Tab",
- components: { Topology, Widget, Trace, Profile, Log, Text },
+ components: { Topology, Widget, Trace, Profile, Log, Text, Ebpf },
props,
setup(props) {
const { t } = useI18n();
@@ -248,6 +250,7 @@ export default defineComponent({
canEditTabName,
showTools,
t,
+ dragIgnoreFrom,
};
},
});
diff --git a/src/views/dashboard/controls/index.ts b/src/views/dashboard/controls/index.ts
index 9829a0d..01fc5dc 100644
--- a/src/views/dashboard/controls/index.ts
+++ b/src/views/dashboard/controls/index.ts
@@ -21,5 +21,6 @@ import Trace from "./Trace.vue";
import Profile from "./Profile.vue";
import Log from "./Log.vue";
import Text from "./Text.vue";
+import Ebpf from "./Ebpf.vue";
-export default { Tab, Widget, Trace, Topology, Profile, Log, Text };
+export default { Tab, Widget, Trace, Topology, Profile, Log, Text, Ebpf };
diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts
index bed9001..c6e4bc3 100644
--- a/src/views/dashboard/data.ts
+++ b/src/views/dashboard/data.ts
@@ -14,6 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+export const dragIgnoreFrom =
+ "svg.d3-trace-tree, .dragger, .micro-topo-chart, .schedules";
export const PodsChartTypes = ["EndpointList", "InstanceList"];
@@ -181,7 +183,8 @@ export const ServiceTools = [
{ name: "library_books", content: "Text", id: "addText" },
{ name: "device_hub", content: "Topology", id: "addTopology" },
{ name: "merge", content: "Trace", id: "addTrace" },
- { name: "timeline", content: "Profile", id: "addProfile" },
+ { name: "timeline", content: "Trace Profiling", id: "addProfile" },
+ { name: "insert_chart", content: "eBPF Profiling", id: "addEbpf" },
{ name: "assignment", content: "Log", id: "addLog" },
];
export const InstanceTools = [
diff --git a/src/views/dashboard/panel/Layout.vue b/src/views/dashboard/panel/Layout.vue
index 39eb8a6..b54a23f 100644
--- a/src/views/dashboard/panel/Layout.vue
+++ b/src/views/dashboard/panel/Layout.vue
@@ -31,7 +31,7 @@ limitations under the License. -->
:key="item.i"
@click="clickGrid(item)"
:class="{ active: dashboardStore.activedGridItem === item.i }"
- drag-ignore-from="svg.d3-trace-tree, .dragger, .micro-topo-chart"
+ :drag-ignore-from="dragIgnoreFrom"
>
<component :is="item.type" :data="item" />
</grid-item>
@@ -45,6 +45,7 @@ import { useDashboardStore } from "@/store/modules/dashboard";
import { useSelectorStore } from "@/store/modules/selectors";
import { LayoutConfig } from "@/types/dashboard";
import controls from "../controls/index";
+import { dragIgnoreFrom } from "../data";
export default defineComponent({
name: "Layout",
@@ -72,6 +73,7 @@ export default defineComponent({
dashboardStore,
clickGrid,
t,
+ dragIgnoreFrom,
};
},
});
diff --git a/src/views/dashboard/panel/Tool.vue b/src/views/dashboard/panel/Tool.vue
index 2c079d7..6d0339b 100644
--- a/src/views/dashboard/panel/Tool.vue
+++ b/src/views/dashboard/panel/Tool.vue
@@ -258,7 +258,7 @@ async function setSourceSelector() {
let currentPod;
if (states.currentPod) {
currentPod = selectorStore.pods.find(
- (d: { id: string }) => d.label === states.currentPod
+ (d: { label: string }) => d.label === states.currentPod
);
} else {
currentPod = selectorStore.pods.find((d: { id: string }) => d.id === pod);
@@ -283,10 +283,10 @@ async function setDestSelector() {
return;
}
const destPod = params.destPodId || selectorStore.destPods[0].id;
- let currentDestPod = "";
+ let currentDestPod = { label: "" };
if (states.currentDestPod) {
currentDestPod = selectorStore.pods.find(
- (d: { id: string }) => d.label === states.currentDestPod
+ (d: { label: string }) => d.label === states.currentDestPod
);
} else {
currentDestPod = selectorStore.destPods.find(
@@ -317,19 +317,23 @@ async function getServices() {
let s;
if (states.currentService) {
s = (selectorStore.services || []).find(
- (d) => d.label === states.currentService
+ (d: { label: string }) => d.label === states.currentService
);
} else {
- s = (selectorStore.services || []).find((d, index) => index === 0);
+ s = (selectorStore.services || []).find(
+ (d: unknown, index: number) => index === 0
+ );
}
selectorStore.setCurrentService(s || null);
let d;
if (states.currentService) {
d = (selectorStore.services || []).find(
- (d) => d.label === states.currentDestService
+ (d: { label: string }) => d.label === states.currentDestService
);
} else {
- d = (selectorStore.services || []).find((d, index) => index === 1);
+ d = (selectorStore.services || []).find(
+ (d: unknown, index: number) => index === 1
+ );
}
selectorStore.setCurrentDestService(d || null);
if (!selectorStore.currentService) {
@@ -431,6 +435,9 @@ function setTabControls(id: string) {
case "addProfile":
dashboardStore.addTabControls("Profile");
break;
+ case "addEbpf":
+ dashboardStore.addTabControls("Ebpf");
+ break;
case "addTopology":
dashboardStore.addTabControls("Topology");
break;
@@ -457,6 +464,9 @@ function setControls(id: string) {
case "addProfile":
dashboardStore.addControl("Profile");
break;
+ case "addEbpf":
+ dashboardStore.addControl("Ebpf");
+ break;
case "addLog":
dashboardStore.addControl("Log");
break;
@@ -484,9 +494,13 @@ async function fetchPods(
if (setPod) {
let p;
if (states.currentPod) {
- p = selectorStore.pods.find((d) => d.label === states.currentPod);
+ p = selectorStore.pods.find(
+ (d: { label: unknown }) => d.label === states.currentPod
+ );
} else {
- p = selectorStore.pods.find((d, index) => index === 0);
+ p = selectorStore.pods.find(
+ (d: unknown, index: number) => index === 0
+ );
}
selectorStore.setCurrentPod(p || null);
states.currentPod = selectorStore.currentPod.label;
@@ -497,9 +511,13 @@ async function fetchPods(
if (setPod) {
let p;
if (states.currentPod) {
- p = selectorStore.pods.find((d) => d.label === states.currentPod);
+ p = selectorStore.pods.find(
+ (d: { label: string }) => d.label === states.currentPod
+ );
} else {
- p = selectorStore.pods.find((d, index) => index === 0);
+ p = selectorStore.pods.find(
+ (d: { label: string }, index: number) => index === 0
+ );
}
selectorStore.setCurrentPod(p || null);
states.currentPod = selectorStore.currentPod.label;
@@ -514,9 +532,13 @@ async function fetchPods(
if (setPod) {
let p;
if (states.currentDestPod) {
- p = selectorStore.pods.find((d) => d.label === states.currentDestPod);
+ p = selectorStore.pods.find(
+ (d: { label: string }) => d.label === states.currentDestPod
+ );
} else {
- p = selectorStore.pods.find((d, index) => index === 0);
+ p = selectorStore.pods.find(
+ (d: { label: string }, index: number) => index === 0
+ );
}
selectorStore.setCurrentDestPod(p || null);
states.currentDestPod = selectorStore.currentDestPod.label;
@@ -530,9 +552,13 @@ async function fetchPods(
if (setPod) {
let p;
if (states.currentDestPod) {
- p = selectorStore.pods.find((d) => d.label === states.currentDestPod);
+ p = selectorStore.pods.find(
+ (d: { label: string }) => d.label === states.currentDestPod
+ );
} else {
- p = selectorStore.pods.find((d, index) => index === 0);
+ p = selectorStore.pods.find(
+ (d: { label: string }, index: number) => index === 0
+ );
}
selectorStore.setCurrentDestPod(p || null);
states.currentDestPod = selectorStore.currentDestPod.label;
diff --git a/src/components/Radio.vue b/src/views/dashboard/related/ebpf/Content.vue
similarity index 52%
copy from src/components/Radio.vue
copy to src/views/dashboard/related/ebpf/Content.vue
index f5aee03..e4b0653 100644
--- a/src/components/Radio.vue
+++ b/src/views/dashboard/related/ebpf/Content.vue
@@ -13,38 +13,38 @@ 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>
- <el-radio-group v-model="selected" @change="checked">
- <el-radio v-for="item in options" :key="item.value" :label="item.value">
- {{ item.label }}
- </el-radio>
- </el-radio-group>
+ <div class="flex-h content">
+ <TaskList />
+ <div class="vis-graph ml-5">
+ <div class="item">
+ <EBPFSchedules />
+ </div>
+ <div class="item">
+ <EBPFStack />
+ </div>
+ </div>
+ </div>
</template>
<script lang="ts" setup>
-import { ref } from "vue";
-import type { PropType } from "vue";
-
-interface Option {
- label: string;
- value: string;
+import TaskList from "./components/TaskList.vue";
+import EBPFSchedules from "./components/EBPFSchedules.vue";
+import EBPFStack from "./components/EBPFStack.vue";
+</script>
+<style lang="scss" scoped>
+.content {
+ height: calc(100% - 30px);
+ width: 100%;
}
-/*global defineProps, defineEmits */
-const emit = defineEmits(["change"]);
-const props = defineProps({
- options: {
- type: Array as PropType<(Option & { disabled: boolean })[]>,
- default: () => [],
- },
- value: {
- type: String as PropType<string>,
- default: "",
- },
- size: { type: null, default: "default" },
-});
-
-const selected = ref<string>(props.value);
+.vis-graph {
+ height: 100%;
+ width: calc(100% - 300px);
+}
-function checked(opt: string) {
- emit("change", opt);
+.item {
+ width: 100%;
+ overflow: auto;
+ height: calc(50% - 10px);
+ padding-bottom: 10px;
}
-</script>
+</style>
diff --git a/src/views/dashboard/related/profile/Header.vue b/src/views/dashboard/related/ebpf/Header.vue
similarity index 58%
copy from src/views/dashboard/related/profile/Header.vue
copy to src/views/dashboard/related/ebpf/Header.vue
index e221539..da7a809 100644
--- a/src/views/dashboard/related/profile/Header.vue
+++ b/src/views/dashboard/related/ebpf/Header.vue
@@ -14,29 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="flex-h header">
- <!-- <div class="mr-10" v-if="dashboardStore.entity==='All'">
- <span class="grey mr-5">{{ t("service") }}:</span>
- <Selector
- size="small"
- :value="service.value"
- :options="profileStore.services"
- placeholder="Select a service"
- @change="changeService"
- />
- </div> -->
- <div class="mr-10">
- <span class="grey mr-5">{{ t("endpointName") }}:</span>
- <el-input v-model="endpointName" class="name" size="small" />
- </div>
- <el-button
- class="search-btn"
- size="small"
- type="primary"
- @click="searchTasks"
- >
- {{ t("search") }}
- </el-button>
- <el-button class="search-btn" size="small" @click="createTask">
+ <div class="title">eBPF Profiling</div>
+ <el-button type="primary" size="small" @click="createTask">
{{ t("newTask") }}
</el-button>
</div>
@@ -52,7 +31,7 @@ limitations under the License. -->
<script lang="ts" setup>
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
-import { useProfileStore } from "@/store/modules/profile";
+import { useEbpfStore } from "@/store/modules/ebpf";
import { useSelectorStore } from "@/store/modules/selectors";
import { ElMessage } from "element-plus";
import NewTask from "./components/NewTask.vue";
@@ -60,47 +39,31 @@ import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { EntityType } from "../../data";
-const profileStore = useProfileStore();
+const ebpfStore = useEbpfStore();
const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const { t } = useI18n();
-// const service = ref<any>({});
-const endpointName = ref<string>("");
const newTask = ref<boolean>(false);
searchTasks();
-// getServices();
-
-// async function getServices() {
-// const res = await profileStore.getServices(dashboardStore.layerId);
-// if (res.errors) {
-// ElMessage.error(res.errors);
-// return;
-// }
-// service.value = profileStore.services[0];
-// searchTasks();
-// }
-
-// function changeService(opt: any[]) {
-// service.value = opt[0];
-// }
async function searchTasks() {
- profileStore.setConditions({
- serviceId:
- (selectorStore.currentService && selectorStore.currentService.id) || "",
- endpointName: endpointName.value,
- });
- const res = await profileStore.getTaskList();
+ const serviceId =
+ (selectorStore.currentService && selectorStore.currentService.id) || "";
+ const res = await ebpfStore.getTaskList(serviceId);
if (res.errors) {
ElMessage.error(res.errors);
}
}
-function createTask() {
+async function createTask() {
+ if (!selectorStore.currentService) {
+ return;
+ }
newTask.value = true;
+ ebpfStore.getCreateTaskData(selectorStore.currentService.id);
}
watch(
@@ -120,12 +83,22 @@ watch(
</script>
<style lang="scss" scoped>
.header {
- padding: 10px;
+ padding: 5px 20px 5px 10px;
font-size: 12px;
border-bottom: 1px solid #dcdfe6;
+ justify-content: space-between;
}
.name {
width: 270px;
}
+
+.new-btn {
+ float: right;
+}
+
+.title {
+ font-weight: bold;
+ line-height: 24px;
+}
</style>
diff --git a/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue b/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue
new file mode 100644
index 0000000..cfdcd91
--- /dev/null
+++ b/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue
@@ -0,0 +1,244 @@
+<!-- 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">
+ <Selector
+ :value="selectedLabels"
+ :options="labels"
+ size="small"
+ placeholder="Please select labels"
+ @change="changeLabels"
+ class="inputs mr-10"
+ :multiple="true"
+ />
+ <el-popover placement="bottom" :width="680" trigger="click">
+ <template #reference>
+ <el-button type="primary" size="small">
+ {{ t("processSelect") }}
+ </el-button>
+ </template>
+ <el-input
+ v-model="searchText"
+ placeholder="Please input name"
+ class="input-with-search"
+ size="small"
+ @change="searchProcesses"
+ >
+ <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">
+ {{ scope.row.attributes.map((d: {name: string, value: string}) => `${d.name}=${d.value}`).join("; ") }}
+ </template>
+ </el-table-column>
+ </el-table>
+ <el-pagination
+ class="pagination"
+ background
+ 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 ref="timeline" class="schedules"></div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from "vue";
+import dayjs from "dayjs";
+import { useI18n } from "vue-i18n";
+import { Option } from "@/types/app";
+import { TableHeader } from "./data";
+import { useEbpfStore } from "@/store/modules/ebpf";
+import { EBPFProfilingSchedule, Process } from "@/types/ebpf";
+import { DataSet, Timeline } from "vis-timeline/standalone";
+import "vis-timeline/styles/vis-timeline-graph2d.css";
+import { ElMessage, ElTable } from "element-plus";
+
+const { t } = useI18n();
+const ebpfStore = useEbpfStore();
+const pageSize = 5;
+/*global Nullable */
+const multipleTableRef = ref<InstanceType<typeof ElTable>>();
+const selectedProcesses = ref<string[]>([]);
+const timeline = ref<Nullable<HTMLDivElement>>(null);
+const visGraph = ref<Nullable<any>>(null);
+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 dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
+ new Date(dayjs(date).format(pattern));
+
+function changeLabels(opt: any[]) {
+ const arr = opt.map((d) => d.value);
+ selectedLabels.value = arr;
+}
+
+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,
+ });
+ if (res.data.errors) {
+ ElMessage.error(res.data.errors);
+ return;
+ }
+}
+
+function visTimeline() {
+ if (visGraph.value) {
+ visGraph.value.destroy();
+ }
+ labels.value = [{ label: "All", value: "0" }];
+ selectedLabels.value = ["0"];
+ processes.value = [];
+ const schedules = ebpfStore.eBPFSchedules.map(
+ (d: EBPFProfilingSchedule, index: number) => {
+ for (const l of d.process.labels) {
+ labels.value.push({ label: l, value: l });
+ }
+ processes.value.push(d.process);
+ return {
+ id: index + 1,
+ content: d.process.name,
+ start: dateFormat(d.startTime),
+ end: dateFormat(d.endTime),
+ };
+ }
+ );
+ searchProcesses();
+ if (!timeline.value) {
+ return;
+ }
+ const h = timeline.value.getBoundingClientRect().height;
+ const items: any = new DataSet(schedules);
+ const options = {
+ height: h,
+ width: "100%",
+ locale: "en",
+ };
+ visGraph.value = new Timeline(timeline.value, items, options);
+}
+
+function changePage(pageIndex: number) {
+ searchProcesses(pageIndex);
+}
+
+function searchProcesses(pageIndex?: any) {
+ const arr = processes.value.filter(
+ (d: { name: string; instanceName: string }) =>
+ d.name.includes(searchText.value) ||
+ d.instanceName.includes(searchText.value)
+ );
+ currentProcesses.value = arr.splice(
+ (pageIndex - 1 || 0) * pageSize,
+ pageSize * (pageIndex || 1)
+ );
+}
+
+watch(
+ () => ebpfStore.eBPFSchedules,
+ () => {
+ visTimeline();
+ }
+);
+</script>
+<style lang="scss" scoped>
+.filters {
+ margin: 5px 0;
+}
+
+.schedules {
+ width: calc(100% - 5px);
+ margin: 0 5px 5px 0;
+ height: calc(100% - 60px);
+ min-height: 150px;
+}
+
+.inputs {
+ width: 300px;
+}
+
+.input-with-search {
+ width: 650px;
+ margin-bottom: 5px;
+}
+
+.pagination {
+ margin-top: 10px;
+}
+</style>
diff --git a/src/views/dashboard/related/ebpf/components/EBPFStack.vue b/src/views/dashboard/related/ebpf/components/EBPFStack.vue
new file mode 100644
index 0000000..d2776e3
--- /dev/null
+++ b/src/views/dashboard/related/ebpf/components/EBPFStack.vue
@@ -0,0 +1,148 @@
+<!-- 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 id="graph-stack" ref="graph">
+ <span class="tip" v-show="ebpfStore.tip">{{ ebpfStore.tip }}</span>
+ </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from "vue";
+import * as d3 from "d3";
+import d3tip from "d3-tip";
+import { flamegraph } from "d3-flame-graph";
+import { useEbpfStore } from "@/store/modules/ebpf";
+import { StackElement } from "@/types/ebpf";
+import "d3-flame-graph/dist/d3-flamegraph.css";
+
+/*global Nullable*/
+const ebpfStore = useEbpfStore();
+const stackTree = ref<Nullable<StackElement>>(null);
+const graph = ref<Nullable<HTMLDivElement>>(null);
+const flameChart = ref<any>(null);
+
+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 - 15)
+ .cellHeight(18)
+ .transitionDuration(750)
+ .minFrameSize(5)
+ .transitionEase(d3.easeCubic as any)
+ .sort(true)
+ .title("")
+ .selfValue(false)
+ .setColorMapper((d, originalColor) =>
+ d.highlight ? "#6aff8f" : originalColor
+ );
+ const tip = (d3tip as any)()
+ .attr("class", "d3-tip")
+ .direction("w")
+ .html(
+ (d: { data: StackElement }) =>
+ `<div class="mb-5">Symbol: ${d.data.name}</div><div class="mb-5">Dump Count: ${d.data.dumpCount}</div>`
+ );
+ flameChart.value.tooltip(tip);
+ d3.select("#graph-stack").datum(stackTree.value).call(flameChart.value);
+}
+
+function processTree(arr: StackElement[]) {
+ const copyArr = JSON.parse(JSON.stringify(arr));
+ const obj: any = {};
+ let res = null;
+ let min = 1;
+ let max = 1;
+ for (const item of copyArr) {
+ item.originId = item.id;
+ item.name = item.symbol;
+ delete item.id;
+ obj[item.originId] = item;
+ if (item.dumpCount > max) {
+ max = item.dumpCount;
+ }
+ if (item.dumpCount < min) {
+ min = item.dumpCount;
+ }
+ }
+ const scale = d3.scaleLinear().domain([min, max]).range([1, 200]);
+ for (const item of copyArr) {
+ if (item.parentId === "0") {
+ const val = Number(scale(item.dumpCount).toFixed(4));
+ res = item;
+ res.value = val;
+ }
+ for (const key in obj) {
+ if (item.originId === obj[key].parentId) {
+ const val = Number(scale(obj[key].dumpCount).toFixed(4));
+
+ obj[key].value = val;
+ if (item.children) {
+ item.children.push(obj[key]);
+ } else {
+ item.children = [obj[key]];
+ }
+ }
+ }
+ }
+ treeForeach([res], (node: StackElement) => {
+ if (node.children) {
+ let val = 0;
+ for (const child of node.children) {
+ val = child.value + val;
+ }
+ node.value = node.value < val ? val : node.value;
+ }
+ });
+
+ return res;
+}
+
+function treeForeach(tree: StackElement[], func: (node: StackElement) => void) {
+ for (const data of tree) {
+ data.children && treeForeach(data.children, func);
+ func(data);
+ }
+ return tree;
+}
+
+watch(
+ () => ebpfStore.analyzeTrees,
+ () => {
+ drawGraph();
+ }
+);
+</script>
+<style>
+#graph-stack {
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+}
+
+.tip {
+ display: inline-block;
+ width: 100%;
+ text-align: center;
+ color: red;
+ margin-top: 20px;
+}
+</style>
diff --git a/src/views/dashboard/related/profile/components/NewTask.vue b/src/views/dashboard/related/ebpf/components/NewTask.vue
similarity index 58%
copy from src/views/dashboard/related/profile/components/NewTask.vue
copy to src/views/dashboard/related/ebpf/components/NewTask.vue
index f5c4401..5a0e63c 100644
--- a/src/views/dashboard/related/profile/components/NewTask.vue
+++ b/src/views/dashboard/related/ebpf/components/NewTask.vue
@@ -14,16 +14,35 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
- <div class="profile-task">
+ <div class="ebpf-task" v-if="eBPFStore.couldProfiling">
<div>
- <div class="label">{{ t("endpointName") }}</div>
- <el-input v-model="endpointName" class="profile-input" size="small" />
+ <div class="label">{{ t("labels") }}</div>
+ <Selector
+ class="profile-input"
+ size="small"
+ :value="labels"
+ :options="eBPFStore.labels"
+ placeholder="Select labels"
+ :multiple="true"
+ @change="changeLabel"
+ />
+ </div>
+ <div>
+ <div class="label">{{ t("targetType") }}</div>
+ <Selector
+ class="profile-input"
+ size="small"
+ :value="type"
+ :options="TargetTypes"
+ placeholder="Select a type"
+ :isRemote="true"
+ @change="changeType"
+ />
</div>
<div>
<div class="label">{{ t("monitorTime") }}</div>
<div>
<Radio
- class="mb-5"
:value="monitorTime"
:options="InitTaskField.monitorTimeEn"
@change="changeMonitorTime"
@@ -40,41 +59,16 @@ limitations under the License. -->
</div>
<div>
<div class="label">{{ t("monitorDuration") }}</div>
- <Radio
- class="mb-5"
- :value="monitorDuration"
- :options="InitTaskField.monitorDuration"
- @change="changeMonitorDuration"
- />
- </div>
- <div>
- <div class="label">{{ t("minThreshold") }} (ms)</div>
- <el-input-number
- size="small"
+ <el-input
class="profile-input"
- :min="0"
- v-model="minThreshold"
- />
- </div>
- <div>
- <div class="label">{{ t("dumpPeriod") }}</div>
- <Radio
- class="mb-5"
- :value="dumpPeriod"
- :options="InitTaskField.dumpPeriod"
- @change="changeDumpPeriod"
- />
- </div>
- <div>
- <div class="label">{{ t("maxSamplingCount") }}</div>
- <Selector
+ v-model="monitorDuration"
size="small"
- :value="maxSamplingCount"
- :options="InitTaskField.maxSamplingCount"
- placeholder="Select a data"
- @change="changeMaxSamplingCount"
- class="profile-input"
+ placeholder="none"
+ type="number"
+ :min="1"
+ :max="60"
/>
+ Min
</div>
<div>
<el-button @click="createTask" type="primary" class="create-task-btn">
@@ -82,78 +76,73 @@ limitations under the License. -->
</el-button>
</div>
</div>
+ <div v-else>{{ t("ebpfTip") }}</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
-import { useProfileStore } from "@/store/modules/profile";
+import { useEbpfStore } from "@/store/modules/ebpf";
import { useSelectorStore } from "@/store/modules/selectors";
import { useAppStoreWithOut } from "@/store/modules/app";
import { ElMessage } from "element-plus";
-import { InitTaskField } from "./data";
+import { InitTaskField, TargetTypes } from "./data";
/* global defineEmits */
const emits = defineEmits(["close"]);
-const profileStore = useProfileStore();
+const eBPFStore = useEbpfStore();
const selectorStore = useSelectorStore();
const appStore = useAppStoreWithOut();
const { t } = useI18n();
-const endpointName = ref<string>("");
+const labels = ref<string[]>([]);
+const type = ref<string>(TargetTypes[0].value);
const monitorTime = ref<string>(InitTaskField.monitorTimeEn[0].value);
-const monitorDuration = ref<string>(InitTaskField.monitorDuration[0].value);
+const monitorDuration = ref<number>(10);
const time = ref<Date>(appStore.durationRow.start);
-const minThreshold = ref<number>(0);
-const dumpPeriod = ref<string>(InitTaskField.dumpPeriod[0].value);
-const maxSamplingCount = ref<string>(InitTaskField.maxSamplingCount[0].value);
function changeMonitorTime(opt: string) {
monitorTime.value = opt;
}
-function changeMonitorDuration(val: string) {
- monitorDuration.value = val;
-}
-
-function changeDumpPeriod(val: string) {
- dumpPeriod.value = val;
+function changeLabel(opt: any[]) {
+ labels.value = opt.map((d) => d.value);
}
-function changeMaxSamplingCount(opt: any[]) {
- maxSamplingCount.value = opt[0].value;
+function changeType(opt: any[]) {
+ type.value = opt[0].value;
}
async function createTask() {
- emits("close");
- const date =
- monitorTime.value === "0" ? appStore.durationRow.start : time.value;
+ if (!labels.value.length) {
+ ElMessage.warning("no labels");
+ return;
+ }
+ const date = monitorTime.value === "0" ? new Date() : time.value;
const params = {
serviceId: selectorStore.currentService.id,
- endpointName: endpointName.value,
+ processLabels: labels.value,
startTime: date.getTime(),
- duration: Number(monitorDuration.value),
- minDurationThreshold: Number(minThreshold.value),
- dumpPeriod: Number(dumpPeriod.value),
- maxSamplingCount: Number(maxSamplingCount.value),
+ duration: monitorDuration.value * 60,
+ targetType: "ON_CPU",
};
- const res = await profileStore.createTask(params);
+ const res = await eBPFStore.createTask(params);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
- const { tip } = res.data;
- if (tip) {
- ElMessage.error(tip);
+ if (!res.data.createTaskData.status) {
+ ElMessage.error(res.data.createTaskData.errorReason);
return;
}
ElMessage.success("Task created successfully");
+ emits("close");
}
function changeTimeRange(val: Date) {
time.value = val;
}
</script>
<style lang="scss" scoped>
-.profile-task {
+.ebpf-task {
margin: 0 auto;
- width: 350px;
+ width: 400px;
}
.date {
diff --git a/src/views/dashboard/related/profile/components/TaskList.vue b/src/views/dashboard/related/ebpf/components/TaskList.vue
similarity index 52%
copy from src/views/dashboard/related/profile/components/TaskList.vue
copy to src/views/dashboard/related/ebpf/components/TaskList.vue
index ca14705..65c93ce 100644
--- a/src/views/dashboard/related/profile/components/TaskList.vue
+++ b/src/views/dashboard/related/ebpf/components/TaskList.vue
@@ -17,32 +17,34 @@ limitations under the License. -->
<div class="profile-task-wrapper flex-v">
<div class="profile-t-tool flex-h">{{ t("taskList") }}</div>
<div class="profile-t-wrapper">
- <div class="no-data" v-show="!profileStore.taskList.length">
+ <div class="no-data" v-show="!ebpfStore.taskList.length">
{{ t("noData") }}
</div>
<table class="profile-t">
<tr
class="profile-tr cp"
- v-for="(i, index) in profileStore.taskList"
+ v-for="(i, index) in ebpfStore.taskList"
@click="changeTask(i)"
:key="index"
>
<td
class="profile-td"
:class="{
- selected: selectedTask.id === i.id,
+ selected: selectedTask.taskId === i.taskId,
}"
>
<div class="ell">
- <span>{{ i.endpointName }}</span>
- <a class="profile-btn r" @click="viewTask($event, i)">
- <Icon iconName="library_books" size="middle" />
+ <span>{{ i.processLabels.join(" ") }}</span>
+ <a class="profile-btn r" @click="viewDetail = true">
+ <Icon iconName="view" size="middle" />
</a>
</div>
<div class="grey ell sm">
- <span class="mr-10 sm">{{ dateFormat(i.startTime) }}</span>
+ <span class="mr-10 sm">{{ dateFormat(i.taskStartTime) }}</span>
<span class="mr-10 sm">
- {{ dateFormat(i.startTime + i.duration * 60 * 1000) }}
+ {{
+ dateFormat(i.taskStartTime + i.fixedTriggerDuration * 1000)
+ }}
</span>
</div>
</td>
@@ -60,126 +62,81 @@ limitations under the License. -->
<div class="profile-detail flex-v">
<div>
<h5 class="mb-10">{{ t("task") }}.</h5>
+ <div class="mb-10 clear item">
+ <span class="g-sm-4 grey">{{ t("taskId") }}:</span>
+ <span class="g-sm-8 wba">
+ {{ selectedTask.taskId }}
+ </span>
+ </div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("service") }}:</span>
- <span class="g-sm-8 wba">{{ service }}</span>
+ <span class="g-sm-8 wba">{{ selectedTask.serviceName }}</span>
</div>
<div class="mb-10 clear item">
- <span class="g-sm-4 grey">{{ t("endpoint") }}:</span>
- <span class="g-sm-8 wba">{{ selectedTask.endpointName }}</span>
+ <span class="g-sm-4 grey">{{ t("labels") }}:</span>
+ <span class="g-sm-8 wba">{{ selectedTask.processLabels }}</span>
</div>
<div class="mb-10 clear item">
<span class="g-sm-4 grey">{{ t("monitorTime") }}:</span>
<span class="g-sm-8 wba">
- {{ dateFormat(selectedTask.startTime) }}
+ {{ dateFormat(selectedTask.taskStartTime) }}
</span>
</div>
<div class="mb-10 clear item">
- <span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span
- ><span class="g-sm-8 wba">{{ selectedTask.duration }} min</span>
- </div>
- <div class="mb-10 clear item">
- <span class="g-sm-4 grey">{{ t("minThreshold") }}:</span>
+ <span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span>
<span class="g-sm-8 wba">
- {{ selectedTask.minDurationThreshold }} ms
+ {{ selectedTask.fixedTriggerDuration / 60 }} min
</span>
</div>
<div class="mb-10 clear item">
- <span class="g-sm-4 grey">{{ t("dumpPeriod") }}:</span>
- <span class="g-sm-8 wba">{{ selectedTask.dumpPeriod }}</span>
+ <span class="g-sm-4 grey">{{ t("triggerType") }}:</span>
+ <span class="g-sm-8 wba">{{ selectedTask.triggerType }}</span>
</div>
<div class="mb-10 clear item">
- <span class="g-sm-4 grey">{{ t("maxSamplingCount") }}:</span>
- <span class="g-sm-8 wba">{{ selectedTask.maxSamplingCount }}</span>
- </div>
- </div>
- <div>
- <h5
- class="mb-10 mt-10"
- v-show="selectedTask.logs && selectedTask.logs.length"
- >
- {{ t("logs") }}.
- </h5>
- <div
- class="log-item"
- v-for="(i, index) in Object.keys(instanceLogs)"
- :key="index"
- >
- <div class="mb-10 sm">
- <span class="mr-10 grey">{{ t("instance") }}:</span>
- <span>{{ i }}</span>
- </div>
- <div v-for="(d, index) in instanceLogs[i]" :key="index">
- <span class="mr-10 grey">{{ t("operationType") }}:</span>
- <span class="mr-20">{{ d.operationType }}</span>
- <span class="mr-10 grey">{{ t("time") }}:</span>
- <span>{{ dateFormat(d.operationTime) }}</span>
- </div>
+ <span class="g-sm-4 grey">{{ t("targetType") }}:</span>
+ <span class="g-sm-8 wba">{{ selectedTask.targetType }}</span>
</div>
</div>
</div>
</el-dialog>
</template>
<script lang="ts" setup>
-import { ref } from "vue";
+import { ref, watch } from "vue";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
-import { useProfileStore } from "@/store/modules/profile";
-import { TaskLog, TaskListItem } from "@/types/profile";
+import { useEbpfStore } from "@/store/modules/ebpf";
+import { EBPFTaskList } from "@/types/ebpf";
import { ElMessage } from "element-plus";
const { t } = useI18n();
-const profileStore = useProfileStore();
+const ebpfStore = useEbpfStore();
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
+const selectedTask = ref<EBPFTaskList | Record<string, never>>({});
const viewDetail = ref<boolean>(false);
-const service = ref<string>("");
-const selectedTask = ref<TaskListItem | Record<string, never>>({});
-const instanceLogs = ref<TaskLog | any>({});
-async function changeTask(item: TaskListItem) {
+async function changeTask(item: EBPFTaskList) {
selectedTask.value = item;
- const res = await profileStore.getSegmentList({ taskID: item.id });
+ const res = await ebpfStore.getEBPFSchedules({
+ taskId: item.taskId,
+ });
if (res.errors) {
ElMessage.error(res.errors);
}
}
-
-async function viewTask(e: Event, item: TaskListItem) {
- window.event ? (window.event.cancelBubble = true) : e.stopPropagation();
- viewDetail.value = true;
- selectedTask.value = item;
- service.value = (
- profileStore.services.filter((s: any) => s.id === item.serviceId)[0] || {}
- ).label;
- const res = await profileStore.getTaskLogs({ taskID: item.id });
-
- if (res.errors) {
- ElMessage.error(res.errors);
- return;
- }
- item.logs = profileStore.taskLogs;
- instanceLogs.value = {};
- for (const d of item.logs) {
- if (instanceLogs.value[d.instanceName]) {
- instanceLogs.value[d.instanceName].push({
- operationType: d.operationType,
- operationTime: d.operationTime,
- });
- } else {
- instanceLogs.value[d.instanceName] = [
- { operationType: d.operationType, operationTime: d.operationTime },
- ];
- }
+watch(
+ () => ebpfStore.taskList,
+ () => {
+ selectedTask.value = ebpfStore.taskList[0] || {};
}
- selectedTask.value = item;
-}
+);
</script>
<style lang="scss" scoped>
.profile-task-list {
width: 300px;
- height: calc((100% - 60px) / 2);
+ height: calc(100% - 10px);
overflow: auto;
+ border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.item span {
@@ -203,7 +160,6 @@ async function viewTask(e: Event, item: TaskListItem) {
.profile-t-wrapper {
overflow: auto;
flex-grow: 1;
- border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.profile-t {
@@ -212,6 +168,7 @@ async function viewTask(e: Event, item: TaskListItem) {
table-layout: fixed;
flex-grow: 1;
position: relative;
+ border: none;
}
.profile-tr {
@@ -220,10 +177,6 @@ async function viewTask(e: Event, item: TaskListItem) {
}
}
-.profile-segment {
- border-top: 1px solid rgba(0, 0, 0, 0.07);
-}
-
.profile-t-tool {
padding: 5px 10px;
font-weight: bold;
@@ -232,10 +185,6 @@ async function viewTask(e: Event, item: TaskListItem) {
background: #f3f4f9;
}
-.log-item {
- margin-top: 20px;
-}
-
.profile-btn {
color: #3d444f;
padding: 1px 3px;
diff --git a/src/views/dashboard/related/ebpf/components/data.ts b/src/views/dashboard/related/ebpf/components/data.ts
new file mode 100644
index 0000000..a538897
--- /dev/null
+++ b/src/views/dashboard/related/ebpf/components/data.ts
@@ -0,0 +1,51 @@
+/**
+ * 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.
+ */
+export const ProfileMode: any[] = [
+ { label: "Include Children", value: "include" },
+ { label: "Exclude Children", value: "exclude" },
+];
+export const NewTaskField = {
+ service: { key: "", label: "None" },
+ monitorTime: { key: "0", label: "monitor now" },
+ monitorDuration: { key: 5, label: "5 min" },
+ minThreshold: 0,
+ dumpPeriod: { key: 10, label: "10ms" },
+ endpointName: "",
+ maxSamplingCount: { key: 5, label: "5" },
+};
+
+export const TargetTypes = [{ label: "ON_CPU", value: "ON_CPU" }];
+
+export const InitTaskField = {
+ monitorTimeEn: [
+ { value: "0", label: "monitor now" },
+ { value: "1", label: "set start time" },
+ ],
+ monitorTimeCn: [
+ { value: "0", label: "此刻" },
+ { value: "1", label: "设置时间" },
+ ],
+ monitorDuration: [
+ { value: "5", label: "5 min" },
+ { value: "10", label: "10 min" },
+ { value: "15", label: "15 min" },
+ ],
+};
+export const TableHeader = [
+ { property: "name", label: "Name" },
+ { property: "instanceName", label: "Instance Name" },
+];
diff --git a/src/views/dashboard/related/profile/Header.vue b/src/views/dashboard/related/profile/Header.vue
index e221539..09bc13b 100644
--- a/src/views/dashboard/related/profile/Header.vue
+++ b/src/views/dashboard/related/profile/Header.vue
@@ -14,19 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="flex-h header">
- <!-- <div class="mr-10" v-if="dashboardStore.entity==='All'">
- <span class="grey mr-5">{{ t("service") }}:</span>
+ <div class="mr-10">
+ <span class="grey mr-5">{{ t("endpointName") }}:</span>
<Selector
+ class="name"
size="small"
- :value="service.value"
- :options="profileStore.services"
- placeholder="Select a service"
- @change="changeService"
+ :value="endpointName"
+ :options="profileStore.endpoints"
+ placeholder="Select a endpoint"
+ :isRemote="true"
+ @change="changeEndpoint"
+ @query="searchEndpoints"
/>
- </div> -->
- <div class="mr-10">
- <span class="grey mr-5">{{ t("endpointName") }}:</span>
- <el-input v-model="endpointName" class="name" size="small" />
</div>
<el-button
class="search-btn"
@@ -65,27 +64,30 @@ const appStore = useAppStoreWithOut();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore();
const { t } = useI18n();
-// const service = ref<any>({});
const endpointName = ref<string>("");
const newTask = ref<boolean>(false);
searchTasks();
-// getServices();
+searchEndpoints("");
-// async function getServices() {
-// const res = await profileStore.getServices(dashboardStore.layerId);
+async function searchEndpoints(keyword: string) {
+ if (!selectorStore.currentService) {
+ return;
+ }
+ const service = selectorStore.currentService.id;
+ const res = await profileStore.getEndpoints(service, keyword);
-// if (res.errors) {
-// ElMessage.error(res.errors);
-// return;
-// }
-// service.value = profileStore.services[0];
-// searchTasks();
-// }
+ if (res.errors) {
+ ElMessage.error(res.errors);
+ return;
+ }
+ endpointName.value = profileStore.endpoints[0].value;
+}
+
+function changeEndpoint(opt: any[]) {
+ endpointName.value = opt[0].value;
+}
-// function changeService(opt: any[]) {
-// service.value = opt[0];
-// }
async function searchTasks() {
profileStore.setConditions({
serviceId:
diff --git a/src/views/dashboard/related/profile/components/NewTask.vue b/src/views/dashboard/related/profile/components/NewTask.vue
index f5c4401..95e57e7 100644
--- a/src/views/dashboard/related/profile/components/NewTask.vue
+++ b/src/views/dashboard/related/profile/components/NewTask.vue
@@ -17,7 +17,16 @@ limitations under the License. -->
<div class="profile-task">
<div>
<div class="label">{{ t("endpointName") }}</div>
- <el-input v-model="endpointName" class="profile-input" size="small" />
+ <Selector
+ class="profile-input"
+ size="small"
+ :value="endpointName"
+ :options="profileStore.endpoints"
+ placeholder="Select a endpoint"
+ :isRemote="true"
+ @change="changeEndpoint"
+ @query="searchEndpoints"
+ />
</div>
<div>
<div class="label">{{ t("monitorTime") }}</div>
@@ -105,6 +114,20 @@ const minThreshold = ref<number>(0);
const dumpPeriod = ref<string>(InitTaskField.dumpPeriod[0].value);
const maxSamplingCount = ref<string>(InitTaskField.maxSamplingCount[0].value);
+async function searchEndpoints(keyword: string) {
+ if (!selectorStore.currentService) {
+ return;
+ }
+ const service = selectorStore.currentService.id;
+ const res = await profileStore.getEndpoints(service, keyword);
+
+ if (res.errors) {
+ ElMessage.error(res.errors);
+ return;
+ }
+ endpointName.value = profileStore.taskEndpoints[0].value;
+}
+
function changeMonitorTime(opt: string) {
monitorTime.value = opt;
}
@@ -121,10 +144,13 @@ function changeMaxSamplingCount(opt: any[]) {
maxSamplingCount.value = opt[0].value;
}
+function changeEndpoint(opt: any[]) {
+ endpointName.value = opt[0].value;
+}
+
async function createTask() {
emits("close");
- const date =
- monitorTime.value === "0" ? appStore.durationRow.start : time.value;
+ const date = monitorTime.value === "0" ? new Date() : time.value;
const params = {
serviceId: selectorStore.currentService.id,
endpointName: endpointName.value,
@@ -153,7 +179,7 @@ function changeTimeRange(val: Date) {
<style lang="scss" scoped>
.profile-task {
margin: 0 auto;
- width: 350px;
+ width: 400px;
}
.date {
diff --git a/src/views/dashboard/related/profile/components/TaskList.vue b/src/views/dashboard/related/profile/components/TaskList.vue
index ca14705..efcbb03 100644
--- a/src/views/dashboard/related/profile/components/TaskList.vue
+++ b/src/views/dashboard/related/profile/components/TaskList.vue
@@ -36,7 +36,7 @@ limitations under the License. -->
<div class="ell">
<span>{{ i.endpointName }}</span>
<a class="profile-btn r" @click="viewTask($event, i)">
- <Icon iconName="library_books" size="middle" />
+ <Icon iconName="view" size="middle" />
</a>
</div>
<div class="grey ell sm">
diff --git a/src/views/dashboard/related/trace/Detail.vue b/src/views/dashboard/related/trace/Detail.vue
index b16b326..3e2caa6 100644
--- a/src/views/dashboard/related/trace/Detail.vue
+++ b/src/views/dashboard/related/trace/Detail.vue
@@ -88,7 +88,7 @@ limitations under the License. -->
</div>
<div>
<el-button
- class="grey"
+ class="grey small"
:class="{ ghost: displayMode !== 'List' }"
@click="displayMode = 'List'"
>
@@ -96,7 +96,7 @@ limitations under the License. -->
{{ t("list") }}
</el-button>
<el-button
- class="grey"
+ class="grey small"
:class="{ ghost: displayMode !== 'Tree' }"
@click="displayMode = 'Tree'"
>
@@ -104,7 +104,7 @@ limitations under the License. -->
{{ t("tree") }}
</el-button>
<el-button
- class="grey"
+ class="grey small"
:class="{ ghost: displayMode !== 'Table' }"
@click="displayMode = 'Table'"
>
@@ -112,7 +112,7 @@ limitations under the License. -->
{{ t("table") }}
</el-button>
<el-button
- class="grey"
+ class="grey small"
:class="{ ghost: displayMode !== 'Statistics' }"
@click="displayMode = 'Statistics'"
>