diff --git a/.github/workflows/ebpf_cpu_watcher.yml b/.github/workflows/ebpf_cpu_watcher.yml index 93dbf8441..84848c083 100644 --- a/.github/workflows/ebpf_cpu_watcher.yml +++ b/.github/workflows/ebpf_cpu_watcher.yml @@ -23,11 +23,23 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential + sudo apt install -y libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential git submodule update --init --recursive - - name: Run cpu_watcher + - name: Build cpu_watcher run: | cd eBPF_Supermarket/CPU_Subsystem/cpu_watcher/ make - sudo ./cpu_watcher + + - name: Run cpu_watcher + run: | + sudo ./eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher + + - name: Build test_cpuwatcher + run: | + cd eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test + make + + - name: Run test_cpuwatcher + run: | + ./eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher diff --git a/.github/workflows/ebpf_kvm_watcher.yml b/.github/workflows/ebpf_kvm_watcher.yml index dc79c8d50..e90c618d5 100644 --- a/.github/workflows/ebpf_kvm_watcher.yml +++ b/.github/workflows/ebpf_kvm_watcher.yml @@ -19,10 +19,12 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - - - name: Test program execution + - name: Install dependencies run: | cd eBPF_Supermarket/kvm_watcher/ make deps - make - + - name: Test program execution + continue-on-error: true + run: | + cd eBPF_Supermarket/kvm_watcher/ + make \ No newline at end of file diff --git a/.github/workflows/ebpf_mem_watcher.yml b/.github/workflows/ebpf_mem_watcher.yml index a3c72420a..6a056740c 100644 --- a/.github/workflows/ebpf_mem_watcher.yml +++ b/.github/workflows/ebpf_mem_watcher.yml @@ -32,4 +32,4 @@ jobs: cd eBPF_Supermarket/Memory_Subsystem/mem_watcher/ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h make - sudo timeout 20 ./mem_watcher + sudo ./mem_watcher -f -i 10 diff --git a/.github/workflows/ebpf_net_manager.yml b/.github/workflows/ebpf_net_manager.yml index 4881a21c7..a0364aef2 100644 --- a/.github/workflows/ebpf_net_manager.yml +++ b/.github/workflows/ebpf_net_manager.yml @@ -36,10 +36,15 @@ jobs: cd eBPF_Supermarket/Network_Subsystem/net_manager/ sudo ./configure sudo make + ifconfig # run - sudo timeout -s SIGINT 5 ./xdp_loader -d ens33 -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi - sudo ./xacladm load ens33 ./conf.d/mac_load.conf - sudo xdp-loader unload ens33 --all + cd testenv + sudo ./testenv.sh setup --name veth-basic02 + cd .. + cd net_manager + sudo timeout -s SIGINT 5 ./xdp_loader -d eth0 -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo ./xdp_loader -d eth0 -S + diff --git a/.github/workflows/ebpf_net_watcher.yml b/.github/workflows/ebpf_net_watcher.yml index ec987c429..4f3372d75 100644 --- a/.github/workflows/ebpf_net_watcher.yml +++ b/.github/workflows/ebpf_net_watcher.yml @@ -45,4 +45,11 @@ jobs: sudo timeout -s SIGINT 5 ./netwatcher -k || if [[ $? != 124 && $? != 0 ]];then exit $?;fi sudo timeout -s SIGINT 5 ./netwatcher -k -T || if [[ $? != 124 && $? != 0 ]];then exit $?;fi sudo timeout -s SIGINT 5 ./netwatcher -I || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -S || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -D || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -M || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -R || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -C || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -T || if [[ $? != 124 && $? != 0 ]];then exit $?;fi + sudo timeout -s SIGINT 5 ./netwatcher -U || if [[ $? != 124 && $? != 0 ]];then exit $?;fi timeout-minutes: 5 diff --git a/.github/workflows/ebpf_stack_analyser.yml b/.github/workflows/ebpf_stack_analyser.yml index b29742b99..d5a37e6a8 100644 --- a/.github/workflows/ebpf_stack_analyser.yml +++ b/.github/workflows/ebpf_stack_analyser.yml @@ -23,30 +23,31 @@ jobs: - name: Install native lib dependencies run: | - git submodule update --init --recursive + git submodule update --init --recursive eBPF_Supermarket/lib/ MagicEyes/ sudo apt install clang libelf1 libelf-dev zlib1g-dev - - name: Run app with native lib + - name: Compile test examples + run: | + cd eBPF_Supermarket/Stack_Analyser/testdir + gcc -o ./usdt_pthread -lpthread ./usdt_pthread.c + gcc -o ./uprobe_malloc ./uprobe_malloc.c + + - name: Compile and run app with native lib run: | cd eBPF_Supermarket/Stack_Analyser - make - gcc -o ./testdir/usdt_pthread ./testdir/usdt_pthread.c - sudo ./stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe u:pthread:pthread_create -c "./testdir/usdt_pthread" -t 5 - - magic-eyes-build-and-test: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 + make -j$(nproc) - - name: Install native lib dependencies - run: | - git submodule update --init --recursive - sudo apt install clang libelf1 libelf-dev zlib1g-dev + sudo ./stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe "vfs_open" probe "t:sched:sched_switch" -d 5 + sudo ./stack_analyzer probe "u:pthread:pthread_create" -c "./testdir/usdt_pthread" -d 5 + sudo ./stack_analyzer probe "c:malloc" -c "./testdir/uprobe_malloc" -d 5 - - name: Run app with native lib + - name: Compile and run MagicEyes app with native lib run: | mkdir -p MagicEyes/build cd MagicEyes/build cmake -DBUILD_STACK_ANALYZER=ON .. - make - sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe u:pthread:pthread_create -c "../../eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread" -t 5 \ No newline at end of file + make -j$(nproc) + + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer on_cpu off_cpu memleak io readahead llc_stat probe "p::vfs_open" probe "t:sched:sched_switch" -d 5 + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer probe "u:pthread:pthread_create" -c "../../eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread" -d 5 + sudo ./src/backend/system_diagnosis/stack_analyzer/stack_analyzer probe "c:malloc" -c "../../eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc" -d 5 diff --git a/.gitignore b/.gitignore index 681a24621..40e63914d 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,4 @@ eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller # Stack_Analyser eBPF_Supermarket/Stack_Analyser/stack_analyzer eBPF_Supermarket/Stack_Analyser/exporter/exporter -eBPF_Supermarket/Stack_Analyser/bpf_skel \ No newline at end of file +eBPF_Supermarket/Stack_Analyser/bpf_skel diff --git a/.gitmodules b/.gitmodules index 675ac6e97..a2d3cd09c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,9 +46,6 @@ [submodule "eBPF_Supermarket/CPU_Subsystem/libbpf"] path = eBPF_Supermarket/CPU_Subsystem/libbpf url = https://github.com/libbpf/libbpf.git -[submodule "eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap"] - path = eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap - url = https://github.com/libbpf/libbpf-bootstrap.git [submodule "eBPF_Supermarket/Network_Subsystem/net_manager/lib/libbpf"] path = eBPF_Supermarket/Network_Subsystem/net_manager/lib/libbpf url = https://github.com/libbpf/libbpf.git @@ -73,3 +70,9 @@ [submodule "eBPF_Supermarket/Memory_Subsystem/bpftool"] path = eBPF_Supermarket/Memory_Subsystem/bpftool url = https://github.com/libbpf/bpftool.git +[submodule "eBPF_Supermarket/lib/bpftool"] + path = eBPF_Supermarket/lib/bpftool + url = https://github.com/libbpf/bpftool.git +[submodule "eBPF_Supermarket/lib/libbpf"] + path = eBPF_Supermarket/lib/libbpf + url = https://github.com/libbpf/libbpf.git diff --git a/MagicEyes/src/visualization/vscode_ext/README.md b/MagicEyes/src/visualization/vscode_ext/README.md new file mode 100644 index 000000000..cc57b0415 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/README.md @@ -0,0 +1,125 @@ +# lmp vscode 插件使用指南 + +### 1. 运行效果 + +![](./images/lmp_vscode_ext.gif) + +### 2. 安装与使用 + +#### 2.1 导入插件 + +![import_vscode_ext](./images/import_vscode_ext.png) + +安装成功如下: + +![lmp_ext_install_success](./images/lmp_ext_install_success.png) + +#### 2.2 设置 + +- 启动grafana(可以在docker中启动),启动prometheus与BPF后端采集程序可以看到数据呈现 +- 设置IP地址与端口,默认端口是`localhost:3000` +- 设置token + +![create_token](./images/create_token.png) + +> [grafana官方_创建token](https://grafana.com/docs/grafana/latest/administration/service-accounts/#create-a-service-account-in-grafana) + +![set_token](./images/set_token.png) + +- 设置可视化面板存放路径 + +> 注意:别忘记面板路径后面加 "/" + +![](./images/set_panel_addr.png) + +面板命名必须遵循如下规则: + +![](./images/panel_name.png) + +若面板不存在,或路径,或名称不对,将出现如下错误提示: + +![](./images/error_info.png) + +- 设置工具配置文件 + +请将配置文件路径输入在设置中,如`/home/fzy/lmp_tool_ext_config.json`,输入完成后,敲击enter键,程序将根据配置文件中的子系统与工具,生成左侧侧边栏的按钮。 + +配置文件写法参照 `.../MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json` + +#### 2.3 如何增加工具 + +配置文件如下。假如有一个工具,名为 mem_checker,输入 memory子系统,则 subsystem_list 不用修改,只需要在内存子系统处增加即可。如果有一个工具,名为 V4L2_tracer,属于 media子系统,则需要在subsystem_list中增加 media 子系统,并在 subsystem 下相应增加,不再赘述。 + +```json +{ + "name" : "lmp_tool_vscode_extension_config", + "version" : "0.0.1", + "subsystem_list" : [ + "CPU", + "memory", + "fs", + "network", + "system_diagnosis", + "hypervisor" + ], + "subsystem" : [ + { + "description" : "Linux CPU子系统观测工具集", + "tools" : [......] + }, + { + "description" : "Linux 内存子系统观测工具集", + "tools" : [ + { + "name": "mem_watcher", + "description" : "内存观测" + }, + { + "name": "mem_checker", + "description" : "内存检查" + } + ] + } +} +``` + + +### 3. 插件开发 + +#### 3.1 开发 + +安装yarn并且通过`yarn install`安装所需依赖 + +> tips: 按 F5 开启调试 + +#### 3.2 开发注意事项 + +1. yo code生成的框架,vscode最小版本是1.90,需要修改为1.74,不然我当前的版本。1.89无法运行插件 +2. tsconfig + +```json +{ + "compilerOptions": { + "module": "commonjs", // 不要用Node16,不然命令会触发失败 + "target": "ES2021", + "lib": ["ES2021"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} +``` + +3. 打包vsix + +```bash +# 进入插件开发文件夹 +vsce package +``` + + + diff --git a/MagicEyes/src/visualization/vscode_ext/images/create_token.png b/MagicEyes/src/visualization/vscode_ext/images/create_token.png new file mode 100644 index 000000000..382f04675 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/create_token.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/error_info.png b/MagicEyes/src/visualization/vscode_ext/images/error_info.png new file mode 100644 index 000000000..90ef0fb71 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/error_info.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png b/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png new file mode 100644 index 000000000..6b780114a Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/import_vscode_ext.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png b/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png new file mode 100644 index 000000000..2f5198b70 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/lmp_ext_install_success.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif b/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif new file mode 100644 index 000000000..e875aa485 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/lmp_vscode_ext.gif differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/panel_name.png b/MagicEyes/src/visualization/vscode_ext/images/panel_name.png new file mode 100644 index 000000000..103edbfd4 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/panel_name.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png b/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png new file mode 100644 index 000000000..20b6d5e5c Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/set_panel_addr.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/images/set_token.png b/MagicEyes/src/visualization/vscode_ext/images/set_token.png new file mode 100644 index 000000000..06eefba18 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/images/set_token.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json new file mode 100644 index 000000000..5dfecab7e --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off" + }, + "ignorePatterns": ["out", "dist", "**/*.d.ts"] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml new file mode 100644 index 000000000..fdcb9c114 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/dashboards.yml @@ -0,0 +1,39 @@ +name: Update dashboards + +on: + push: + branches: + - main + paths: + - dashboards/**.json + +jobs: + update-dashboards: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Get changed dashboards + id: changed-files + uses: tj-actions/changed-files@v41 + with: + files: dashboards/**.json + + - name: Update changed dashboards in Grafana + if: steps.changed-files.outputs.any_changed == 'true' + run: | + tmp=$(mktemp) + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + uid=$(jq -r '.uid' $file) + dashboardInfo=$(curl -H "Authorization: Bearer $GRAFANA_API_TOKEN" $GRAFANA_INSTANCE_URL/api/dashboards/uid/$uid) + currentVersion=$(echo $dashboardInfo | jq -r '.meta.version') + jq --argjson v $currentVersion '.version = $v' $file > $tmp && mv $tmp $file + dashboardJson='{"dashboard":'"$(jq -c . $file)"',"message":"'"$COMMIT_MESSAGE"'"}' + curl -X POST $GRAFANA_INSTANCE_URL/api/dashboards/db -H "Content-Type: application/json" -H "Authorization: Bearer $GRAFANA_API_TOKEN" -d "$dashboardJson" + done + env: + GRAFANA_INSTANCE_URL: ${{ secrets.GRAFANA_INSTANCE_URL }} + GRAFANA_API_TOKEN: ${{ secrets.GRAFANA_API_TOKEN }} + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml new file mode 100644 index 000000000..281dfad47 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.github/workflows/publish.yml @@ -0,0 +1,48 @@ +on: + push: + tags: + - "*" + +# These permissions are needed to assume roles from Github's OIDC. +permissions: + contents: write + id-token: write + +name: Publish Extension +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npm run compile + - run: npm run lint + publish: + needs: eslint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/repo/grafana// path in Vault + repo_secrets: | + OPEN_VSX_TOKEN=openvsx:token + VS_MARKETPLACE_TOKEN=vscode-marketplace:token + - name: Publish to Open VSX + uses: HaaLeo/publish-vscode-extension@v0 + with: + pat: ${{ env.OPEN_VSX_TOKEN }} + registryUrl: https://open-vsx.org + - name: Publish to Visual Studio Marketplace + id: publishToMarketplace + uses: HaaLeo/publish-vscode-extension@v0 + with: + pat: ${{ env.VS_MARKETPLACE_TOKEN }} + registryUrl: https://marketplace.visualstudio.com + - uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: "${{ steps.publishToMarketplace.outputs.vsixPath }}" + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore new file mode 100644 index 000000000..f27bbfb36 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.gitignore @@ -0,0 +1,5 @@ +!.vscode +.yarn +node_modules +.idea +dist \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json new file mode 100644 index 000000000..57dbdae42 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json new file mode 100644 index 000000000..0fcf4f973 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/launch.json @@ -0,0 +1,35 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--disable-extensions" + ], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index", + "--disable-extensions" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "tasks: watch-tests" + } + ] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json new file mode 100644 index 000000000..64ee92966 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/settings.json @@ -0,0 +1,13 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, // set this to true to hide the "out" folder with the compiled JS files + "dist": false // set this to true to hide the "dist" folder with the compiled JS files + }, + "search.exclude": { + "out": true, // set this to false to include "out" folder in search results + "dist": true // set this to false to include "dist" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json new file mode 100644 index 000000000..9e3300b04 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscode/tasks.json @@ -0,0 +1,37 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$ts-webpack-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "watch-tests", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": "build" + }, + { + "label": "tasks: watch-tests", + "dependsOn": ["npm: watch", "npm: watch-tests"], + "problemMatcher": [] + } + ] +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore new file mode 100644 index 000000000..c6136798a --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.vscodeignore @@ -0,0 +1,13 @@ +.vscode/** +.vscode-test/** +out/** +node_modules/** +src/** +.gitignore +.yarnrc +webpack.config.js +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc new file mode 100644 index 000000000..f757a6ac5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/.yarnrc @@ -0,0 +1 @@ +--ignore-engines true \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md new file mode 100644 index 000000000..c8a5139de --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CHANGELOG.md @@ -0,0 +1,34 @@ +# Change Log + +All notable changes to the "grafana-vscode" extension will be documented in this file. + +## v0.0.16 +- Add additional proxy endpoints (to support more dashboards/etc) (#80) + +## v0.0.15 +- Follow redirects and don't fail on trailing slashes (#77) +- Theming (light/dark) support for Grafana (#74) + +## v0.0.14 +- Improved readme (#62) + +## v0.0.13 +- Add telemetry that will allow us to evaluate usefulness of this extension (#48) + +## v0.0.12 +- Removed Kiosk mode - this was preventing the 'add panel' option from showing (#59) + +## v0.0.11 +- Fixed usage on Windows (#57) + +## v0.0.10 +- Readme tweaks (#54) + +## v0.0.9 +- Added support for vscodium (#53) + +## v0.0.8 +- Improved readme (#52) + +## v0.0.7 +- First release to VSCode Marketplace diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS new file mode 100644 index 000000000..d2c7fd858 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/CODEOWNERS @@ -0,0 +1,7 @@ +# Docs on CODEOWNERS: +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# +# Later codeowner matches take precedence over earlier ones. + +# Default owner +* @grafana/platform-cat diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE new file mode 100644 index 000000000..373dde574 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Grafana Labs + + Licensed 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. diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md new file mode 100644 index 000000000..3937cb2a0 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/README.md @@ -0,0 +1,78 @@ +# VS Code Extension for Grafana + +Grafana has an extensive UI for editing its dashboards. For many, this is sufficient for their needs. If this is you, this extension is not for you. + +However, some wish to use software development tools (e.g. git) to manage their dashboards and other observability resources. Dashboards can be exported as JSON, however this JSON is hard to understand and interpret. This extension allows you to: +- Open a Grafana dashboard JSON file +- Start a live preview of that dashboard inside VS Code, connected to live data from a Grafana instance of your choice +- Edit the dashboard in the preview, using the normal Grafana dashboard editor UI +- From the editor UI, save the updated dashboard back to the original JSON file + +Managing dashboards as code moves the single source of truth from Grafana itself to your version control system, which allows for dashboards to participate in gitops style workflows that are commonly used for much of the rest of software development. + +> **This library is experimental** +> +> The code in this repository should be considered experimental. Documentation is only +> available alongside the code. It comes without support, but we are keen to receive +> feedback on the product and suggestions on how to improve it, though we cannot commit to +> resolution of any particular issue. No SLAs are available. It is not meant to be used in +> production environments, and the risks are unknown/high. +> +> Additional information can be found in [Release life cycle for Grafana Labs](https://grafana.com/docs/release-life-cycle/). + +## Why Work With Dashboards as Code? + +- JSON dashboards can be stored in your version control system. This provides a simple solution for rollback, history, auditing, and even change control. +- If you have change-control policies for your Grafana stack, this extension allows you and other developers to test and verify dashboard changes before deploying them +- Dashboards can be generated with tools like [Grafonnet](https://grafana.github.io/grafonnet/index.html) +- Dashboards can be integrated into your IaC practices using [Terraform, Ansible, Grafana Operator, or Grizzly](https://grafana.com/blog/2022/12/06/a-complete-guide-to-managing-grafana-as-code-tools-tips-and-tricks/) + +## Features + +- Reads a dashboard JSON you have locally. +- Opens the dashboard configured in the JSON in a running Grafana instance, right inside your IDE. +- Allows you to edit the dashboard from the UI. +- Saves your changes to _your_ JSON when you hit "Save" in the preview. + +## Requirements + +- Have a dashboard JSON handy. +- Have a running instance of Grafana locally _or_ have access to a hosted Grafana instance. +- If you intend to use a dashboard across multiple Grafana instances, you will need to use datasources that have been deployed via the API, as these datasources will require consistent UIDs. + +## Usage: + +### Install from the Marketplace + +1. Select the Extensions icon (![extensions icon](./public/extensions-icon.png)) on the left bar in VSCode. +2. Enter `Grafana` into the search box. Select the option for `Grafana / Grafana Editor` and click `Install`. +3. Open the Settings tab inside the extension (CTRL+, (comma) or `cmd` + `,` on Mac) and search for `grafana`. Then select `Extensions`. + +### Configure the Extension +1. Provide the default URL for your Grafana instance in the `URL` field. If you are using a local Grafana instance, the default value is `http://localhost:3000`. +2. Create a [Service account in Grafana](https://grafana.com/docs/grafana/latest/administration/service-accounts/#create-a-service-account-in-grafana) and add a token to it. +3. In the VS Code settings, click `Set your token, securely` then paste your token into the popup. Press ENTER. + +### Using the Extension +1. Open a folder on your computer that has some dashboard JSON (if you don't have any of your own, navigate to the `dashboards` folder of [this repo](https://github.com/grafana/grafana-vs-code-extension/tree/main/dashboards)). +2. Right-click on a dashboard JSON file in the file explorer and select `Edit in Grafana`. +3. Have fun! +4. Note, clicking `save` on your dashboard will update the JSON file in your local folder. + +### Run from Repository +1. If using local Grafana, start Grafana locally or via Docker. +2. Run `yarn install` in this repo. +3. Open this repo VS Code, then press `F5` to start the extension locally. +4. Continue from step 4 above. + +### Develop +To make changes to this codebase, follow the instructions about how to run from this repository. Then, in your original VS Code window, make changes to the extension. Then, restart the extension with either CTRL+SHIFT+F5 (CMD+SHIFT+F5 on a Mac) or by clicking the green restart circle. + +To view debug logs, use CTRL+SHIFT+P (CMD+SHIFT+P on Mac) then select "Developer: Open Webview Developer Tools". + +## Extension Settings +- `grafana-vscode.URL`: Set the URL of the Grafana instance you want to open the dashboard in. Defaults to 'http://localhost:3000'. +- `grafana-vscode.service-account-token`: A Service Account token. This is stored in the operating system secret store. Defaults to an empty string. + +## Extension communication with Grafana +Details of how this extension communicates with Grafana is available [here](https://github.com/grafana/grafana-vs-code-extension/blob/main/how-it-works.md). diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana new file mode 100644 index 000000000..20d0d69fe --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.grafana @@ -0,0 +1,703 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4194, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-20Hz-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Streaming", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "Should be smaller given the longer value", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 15, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, -5\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 0, + "y": 10 + }, + "id": 16, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & No room for value", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 8, + "y": 10 + }, + "id": 17, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "text": { + "valueSize": 100 + }, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & Always show value", + "type": "barchart" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 10 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 9, + "refId": "A" + } + ], + "title": "Fixed value sizing", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 7, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 18, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 19, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & little room", + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "gdev", + "panel-tests", + "barchart" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BarChart - Panel Tests - Value sizing Copy", + "uid": "wziLqrvnz", + "version": 5, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json new file mode 100644 index 000000000..20d0d69fe --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/streaming.json @@ -0,0 +1,703 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4194, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-20Hz-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Streaming", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "Should be smaller given the longer value", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 2, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 15, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, -5\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 0, + "y": 10 + }, + "id": 16, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & No room for value", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 8, + "y": 10 + }, + "id": 17, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "auto", + "showValue": "always", + "stacking": "none", + "text": { + "valueSize": 100 + }, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & Always show value", + "type": "barchart" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 10 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 9, + "refId": "A" + } + ], + "title": "Fixed value sizing", + "type": "timeseries" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 7, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 18, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.82, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 19, + "options": { + "barRadius": 0, + "barWidth": 1, + "fullHighlight": false, + "groupWidth": 0.89, + "legend": { + "calcs": [ + "max" + ], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "auto show values & little room", + "type": "barchart" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "gdev", + "panel-tests", + "barchart" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BarChart - Panel Tests - Value sizing Copy", + "uid": "wziLqrvnz", + "version": 5, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana new file mode 100644 index 000000000..416609ee5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.grafana @@ -0,0 +1,227 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4278, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "channel": "plugin/testdata/random-2s-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "filter": { + "fields": [ + "Time", + "Min" + ] + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Test dashboard", + "uid": "e9abc1a5-1b8f-4327-83e4-0b3c2b3722a9", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json new file mode 100644 index 000000000..416609ee5 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/dashboards/test-dashboard.json @@ -0,0 +1,227 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 4278, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "fullHighlight": false, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 0, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "channel": "plugin/testdata/random-2s-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "filter": { + "fields": [ + "Time", + "Min" + ] + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Test dashboard", + "uid": "e9abc1a5-1b8f-4327-83e4-0b3c2b3722a9", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md new file mode 100644 index 000000000..06432bd5d --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/how-it-works.md @@ -0,0 +1,24 @@ +# Extension communication with Grafana + +The below diagram explains how this extension communicates with Grafana. + +For best viewing, view this page on GitHub. + +```mermaid +sequenceDiagram + participant Webview as Webview
(inside the VS Code Extension) + participant Iframe as Iframe (Grafana)
(rendered inside the extension's webview) + participant ProxyServer as Proxy server
(running inside the extension) + participant Grafana as Grafana Instance
(running outside the extension) + participant FileSystem as File system + + Note over ProxyServer: Starts on random port + Webview->>Iframe: Render an iframe for Grafana. Callback URL to the proxy is an iframe src URL param + Iframe->>ProxyServer: Requests HTML dashboard page/etc + ProxyServer->>Grafana: Requests HTML dashboards page/etc + Iframe->>ProxyServer: Request to retrieve the JSON for opened dashboard + FileSystem->>ProxyServer: Retrieve JSON + ProxyServer-->>Iframe: JSON for opened dashboard + Iframe->>ProxyServer: Edited dashboard JSON on save + ProxyServer->>FileSystem: Edited dashboard JSON +``` diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg new file mode 100644 index 000000000..af08a09f7 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/media/StartTV.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json new file mode 100644 index 000000000..e22b892cc --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json @@ -0,0 +1,191 @@ +{ + "name": "grafana-vscode", + "author": "Grafana Labs", + "displayName": "lmp_Grafana", + "description": "Grafana Editor for lmp", + "icon": "public/grafana_icon.png", + "version": "0.0.16", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/grafana/grafana-vs-code-extension" + }, + "engines": { + "vscode": "^1.76.0" + }, + "publisher": "Grafana", + "categories": [ + "Visualization" + ], + "keywords": [ + "Grafana", + "dashboards" + ], + "activationEvents": [ + "onStartupFinished" + ], + "main": "./dist/extension.js", + "contributes": { + "customEditors": [ + { + "viewType": "grafana.dashboard", + "displayName": "Grafana", + "selector": [ + { + "filenamePattern": "*.grafana" + } + ] + } + ], + "commands": [ + { + "command": "grafana-vscode.openUrl", + "title": "Edit in Grafana" + } + ], + "menus": { + "commandPalette": [ + { + "command": "grafana-vscode.openUrl", + "when": "false" + } + ], + "explorer/context": [ + { + "command": "grafana-vscode.openUrl", + "when": "resourceExtname == .json" + } + ] + }, + "iconThemes": [ + { + "id": "grafana", + "label": "Grafana", + "path": "./public/icon-theme.json" + } + ], + "configuration": { + "title": "Grafana", + "properties": { + "grafana-vscode.URL": { + "type": "string", + "default": "http://localhost:3000", + "description": "Grafana instance URL", + "order": 1 + }, + "grafana-vscode.service-account-token": { + "type": "boolean", + "default": true, + "markdownDescription": "A service account token for your Grafana instance. Click the link below to add this to secure storage.\n\n[Set your token, securely](command:grafana-vscode.setPassword)", + "order": 2 + }, + "grafana-vscode.theme": { + "type": "string", + "default": "inherit", + "enum": ["inherit", "fixed", "dark", "light"], + "enumDescriptions": [ + "Inherit Grafana theme from VSCode", + "Use Grafana's own default theme", + "Use dark Grafana theme", + "Use light Grafana theme" + ] + }, + "grafana-vscode.telemetry": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable basic telemetry. All data is anonymous and only used to help with feature prioritization/gloating/etc.", + "order": 3 + }, + "grafana-vscode.default_panel_path": { + "type": "string", + "default": "/home/fzy/Desktop/panels/", + "description": "the default panels search path", + "order": 4 + }, + "grafana-vscode.default_tool_config_file": { + "type": "string", + "default": "/home/fzy/lmp_tool_ext_config.json", + "description": "the default tool config file", + "order": 5 + } + } + }, + "viewsContainers": { + "activitybar": [ + { + "id": "lmp_visualization", + "title": "lmp_visualization", + "icon": "media/StartTV.svg" + } + ] + }, + "views": { + "lmp_visualization": [ + { + "id": "lmp_visualization.panel", + "name": "Panel" + }, + { + "id": "lmp_visualization.about", + "name": "about" + } + ] + }, + "viewsWelcome": [ + { + "view": "lmp_visualization.panel", + "contents": "this is welcome content", + "when": "true" + }, + { + "view": "lmp_visualization.about", + "contents": "基于grafana与prometheus的vscode可视化插件, 隶属于智能车载OS诊断与优化专家系统", + "when": "true" + } + ] + }, + "scripts": { + "vscode:prepublish": "yarn run package", + "compile": "webpack", + "watch": "webpack --watch", + "package": "webpack --mode production --devtool hidden-source-map", + "compile-tests": "tsc -p . --outDir out", + "watch-tests": "tsc -p . -w --outDir out", + "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", + "lint": "eslint src --ext ts", + "test": "node ./out/test/runTest.js" + }, + "devDependencies": { + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "@types/glob": "^8.1.0", + "@types/http-proxy": "^1.17.10", + "@types/mocha": "^10.0.1", + "@types/node": "16.x", + "@types/source-map-support": "^0.5.8", + "@types/vscode": "^1.76.0", + "@typescript-eslint/eslint-plugin": "^5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "@vscode/test-electron": "^2.2.3", + "eslint": "^8.34.0", + "glob": "^8.1.0", + "mocha": "^10.2.0", + "prettier": "3.0.2", + "ts-loader": "^9.4.2", + "typescript": "^4.9.5", + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1" + }, + "dependencies": { + "@types/uuid": "^9.0.6", + "axios": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.18.2", + "http-proxy": "^1.18.1", + "http-proxy-middleware": "^2.0.6", + "open": "^8.4.2", + "source-map-support": "^0.5.21", + "transformer-proxy": "^0.3.5", + "uuid": "^9.0.1" + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html new file mode 100644 index 000000000..2a6c37c1b --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/error.html @@ -0,0 +1,25 @@ + +

A problem occurred connecting to Grafana

${error} +

+ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png new file mode 100644 index 000000000..e461ccdba Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/extensions-icon.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png new file mode 100644 index 000000000..4332a33f2 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/grafana_icon.png differ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json new file mode 100644 index 000000000..c24a46150 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/icon-theme.json @@ -0,0 +1,10 @@ +{ + "iconDefinitions": { + "grafana": { + "iconPath": "./grafana_icon.png" + } + }, + "fileExtensions": { + "grafana": "grafana" + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css new file mode 100644 index 000000000..23551dc65 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/iframe.css @@ -0,0 +1,31 @@ + +.sidemenu { + display: none !important; +} + +[aria-label="Share dashboard or panel"] { + display: none !important; +} + +[aria-label="Search links"] { + pointer-events: none; + cursor: default; +} + +[aria-label="Breadcrumbs"] { + pointer-events: none; + cursor: default; +} + +[aria-label="Toggle menu"] { + pointer-events: none; + cursor: default; +} + +[title="Toggle top search bar"] { + display: none !important; +} + +.main-view > div:first-of-type > div:first-of-type { + display: none !important; +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html new file mode 100644 index 000000000..482fc12a6 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/public/webview.html @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts new file mode 100644 index 000000000..7f0ab80ce --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/editor.ts @@ -0,0 +1,84 @@ +import * as vscode from "vscode"; +import * as fs from "fs"; +import { port } from "./server"; + +export class GrafanaEditorProvider implements vscode.CustomTextEditorProvider { + static webviewContent = ""; + static webviewErrorContent = ""; + + static readonly viewType = "grafana.dashboard"; + + public static register(context: vscode.ExtensionContext): vscode.Disposable { + const provider = new GrafanaEditorProvider(context); + const providerRegistration = vscode.window.registerCustomEditorProvider( + GrafanaEditorProvider.viewType, + provider, + { + webviewOptions: { + retainContextWhenHidden: true, + }, + }, + ); + this.webviewContent = fs.readFileSync( + context.asAbsolutePath("public/webview.html"), + "utf-8", + ); + this.webviewContent = this.webviewContent.replaceAll("${editor}", "VSCode"); + + return providerRegistration; + } + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Called when our custom editor is opened. + */ + public async resolveCustomTextEditor( + document: vscode.TextDocument, + webviewPanel: vscode.WebviewPanel, + _token: vscode.CancellationToken, + ) { + webviewPanel.webview.options = { + enableScripts: true, + }; + + webviewPanel.webview.html = this.getHtmlForWebview(document); + } + + private getTheme(): string { + + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const theme = settings.get("theme"); + if (theme === "dark" || theme === "light") { + return `theme=${theme}&`; + } + if (theme === "fixed") { + return ""; + } + + const kind = vscode.window.activeColorTheme.kind; + if (kind === vscode.ColorThemeKind.Light || kind === vscode.ColorThemeKind.HighContrastLight) { + return "theme=light&"; + } else { + return "theme=dark&"; + } + } + + /** + * Get the static html used for the editor webviews. + */ + private getHtmlForWebview(document: vscode.TextDocument): string { + const dash = JSON.parse(document.getText()); + const uid: string = dash.uid; + const theme = this.getTheme(); + + let view = GrafanaEditorProvider.webviewContent.replaceAll( + "${filename}", + document.uri.fsPath, + ); + view = view.replaceAll("${port}", port.toString()); + view = view.replaceAll("${uid}", uid); + view = view.replaceAll("${theme}", theme); + return view; + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts new file mode 100644 index 000000000..152b3c7b2 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts @@ -0,0 +1,473 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from "vscode"; +import { startServer, restartServer, stopServer, TOKEN_SECRET } from "./server"; +import { GrafanaEditorProvider } from "./editor"; +import { install as installSourceMapSupport } from 'source-map-support'; +import { sendTelemetry } from "./telemetry"; +import { setVersion } from "./util"; + +import * as fs from 'fs' // fzy: 为了检查面板文件是否存在 + + +let default_panel_path = "/home/fzy/Desktop/panels/"; // fzy: 为了检查面板文件是否存在 +let default_tool_config_file = "/home/fzy/lmp_tool_ext_config.json"; +let sub_key = 0; // 子系统计数,用于与element.label进行匹配 + + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export async function activate(ctx: vscode.ExtensionContext) { + + setVersion(ctx.extension.packageJSON.version); + startServer(ctx.secrets, ctx.extensionPath); + + ctx.subscriptions.push(GrafanaEditorProvider.register(ctx)); + + ctx.subscriptions.push( + vscode.commands.registerCommand( + "grafana-vscode.openUrl", + (uri: vscode.Uri) => { + sendTelemetry(ctx); + vscode.commands.executeCommand( + "vscode.openWith", + uri, + GrafanaEditorProvider.viewType, + ); + }), + ); + // fzy + // ------------------------------------------------------------------------------------- + TreeViewProvider.initTreeViewItem(); + ctx.subscriptions.push(vscode.commands.registerCommand('itemClick', (label) => { + let file_path = default_panel_path + label + '.json'; + //console.log('file_path = ', file_path); + if (fs.existsSync(file_path)) { + let uri = vscode.Uri.file(file_path); + // 获取label + // 根据label确定uri + //console.log('uri = ', uri); + //console.log('lable = ', label); + sendTelemetry(ctx); + vscode.commands.executeCommand( + "vscode.openWith", + uri, + GrafanaEditorProvider.viewType, + ); + } + else { + let panel_search_info = label + " 可视化面板文件不存在,请检查!" + vscode.window.showErrorMessage(panel_search_info); + } + + })); + // ------------------------------------------------------------------------------------- + + vscode.workspace.onDidChangeConfiguration(async(event) => { + if (event.affectsConfiguration("grafana-vscode.URL")) { + restartServer(ctx.secrets, ctx.extensionPath); + } + // fzy, 让用户可以在设置中修改面板放置路径 + if (event.affectsConfiguration("grafana-vscode.default_panel_path")) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + + default_panel_path = String(settings.get("default_panel_path")); + //console.log("path = ", default_panel_path); + } + // fzy, 让用户可以在设置中修改json配置文件放置路径及配置文件名字 + if (event.affectsConfiguration("grafana-vscode.default_tool_config_file")) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + default_tool_config_file = String(settings.get("default_tool_config_file")); + //console.log("file = ", default_tool_config_file); + sub_key = 0; // 全局变量先清零,不然 subsystem无法匹配 + TreeViewProvider.initTreeViewItem(); + } + }); + + vscode.commands.registerCommand('grafana-vscode.setPassword', async () => { + const passwordInput = await vscode.window.showInputBox({ + password: true, + placeHolder: "My Grafana service account token", + title: "Enter the service account token for your Grafana instance. This value will be stored securely in your operating system's secure key store." + }) ?? ''; + await ctx.secrets.store(TOKEN_SECRET, passwordInput); + restartServer(ctx.secrets, ctx.extensionPath); + }); + + installSourceMapSupport(); +} + +// This method is called when your extension is deactivated +export function deactivate() { + stopServer(); +} + + + +// --------------------------------------------------------------------------------- +// fzy +import { CancellationToken, Event, ProviderResult, TreeDataProvider, TreeItem, TreeItemCollapsibleState, window} from "vscode"; +import { json } from "stream/consumers"; + +// 扩展 TreeItem +/* +export class TreeItemNode extends TreeItem { + constructor( + public readonly label: string = '', + public readonly collapsibleState: TreeItemCollapsibleState, + ){ + super(label, collapsibleState); + } + + command = { + title: this.label, + command: 'itemClick', + tooltip: this.label, + arguments: [ + this.label, + ] + }; + // 获取json文件路径 + // path = TreeItemNode.getPanelUrl(this.label); + + //static getPanelUrl(label: string):Uri { + // return Uri.file(join(__filename)); + //} +} +*/ + +export class TreeViewProvider implements TreeDataProvider { + + onDidChangeTreeData?: Event | undefined; + + getTreeItem(element: TreeItem): TreeItem | Thenable { + return element; + } + getChildren(element?: TreeItem | undefined): ProviderResult { + let jsonData: any; // 保存 json 数据 + jsonData = readLmpConfig(); // 读取json配置文件信息 + + + let arr: TreeItem[] = new Array(); + // treeview 根节点 + if (element == undefined) { + for (const key in jsonData.subsystem_list) { + let item: TreeItem = new TreeItem(jsonData.subsystem_list[key], TreeItemCollapsibleState.Expanded); + item.description = jsonData.subsystem[key].description; + arr.push(item); + } + /* + let item1: TreeItem = new TreeItem("CPU", TreeItemCollapsibleState.Expanded); + item1.description = "Linux CPU子系统观测工具集"; + arr.push(item1); + + let item2: TreeItem = new TreeItem("network", TreeItemCollapsibleState.Expanded); + item2.description = "Linux 网络子系统观测工具集"; + arr.push(item2); + + let item3: TreeItem = new TreeItem("memory", TreeItemCollapsibleState.Expanded); + item3.description = "Linux 内存子系统观测工具集"; + arr.push(item3); + + let item4: TreeItem = new TreeItem("system_diagnosis", TreeItemCollapsibleState.Expanded); + item4.description = "Linux 系统诊断工具集"; + arr.push(item4); + + let item5: TreeItem = new TreeItem("hypervisior", TreeItemCollapsibleState.Expanded); + item5.description = "Linux 虚拟化子系统工具集"; + arr.push(item5); + */ + + return arr; + } + // treeview 子节点 + else { + //for (let sub_key = 0; sub_key <= 5; sub_key++) { + if (element.label == jsonData.subsystem_list[sub_key]) { // 遍历所有子系统 + //console.log("jsonData.subsystem_list[key] = ", jsonData.subsystem_list[sub_key]); + for (const tool_num in jsonData.subsystem[sub_key].tools) { // 遍历子系统下的所有工具 + let tool_name = jsonData.subsystem[sub_key].tools[tool_num].name; + let tool_description = jsonData.subsystem[sub_key].tools[tool_num].description; + let item1:TreeItem = new TreeItem(tool_name, TreeItemCollapsibleState.None); + item1.description = tool_description; + let tool_command = { + title: tool_name, + command: 'itemClick', + tooltip: "点击将呈现工具的grafana可视化面板", + arguments: [ + tool_name + ] + } + item1.command = tool_command; + arr.push(item1); + } + sub_key++; // key + 1, 匹配下一个子系统 + //console.log("sub_key = ", sub_key); + return arr; + } + else { + return null; + } + } + } + /* + if (element.label == 'CPU') { + // ***************************************************************************** + //let item1: TreeItem = new TreeItem("cpu_watcher", TreeItemCollapsibleState.None); + let cpu_watcher_label = "cpu_watcher"; + let item1:TreeItem = new TreeItem(cpu_watcher_label, TreeItemCollapsibleState.None); + item1.description = "cpu观测"; + let command_cpu_watcher = { + title: cpu_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现cpu_watcher的grafana的可视化面板", + arguments: [ + cpu_watcher_label + ] + } + item1.command = command_cpu_watcher; + arr.push(item1); + // ***************************************************************************** + let proc_iamge_label = "proc_image"; + let item2: TreeItem = new TreeItem(proc_iamge_label, TreeItemCollapsibleState.None); + item2.description = "进程画像"; + let command_proc_image = { + title: proc_iamge_label, + command: 'itemClick', + tooltip: "点击将呈现proc_image的grafana的可视化面板", + arguments: [ + proc_iamge_label + ] + } + item2.command = command_proc_image; + arr.push(item2); + // ***************************************************************************** + return arr; + } + else if (element.label == 'network') { + // ***************************************************************************** + let net_watcher_label = "net_watcher"; + let network_item1: TreeItem = new TreeItem(net_watcher_label, TreeItemCollapsibleState.None); + network_item1.description = "网络观测"; + let command_net_watcher = { + title: net_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现net_watcher的grafana的可视化面板", + arguments: [ + net_watcher_label + ] + } + network_item1.command = command_net_watcher; + arr.push(network_item1); + // ***************************************************************************** + let net_manager_label = "net_manager"; + let network_item2: TreeItem = new TreeItem(net_manager_label, TreeItemCollapsibleState.None); + network_item2.description = "网络管理"; + let command_net_manager = { + title: net_manager_label, + command: 'itemClick', + tooltip: "点击将呈现net_manager的grafana的可视化面板", + arguments: [ + net_manager_label + ] + } + network_item2.command = command_net_manager; + arr.push(network_item2); + // ***************************************************************************** + return arr; + } + else if (element.label == 'memory') { + let mem_watcher_label = "mem_watcher"; + let memory_item1: TreeItem = new TreeItem(mem_watcher_label, TreeItemCollapsibleState.None); + memory_item1.description = "内存观测"; + let command_mem_watcher = { + title: mem_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现mem_watcher的grafana的可视化面板", + arguments: [ + mem_watcher_label + ] + } + memory_item1.command = command_mem_watcher; + arr.push(memory_item1); + + return arr; + } + else if (element.label == 'system_diagnosis') { + let stack_analyzer_label = "stack_analyzer"; + let system_diagnosis_item1: TreeItem = new TreeItem(stack_analyzer_label, TreeItemCollapsibleState.None); + system_diagnosis_item1.description = "栈调用分析器"; + let command_stack_analyzer = { + title: stack_analyzer_label, + command: 'itemClick', + tooltip: "点击将呈现stack_analyzer的grafana的可视化面板", + arguments: [ + stack_analyzer_label + ] + } + system_diagnosis_item1.command = command_stack_analyzer; + arr.push(system_diagnosis_item1); + + return arr; + } + else if (element.label == 'hypervisior') { + let kvm_watcher_label = "kvm_watcher"; + let hypervisior_item1: TreeItem = new TreeItem(kvm_watcher_label, TreeItemCollapsibleState.None); + hypervisior_item1.description = "kvm虚拟化"; + let command_kvm_watcher = { + title: kvm_watcher_label, + command: 'itemClick', + tooltip: "点击将呈现kvm_watcher的grafana的可视化面板", + arguments: [ + kvm_watcher_label + ] + } + hypervisior_item1.command = command_kvm_watcher; + arr.push(hypervisior_item1); + + return arr; + } + else { + return null; + } + + } + } + */ + public static initTreeViewItem(){ + const treeViewProvider = new TreeViewProvider(); + window.registerTreeDataProvider('lmp_visualization.panel',treeViewProvider); + } + +} + + +export function readLmpConfig() { + let data:any; + if (fs.existsSync(default_tool_config_file)) //判断是否存在此文件 + { + try { + //读取文件内容,并转化为Json对象 + + data = JSON.parse(fs.readFileSync(default_tool_config_file, "utf8")); + // console.log(jsonData); + //获取Json里key为data的数据 + //const data = userBugsJson['data']; + } + catch (error){ + console.error('Error parsing JSON:', error); + } + } + else { + console.error("no config file"); + let config_search_info =" json配置文件不存在,请检查!" + vscode.window.showErrorMessage(config_search_info); + } + return data; +} + +// fzy end +// --------------------------------------------------------------------------------- + +// backup +/** + export class TreeViewProvider implements TreeDataProvider { + onDidChangeTreeData?: Event | undefined; + + getTreeItem(element: TreeItemNode): TreeItem | Thenable { + return element; + } + getChildren(element?: TreeItem | undefined): ProviderResult { + let arr: TreeItem[] = new Array(); + // treeview 根节点 + if (element == undefined) { + let item1: TreeItem = new TreeItem("CPU", TreeItemCollapsibleState.Expanded); + item1.description = "Linux CPU子系统观测工具集"; + arr.push(item1); + + let item2: TreeItem = new TreeItem("network", TreeItemCollapsibleState.Expanded); + item2.description = "Linux 网络子系统观测工具集"; + arr.push(item2); + + let item3: TreeItem = new TreeItem("memory", TreeItemCollapsibleState.Expanded); + item3.description = "Linux 内存子系统观测工具集"; + arr.push(item3); + + let item4: TreeItem = new TreeItem("system_diagnosis", TreeItemCollapsibleState.Expanded); + item4.description = "Linux 系统诊断工具集"; + arr.push(item4); + + let item5: TreeItem = new TreeItem("hypervisior", TreeItemCollapsibleState.Expanded); + item5.description = "Linux 虚拟化子系统工具集"; + arr.push(item5); + + return arr; + } + // treeview 子节点 + else { + if (element.label == 'CPU') { + //let item1: TreeItem = new TreeItem("cpu_watcher", TreeItemCollapsibleState.None); + let item1:TreeItemNode = new TreeItemNode("cpu_watcher", TreeItemCollapsibleState.None); + item1.description = "cpu观测"; + arr.push(item1); + + let item2: TreeItemNode = new TreeItemNode("proc_image", TreeItemCollapsibleState.None); + item2.description = "进程画像"; + arr.push(item2); + + return arr; + } + else if (element.label == 'network') { + let network_item1: TreeItemNode = new TreeItemNode("net_watcher", TreeItemCollapsibleState.None); + network_item1.description = "网络观测"; + + //let command = { + //title: 'net_watcher', + //command: 'itemClick', + //tooltip: "点击将呈现net_watcher的grafana的可视化面板", + //arguments: [ + //] + //} + //network_item1.command = command; + + arr.push(network_item1); + + let network_item2: TreeItemNode = new TreeItemNode("net_manager", TreeItemCollapsibleState.None); + network_item2.description = "网络管理"; + arr.push(network_item2); + + return arr; + } + else if (element.label == 'memory') { + let memory_item1: TreeItemNode = new TreeItemNode("mem_watcher", TreeItemCollapsibleState.None); + memory_item1.description = "内存观测"; + arr.push(memory_item1); + + return arr; + } + else if (element.label == 'system_diagnosis') { + let system_diagnosis_item1: TreeItemNode = new TreeItemNode("stack_analyzer", TreeItemCollapsibleState.None); + system_diagnosis_item1.description = "栈调用分析器"; + arr.push(system_diagnosis_item1); + + return arr; + } + else if (element.label == 'hypervisior') { + let hypervisior_item1: TreeItemNode = new TreeItemNode("kvm_watcher", TreeItemCollapsibleState.None); + hypervisior_item1.description = "kvm虚拟化"; + arr.push(hypervisior_item1); + + return arr; + } + else { + return null; + } + } + } + public static initTreeViewItem(){ + const treeViewProvider = new TreeViewProvider(); + window.registerTreeDataProvider('lmp_visualization.panel',treeViewProvider); + } + +} + */ diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts new file mode 100644 index 000000000..43b5aa00a --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/middleware.ts @@ -0,0 +1,16 @@ +import { Response, Request, NextFunction } from "express"; + +export function detectRequestSource( + req: Request, + res: Response, + next: NextFunction, +) { + const userAgent = req.headers["user-agent"]; + + if ((userAgent?.includes("Code") || userAgent?.includes("code")) + && userAgent?.includes("Electron")) { + next(); + } else { + res.status(403).send("Access Denied"); + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts new file mode 100644 index 000000000..9f618c3ee --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/server.ts @@ -0,0 +1,261 @@ +import * as express from "express"; +import { Server, createServer } from "http"; +import { createProxyServer } from "http-proxy"; +import * as fs from "fs"; +import * as vscode from "vscode"; +import * as cors from "cors"; +import { detectRequestSource } from "./middleware"; +import axios from "axios"; +import * as path from "path"; +import * as util from "./util"; + +export let port = 0; + +let server: Server; + +export const TOKEN_SECRET = "grafana-vscode.token"; + +export async function startServer(secrets: vscode.SecretStorage, extensionPath: string) { + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const token = await secrets.get(TOKEN_SECRET); + let URL = String(settings.get("URL")); + if (URL.slice(-1) === "/") { + URL = URL.slice(0, -1); + } + + const corsOptions = { + origin: `http://localhost:${port}`, + optionsSuccessStatus: 200, + }; + + const app = express(); + app.use(detectRequestSource); + server = createServer(app); + + const proxy = createProxyServer({ + target: URL, + changeOrigin: !URL.includes("localhost"), + ws: true, + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${token}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + + server.on("upgrade", function (req, socket, head) { + proxy.ws(req, socket, head, {}); + }); + + const sendErrorPage = (res: express.Response, message: string) => { + const errorFile = path.join(extensionPath, "public/error.html"); + let content = fs.readFileSync(errorFile, "utf-8"); + content = content.replaceAll("${error}", message); + res.write(content); + }; + + /* + * Note, this method avoids using `proxy.web`, implementing its own proxy + * event using Axios. This is because Grafana returns `X-Frame-Options: deny` + * which breaks our ability to place Grafana inside an iframe. `http-proxy` + * will not remove that header once it is added. Therefore we need a different + * form of proxy. + * + * This security protection does not apply to this situation - given we own + * both the connection to the backend as well as the webview. Therefore + * it is reasonable remove this header in this context. + * + * This method also doubles as connection verification. If an issue is + * encountered connecting to Grafana, rather than reporting an HTTP error, + * it returns an alternate HTML page to the user explaining the error, and + * offering a "refresh" option. + */ + app.get("/d/:uid/:slug", async function (req, res) { + try { + const resp = await axios.get(URL + req.url, { + maxRedirects: 5, + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Authorization: `Bearer ${token}`, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + res.write(resp.data); + } catch (e) { + let msg = ""; + if (URL === "") { + msg += "

Error: URL is not defined

"; + } + if (token === "") { + msg += "

Warning: No service account token specified.

"; + } + if (axios.isAxiosError(e)) { + if (e.response?.status === 302) { + sendErrorPage(res, msg+ "

Authentication error

"); + } else { + sendErrorPage(res, msg + `

${e.message}

`); + } + } else if (e instanceof Error) { + sendErrorPage(res, msg + `

${e.message}

`); + } else { + sendErrorPage(res, msg + "

" + String(e) + "

"); + } + } + }); + + app.get( + "/api/dashboards/uid/:uid", + express.json(), + cors(corsOptions), + (req, res) => { + const refererParams = new URLSearchParams(req.headers.referer); + const filename = refererParams.get("filename"); + if (filename === null) { + console.log("Filename not specified in referer"); + res.sendStatus(500); + return; + } + fs.readFile(filename, "utf-8", (err, data) => { + if (err) { + console.error("Error reading file:", err); + res.sendStatus(500); + return; + } + const dash: any = JSON.parse(data); + const wrapper = { + dashboard: dash, + meta: { + isStarred: false, + folderId: 0, + folderUid: "", + url: `/d/${dash.uid}/slug`, + }, + }; + + res.send(wrapper); + }); + }, + ); + + app.post( + "/api/dashboards/db/", + express.json(), + cors(corsOptions), + (req, res) => { + const refererParams = new URLSearchParams(req.headers.referer); + const filename = refererParams.get("filename"); + if (!filename) { + res.send(500); + return; + } + const uid = req.headers.referer?.split("/")[4]; + const jsonData = JSON.stringify(req.body.dashboard, null, 2); + + fs.writeFile(filename, jsonData, "utf-8", (err) => { + if (err) { + console.error("Error writing file:", err); + res.sendStatus(500); + } else { + res.send({ + id: 1, + slug: "slug", + status: "success", + uid: uid, + url: `/d/${uid}/slug`, + version: 1, + }); + } + }); + }, + ); + + app.get( + "/api/access-control/user/actions", + express.json(), + cors(corsOptions), + (req, res) => { + res.send({ + /* eslint-disable-next-line @typescript-eslint/naming-convention */ + "dashboards:write": true, + }); + return; + }, + ); + + const mustProxyGET = [ + "/public/*", + "/api/datasources/proxy/*", + "/api/datasources/*", + "/api/plugins/*", + ]; + for (const path of mustProxyGET) { + app.get(path, function (req, res) { + proxy.web(req, res, {}); + }); + } + + const mustProxyPOST = [ + "/api/ds/query", + "/api/datasources/proxy/*", + ]; + for (const path of mustProxyPOST) { + app.post(path, function (req, res) { + proxy.web(req, res, {}); + }); + } + + const blockJSONget: { [name: string]: any } = { + /* eslint-disable @typescript-eslint/naming-convention */ + "/api/ma/events": [], + "/api/live/publish": [], + "/api/live/list": [], + "/api/user/orgs": [], + "/api/annotations": [], + "/api/search": [], + "/api/usage/*": [], + "/api/prometheus/grafana/api/v1/rules": { + status: "success", + data: { groups: [] }, + }, + "/avatar/*": "", + "/api/folders": [], + /* eslint-enable @typescript-eslint/naming-convention */ + }; + for (const path in blockJSONget) { + app.get(path, function (req, res) { + res.send(blockJSONget[path]); + }); + } + + const blockJSONpost: { [name: string]: any } = { + /* eslint-disable @typescript-eslint/naming-convention */ + "/api/frontend-metrics": [], + "/api/search-v2": [], + "/api/live/publish": {}, + /* eslint-enable @typescript-eslint/naming-convention */ + }; + for (const path in blockJSONpost) { + app.post(path, function (req, res) { + res.send(blockJSONpost[path]); + }); + } + + server.listen(port, () => { + //@ts-expect-error + port = server?.address()?.port; + console.log("Server started"); + }); +} + +export function restartServer(secrets: vscode.SecretStorage, extensionPath: string) { + console.log("Restarting server"); + stopServer(); + startServer(secrets, extensionPath); +} +export function stopServer() { + if (server) { + server.close(); + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts new file mode 100644 index 000000000..b6bcc7854 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/telemetry.ts @@ -0,0 +1,88 @@ +import * as vscode from "vscode"; +import axios from "axios"; +import {v4 as uuidv4} from 'uuid'; +import * as util from "./util"; + +const LAST_UPDATED_DATE = "lastUpdatedDate"; +const INSTALLATION_DATE = "installDate"; +const INSTALLATION_UUID = "installUUID"; +const RECENT_VIEWS = "recentViews"; + +const URL = "https://stats.grafana.org/vscode-usage-report"; + +/* + * Sends a single anonymous telemetry call once per day, allowing tracking of + * usage - reports on first opening of a dashboard each day. + */ +export async function sendTelemetry(ctx: vscode.ExtensionContext) { + + const settings = vscode.workspace.getConfiguration("grafana-vscode"); + const enableTelemetry = settings.get("telemetry"); + if (!enableTelemetry) { + return; + } + const lastUpdatedDate = ctx.globalState.get(LAST_UPDATED_DATE); + const today = new Date(); + + if (lastUpdatedDate === undefined) { + const uuid = uuidv4(); + await sendEvent("first", uuid, today.toISOString(), 1); + ctx.globalState.update(LAST_UPDATED_DATE, today); + ctx.globalState.update(INSTALLATION_UUID, uuid); + ctx.globalState.update(INSTALLATION_DATE, today); + ctx.globalState.update(RECENT_VIEWS, 0); + } else { + let recentViews = ctx.globalState.get(RECENT_VIEWS); + recentViews = (recentViews === undefined) ? 1 : recentViews+1; + + if (differentDay(new Date(lastUpdatedDate), today)) { + let uuid = ctx.globalState.get(INSTALLATION_UUID); + let installDate = ctx.globalState.get(INSTALLATION_DATE); + if (uuid === undefined) { + console.log("UUID undefined. Shouldn't happen."); + uuid = uuidv4(); + ctx.globalState.update(INSTALLATION_UUID, uuid); + } + if (installDate === undefined) { + console.log("Install date undefined. Shouldn't happen."); + installDate = (new Date(lastUpdatedDate)).toISOString(); + ctx.globalState.update(INSTALLATION_DATE, installDate); + } + await sendEvent("subsequent", uuid as string, installDate as string, recentViews); + ctx.globalState.update(LAST_UPDATED_DATE, today); + recentViews = 0; + } + ctx.globalState.update(RECENT_VIEWS, recentViews); + } +} + +function differentDay(d1: Date, d2: Date) { + return d1.getDate() !== d2.getDate() || + d1.getMonth() !== d2.getMonth() || + d1.getFullYear() !== d2.getFullYear(); +} + +async function sendEvent(eventType: string, uuid: string, installDate: string, views: number | undefined) { + try { + const data = { + uuid: uuid, + eventType: eventType, + timestamp: Date(), + createdAt: installDate, + os: process.platform, + arch: process.arch, + packaging: "unknown", + views: views, + version: util.getVersion(), + }; + + await axios.post(URL, data, { + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'User-Agent': util.getUserAgent(), + }, + }); + } catch(e) { + console.log("Telemetry error", e, "for event", eventType); + } +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts new file mode 100644 index 000000000..8a6ab0e15 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/runTest.ts @@ -0,0 +1,23 @@ +import * as path from "path"; + +import { runTests } from "@vscode/test-electron"; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, "./suite/index"); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error("Failed to run tests", err); + process.exit(1); + } +} + +main(); diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts new file mode 100644 index 000000000..2f671d3c7 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/extension.test.ts @@ -0,0 +1,15 @@ +import * as assert from "assert"; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from "vscode"; +// import * as myExtension from '../../extension'; + +suite("Extension Test Suite", () => { + vscode.window.showInformationMessage("Start all tests."); + + test("Sample test", () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts new file mode 100644 index 000000000..2cb7d7d8b --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/test/suite/index.ts @@ -0,0 +1,38 @@ +import * as path from "path"; +import * as Mocha from "mocha"; +import * as glob from "glob"; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + color: true, + }); + + const testsRoot = path.resolve(__dirname, ".."); + + return new Promise((c, e) => { + glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); + }); +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts new file mode 100644 index 000000000..55ef02df8 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/util.ts @@ -0,0 +1,15 @@ +let userAgent: string; +let version: string; + +export function setVersion(v: string) { + version = v; + userAgent = `Grafana VSCode Extension/v${version}`; +} + +export function getVersion(): string { + return version; +} + +export function getUserAgent(): string { + return userAgent; +} \ No newline at end of file diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json new file mode 100644 index 000000000..708b31212 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2021", + "lib": ["ES2021"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js new file mode 100644 index 000000000..e6ed48c6e --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/webpack.config.js @@ -0,0 +1,48 @@ +//@ts-check + +"use strict"; + +const path = require("path"); + +//@ts-check +/** @typedef {import('webpack').Configuration} WebpackConfig **/ + +/** @type WebpackConfig */ +const extensionConfig = { + target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ + mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + + entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + output: { + // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ + path: path.resolve(__dirname, "dist"), + filename: "extension.js", + libraryTarget: "commonjs2", + }, + externals: { + vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ + // modules added here also need to be added in the .vscodeignore file + }, + resolve: { + // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader + extensions: [".ts", ".js"], + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + loader: "ts-loader", + }, + ], + }, + ], + }, + devtool: "nosources-source-map", + infrastructureLogging: { + level: "log", // enables logging required for problem matchers + }, +}; +module.exports = [extensionConfig]; diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock new file mode 100644 index 000000000..c983e7f2c --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/yarn.lock @@ -0,0 +1,2707 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.52.0": + version "8.52.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c" + integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA== + +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== + dependencies: + "@humanwhocodes/object-schema" "^2.0.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/body-parser@*": + version "1.19.4" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.4.tgz#78ad68f1f79eb851aa3634db0c7f57f6f601b462" + integrity sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.37" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.37.tgz#c66a96689fd3127c8772eb3e9e5c6028ec1a9af5" + integrity sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.13": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.15.tgz#eb143aa2f8807ddd78e83cbff141bbedd91b60ee" + integrity sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.6" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.6.tgz#585578b368ed170e67de8aae7b93f54a1b2fdc26" + integrity sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.44.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.6.tgz#60e564551966dd255f4c01c459f0b4fb87068603" + integrity sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.4.tgz#d9748f5742171b26218516cf1828b8eafaf8a9fa" + integrity sha512-2JwWnHK9H+wUZNorf2Zr6ves96WHoWDJIftkcxPKsS7Djta6Zu519LarhRNljPXkpsZR2ZMwNCPeW7omW07BJw== + +"@types/express-serve-static-core@^4.17.33": + version "4.17.39" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz#2107afc0a4b035e6cb00accac3bdf2d76ae408c8" + integrity sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@^4.17.17": + version "4.17.20" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.20.tgz#e7c9b40276d29e38a4e3564d7a3d65911e2aa433" + integrity sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/glob@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + +"@types/http-errors@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.3.tgz#c54e61f79b3947d040f150abd58f71efb422ff62" + integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA== + +"@types/http-proxy@^1.17.10", "@types/http-proxy@^1.17.8": + version "1.17.13" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.13.tgz#dd3a4da550580eb0557d4c7128a2ff1d1a38d465" + integrity sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1" + integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== + +"@types/mime@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.3.tgz#886674659ce55fe7c6c06ec5ca7c0eb276a08f91" + integrity sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ== + +"@types/mime@^1": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.4.tgz#a4ed836e069491414bab92c31fdea9e557aca0d9" + integrity sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw== + +"@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/mocha@^10.0.1": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.3.tgz#4804fe9cd39da26eb62fa65c15ea77615a187812" + integrity sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ== + +"@types/node@*": + version "20.8.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e" + integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w== + dependencies: + undici-types "~5.26.4" + +"@types/node@16.x": + version "16.18.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.60.tgz#0b0f4316906f1bd0e03b640363f67bd4e86958bd" + integrity sha512-ZUGPWx5vKfN+G2/yN7pcSNLkIkXEvlwNaJEd4e0ppX7W2S8XAkdc/37hM4OUNJB9sa0p12AOvGvxL4JCPiz9DA== + +"@types/qs@*": + version "6.9.9" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.9.tgz#66f7b26288f6799d279edf13da7ccd40d2fa9197" + integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg== + +"@types/range-parser@*": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.6.tgz#7cb33992049fd7340d5b10c0098e104184dfcd2a" + integrity sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA== + +"@types/semver@^7.3.12": + version "7.5.4" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" + integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ== + +"@types/send@*": + version "0.17.3" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.3.tgz#81b2ea5a3a18aad357405af2d643ccbe5a09020b" + integrity sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.4" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.4.tgz#44b5895a68ca637f06c229119e1c774ca88f81b2" + integrity sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/source-map-support@^0.5.8": + version "0.5.9" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.5.9.tgz#750860ba0dc3eb2929a940ac630e6ef60407f6b6" + integrity sha512-91Jf4LyPAObBTFbpW3bSDK1ncdwXohvlBmzffSj7/44SY+1mD/HhesdfspCMxPIJwllgN2G4eVFatGs4Zw/lnw== + dependencies: + source-map "^0.6.0" + +"@types/uuid@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.6.tgz#c91ae743d8344a54b2b0c691195f5ff5265f6dfb" + integrity sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew== + +"@types/vscode@^1.76.0": + version "1.83.1" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.83.1.tgz#fe51b3d913a5c5b265a622179ae4ab6c0af7d54e" + integrity sha512-BHu51NaNKOtDf3BOonY3sKFFmZKEpRkzqkZVpSYxowLbs5JqjOQemYFob7Gs5rpxE5tiGhfpnMpcdF/oKrLg4w== + +"@typescript-eslint/eslint-plugin@^5.53.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.53.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vscode/test-electron@^2.2.3": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.3.6.tgz#61a6ec1b4bdc9a2a694a9d7d86b9cb8b82c5a116" + integrity sha512-M31xGH0RgqNU6CZ4/9g39oUMJ99nLzfjA+4UbtIQ6TcXQ6+2qkjOOxedmPBDDCg26/3Al5ubjY80hIoaMwKYSw== + dependencies: + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + jszip "^3.10.1" + semver "^7.5.2" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.14.5: + version "4.22.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001541: + version "1.0.30001559" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz#95a982440d3d314c471db68d02664fb7536c5a30" + integrity sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA== + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.535: + version "1.4.572" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.572.tgz#ed9876658998138fe9e3aa47ecfa0bf914192a86" + integrity sha512-RlFobl4D3ieetbnR+2EpxdzFl9h0RAJkPK3pfiwMug2nhBin2ZCsGIAJWdpNniLz43sgXam/CgipOmvTA+rUiA== + +emitter-component@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" + integrity sha512-G+mpdiAySMuB7kesVRLuyvYRqDmshB7ReKEVuyBPkzQlmiDiLrt7hHHIy4Aff552bgknVN7B2/d3lzhGO5dvpQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + +es-module-lexer@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.1.tgz#c1b0dd5ada807a3b3155315911f364dc4e909db1" + integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.34.0: + version "8.52.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc" + integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.52.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +express@^4.18.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0, find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.1.tgz#a02a15fdec25a8f844ff7cc658f03dd99eb4609b" + integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.2.tgz#78fcecd6d870551aa5547437cdae39d4701dca5b" + integrity sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^7.0.4: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.2.1, safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^7.3.4, semver@^7.3.7, semver@^7.5.2: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-support@^0.5.21, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stream@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" + integrity sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g== + dependencies: + emitter-component "^1.1.1" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.16.8: + version "5.24.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364" + integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +transformer-proxy@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/transformer-proxy/-/transformer-proxy-0.3.5.tgz#40ecf87f846e8b1603a6d22b49d60d81f57ce512" + integrity sha512-mBbKwpGq45DAa4AJQU8osDL7rV065VKCEhVH7g7EwVT3oYogABso+ZwFz5XuvNVI9+tfgwk9NMKE+JL+bue/mw== + dependencies: + promise "^7.0.4" + stream "0.0.2" + util "^0.10.3" + +ts-loader@^9.4.2: + version "9.5.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.0.tgz#f0a51dda37cc4d8e43e6cb14edebbc599b0c3aa2" + integrity sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + source-map "^0.7.4" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@^5.0.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.75.0: + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix b/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix new file mode 100644 index 000000000..9aea7d416 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_vscode_ext_0.01.vsix differ diff --git a/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json b/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json new file mode 100644 index 000000000..0102d7e69 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/tool_config_sample/lmp_tool_ext_config.json @@ -0,0 +1,80 @@ +{ + "name" : "lmp_tool_vscode_extension_config", + "version" : "0.0.1", + "subsystem_list" : [ + "CPU", + "memory", + "fs", + "network", + "system_diagnosis", + "hypervisor" + ], + "subsystem" : [ + { + "description" : "Linux CPU子系统观测工具集", + "tools" : [ + { + "name": "cpu_watcher", + "description" : "cpu观测" + }, + { + "name": "proc_image", + "description" : "进程画像" + } + ] + }, + { + "description" : "Linux 内存子系统观测工具集", + "tools" : [ + { + "name": "mem_watcher", + "description" : "内存观测" + } + ] + }, + { + "description" : "Linux 文件子系统观测工具集", + "tools" : [ + { + "name": "fast_fuse", + "description" : "FUSE 性能优化" + } + ] + }, + { + "description" : "Linux 网络子系统观测工具集", + "tools" : [ + { + "name": "net_watcher", + "description" : "网络观测" + }, + { + "name": "net_manager", + "description" : "网络优化与加速" + } + ] + }, + { + "description" : "Linux 系统诊断子系统观测工具集", + "tools" : [ + { + "name": "stack_analyzer", + "description" : "栈调用分析器" + } + ] + }, + { + "description" : "Linux 虚拟化子系统观测工具集", + "tools" : [ + { + "name": "kvm_watcher", + "description" : "kvm状态分析" + } + ] + } + ] +} + + + + diff --git a/eBPF_Supermarket/CPU_Subsystem/blazesym b/eBPF_Supermarket/CPU_Subsystem/blazesym index 90eb4e08a..dc343b7e9 160000 --- a/eBPF_Supermarket/CPU_Subsystem/blazesym +++ b/eBPF_Supermarket/CPU_Subsystem/blazesym @@ -1 +1 @@ -Subproject commit 90eb4e08a25d702776f35b6b7fea97900213e3d5 +Subproject commit dc343b7e99f7194f41160a8fc9f6f4a1cf6d4485 diff --git a/eBPF_Supermarket/CPU_Subsystem/bpftool b/eBPF_Supermarket/CPU_Subsystem/bpftool index 06c61eccd..8485b9fba 160000 --- a/eBPF_Supermarket/CPU_Subsystem/bpftool +++ b/eBPF_Supermarket/CPU_Subsystem/bpftool @@ -1 +1 @@ -Subproject commit 06c61eccd3b8a6ff3df3e451a2a93058913124fc +Subproject commit 8485b9fba9b3bb3bd311b00632d2d22c0eee2e13 diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile index efe1a9de9..1e1d4eaab 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/Makefile @@ -38,12 +38,15 @@ VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./ +INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS =cs_delay sar sc_delay preempt schedule_delay mq_delay +APPS =cs_delay sar sc_delay preempt schedule_delay mq_delay mutrace TARGETS=cpu_watcher +CONTROLLER := controller + +SRC_DIR = ./include # Get Clang's default includes on this system. We'll explicitly add these dirs @@ -79,12 +82,12 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(TARGETS) +all: $(CONTROLLER) $(TARGETS) .PHONY: clean clean: $(call msg,CLEAN) - $(Q)rm -rf $(OUTPUT) $(TARGETS) + $(Q)rm -rf $(OUTPUT) $(TARGETS) $(CONTROLLER) $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) @@ -112,7 +115,7 @@ $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -126,14 +129,40 @@ $(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h # Build user-space code +$(OUTPUT)/%.o: $(SRC_DIR)/%.c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/%.o: $(CONTROLLER).c | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + $(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ # Build application binary +$(CONTROLLER): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) + $(call msg,BINARY,$@) + $(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + $(TARGETS): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(call msg,BINARY,$@) - $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +SUCCESS_MESSAGE: + @echo "\e[38;2;255;0;0m __ __ \e[0m" + @echo "\e[38;2;255;128;0m _________ __ __ _ ______ _/ /______/ /_ ___ _____\e[0m" + @echo "\e[38;2;255;255;0m / ___/ __ \/ / / / | | /| / / __ / __/ ___/ __ \/ _ \/ ___/\e[0m" + @echo "\e[38;2;128;255;0m/ /__/ /_/ / /_/ / | |/ |/ / /_/ / /_/ /__/ / / / __/ / \e[0m" + @echo "\e[38;2;0;255;0m\___/ .___/\__,_/ |__/|__/\__,_/\__/\___/_/ /_/\___/_/ \e[0m" + @echo "\e[38;2;0;255;128m /_/ \e[0m" + @echo "\e[38;2;0;255;255mSuccessful to compile cpu_watcher tools: \e[0m" + @echo "\e[38;2;0;255;255mPlease start your use ~ \e[0m" + + +all: $(TARGETS) SUCCESS_MESSAGE + # delete failed targets .DELETE_ON_ERROR: diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md index f64e4a8a8..ce938ded0 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/README.md @@ -29,14 +29,20 @@ make clean 清除生成文件 | 参数 | 描述 | | :----------------: | :----------------------------------------: | -| -s :SAR | 实时采集SAR的各项指标,每秒输出一次 | +| -s :SAR | 实时采集SAR的各项指标 | +| -i:interval | 修改SAR功能的输出间隔 | +| -P:percent | 按照百分比输出SAR功能的各项指标 | | -p:preempt_time | 实时采集当前系统的每次抢占调度详细信息 | | -d:schedule_delay | 实时采集当前系统的调度时延 | | -S:syscall_delay | 实时采集当前系统调用时间 | | -m:mq_delay | 实时采集当前消息队列通信时延 | | -c:cs_delay | 实时对内核函数schedule()的执行时长进行测试 | -### 1.SAR 统计功能(每秒输出一次): +### 1.SAR 统计功能: + +```shell +./cpu_watcher -s +``` #### 输出效果: @@ -56,6 +62,16 @@ make clean 清除生成文件 16:18:14 43 1032 1 577 19513 1704 3919 26 10 30 ``` +​ 使用参数i可以调整输出间隔,默认为1s,参数p可以按照cpu核数和自定义的输出间隔对数据进行归一化,并以百分比的形式输出,且大于60%的数据会标红输出: + +```shell +./cpu_watcher -s -i 2 -P +``` +#### 输出效果: + +![image13](image/image13.png) + + 对上述参数的解释: - `proc/s`: 每秒创建的进程数,此数值是通过fork数来统计的。 @@ -68,6 +84,10 @@ make clean 清除生成文件 - `sysc/ms`: CPU执行用户程序系统调用(`syscall`)所占用的时间,是所有CPU的叠加。 - ` utime/ms`:CPU执行普通用户进程时,花在用户态的时间,是所有CPU的叠加。 +原理介绍: + +[libbpf_sar工具原理分析](docs/libbpf_sar.md) + ### **2.统计抢占调度时间:** ​ 统计系统中发生抢占调度的情况,包括抢占进程的`pid`与进程名,以及被强占进程的`pid`,和本次抢占时间,单位纳秒。 @@ -95,21 +115,22 @@ node 14221 2589 3355 ### 3.**统计调度延迟:** -​ 分析系统中进程调度的延迟情况,提供相关统计数据,输出包括当前系统的最大调度延迟、最小调度延迟、平均调度延迟。 +​ 分析系统中进程调度的延迟情况,提供相关统计数据,输出包括当前系统的最大调度延迟、最小调度延迟、平均调度延迟,以及对应进程的名字。 #### 输出效果: ``` - TIME avg_delay/μs max_delay/μs min_delay/μs -17:31:28 35.005000 97.663000 9.399000 -17:31:29 326.518000 12618.465000 7.994000 -17:31:30 455.837000 217053.545000 6.462000 -17:31:31 422.582000 217053.545000 6.462000 -17:31:32 382.627000 217053.545000 6.462000 -17:31:33 360.499000 217053.545000 6.462000 -17:31:34 364.805000 217053.545000 6.462000 -17:31:35 362.039000 217053.545000 6.462000 -17:31:36 373.751000 217053.545000 6.462000 + TIME avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name +22:06:02 642.770000 60711.755000 node 5.227000 cpu_watcher +22:06:03 510.041000 60711.755000 node 5.227000 cpu_watcher +22:06:04 491.107000 60711.755000 node 5.227000 cpu_watcher +22:06:05 468.128000 60711.755000 node 5.227000 cpu_watcher +22:06:06 454.244000 60711.755000 node 5.227000 cpu_watcher +22:06:07 472.455000 61931.163000 node 5.227000 cpu_watcher +22:06:08 441.756000 61931.163000 node 3.360000 cpu_watcher +22:06:09 442.631000 61931.163000 node 3.360000 cpu_watcher +22:06:10 407.389000 61931.163000 node 2.549000 cpu_watcher +22:06:11 426.593000 62247.982000 node 2.549000 cpu_watcher ``` 原理介绍: @@ -214,9 +235,11 @@ per_len = 1000 ​ 获取内核全局变量,直接从内核全局变量读取信息。如proc/s就是通过直接读取total_forks内核全局变量来计算每秒产生进程数的。 +## 五、cpu_watcher可视化 +[cpu_watcher可视化指南](docs/cpu_watcher_vis_guide.md) -## 五、未来展望 +## 六、未来展望 目前`cpu_watcher`工具的总体框架已经完成,工具所能满足的功能已覆盖CPU所涉及的大部分性能指标。下一阶段,本工具将从以下几个方向进行开发和优化: @@ -229,4 +252,4 @@ per_len = 1000 如果你也对cpu_watcher或ebpf感兴趣,欢迎加入我们一起开发cpu_watcher工具,希望我们可以共同成长。 -**cpu_watcher负责人:** albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +**cpu_watcher负责人:** albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c new file mode 100644 index 000000000..caaf8c408 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c @@ -0,0 +1,78 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +const int ctrl_key = 0; +//记录时间戳; +BPF_ARRAY(start,int,u64,1); +BPF_ARRAY(cs_ctrl_map,int,struct cs_ctrl,1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct cs_ctrl *get_cs_ctrl(void) { + struct cs_ctrl *cs_ctrl; + cs_ctrl = bpf_map_lookup_elem(&cs_ctrl_map, &ctrl_key); + if (!cs_ctrl || !cs_ctrl->cs_func) { + return NULL; + } + return cs_ctrl; +} + +SEC("kprobe/schedule") +int BPF_KPROBE(schedule) +{ + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); + u64 t1; + t1 = bpf_ktime_get_ns()/1000; + int key =0; + bpf_map_update_elem(&start,&key,&t1,BPF_ANY); + return 0; +} + +SEC("kretprobe/schedule") +int BPF_KRETPROBE(schedule_exit) +{ + struct cs_ctrl *cs_ctrl = get_cs_ctrl(); + u64 t2 = bpf_ktime_get_ns()/1000; + u64 t1,delay; + int key = 0; + u64 *val = bpf_map_lookup_elem(&start,&key); + if (val != 0) + { + t1 = *val; + delay = t2 - t1; + bpf_map_delete_elem(&start, &key); + }else{ + return 0; + } + struct event *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + e->t1=t1; + e->t2=t2; + e->delay=delay; + bpf_ringbuf_submit(e, 0); + return 0; +} + diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c similarity index 92% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c index 92589afd6..3aa9dd92e 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/mq_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c @@ -1,4 +1,4 @@ -// Copyright 2024 The LMP Authors. +// Copyright 2023 The LMP Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: albert_xuu@163.com +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include "vmlinux.h" #include //包含了BPF 辅助函数 @@ -23,15 +23,26 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; - +const int ctrl_key = 0; BPF_HASH(send_msg1,pid_t,struct send_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 BPF_HASH(send_msg2,u64,struct send_events,1024);//记录msg->time的关系; BPF_HASH(rcv_msg1,pid_t,struct rcv_events,1024);//记录pid->u_msg_ptr的关系;do_mq_timedsend入参 +BPF_ARRAY(mq_ctrl_map,int,struct mq_ctrl,1); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +static inline struct mq_ctrl *get_mq_ctrl(void) { + struct mq_ctrl *mq_ctrl; + mq_ctrl = bpf_map_lookup_elem(&mq_ctrl_map, &ctrl_key); + if (!mq_ctrl || !mq_ctrl->mq_func) { + return NULL; + } + return mq_ctrl; +} + + // int print_send_info(struct send_events * mq_send_info,int flag){ // bpf_printk("---------------------test----------------------------test--------------------------test--------------------------------------------test---------------------test---------------------test\n"); // bpf_printk("send_msg_prio: %-8lu\n",mq_send_info->msg_prio); @@ -63,6 +74,7 @@ int BPF_KPROBE(mq_timedsend,mqd_t mqdes, const char *u_msg_ptr, size_t msg_len, unsigned int msg_prio, struct timespec64 *ts) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 send_enter_time = bpf_ktime_get_ns();//开始发送信息时间; int pid = bpf_get_current_pid_tgid();//发送端pid @@ -82,6 +94,7 @@ int BPF_KPROBE(mq_timedsend,mqd_t mqdes, const char *u_msg_ptr, /*仅获取mq_send_info -> src*/ SEC("kprobe/load_msg") int BPF_KPROBE(load_msg_enter,const void *src, size_t len){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid();//发送端pid /*记录load入参src*/ struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); @@ -96,6 +109,7 @@ int BPF_KPROBE(load_msg_enter,const void *src, size_t len){ /*获取消息块作为key,并建立 message -> mq_send_info 的哈希表*/ SEC("kretprobe/load_msg") int BPF_KRETPROBE(load_msg_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid();//发送端pid /*构建消息块结构体,作为key*/ struct send_events *mq_send_info = bpf_map_lookup_elem(&send_msg1, &pid); @@ -121,6 +135,7 @@ int BPF_KRETPROBE(load_msg_exit,void *ret){ SEC("kretprobe/do_mq_timedsend") int BPF_KRETPROBE(do_mq_timedsend_exit,void *ret) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); bpf_printk("do_mq_timedsend_exit----------------------------------------------------------------\n"); u64 send_exit_time = bpf_ktime_get_ns();//开始发送信息时间; int pid = bpf_get_current_pid_tgid();//发送端pid @@ -148,6 +163,7 @@ int BPF_KPROBE(mq_timedreceive_entry,mqd_t mqdes, const char __user *u_msg_ptr, size_t msg_len, unsigned int msg_prio, struct timespec64 *ts) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 rcv_enter_time = bpf_ktime_get_ns(); int pid = bpf_get_current_pid_tgid(); @@ -165,6 +181,7 @@ int BPF_KPROBE(mq_timedreceive_entry,mqd_t mqdes, const char __user *u_msg_ptr, SEC("kprobe/store_msg") int BPF_KPROBE(store_msg,void __user *dest, struct msg_msg *msg, size_t len) { + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); int pid = bpf_get_current_pid_tgid(); /*make key*/ @@ -192,6 +209,7 @@ int BPF_KPROBE(store_msg,void __user *dest, struct msg_msg *msg, size_t len) SEC("kretprobe/do_mq_timedreceive") int BPF_KRETPROBE(do_mq_timedreceive_exit,void *ret){ + struct mq_ctrl *mq_ctrl = get_mq_ctrl(); u64 rcv_exit_time = bpf_ktime_get_ns(); int pid = bpf_get_current_pid_tgid(); u64 send_enter_time,delay; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c new file mode 100644 index 000000000..79212119b --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mutrace.bpf.c @@ -0,0 +1,272 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include +#include +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +const int ctrl_key = 0; +BPF_HASH(kmutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(umutex_info_map, u64, struct mutex_info, 1024); +BPF_HASH(trylock_map, u64, struct trylock_info, 1024); +BPF_ARRAY(mu_ctrl_map, int, struct mu_ctrl, 1); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +static inline struct mu_ctrl *get_mu_ctrl(void) { + struct mu_ctrl *mu_ctrl; + mu_ctrl = bpf_map_lookup_elem(&mu_ctrl_map, &ctrl_key); + if (!mu_ctrl || !mu_ctrl->mu_func) { + return NULL; + } + return mu_ctrl; +} + +/*----------------------------------------------*/ +/* 内核态互斥锁 */ +/*----------------------------------------------*/ + +SEC("kprobe/mutex_lock") +int BPF_KPROBE(trace_mutex_lock, struct mutex *lock) { + u64 lock_addr = (u64)lock; // 获取锁地址 + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + info->acquire_time = ts; // 保存锁获取时间 + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = 0, + .acquire_time = ts, + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + return 0; +} + +SEC("kprobe/mutex_trylock") +int BPF_KPROBE(trace_mutex_trylock, struct mutex *lock) { + int ret = PT_REGS_RC(ctx); + if (ret != 0) { // 成功获取锁 + u64 lock_addr = (u64)lock; // 获取锁地址 + u64 ts = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + info->acquire_time = ts; + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = 0, + .acquire_time = ts, + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + } + return 0; +} + +SEC("kprobe/__mutex_lock_slowpath") +int BPF_KPROBE(trace_mutex_lock_slowpath, struct mutex *lock) { + struct mu_ctrl *mu_ctrl = get_mu_ctrl(); + struct mutex_contention_event *e; + struct task_struct *owner_task; + struct task_struct *contender_task; + pid_t pid = bpf_get_current_pid_tgid(); + long owner; + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) { + return 0; + } + e->contender_pid = pid; + e->ptr = lock_addr; + bpf_get_current_comm(&e->contender_name, sizeof(e->contender_name)); + bpf_probe_read_kernel(&owner, sizeof(owner), &lock->owner); + owner_task = (struct task_struct *)(owner & ~0x1L); + contender_task = (struct task_struct *)bpf_get_current_task(); + bpf_probe_read_kernel(&e->contender_prio, sizeof(e->contender_prio), &contender_task->prio); + if (owner_task) { + bpf_probe_read_kernel(&e->owner_pid, sizeof(e->owner_pid), &owner_task->pid); + bpf_probe_read_kernel_str(&e->owner_name, sizeof(e->owner_name), owner_task->comm); + bpf_probe_read_kernel(&e->owner_prio, sizeof(e->owner_prio), &owner_task->prio); + } else { + e->owner_pid = 0; + __builtin_memset(e->owner_name, 0, sizeof(e->owner_name)); + } + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + u64 contention_start = ts; + info->contended_total += (contention_start - info->acquire_time); // 更新争用时间 + info->count++; // 更新争用次数 + } else { + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 1, // 初始化争用次数 + .last_owner = 0, + .acquire_time = ts, // 初始化获取时间 + .ptr = lock_addr + }; + __builtin_memset(new_info.last_name, 0, sizeof(new_info.last_name)); + bpf_map_update_elem(&kmutex_info_map, &lock_addr, &new_info, BPF_ANY); + } + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("kprobe/mutex_unlock") +int BPF_KPROBE(trace_mutex_unlock, struct mutex *lock) { + u64 lock_addr = (u64)lock; + u64 ts = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + struct mutex_info *info = bpf_map_lookup_elem(&kmutex_info_map, &lock_addr); + if (info) { + u64 held_time = ts - info->acquire_time; // 计算锁被持有的时间 + info->locked_total += held_time; // 更新锁被持有的总时间 + if (held_time > info->locked_max) { + info->locked_max = held_time; // 更新锁被持有的最长时间 + } + info->last_owner = pid; // 更新最后一次持有该锁的线程ID + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); // 更新最后一次持有该锁的线程名称 + } + return 0; +} + + + +/*----------------------------------------------*/ +/* 用户态互斥锁 */ +/*----------------------------------------------*/ + + + +SEC("uprobe/pthread_mutex_lock") +int BPF_KPROBE(pthread_mutex_lock, void *__mutex) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + pid_t pid = pid_tgid >> 32; + u64 now = bpf_ktime_get_ns(); + + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + if (info->acquire_time > 0) { + // 如果 acquire_time 已经被设置,说明锁被争用 + info->contended_total += (now - info->acquire_time); + info->count += 1; + } + info->acquire_time = now; + info->last_owner = pid; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } else { + // 初始化 mutex_info + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = pid, + .acquire_time = now, + .ptr = (u64)__mutex, + }; + bpf_get_current_comm(&new_info.last_name, sizeof(new_info.last_name)); + bpf_map_update_elem(&umutex_info_map, &__mutex, &new_info, BPF_ANY); + } + return 0; +} + +SEC("uprobe/__pthread_mutex_trylock") +int BPF_KPROBE(__pthread_mutex_trylock, void *__mutex) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + u64 now = bpf_ktime_get_ns(); + struct trylock_info info = { + .__mutex = __mutex, + .start_time = now, + }; + bpf_map_update_elem(&trylock_map, &pid_tgid, &info, BPF_ANY); + return 0; +} + +SEC("uretprobe/__pthread_mutex_trylock") +int BPF_KRETPROBE(ret_pthread_mutex_trylock, int ret) { + u64 pid_tgid = bpf_get_current_pid_tgid(); + struct trylock_info *try_info = bpf_map_lookup_elem(&trylock_map, &pid_tgid); + if (!try_info) { + return 0; + } + void *__mutex = try_info->__mutex; + u64 now = bpf_ktime_get_ns(); + if (ret == 0) { + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + if (info->acquire_time > 0) { + // 如果 acquire_time 已经被设置,说明锁被争用 + info->contended_total += (now - info->acquire_time); + info->count += 1; + } + info->acquire_time = now; + info->last_owner = pid_tgid >> 32; + bpf_get_current_comm(&info->last_name, sizeof(info->last_name)); + } else { + // 初始化 mutex_info + struct mutex_info new_info = { + .locked_total = 0, + .locked_max = 0, + .contended_total = 0, + .count = 0, + .last_owner = pid_tgid >> 32, + .acquire_time = now, + .ptr = (u64)__mutex, + }; + bpf_get_current_comm(&new_info.last_name, sizeof(new_info.last_name)); + bpf_map_update_elem(&umutex_info_map, &__mutex, &new_info, BPF_ANY); + } + } + bpf_map_delete_elem(&trylock_map, &pid_tgid); + return 0; +} + +SEC("uprobe/pthread_mutex_unlock") +int BPF_KPROBE(pthread_mutex_unlock, void *__mutex){ + u64 now = bpf_ktime_get_ns(); + struct mutex_info *info = bpf_map_lookup_elem(&umutex_info_map, &__mutex); + if (info) { + u64 locked_time = now - info->acquire_time; + info->locked_total += locked_time; + if (locked_time > info->locked_max) { + info->locked_max = locked_time; + } + info->acquire_time = 0; + } + return 0; +} + diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c similarity index 56% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c index 0abbbc781..bf650a6e0 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/preempt.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,17 +23,27 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define TIF_NEED_RESCHED 3 - +const int ctrl_key = 0; // 记录时间戳 BPF_HASH(preemptTime, pid_t, u64, 4096); - +BPF_ARRAY(preempt_ctrl_map,int,struct preempt_ctrl,1); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +static inline struct preempt_ctrl *get_preempt_ctrl(void) { + struct preempt_ctrl *preempt_ctrl; + preempt_ctrl = bpf_map_lookup_elem(&preempt_ctrl_map, &ctrl_key); + if (!preempt_ctrl || !preempt_ctrl->preempt_func) { + return NULL; + } + return preempt_ctrl; +} + SEC("tp_btf/sched_switch") int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); u64 start_time = bpf_ktime_get_ns(); pid_t prev_pid = BPF_CORE_READ(prev, pid); @@ -33,8 +59,10 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s return 0; } -SEC("kprobe/finish_task_switch") +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") int BPF_KPROBE(finish_task_switch, struct task_struct *prev) { + struct preempt_ctrl *preempt_ctrl = get_preempt_ctrl(); u64 end_time = bpf_ktime_get_ns(); pid_t pid = BPF_CORE_READ(prev, pid); u64 *val; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c similarity index 80% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c index 4e48d49c7..eb4012def 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sar.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,7 +23,7 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; const volatile long long unsigned int forks_addr = 0; - +const int ctrl_key = 0; #define PF_IDLE 0x00000002 /* I am an IDLE thread */ #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ @@ -35,11 +51,23 @@ BPF_ARRAY(kt_LastTime,u32,u64,1); BPF_ARRAY(ut_LastTime,u32,u64,1); BPF_ARRAY(tick_user,u32,u64,1); BPF_ARRAY(symAddr,u32,u64,1); +BPF_ARRAY(sar_ctrl_map,int,struct sar_ctrl,1); + +static inline struct sar_ctrl *get_sar_ctrl(void) { + struct sar_ctrl *sar_ctrl; + sar_ctrl = bpf_map_lookup_elem(&sar_ctrl_map, &ctrl_key); + if (!sar_ctrl || !sar_ctrl->sar_func) { + return NULL; + } + return sar_ctrl; +} + // 统计fork数 -//SEC("kprobe/finish_task_switch.isra.0") -SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") int kprobe__finish_task_switch(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = 0; u64 val, *valp = NULL; unsigned long total_forks; @@ -57,6 +85,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx) //获取进程切换数; SEC("tracepoint/sched/sched_switch") int trace_sched_switch2(struct cswch_args *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); pid_t prev = info->prev_pid, next = info->next_pid; if (prev != next) { u32 key = 0; @@ -75,9 +104,10 @@ int trace_sched_switch2(struct cswch_args *info) { return 0; } -SEC("kprobe/finish_task_switch") -//SEC("kprobe/finish_task_switch.isra.0") +// SEC("kprobe/finish_task_switch") +SEC("kprobe/finish_task_switch.isra.0") int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); pid_t pid=BPF_CORE_READ(prev,pid); u64 *val, time = bpf_ktime_get_ns(); u64 delta; @@ -108,6 +138,7 @@ int BPF_KPROBE(finish_task_switch,struct task_struct *prev){ //统计运行队列长度 SEC("kprobe/update_rq_clock") int BPF_KPROBE(update_rq_clock,struct rq *rq){ + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = 0; u64 val = BPF_CORE_READ(rq,nr_running); bpf_map_update_elem(&runqlen,&key,&val,BPF_ANY); @@ -117,6 +148,7 @@ int BPF_KPROBE(update_rq_clock,struct rq *rq){ //软中断 SEC("tracepoint/irq/softirq_entry") int trace_softirq_entry(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->vec; u64 val = bpf_ktime_get_ns(); bpf_map_update_elem(&softirqCpuEnterTime, &key, &val, BPF_ANY); @@ -125,6 +157,7 @@ int trace_softirq_entry(struct __softirq_info *info) { SEC("tracepoint/irq/softirq_exit") int trace_softirq_exit(struct __softirq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->vec; u64 now = bpf_ktime_get_ns(), *valp = 0; valp =bpf_map_lookup_elem(&softirqCpuEnterTime, &key); @@ -143,6 +176,7 @@ int trace_softirq_exit(struct __softirq_info *info) { 注意这是所有CPU时间的叠加,平均到每个CPU应该除以CPU个数。*/ SEC("tracepoint/irq/irq_handler_entry") int trace_irq_handler_entry(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->irq; u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&irq_cpu_enter_start, &key, &ts, BPF_ANY); @@ -151,6 +185,7 @@ int trace_irq_handler_entry(struct __irq_info *info) { SEC("tracepoint/irq/irq_handler_exit") int trace_irq_handler_exit(struct __irq_info *info) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u32 key = info->irq; u64 now = bpf_ktime_get_ns(), *ts = 0; ts = bpf_map_lookup_elem(&irq_cpu_enter_start, &key); @@ -170,6 +205,7 @@ int trace_irq_handler_exit(struct __irq_info *info) { //tracepoint:power_cpu_idle 表征了CPU进入IDLE的状态,比较准确 SEC("tracepoint/power/cpu_idle") int trace_cpu_idle(struct idleStruct *pIDLE) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); u64 delta, time = bpf_ktime_get_ns(); u32 key = pIDLE->cpu_id; if (pIDLE->state == -1) { @@ -199,6 +235,7 @@ static __always_inline int user_mode(struct pt_regs *regs) // 两个CPU各自会产生一个调用,这正好方便我们使用 SEC("perf_event") int tick_update(struct pt_regs *ctx) { + struct sar_ctrl *sar_ctrl = get_sar_ctrl(); // bpf_trace_printk("cs_rpl = %x\n", ctx->cs & 3); u32 key = 0; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c new file mode 100644 index 000000000..ee224283a --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c @@ -0,0 +1,89 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + +#include "vmlinux.h" +#include +#include +#include "cpu_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + + +const int ctrl_key = 0; +BPF_PERCPU_HASH(SyscallEnterTime,pid_t,u64,512); +BPF_PERCPU_HASH(Events,pid_t,u64,10); +BPF_ARRAY(sc_ctrl_map,int,struct sc_ctrl,1); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps");//环形缓冲区; + +static inline struct sc_ctrl *get_sc_ctrl(void) { + struct sc_ctrl *sc_ctrl; + sc_ctrl = bpf_map_lookup_elem(&sc_ctrl_map, &ctrl_key); + if (!sc_ctrl || !sc_ctrl->sc_func) { + return NULL; + } + return sc_ctrl; +} + + +SEC("tracepoint/raw_syscalls/sys_enter") +int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + u64 start_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid(); + u64 syscall_id = (u64)args->id; + + //bpf_printk("ID:%ld\n",syscall_id); + bpf_map_update_elem(&Events,&pid,&syscall_id,BPF_ANY); + bpf_map_update_elem(&SyscallEnterTime,&pid,&start_time,BPF_ANY); + return 0; +} + +SEC("tracepoint/raw_syscalls/sys_exit") +int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ + struct sc_ctrl *sc_ctrl = get_sc_ctrl(); + u64 exit_time = bpf_ktime_get_ns()/1000; + pid_t pid = bpf_get_current_pid_tgid() ; + u64 syscall_id; + u64 start_time, delay; + u64 *val = bpf_map_lookup_elem(&SyscallEnterTime, &pid); + if(val !=0){ + start_time = *val; + delay = exit_time - start_time; + bpf_map_delete_elem(&SyscallEnterTime, &pid); + }else{ + return 0; + } + u64 *val2 = bpf_map_lookup_elem(&Events, &pid); + if(val2 !=0){ + syscall_id = *val2; + bpf_map_delete_elem(&SyscallEnterTime, &pid); + }else{ + return 0; + } + struct syscall_events *e; + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + e->pid = pid; + e->delay = delay; + bpf_get_current_comm(&e->comm, sizeof(e->comm)); + e->syscall_id = syscall_id; + bpf_ringbuf_submit(e, 0); + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c similarity index 51% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c index fda48bda6..db1b2a363 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/schedule_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c @@ -1,3 +1,19 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com + #include #include #include @@ -7,14 +23,27 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL"; #define TASK_RUNNING 0x0000 -BPF_HASH(has_scheduled,struct proc_id, bool, 10240); -BPF_HASH(enter_schedule,struct proc_id, struct schedule_event, 10240); -BPF_ARRAY(sys_schedule,int,struct sum_schedule,1); +const int ctrl_key = 0; +BPF_HASH(has_scheduled,struct proc_id, bool, 10240);//记录该进程是否调度过 +BPF_HASH(enter_schedule,struct proc_id, struct schedule_event, 10240);//记录该进程上运行队列的时间 +BPF_ARRAY(sys_schedule,int,struct sum_schedule,1);//记录整个系统的调度延迟 +BPF_ARRAY(threshold_schedule,int,struct proc_schedule,10240);//记录每个进程的调度延迟 +BPF_HASH(proc_histories,struct proc_id, struct proc_history, 10240);//记录每个进程运行前的两个进程 +BPF_ARRAY(schedule_ctrl_map,int,struct schedule_ctrl,1); +static inline struct schedule_ctrl *get_schedule_ctrl(void) { + struct schedule_ctrl *sched_ctrl; + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map, &ctrl_key); + if (!sched_ctrl || !sched_ctrl->schedule_func) { + return NULL; + } + return sched_ctrl; +}//查找控制结构体 SEC("tp_btf/sched_wakeup") int BPF_PROG(sched_wakeup, struct task_struct *p) { - pid_t pid = BPF_CORE_READ(p, pid); + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + pid_t pid = p->pid; int cpu = bpf_get_smp_processor_id(); struct schedule_event *schedule_event; struct proc_id id= {}; @@ -40,7 +69,11 @@ int BPF_PROG(sched_wakeup, struct task_struct *p) { SEC("tp_btf/sched_wakeup_new") int BPF_PROG(sched_wakeup_new, struct task_struct *p) { - pid_t pid = BPF_CORE_READ(p, pid); + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); + if(!sched_ctrl || !sched_ctrl->schedule_func) + return 0; + pid_t pid = p->pid; int cpu = bpf_get_smp_processor_id(); struct proc_id id= {}; u64 current_time = bpf_ktime_get_ns(); @@ -60,6 +93,9 @@ int BPF_PROG(sched_wakeup_new, struct task_struct *p) { SEC("tp_btf/sched_switch") int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); + struct proc_history *history; + struct proc_history new_history; u64 current_time = bpf_ktime_get_ns(); pid_t prev_pid = prev->pid; unsigned int prev_state = prev->__state; @@ -70,17 +106,17 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s struct schedule_event *schedule_event; struct sum_schedule *sum_schedule; int key = 0; - struct proc_id next_id= {}; + struct proc_id next_id = {}; u64 delay; if (prev_state == TASK_RUNNING) { - struct proc_id prev_pd= {}; + struct proc_id prev_pd = {}; prev_pd.pid = prev_pid; if (prev_pid == 0) { prev_pd.cpu_id = prev_cpu; - } + } schedule_event = bpf_map_lookup_elem(&enter_schedule, &prev_pd); if (!schedule_event) { - struct schedule_event schedule_event2 ; + struct schedule_event schedule_event2; bool issched = false; schedule_event2.pid = prev_pid; schedule_event2.count = 1; @@ -97,38 +133,71 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s next_id.cpu_id = next_cpu; } schedule_event = bpf_map_lookup_elem(&enter_schedule, &next_id); - if (!schedule_event) return 0; + if (!schedule_event) return 0; issched = bpf_map_lookup_elem(&has_scheduled, &next_id); - if (!issched) return 0; + if (!issched) return 0; if (*issched) { schedule_event->count++; } else { *issched = true; - } + } delay = current_time - schedule_event->enter_time; + struct proc_schedule proc_schedule; + proc_schedule.delay = delay; + proc_schedule.id= next_id; + bpf_probe_read_kernel_str(&proc_schedule.proc_name, sizeof(proc_schedule.proc_name), next->comm); + bpf_map_update_elem(&threshold_schedule, &key, &proc_schedule, BPF_ANY); sum_schedule = bpf_map_lookup_elem(&sys_schedule, &key); if (!sum_schedule) { - struct sum_schedule sum_schedule= {}; + struct sum_schedule sum_schedule = {}; sum_schedule.sum_count++; sum_schedule.sum_delay += delay; - if (delay > sum_schedule.max_delay) + if (delay > sum_schedule.max_delay) { sum_schedule.max_delay = delay; - if (sum_schedule.min_delay == 0 || delay < sum_schedule.min_delay) + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_max, sizeof(sum_schedule.proc_name_max), next->comm); + } + } else if (sum_schedule.min_delay == 0 || delay < sum_schedule.min_delay) { sum_schedule.min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule.proc_name_min, sizeof(sum_schedule.proc_name_min), next->comm); + } + } bpf_map_update_elem(&sys_schedule, &key, &sum_schedule, BPF_ANY); } else { sum_schedule->sum_count++; sum_schedule->sum_delay += delay; - if (delay > sum_schedule->max_delay) + if (delay > sum_schedule->max_delay) { sum_schedule->max_delay = delay; - if (sum_schedule->min_delay == 0 || delay < sum_schedule->min_delay) + bpf_probe_read_kernel_str(&sum_schedule->proc_name_max, sizeof(sum_schedule->proc_name_max), next->comm); + } else if (sum_schedule->min_delay == 0 || delay < sum_schedule->min_delay) { sum_schedule->min_delay = delay; + if (next->pid != 0) { + bpf_probe_read_kernel_str(&sum_schedule->proc_name_min, sizeof(sum_schedule->proc_name_min), next->comm); + } + } + } + history = bpf_map_lookup_elem(&proc_histories, &next_id); + if (history) { + // 如果找到了,更新历史记录 + new_history.last[0] = history->last[1]; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); + } else { + // 如果没有找到,初始化新的历史记录 + new_history.last[0].pid = 0; // 初始化为0,表示没有历史信息 + new_history.last[0].comm[0] = '\0'; + new_history.last[1].pid = prev->pid; + bpf_probe_read_kernel_str(&new_history.last[1].comm, sizeof(new_history.last[1].comm), prev->comm); + bpf_map_update_elem(&proc_histories, &next_id, &new_history, BPF_ANY); } return 0; } SEC("tracepoint/sched/sched_process_exit") int sched_process_exit(void *ctx) { + struct schedule_ctrl *sched_ctrl = get_schedule_ctrl(); struct task_struct *p = (struct task_struct *)bpf_get_current_task(); pid_t pid = BPF_CORE_READ(p, pid); int cpu = bpf_get_smp_processor_id(); @@ -147,4 +216,4 @@ int sched_process_exit(void *ctx) { bpf_map_delete_elem(&has_scheduled, &id); } return 0; -} +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c new file mode 100644 index 000000000..2c319dfe0 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c @@ -0,0 +1,281 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +// +// used to control the execution of proc_image tool +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu_watcher_helper.h" + +static struct env { + // 1代表activate;2代表unactivate;3代表finish + int usemode; + bool SAR; + bool percent; + bool CS_DELAY; + bool SYSCALL_DELAY; + bool MIN_US_SET; + int MIN_US; + bool PREEMPT; + bool SCHEDULE_DELAY; + bool MQ_DELAY; + int freq; + bool mutrace; + bool mutex_detail; + bool umutex; +} env = { + .usemode = 0, + .SAR = false, + .percent = false, + .CS_DELAY = false, + .SYSCALL_DELAY = false, + .MIN_US_SET = false, + .MIN_US = 10000, + .PREEMPT = false, + .SCHEDULE_DELAY = false, + .MQ_DELAY = false, + .freq = 99, + .mutrace = false, + .mutex_detail = false, + .umutex = false, +}; + +const char argp_program_doc[] ="Trace process to get cpu watcher.\n"; + +static const struct argp_option opts[] = { + { "activate", 'a', NULL, 0, "Set startup policy of proc_image tool" }, + { "unactivate", 'u', NULL, 0, "Initialize to the original unactivated state" }, + { "finish", 'f', NULL, 0, "Finish to run eBPF tool" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"percent", 'P', 0, 0, "Format data as percentages" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"schedule_delay_min_us_set", 'e', "THRESHOLD", 0, "Print scheduling delays that exceed the threshold (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print kernel mutex contend" }, + {"mutex_detail", 'i', 0, 0, "Print kernel mutex details" }, + {"umutex", 'b', 0, 0, "Print user mutex details" }, + { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'a': + env.usemode = 1; + break; + case 'u': + env.usemode = 2; + break; + case 'f': + env.usemode = 3; + break; + case 's': + env.SAR = true; + break; + case 'P': + env.percent = true; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'e': + env.MIN_US_SET = true; + if (arg) { + env.MIN_US = strtol(arg, NULL, 10); + if (env.MIN_US <= 0) { + fprintf(stderr, "Invalid value for min_us: %d\n", env.MIN_US); + argp_usage(state); + } + } else { + env.MIN_US = 10000; + } + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.mutrace = true; + break; + case 'i': + env.mutex_detail = true; + break; + case 'b': + env.umutex = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +int deactivate_mode(){ + int err; + + if(env.SAR){ + struct sar_ctrl sar_ctrl = {false,false,0}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {false,0}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {false,0}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {false,0}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + if(env.SCHEDULE_DELAY){ + struct schedule_ctrl schedule_ctrl = {false,false,10000,0}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {false,0}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + if(env.mutrace){ + struct mu_ctrl mu_ctrl = {false,false,0}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + return 0; +} + +static void sig_handler(int signo) +{ + deactivate_mode(); +} + +int main(int argc, char **argv) +{ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + signal(SIGALRM,sig_handler); + signal(SIGINT,sig_handler); + signal(SIGTERM,sig_handler); + + if(env.usemode == 1){ // activate mode + if(env.SAR){ + struct sar_ctrl sar_ctrl = {true,env.percent,SAR_WACTHER+env.percent}; + err = update_sar_ctrl_map(sar_ctrl); + if(err < 0) return err; + } + + if(env.CS_DELAY){ + struct cs_ctrl cs_ctrl = {true,CS_WACTHER}; + err = update_cs_ctrl_map(cs_ctrl); + if(err < 0) return err; + } + + if(env.SYSCALL_DELAY){ + struct sc_ctrl sc_ctrl = {true,SC_WACTHER}; + err = update_sc_ctrl_map(sc_ctrl); + if(err < 0) return err; + } + + if(env.PREEMPT){ + struct preempt_ctrl preempt_ctrl = {true,PREEMPT_WACTHER}; + err = update_preempt_ctrl_map(preempt_ctrl); + if(err < 0) return err; + } + + if(env.SCHEDULE_DELAY){ + /* + *1.未设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 0;输出方式为schedule输出 + *2.已设置env.MIN_US_SET时, prev_watcher = SCHEDULE_WACTHER + 1;输出方式为-e输出 + */ + struct schedule_ctrl schedule_ctrl = {true,env.MIN_US_SET,env.MIN_US,SCHEDULE_WACTHER+env.MIN_US_SET}; + err = update_schedule_ctrl_map(schedule_ctrl); + if(err < 0) return err; + } + + if(env.MQ_DELAY){ + struct mq_ctrl mq_ctrl = {true,MQ_WACTHER}; + err = update_mq_ctrl_map(mq_ctrl); + if(err < 0) return err; + } + + if(env.mutrace){ + if (env.umutex){ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+2}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + else{ + struct mu_ctrl mu_ctrl = {true,env.mutex_detail,env.umutex,MUTEX_WATCHER+env.mutex_detail}; + err = update_mu_ctrl_map(mu_ctrl); + if(err < 0) return err; + } + } + }else if(env.usemode == 2){ // deactivate mode + err = deactivate_mode(); + if(err<0){ + fprintf(stderr, "Failed to deactivate\n"); + return err; + } + }else if(env.usemode == 3){ // finish mode + const char *command = "pkill cpu_watcher"; + int status = system(command); + if (status == -1) { + perror("system"); + } + }else{ + // 输出help信息 + printf("Please enter the usage mode(activate/deactivate/finish) before selecting the function\n"); + argp_help(&argp, stderr, ARGP_HELP_LONG, argv[0]); + } + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c index 709b29c74..e50e23ab2 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include @@ -21,21 +21,29 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include #include -#include "cpu_watcher.h" +#include "cpu_watcher_helper.h" #include "sar.skel.h" #include "cs_delay.skel.h" #include "sc_delay.skel.h" #include "preempt.skel.h" #include "schedule_delay.skel.h" #include "mq_delay.skel.h" +#include "mutrace.skel.h" typedef long long unsigned int u64; typedef unsigned int u32; + + struct list_head { struct list_head *next; struct list_head *prev; @@ -50,47 +58,72 @@ struct msg_msg { static struct env { int time; + int period; bool enable_proc; bool SAR; bool CS_DELAY; bool SYSCALL_DELAY; bool PREEMPT; bool SCHEDULE_DELAY; - bool MQ_DELAY; + bool MQ_DELAY; int freq; + bool EWMA; + int cycle; + int MUTRACE; } env = { .time = 0, + .period = 1, .enable_proc = false, .SAR = false, .CS_DELAY = false, .SYSCALL_DELAY = false, .PREEMPT = false, .SCHEDULE_DELAY = false, - .MQ_DELAY = false, - .freq = 99 + .MQ_DELAY = false, + .freq = 99, + .EWMA = false, + .cycle = 0, + .MUTRACE = false, }; + struct cs_delay_bpf *cs_skel; struct sar_bpf *sar_skel; struct sc_delay_bpf *sc_skel; struct preempt_bpf *preempt_skel; struct schedule_delay_bpf *sd_skel; struct mq_delay_bpf *mq_skel; - -u64 softirq = 0;//初始化softirq; -u64 irqtime = 0;//初始化irq; -u64 idle = 0;//初始化idle;s +struct mutrace_bpf *mu_skel; + +static int csmap_fd; +static int sarmap_fd; +struct sar_ctrl sar_ctrl= {}; +static int scmap_fd; +static int preemptmap_fd; +static int schedulemap_fd; +struct schedule_ctrl sd_ctrl = {}; +static int mqmap_fd; +static int mumap_fd; +struct mu_ctrl mu_ctrl = {}; + +//static int prev_watcher = 0;//上一个使用的工具,用于在切换使用功能时,打印不用功能的表头; + +u64 softirq = 0; +u64 irqtime = 0; +u64 idle = 0; u64 sched = 0; u64 proc = 0; unsigned long ktTime = 0; unsigned long utTime = 0; -u64 tick_user = 0;//初始化sys; +u64 tick_user = 0; + int sc_sum_time = 0 ; int sc_max_time = 0 ; int sc_min_time = SYSCALL_MIN_TIME ; int sys_call_count = 0; +bool ifprint = 0; int preempt_count = 0 ; @@ -98,51 +131,69 @@ int sum_preemptTime = 0 ; int preempt_start_print = 0 ; /*设置传参*/ -const char argp_program_doc[] ="cpu wacher is in use ....\n"; +const char argp_program_doc[] = "cpu watcher is in use ....\n"; static const struct argp_option opts[] = { - { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, - {"libbpf_sar", 's', 0,0,"print sar_info (the data of cpu)"}, - {"cs_delay", 'c', 0,0,"print cs_delay (the data of cpu)"}, - {"syscall_delay", 'S', 0,0,"print syscall_delay (the data of syscall)"}, - {"preempt_time", 'p', 0,0,"print preempt_time (the data of preempt_schedule)"}, - {"schedule_delay", 'd', 0,0,"print schedule_delay (the data of cpu)"}, - {"mq_delay", 'm', 0,0,"print mq_delay"}, - { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, - {0}, + { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, + { "period", 'i', "INTERVAL", 0, "Period interval in seconds" }, + {"libbpf_sar", 's', 0, 0, "Print sar_info (the data of cpu)" }, + {"cs_delay", 'c', 0, 0, "Print cs_delay (the data of cpu)" }, + {"syscall_delay", 'S', 0, 0, "Print syscall_delay (the data of syscall)" }, + {"preempt_time", 'p', 0, 0, "Print preempt_time (the data of preempt_schedule)" }, + {"schedule_delay", 'd', 0, 0, "Print schedule_delay (the data of cpu)" }, + {"mq_delay", 'm', 0, 0, "Print mq_delay(the data of proc)" }, + {"mutrace", 'x', 0, 0, "Print mutrace data(the data of cpu)" }, + {"ewma", 'E',0,0,"dynamic filte the data"}, + {"cycle", 'T',"CYCLE",0,"Periods of the ewma"}, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + { 0 }, }; + static error_t parse_arg(int key, char *arg, struct argp_state *state) { - switch (key) { - case 't': - env.time = strtol(arg, NULL, 10); - if(env.time) alarm(env.time); - break; - case 's': - env.SAR = true; - break; - case 'c': - env.CS_DELAY = true; - break; - case 'S': - env.SYSCALL_DELAY = true; - break; - case 'p': - env.PREEMPT = true; - break; - case 'd': - env.SCHEDULE_DELAY = true; - break; - case 'm': - env.MQ_DELAY = true; + switch (key) { + case 't': + env.time = strtol(arg, NULL, 10); + if (env.time) alarm(env.time); + break; + case 'i': + env.period = strtol(arg, NULL, 10); + break; + case 's': + env.SAR = true; + break; + case 'c': + env.CS_DELAY = true; + break; + case 'S': + env.SYSCALL_DELAY = true; + break; + case 'p': + env.PREEMPT = true; + break; + case 'd': + env.SCHEDULE_DELAY = true; + break; + case 'm': + env.MQ_DELAY = true; + break; + case 'x': + env.MUTRACE = true; break; - case 'h': - argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + case 'E': + env.EWMA = true; break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; + case 'T': + env.cycle = strtol(arg, NULL, 10); + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } + static const struct argp argp = { .options = opts, .parser = parse_arg, @@ -176,7 +227,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, .config = PERF_COUNT_SW_CPU_CLOCK, }; int i, fd; - for (i = 0; i < nr_cpus; i++) { fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0); if (fd < 0) { @@ -196,7 +246,6 @@ static int open_and_attach_perf_event(int freq, struct bpf_program *prog, return -1; } } - return 0; } @@ -207,23 +256,42 @@ u64 find_ksym(const char* target_symbol) { perror("Failed to open /proc/kallsyms"); return 1; } - char symbol_name[99]; u64 symbol_address = 0; - while (fscanf(file, "%llx %*c %s\n", &symbol_address, symbol_name) != EOF) { if (strcmp(symbol_name, target_symbol) == 0) { break; } } - fclose(file); - return symbol_address; } static int print_all() { + int err,key=0; + err = bpf_map_lookup_elem(sarmap_fd, &key, &sar_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sar_ctrl.sar_func) return 0; + if(sar_ctrl.prev_watcher == SAR_WACTHER + 1) { + printf(" time proc/s cswch/s runqlen irqTime/%% softirq/%% idle/%% kthread/%% sysc/%% utime/%% sys/%% \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (sar_ctrl.prev_watcher == SAR_WACTHER){ + printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms \n"); + sar_ctrl.prev_watcher = SAR_WACTHER + 2; + err = bpf_map_update_elem(sarmap_fd, &key, &sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + int nprocs = get_nprocs(); /*proc:*/ int key_proc = 1; int err_proc, fd_proc = bpf_map__fd(sar_skel->maps.countMap); @@ -236,6 +304,7 @@ static int print_all() u64 __proc; __proc = total_forks - proc; proc = total_forks; + /*cswch:*/ int key_cswch = 0; int err_cswch, fd_cswch = bpf_map__fd(sar_skel->maps.countMap); @@ -320,7 +389,7 @@ static int print_all() unsigned long dtaUT = utTime -_utTime; /*sys*/ - int key_sys = 0,next_key; + int key_sys = 0; int err_sys, fd_sys = bpf_map__fd(sar_skel->maps.tick_user); u64 __tick_user =0 ;// 用于存储从映射中查找到的值 __tick_user = tick_user; @@ -338,15 +407,34 @@ static int print_all() if(env.enable_proc){ time_t now = time(NULL); struct tm *localTime = localtime(&now); - printf("%02d:%02d:%02d %8llu %8llu %6d %8llu %10llu %8llu %10llu %8llu %8lu %8lu\n", + if (sar_ctrl.percent == true){ + printf("%02d:%02d:%02d %8llu %8llu %6d ",localTime->tm_hour, localTime->tm_min, localTime->tm_sec,__proc, __sched, runqlen); + // 大于百分之60的标红输出 + double values[7] = { + (double)dtairqtime / 10000000 / nprocs / env.period, + (double)dtasoftirq / 10000000 / nprocs / env.period, + (double)dtaidle / 10000000 / nprocs / env.period, + (double)dtaKT / 10000000 / nprocs / env.period, + (double)dtaSysc / 10000000 / nprocs / env.period, + (double)dtaUTRaw / 10000000 / nprocs / env.period, + (double)dtaSys / 10000000 / nprocs / env.period + }; + for (int i = 0; i < 7; i++) { + if (values[i] > 60.0) { + printf("\033[1;31m"); // 设置为红色 + } + printf("%10.2f ", values[i]); + printf("\033[0m"); // 重置为默认颜色 + } + printf("\n"); + }else{printf("%02d:%02d:%02d %8llu %8llu %6d %8llu %10llu %8llu %10lu %8llu %8llu %8llu\n", localTime->tm_hour, localTime->tm_min, localTime->tm_sec, __proc,__sched,runqlen,dtairqtime/1000,dtasoftirq/1000,dtaidle/1000000, - dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000); + dtaKT/1000,dtaSysc / 1000000,dtaUTRaw/1000000,dtaSys / 1000000);} } else{ env.enable_proc = true; } - return 0; } @@ -354,49 +442,48 @@ int count[25]={0};//定义一个count数组,用于汇总schedul()调度时间 static int handle_event(void *ctx, void *data,unsigned long data_sz) { const struct event *e = data; - printf("t1:%lu t2:%lu delay:%lu\n",e->t1,e->t2,e->delay); - + printf("t1:%llu t2:%llu delay:%llu\n",e->t1,e->t2,e->delay); int dly=(int)(e->delay),i=0; while (dly > 1){ dly /= 2; i ++; } - count[i]++;//记录时间间隔次数; + count[i]++; return 0; } static int print_hstgram(int i,int max,int per_len) { int cnt=count[i]; if(per_len==1){ - while(cnt>0){//打印 + while(cnt>0){ printf("*"); cnt--; } } - while(cnt-per_len>=0){//打印 + while(cnt-per_len>=0){ printf("*"); cnt-=per_len; } printf("\n"); return per_len; } -double pow(int n,int k)//实现pow函数 +double my_pow(int n,int k)//实现pow函数 { if (k > 0) - return n * pow(n, k - 1); + return n * my_pow(n, k - 1); else if (k == 0) return 1; else - return 1.0 / pow(n, -k); + return 1.0 / my_pow(n, -k); } static void histogram() { int log10[15]={0},max=0,per_len=1; - for(int i=0;i<10;i++){//log10(count[i]); + for(int i=0;i<10;i++){ int tmp=count[i],cnt=0; while (tmp >= 10){ tmp /= 10; - cnt ++;//幂次 + cnt ++; } log10[cnt]++; } @@ -406,13 +493,13 @@ static void histogram() max=i; } - while(max>0){//pow(10,max); + while(max>0){ per_len *=10 ; max--; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + time_t now = time(NULL); + struct tm *localTime = localtime(&now); printf("\nTime : %02d:%02d:%02d \n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); printf("%-24s \t%-12s \t%-12s \n","cs_delay","Count","Distribution"); printf("%d\t=>\t%-8d \t%-12d \t|",0,1,count[0]); @@ -420,28 +507,49 @@ static void histogram() printf("%d\t=>\t%-8d \t%-12d \t|",2,3,count[1]); print_hstgram(1,max,per_len); for(int i=2;i<20;i++){ - printf("%d\t=>\t%-8d \t%-12d \t|",(int)pow(2,i),(int)pow(2,(i+1))-1,count[i]); + printf("%d\t=>\t%-8d \t%-12d \t|",(int)my_pow(2,i),(int)my_pow(2,(i+1))-1,count[i]); print_hstgram(i,max,per_len); } printf("per_len = %d\n",per_len); } -// static void max_print(){ - -// int sc_average_time = sc_sum_time/sys_call_count; -// printf("Average_Syscall_Time: %8d ms\n",sc_average_time); -// printf("MAX_Syscall_Time: %8d ms\n",sc_max_time); -// printf("MIN_Syscall_Time: %8d ms\n",sc_min_time); -// } +struct ewma_info ewma_syscall_delay = {}; static int syscall_delay_print(void *ctx, void *data,unsigned long data_sz) { + int err,key = 0; + struct sc_ctrl sc_ctrl ={}; + + err = bpf_map_lookup_elem(scmap_fd,&key,&sc_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sc_ctrl.sc_func) return 0; const struct syscall_events *e = data; - printf("pid: %-8llu comm: %-10s syscall_id: %-8ld delay: %-8llu\n", - e->pid,e->comm,e->syscall_id,e->delay); + if(e->delay<0||e->delay>1000000) return 0; + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + + if(env.EWMA==0){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + else{ + ewma_syscall_delay.cycle = env.cycle; + if(dynamic_filter(&ewma_syscall_delay,e->delay)){ + printf("%02d:%02d:%02d %-8u %-15lld %-15lld\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->pid,e->syscall_id,e->delay); + } + } + return 0; } + + //抢占时间输出 static int preempt_print(void *ctx, void *data, unsigned long data_sz) { @@ -452,49 +560,204 @@ static int preempt_print(void *ctx, void *data, unsigned long data_sz) return 0; } -static int schedule_print(struct bpf_map *sys_fd) +static int attach(struct mutrace_bpf *mu_skel) { - int key = 0; - struct sum_schedule info; - int err, fd = bpf_map__fd(sys_fd); - time_t now = time(NULL); - struct tm *localTime = localtime(&now); - int hour = localTime->tm_hour; - int min = localTime->tm_min; - int sec = localTime->tm_sec; - unsigned long long avg_delay; - - err = bpf_map_lookup_elem(fd, &key, &info); - if (err < 0) { - fprintf(stderr, "failed to lookup infos: %d\n", err); - return -1; + int err; + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_lock,pthread_mutex_lock); + ATTACH_UPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,__pthread_mutex_trylock); + ATTACH_URETPROBE_CHECKED(mu_skel,__pthread_mutex_trylock,ret_pthread_mutex_trylock); + ATTACH_UPROBE_CHECKED(mu_skel,pthread_mutex_unlock,pthread_mutex_unlock); + err = mutrace_bpf__attach(mu_skel); + CHECK_ERR(err, "Failed to attach BPF skeleton"); + return 0; + +} + + +//mutrace输出 +static int mutrace_print(void *ctx, void *data, unsigned long data_sz) { + int err,key = 0; + err = bpf_map_lookup_elem(mumap_fd,&key,&mu_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!mu_ctrl.mu_func) return 0; + if(mu_ctrl.prev_watcher == MUTEX_WATCHER ){ + printf("%s\n"," lock_ptr owner_pid owner_comm owner_prio contender_pid contender_comm contender_prio contender_count"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +1) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + }else if (mu_ctrl.prev_watcher == MUTEX_WATCHER +2) { + printf("%s\n"," lock_ptr locked_total locked_max contended_total count last_owner last_owmer_name"); + mu_ctrl.prev_watcher = MUTEX_WATCHER + 9;//打印表头功能关 + err = bpf_map_update_elem(mumap_fd, &key, &mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + if(!mu_ctrl.mutex_detail&& (!mu_ctrl.umutex)){ + const struct mutex_contention_event *e = data; + if (e->owner_pid == 0 || e->contender_pid == 0||e->owner_pid == 1) { + return 0; + } + // 增加锁争用次数 + increment_lock_count(e->ptr); + uint64_t contention_count = get_lock_count(e->ptr); + printf("%15llu %15d %15s %15d %15d %15s %15d %15ld\n", e->ptr, e->owner_pid, e->owner_name, e->owner_prio,e->contender_pid, e->contender_name, e->contender_prio,contention_count); + } + return 0; +} + +static int kmutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.kmutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15lluns %15lluns %15lluns %15d %15d %20s\n", + next_key, info.locked_total, info.locked_max, info.contended_total, info.count, info.last_owner, info.last_name); + } + key = next_key; + } + return 0; +} + +static int umutex_detail() { + int fd = bpf_map__fd(mu_skel->maps.umutex_info_map); + u64 key, next_key; + struct mutex_info info; + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + int err = bpf_map_lookup_elem(fd, &next_key, &info); + if (err == 0 && info.contended_total != 0) { // 添加过滤条件 + printf(" %15llu %15llums %15llums %15llums %15d %15d %20s\n", + next_key, info.locked_total/1000000, info.locked_max/1000000, info.contended_total/1000000, info.count, info.last_owner, info.last_name); + } + key = next_key; } - avg_delay = info.sum_delay / info.sum_count; - printf("%02d:%02d:%02d | %-15lf %-15lf %-15lf |\n", - hour, min, sec, avg_delay / 1000.0, info.max_delay / 1000.0, info.min_delay / 1000.0); return 0; } +static int schedule_print() +{ + int err,key = 0; + err = bpf_map_lookup_elem(schedulemap_fd,&key,&sd_ctrl); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if(!sd_ctrl.schedule_func) return 0; + + if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER ){ + printf("%-8s %s\n", " TIME ", "avg_delay/μs max_delay/μs max_proc_name min_delay/μs min_proc_name"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关 + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + else if(sd_ctrl.prev_watcher == SCHEDULE_WACTHER +1){ + // printf("sd_ctrl.prev_watcher = %d\n",sd_ctrl.prev_watcher); + printf("调度延时大于%dms的进程:\n",sd_ctrl.min_us/1000); + printf("%s\n","pid COMM schedule_delay/us"); + sd_ctrl.prev_watcher = SCHEDULE_WACTHER + 9;//打印表头功能关. + err = bpf_map_update_elem(schedulemap_fd, &key, &sd_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + } + } + + if(!sd_ctrl.min_us_set){ + struct sum_schedule info; + int err, fd = bpf_map__fd(sd_skel->maps.sys_schedule); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + int hour = localTime->tm_hour; + int min = localTime->tm_min; + int sec = localTime->tm_sec; + unsigned long long avg_delay; + err = bpf_map_lookup_elem(fd, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + avg_delay = info.sum_delay / info.sum_count; + if (!ifprint) { + ifprint=1; + }else{ + printf("%02d:%02d:%02d %-15lf %-15lf %10s %15lf %15s\n", + hour, min, sec, avg_delay / 1000.0, info.max_delay / 1000.0,info.proc_name_max,info.min_delay / 1000.0,info.proc_name_min); + } + } + else{ + struct proc_schedule info; + struct proc_id id_key; + struct proc_history prev_info; + int key = 0; + int err, fd1 = bpf_map__fd(sd_skel->maps.threshold_schedule),fd2 = bpf_map__fd(sd_skel->maps.proc_histories); + err = bpf_map_lookup_elem(fd1, &key, &info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos: %d\n", err); + return -1; + } + if (info.delay / 1000 > sd_ctrl.min_us&&info.id.pid!=0) { + id_key.pid = info.id.pid; + id_key.cpu_id = info.id.cpu_id; + err = bpf_map_lookup_elem(fd2, &id_key, &prev_info); + if (err < 0) { + fprintf(stderr, "Failed to lookup proc_histories with PID %d and CPU ID %d: %d\n", id_key.pid, id_key.cpu_id, err); + return -1; + } + if (!entry_exists(info.id.pid, info.proc_name, info.delay / 1000)) { + printf("%-10d %-16s %15lld", info.id.pid, info.proc_name, info.delay / 1000); + add_entry(info.id.pid, info.proc_name, info.delay / 1000); + for (int i = 0; i < 2; i++) { + if (prev_info.last[i].pid != 0) { + printf(" Previous Process %d: PID=%-10d Name=%-16s ", i+1, prev_info.last[i].pid, prev_info.last[i].comm); + } + } + printf("\n"); + } + + } + } + + return 0; +} + + static int mq_event(void *ctx, void *data,unsigned long data_sz) { time_t now = time(NULL);// 获取当前时间 struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 - printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); - printf("-----------------------------------------------------------------------------------------------------------------------\n"); const struct mq_events *e = data; - - printf("Mqdes: %-8llu msg_len: %-8llu msg_prio: %-8llu\n",e->mqdes,e->msg_len,e->msg_prio); - printf("SND_PID: %-8lu SND_enter_time: %-16llu SND_exit_time: %-16llu\n", - e->send_pid,e->send_enter_time,e->send_exit_time); - printf("RCV_PID: %-8lu RCV_enter_time: %-16llu RCV_exit_time: %-16llu\n", - e->rcv_pid,e->rcv_enter_time,e->rcv_exit_time); - printf("-------------------------------------------------------------------------------\n"); - - printf("SND_Delay/ms: %-8.2f RCV_Delay/ms: %-8.2f Delay/ms: %-8.5f\n", - (e->send_exit_time - e->send_enter_time)/1000000.0, - (e->rcv_exit_time - e->rcv_enter_time)/1000000.0, - (e->rcv_exit_time - e->send_enter_time)/1000000.0); - printf("-----------------------------------------------------------------------------------------------------------------------\n\n"); + float send_delay,rcv_delay,delay; + if(!e->send_enter_time || !e->send_exit_time || !e->rcv_enter_time || !e->rcv_exit_time) { + printf("erro!\n"); + return 0; + } + send_delay = (e->send_exit_time - e->send_enter_time)/1000000.0; + rcv_delay = (e->rcv_exit_time - e->rcv_enter_time)/1000000.0; + if(e->send_enter_time < e->rcv_enter_time){ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0; + }else{ + delay = (e->rcv_exit_time - e->send_enter_time)/1000000.0 + send_delay + rcv_delay; + } + printf("%02d:%02d:%02d %-8u %-8u %-8u \t%-16llu %-16llu %-16llu %-16llu\t%-15.5f %-15.5f %-15.5f\n", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + e->mqdes,e->send_pid,e->rcv_pid, + e->send_enter_time,e->send_exit_time,e->rcv_enter_time,e->rcv_exit_time, + send_delay,rcv_delay,delay); return 0; } @@ -503,6 +766,14 @@ static int mq_event(void *ctx, void *data,unsigned long data_sz) int main(int argc, char **argv) { struct ring_buffer *rb = NULL; + struct bpf_map *cs_ctrl_map = NULL; + struct bpf_map *sar_ctrl_map = NULL; + struct bpf_map *sc_ctrl_map = NULL; + struct bpf_map *preempt_ctrl_map = NULL; + struct bpf_map *schedule_ctrl_map = NULL; + struct bpf_map *mq_ctrl_map = NULL; + struct bpf_map *mu_ctrl_map = NULL; + int key = 0; int err; err = argp_parse(&argp, argc, argv, 0, NULL, NULL); if (err) @@ -544,6 +815,19 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cs_delay_cleanup; } + + err = common_pin_map(&cs_ctrl_map,cs_skel->obj,"cs_ctrl_map",cs_ctrl_path); + if(err < 0){ + goto cs_delay_cleanup; + } + csmap_fd = bpf_map__fd(cs_ctrl_map); + struct cs_ctrl init_value = {false,CS_WACTHER}; + err = bpf_map_update_elem(csmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto cs_delay_cleanup; + } + /* Attach tracepoints */ err = cs_delay_bpf__attach(cs_skel); if (err) @@ -570,6 +854,17 @@ int main(int argc, char **argv) goto preempt_cleanup; } + err = common_pin_map(&preempt_ctrl_map,preempt_skel->obj,"preempt_ctrl_map",preempt_ctrl_path); + if(err < 0){ + goto preempt_cleanup; + } + preemptmap_fd = bpf_map__fd(preempt_ctrl_map); + struct preempt_ctrl init_value = {false,PREEMPT_WACTHER}; + err = bpf_map_update_elem(preemptmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto preempt_cleanup; + } err = preempt_bpf__attach(preempt_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); @@ -597,6 +892,17 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto sc_delay_cleanup; } + err = common_pin_map(&sc_ctrl_map,sc_skel->obj,"sc_ctrl_map",sc_ctrl_path); + if(err < 0){ + goto sc_delay_cleanup; + } + scmap_fd = bpf_map__fd(sc_ctrl_map); + struct sc_ctrl init_value = {false,SC_WACTHER}; + err = bpf_map_update_elem(scmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sc_delay_cleanup; + } /* Attach tracepoints */ err = sc_delay_bpf__attach(sc_skel); if (err) @@ -604,13 +910,18 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sc_delay_cleanup; } + printf("%-8s %-8s %-15s %-15s\n","Time","Pid","syscall_id","delay/ms"); rb = ring_buffer__new(bpf_map__fd(sc_skel->maps.rb), syscall_delay_print, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto sc_delay_cleanup; } + + }else if(env.SCHEDULE_DELAY){ + + sd_skel = schedule_delay_bpf__open(); if (!sd_skel) { fprintf(stderr, "Failed to open and load BPF skeleton\n"); @@ -621,12 +932,23 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto schedule_cleanup; } + err = common_pin_map(&schedule_ctrl_map,sd_skel->obj,"schedule_ctrl_map",schedule_ctrl_path); + if(err < 0){ + goto schedule_cleanup; + } + schedulemap_fd = bpf_map__fd(schedule_ctrl_map); + struct schedule_ctrl init_value = {false,false,10000,SCHEDULE_WACTHER}; + + err = bpf_map_update_elem(schedulemap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto schedule_cleanup; + } err = schedule_delay_bpf__attach(sd_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto schedule_cleanup; } - printf("%-8s %s\n", " TIME ", "avg_delay/μs max_delay/μs min_delay/μs"); }else if (env.SAR){ /* Load and verify BPF application */ sar_skel = sar_bpf__open(); @@ -649,13 +971,24 @@ int main(int argc, char **argv) if (err) goto sar_cleanup; + err = common_pin_map(&sar_ctrl_map,sar_skel->obj,"sar_ctrl_map",sar_ctrl_path); + if(err < 0){ + goto sar_cleanup; + } + sarmap_fd = bpf_map__fd(sar_ctrl_map); + struct sar_ctrl init_value = {false,false,SAR_WACTHER}; + err = bpf_map_update_elem(sarmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto sar_cleanup; + } + err = sar_bpf__attach(sar_skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sar_cleanup; } - printf(" time proc/s cswch/s runqlen irqTime/us softirq/us idle/ms kthread/us sysc/ms utime/ms sys/ms \n"); }else if(env.MQ_DELAY){ /* Load and verify BPF application */ mq_skel = mq_delay_bpf__open(); @@ -671,6 +1004,19 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto mq_delay_cleanup; } + + err = common_pin_map(&mq_ctrl_map,mq_skel->obj,"mq_ctrl_map",mq_ctrl_path); + if(err < 0){ + goto mq_delay_cleanup; + } + mqmap_fd = bpf_map__fd(mq_ctrl_map); + struct mq_ctrl init_value = {false,MQ_WACTHER}; + err = bpf_map_update_elem(mqmap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mq_delay_cleanup; + } + /* Attach tracepoints */ err = mq_delay_bpf__attach(mq_skel); if (err) @@ -678,16 +1024,62 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto mq_delay_cleanup; } + printf("%-8s %-8s %-8s %-8s \t%-16s %-16s %-16s %-16s\t%-15s %-15s %-15s\n","Time","Mqdes","SND_PID","RCV_PID","SND_Enter","SND_EXit","RCV_Enter","RCV_EXit","SND_Delay/ms","RCV_Delay/ms","Delay/ms"); rb = ring_buffer__new(bpf_map__fd(mq_skel->maps.rb), mq_event, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto mq_delay_cleanup; } + }else if (env.MUTRACE) { + mu_skel = mutrace_bpf__open(); + if (!mu_skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + err = mutrace_bpf__load(mu_skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto mutrace_cleanup; + } + err = common_pin_map(&mu_ctrl_map,mu_skel->obj,"mu_ctrl_map",mu_ctrl_path); + if(err < 0){ + goto mutrace_cleanup; + } + mumap_fd = bpf_map__fd(mu_ctrl_map); + struct mu_ctrl init_value = {false,false,false,MUTEX_WATCHER}; + + err = bpf_map_update_elem(mumap_fd, &key, &init_value, 0); + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + goto mutrace_cleanup; + } + //ctrl + if(err < 0){ + fprintf(stderr, "Failed to update elem\n"); + goto mutrace_cleanup; + } + err = attach(mu_skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto mutrace_cleanup; + } + + rb = ring_buffer__new(bpf_map__fd(mu_skel->maps.rb), mutrace_print, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto mutrace_cleanup; + } } while (!exiting) { if(env.SAR){ - sleep(1); + sleep(env.period); err = print_all(); if (err == -EINTR) { err = 0; @@ -722,11 +1114,11 @@ int main(int argc, char **argv) printf("Error polling perf buffer: %d\n", err); break; } - time_t now = time(NULL);// 获取当前时间 - struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 - printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); - printf("----------------------------------------------------------------------------------------------------------\n"); - sleep(1); + // time_t now = time(NULL);// 获取当前时间 + // struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + // printf("\n\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + // printf("----------------------------------------------------------------------------------------------------------\n"); + // sleep(1); } else if (env.PREEMPT) { err = ring_buffer__poll(rb, 100 /* timeout, ms */); @@ -753,7 +1145,7 @@ int main(int argc, char **argv) sleep(2); } else if (env.SCHEDULE_DELAY){ - err = schedule_print(sd_skel->maps.sys_schedule); + err = schedule_print(); if (err == -EINTR) { err = 0; break; @@ -761,7 +1153,9 @@ int main(int argc, char **argv) if (err < 0) { break; } - sleep(1); + if(env.SCHEDULE_DELAY&&!sd_ctrl.min_us_set){ + sleep(1); + } } else if(env.MQ_DELAY){ err = ring_buffer__poll(rb, 1000 /* timeout, s */); @@ -774,6 +1168,27 @@ int main(int argc, char **argv) break; } } + else if (env.MUTRACE) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + if(env.MUTRACE&&mu_ctrl.mutex_detail){ + err = kmutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + }else if(env.MUTRACE&&mu_ctrl.umutex){ + err = umutex_detail(); + sleep(1); + printf("-------------------------------------------------------------\n"); + } + + } else { printf("正在开发中......\n-c 打印cs_delay:\t对内核函数schedule()的执行时长进行测试;\n-s sar工具;\n-y 打印sc_delay:\t系统调用运行延迟进行检测; \n-p 打印preempt_time:\t对抢占调度时间输出;\n"); break; @@ -781,30 +1196,41 @@ int main(int argc, char **argv) } cs_delay_cleanup: + bpf_map__unpin(cs_ctrl_map, cs_ctrl_path); ring_buffer__free(rb); cs_delay_bpf__destroy(cs_skel); return err < 0 ? -err : 0; sar_cleanup: + bpf_map__unpin(sar_ctrl_map, sar_ctrl_path); sar_bpf__destroy(sar_skel); return err < 0 ? -err : 0; sc_delay_cleanup: + bpf_map__unpin(sc_ctrl_map, sc_ctrl_path); ring_buffer__free(rb); sc_delay_bpf__destroy(sc_skel); return err < 0 ? -err : 0; preempt_cleanup: + bpf_map__unpin(preempt_ctrl_map, preempt_ctrl_path); ring_buffer__free(rb); preempt_bpf__destroy(preempt_skel); return err < 0 ? -err : 0; schedule_cleanup: + bpf_map__unpin(schedule_ctrl_map, schedule_ctrl_path); schedule_delay_bpf__destroy(sd_skel); return err < 0 ? -err : 0; mq_delay_cleanup: + bpf_map__unpin(mq_ctrl_map, mq_ctrl_path); ring_buffer__free(rb); mq_delay_bpf__destroy(mq_skel); return err < 0 ? -err : 0; + +mutrace_cleanup: + ring_buffer__free(rb); + mutrace_bpf__destroy(mu_skel); + return err < 0 ? -err : 0; } diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c deleted file mode 100644 index c94c5d1bf..000000000 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cs_delay.bpf.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include -#include -#include "cpu_watcher.h" - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -//记录时间戳; -BPF_ARRAY(start,int,u64,1); -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -SEC("kprobe/schedule") -int BPF_KPROBE(schedule) -{ - u64 t1; - t1 = bpf_ktime_get_ns()/1000; - int key =0; - bpf_map_update_elem(&start,&key,&t1,BPF_ANY); - return 0; -} - -SEC("kretprobe/schedule") -int BPF_KRETPROBE(schedule_exit) -{ - u64 t2 = bpf_ktime_get_ns()/1000; - u64 t1,delay; - int key = 0; - u64 *val = bpf_map_lookup_elem(&start,&key); - if (val != 0) - { - t1 = *val; - delay = t2 - t1; - bpf_map_delete_elem(&start, &key); - }else{ - return 0; - } - struct event *e; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - e->t1=t1; - e->t2=t2; - e->delay=delay; - bpf_ringbuf_submit(e, 0); - return 0; -} - diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png new file mode 100644 index 000000000..566fd1c1e Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/docs/image/image13.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json new file mode 100644 index 000000000..9177bf611 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/grafana_cpu_watcher_dashboard.json @@ -0,0 +1,1177 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"avg_delay/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"max_delay/us\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"min_delay/μs\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "schedule_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"irqTime/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "irqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"duration_ns\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "preempt_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"proc/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "procs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"sys/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"softirq/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "softirqTime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"utime/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "utime", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"idle/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "idle", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"kthread/us\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "kthread", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 40 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"cswch/s\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "cswch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 48 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "right", + "showLegend": false + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "ddlnxe6bsiha8c" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"delay/ms\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "1", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "sys_delay", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "cpu_watcher_vis", + "uid": "cdlnxoxcy4zr4b", + "version": 4, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h similarity index 54% rename from eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h rename to eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h index 6812096ce..7ffb2b3fa 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.h +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h @@ -12,17 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: zhangziheng0525@163.com -// -// eBPF map for libbpf sar +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_H +#define CPU_WATCHER_H + #include #include -typedef long long unsigned int u64; +typedef unsigned long long u64; typedef unsigned int u32; -typedef __kernel_mqd_t mqd_t; +typedef __kernel_mqd_t mqd_t; #define __user -#define MAX_CPU_NR 128 +#define MAX_CPU_NR 128 #define TASK_COMM_LEN 20 #define SYSCALL_MIN_TIME 1E7 #define MAX_SYSCALL_COUNT 100 @@ -36,56 +37,49 @@ typedef __kernel_mqd_t mqd_t; /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ +#define BPF_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ } name SEC(".maps") - /// @brief 创建一个指定名字和键值类型的ebpf散列表 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES 哈希map容量 -#define BPF_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - +#define BPF_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /// @brief 创建一个指定名字和键值类型的ebpf每CPU数组 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_ARRAY(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - +#define BPF_PERCPU_ARRAY(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /// @brief 创建一个指定名字和键值类型的ebpf每CPU散列表 /// @param name 新散列表的名字 /// @param type1 键的类型 /// @param type2 值的类型 /// @param MAX_ENTRIES map容量 -#define BPF_PERCPU_HASH(name, type1,type2,MAX_ENTRIES ) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") +#define BPF_PERCPU_HASH(name, type1, type2, MAX_ENTRIES) \ + struct { \ + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); \ + __uint(key_size, sizeof(type1)); \ + __uint(value_size, sizeof(type2)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") /*----------------------------------------------*/ /* cs_delay结构体 */ @@ -93,22 +87,22 @@ typedef __kernel_mqd_t mqd_t; #ifndef __CS_DELAY_H #define __CS_DELAY_H struct event { - long unsigned int t1; - long unsigned int t2; - long unsigned int delay; + u64 t1; + u64 t2; + u64 delay; }; #endif /* __CS_DELAY_H */ /*----------------------------------------------*/ /* syscall_delay结构体 */ /*----------------------------------------------*/ -struct syscall_flags{ - long unsigned int start_time; +struct syscall_flags { + u64 start_time; int syscall_id; }; struct syscall_events {//每个进程一个 - int pid,count; + int pid, count; char comm[TASK_COMM_LEN]; u64 delay; u64 syscall_id; @@ -116,7 +110,7 @@ struct syscall_events {//每个进程一个 /*----------------------------------------------*/ /* preempt_event结构体 */ /*----------------------------------------------*/ -struct preempt_event{ +struct preempt_event { pid_t prev_pid; pid_t next_pid; unsigned long long duration; @@ -126,22 +120,68 @@ struct preempt_event{ /* schedule_delay相关结构体 */ /*----------------------------------------------*/ //标识不同进程 -struct proc_id{ +struct proc_id { int pid; int cpu_id; }; //标识该进程的调度信息 -struct schedule_event{ +struct schedule_event { int pid; int count;//调度次数 unsigned long long enter_time; }; //整个系统所有调度信息 -struct sum_schedule{ +struct sum_schedule { unsigned long long sum_count; unsigned long long sum_delay; unsigned long long max_delay; unsigned long long min_delay; + char proc_name_max[TASK_COMM_LEN]; + char proc_name_min[TASK_COMM_LEN]; +}; + +struct proc_schedule { + struct proc_id id; + unsigned long long delay; + char proc_name[TASK_COMM_LEN]; +}; + +struct proc_info { + pid_t pid; + char comm[TASK_COMM_LEN]; +}; + +struct proc_history { + struct proc_info last[2]; // 存储最后两个调度的进程信息 +}; + +/*----------------------------------------------*/ +/* mutrace相关结构体 */ +/*----------------------------------------------*/ +struct mutex_info { + u64 locked_total;//锁被持有的总时间 + u64 locked_max;//锁被持有的最长时间 + u64 contended_total;//锁发生竞争的总时间 + int count;//记录锁被争用的总次数 + pid_t last_owner;//最后一次持有该锁的线程 ID + char last_name[TASK_COMM_LEN]; + u64 acquire_time; // 锁每次被获取的时间戳,方便后续计算 + u64 ptr;//地址 +}; + +struct mutex_contention_event { + u64 ptr;//锁地址 + pid_t owner_pid;//持有者pid + pid_t contender_pid;//抢占者pid + char contender_name[TASK_COMM_LEN]; + char owner_name[TASK_COMM_LEN]; + int owner_prio; + int contender_prio; +}; + +struct trylock_info { + void *__mutex; + u64 start_time; }; /*----------------------------------------------*/ @@ -151,36 +191,34 @@ struct mq_events { int send_pid; int rcv_pid; mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - - u64 send_enter_time; - u64 send_exit_time; - u64 rcv_enter_time; - u64 rcv_exit_time; + size_t msg_len; + unsigned int msg_prio; + u64 send_enter_time; + u64 send_exit_time; + u64 rcv_enter_time; + u64 rcv_exit_time; }; struct send_events { int send_pid; u64 Key_msg_ptr; - mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - const char *u_msg_ptr; - const void *src; - u64 send_enter_time; - u64 send_exit_time; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *src; + u64 send_enter_time; + u64 send_exit_time; }; struct rcv_events { int rcv_pid; u64 Key_msg_ptr; mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - const char *u_msg_ptr; - const void *dest; - u64 rcv_enter_time; - u64 rcv_exit_time; + size_t msg_len; + unsigned int msg_prio; + const char *u_msg_ptr; + const void *dest; + u64 rcv_enter_time; + u64 rcv_exit_time; }; /*----------------------------------------------*/ /* cswch_args结构体 */ @@ -219,4 +257,49 @@ struct idleStruct { u64 pad; unsigned int state; unsigned int cpu_id; -}; \ No newline at end of file +}; + +/*----------------------------------------------*/ +/* 控制板块 */ +/*----------------------------------------------*/ +struct sar_ctrl{ + bool sar_func; + bool percent; + int prev_watcher; +}; + +struct cs_ctrl{ + bool cs_func; + int prev_watcher; +}; + +struct sc_ctrl{ + bool sc_func; + int prev_watcher; +}; + +struct preempt_ctrl{ + bool preempt_func; + int prev_watcher; +}; + +struct schedule_ctrl{ + bool schedule_func; + bool min_us_set; + int min_us; + int prev_watcher; +}; + +struct mq_ctrl{ + bool mq_func; + int prev_watcher; +}; + +struct mu_ctrl{ + bool mu_func; + bool mutex_detail; + bool umutex; + int prev_watcher; +}; + +#endif // CPU_WATCHER_H \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h new file mode 100644 index 000000000..08e38f0e1 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher_helper.h @@ -0,0 +1,353 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com +#ifndef CPU_WATCHER_HELPER_H +#define CPU_WATCHER_HELPER_H + +#include +#include "cpu_watcher.h" + +#define SAR_WACTHER 10 +#define CS_WACTHER 20 +#define SC_WACTHER 30 +#define PREEMPT_WACTHER 40 +#define SCHEDULE_WACTHER 50 +#define MQ_WACTHER 60 +#define MUTEX_WATCHER 70 +#define HASH_SIZE 1024 + +/*----------------------------------------------*/ +/* ewma算法 */ +/*----------------------------------------------*/ +//滑动窗口周期,用于计算alpha +#define CYCLE 10 +//阈值容错空间; +#define TOLERANCE 1.0 +struct ewma_info{ + double previousEWMA; + int count; + int cycle;//cycle是滑动窗口周期大小 +}; + +double calculateEWMA(double previousEWMA, double dataPoint, double alpha) { + return alpha * dataPoint + (1 - alpha) * previousEWMA;//当前时间点的ewma +} + +bool dynamic_filter(struct ewma_info *ewma_syscall_delay, double dataPoint) { + double alpha,threshold; + if(ewma_syscall_delay->cycle==0) alpha = 2.0 /(CYCLE + 1); // 计算 alpha + else alpha = 2.0 /(ewma_syscall_delay->cycle + 1); + + if(ewma_syscall_delay->previousEWMA == 0) {//初始化ewma算法,则赋值previousEWMA = dataPoint 并打印 + ewma_syscall_delay->previousEWMA = dataPoint; + return 1; + } + if(ewma_syscall_delay->count <30){ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + return 1; + } + else{ + ewma_syscall_delay->previousEWMA = calculateEWMA(ewma_syscall_delay->previousEWMA,dataPoint,alpha);//计算 + threshold = ewma_syscall_delay->previousEWMA * TOLERANCE; + if(dataPoint >= threshold) return 1; + } + return 0; +} + +/*----------------------------------------------*/ +/* bpf file system */ +/*----------------------------------------------*/ +const char *sar_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sar_ctrl_map"; +const char *cs_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/cs_ctrl_map"; +const char *sc_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/sc_ctrl_map"; +const char *preempt_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/preempt_ctrl_map"; +const char *schedule_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/schedule_ctrl_map"; +const char *mq_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mq_ctrl_map"; +const char *mu_ctrl_path = "/sys/fs/bpf/cpu_watcher_map/mu_ctrl_map"; + +int common_pin_map(struct bpf_map **bpf_map, const struct bpf_object *obj, const char *map_name, const char *ctrl_path) +{ + int ret; + + *bpf_map = bpf_object__find_map_by_name(obj, map_name);//查找具有指定名称的 BPF 映射 + if (!*bpf_map) { + fprintf(stderr, "Failed to find BPF map\n"); + return -1; + } + // 用于防止上次没有成功 unpin 掉这个 map + bpf_map__unpin(*bpf_map, ctrl_path); + ret = bpf_map__pin(*bpf_map, ctrl_path); + if (ret){ + fprintf(stderr, "Failed to pin BPF map\n"); + return -1; + }//找到pin上 + + return 0; +} + +int update_sar_ctrl_map(struct sar_ctrl sar_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sar_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sar_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sar_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sar_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_cs_ctrl_map(struct cs_ctrl cs_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(cs_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open cs_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&cs_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update cs_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_sc_ctrl_map(struct sc_ctrl sc_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(sc_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open sc_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&sc_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update sc_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_preempt_ctrl_map(struct preempt_ctrl preempt_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(preempt_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open preempt_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&preempt_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update preempt_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_schedule_ctrl_map(struct schedule_ctrl schedule_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(schedule_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open schedule_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&schedule_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update schedule_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mq_ctrl_map(struct mq_ctrl mq_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mq_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mq_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} + +int update_mu_ctrl_map(struct mu_ctrl mu_ctrl){ + int err,key = 0; + int srcmap_fd; + + srcmap_fd = bpf_obj_get(mu_ctrl_path); + if (srcmap_fd < 0) { + fprintf(stderr,"Failed to open mq_ctrl_map file\n"); + return srcmap_fd; + } + err = bpf_map_update_elem(srcmap_fd,&key,&mu_ctrl, 0); + if(err < 0){ + fprintf(stderr, "Failed to update mq_ctrl_map elem\n"); + return err; + } + + return 0; +} +/*----------------------------------------------*/ +/* mutex_count */ +/*----------------------------------------------*/ + +typedef struct { + uint64_t ptr; + uint64_t count; +} lock_count_t; + +lock_count_t lock_counts[HASH_SIZE]; + +static uint64_t hash(uint64_t ptr) { + return ptr % HASH_SIZE; +} + +static void increment_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + lock_counts[h].ptr = ptr; + lock_counts[h].count = 1; + } else { + lock_counts[h].count++; + } +} + +static uint64_t get_lock_count(uint64_t ptr) { + uint64_t h = hash(ptr); + while (lock_counts[h].ptr != 0 && lock_counts[h].ptr != ptr) { + h = (h + 1) % HASH_SIZE; + } + if (lock_counts[h].ptr == 0) { + return 0; + } else { + return lock_counts[h].count; + } +} + +/*----------------------------------------------*/ +/* hash */ +/*----------------------------------------------*/ + + +struct output_entry { + int pid; + char comm[16]; + long long delay; +}; + + +struct output_entry seen_entries[MAX_ENTRIES]; +int seen_count = 0; + + +bool entry_exists(int pid, const char *comm, long long delay) { + for (int i = 0; i < seen_count; i++) { + if (seen_entries[i].pid == pid && + strcmp(seen_entries[i].comm, comm) == 0 && + seen_entries[i].delay == delay) { + return true; + } + } + return false; +} + + +void add_entry(int pid, const char *comm, long long delay) { + if (seen_count < MAX_ENTRIES) { + seen_entries[seen_count].pid = pid; + strncpy(seen_entries[seen_count].comm, comm, sizeof(seen_entries[seen_count].comm)); + seen_entries[seen_count].delay = delay; + seen_count++; + } +} +/*----------------------------------------------*/ +/* uprobe */ +/*----------------------------------------------*/ +static const char object[] = "/usr/lib/x86_64-linux-gnu/libc.so.6"; + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .retprobe = is_retprobe, \ + .func_name = #sym_name); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + -1, \ + object, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + fprintf(stderr, "[%s] no program attached for" #prog_name "\n", strerror(errno)); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + +#define CHECK_ERR(cond, info) \ + if (cond) \ + { \ + fprintf(stderr, "[%s]" info "\n", strerror(errno)); \ + return -1; \ + } + +#define warn(...) fprintf(stderr, __VA_ARGS__) + + +#endif // CPU_WATCHER_HELPER_H \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c deleted file mode 100644 index ec642bf17..000000000 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/sc_delay.bpf.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "vmlinux.h" -#include //包含了BPF 辅助函数 -#include -#include "cpu_watcher.h" - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; - -// 定义数组映射 -//BPF_PERCPU_HASH(SyscallEnterTime,pid_t,struct syscall_flags,512);//记录时间戳 -BPF_PERCPU_HASH(SyscallEnterTime,pid_t,u64,512);//记录时间戳 -BPF_PERCPU_HASH(Events,pid_t,u64,10); - -struct { - __uint(type, BPF_MAP_TYPE_RINGBUF); - __uint(max_entries, 256 * 1024); -} rb SEC(".maps");//环形缓冲区; - - -SEC("tracepoint/raw_syscalls/sys_enter")//进入系统调用 -int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ - u64 start_time = bpf_ktime_get_ns()/1000;//ms - pid_t pid = bpf_get_current_pid_tgid();//获取到当前进程的pid - u64 syscall_id = (u64)args->id; - - //bpf_printk("ID:%ld\n",syscall_id); - bpf_map_update_elem(&Events,&pid,&syscall_id,BPF_ANY); - bpf_map_update_elem(&SyscallEnterTime,&pid,&start_time,BPF_ANY); - return 0; -} - -SEC("tracepoint/raw_syscalls/sys_exit")//退出系统调用 -int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ - u64 exit_time = bpf_ktime_get_ns()/1000;//ms - pid_t pid = bpf_get_current_pid_tgid() ;//获取到当前进程的pid - u64 syscall_id; - u64 start_time, delay; - - u64 *val = bpf_map_lookup_elem(&SyscallEnterTime, &pid); - if(val !=0){ - start_time = *val; - delay = exit_time - start_time; - bpf_map_delete_elem(&SyscallEnterTime, &pid); - }else{ - return 0; - } - - u64 *val2 = bpf_map_lookup_elem(&Events, &pid); - if(val2 !=0){ - syscall_id = *val2; - bpf_map_delete_elem(&SyscallEnterTime, &pid); - }else{ - return 0; - } - - - struct syscall_events *e; - e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - - e->pid = pid; - e->delay = delay; - bpf_get_current_comm(&e->comm, sizeof(e->comm)); - e->syscall_id = syscall_id; - - bpf_ringbuf_submit(e, 0); - - - return 0; -} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile new file mode 100644 index 000000000..8e5da9b71 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/Makefile @@ -0,0 +1,21 @@ +CC = gcc +CFLAGS = -Wall -Wextra +LDFLAGS = -lrt + +.PHONY: all clean + +all: test_cpuwatcher sender receiver + +sender: mq_test_sender.c + $(CC) $(CFLAGS) -o sender mq_test_sender.c $(LDFLAGS) + +receiver: mq_test_receiver.c + $(CC) $(CFLAGS) -o receiver mq_test_receiver.c $(LDFLAGS) + +test_cpuwatcher: test_cpuwatcher.c + $(CC) $(CFLAGS) -o test_cpuwatcher test_cpuwatcher.c + +clean: + rm -f test_cpuwatcher sender receiver + # 清除 stress-ng 生成的临时文件 + rm -rf /tmp-stress-ng* diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c new file mode 100644 index 000000000..240202514 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_receiver.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_NAME "/test_queue" +#define MSG_SIZE 50 + +int main() { + mqd_t mq; + char msg_buffer[MSG_SIZE]; + unsigned int priority; + + // 打开消息队列 + mq = mq_open(QUEUE_NAME, O_RDONLY); + if (mq == (mqd_t)-1) { + perror("mq_open"); + exit(1); + } + + // 接收消息 + while (1) { + if (mq_receive(mq, msg_buffer, MSG_SIZE, &priority) == -1) { + perror("mq_receive"); + break; + } + printf("Received: %s\n", msg_buffer); + } + + // 关闭消息队列 + mq_close(mq); + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c new file mode 100644 index 000000000..43c66e55b --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/mq_test_sender.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_NAME "/test_queue" +#define MSG_SIZE 50 +#define MAX_MSGS 10 + +int main() { + mqd_t mq; + struct mq_attr attr; + char msg_buffer[MSG_SIZE]; + unsigned int priority = 1; + int i; + + // 设置消息队列属性 + attr.mq_flags = 0; + attr.mq_maxmsg = MAX_MSGS; + attr.mq_msgsize = MSG_SIZE; + attr.mq_curmsgs = 0; + + // 创建或打开消息队列 + mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr); + if (mq == (mqd_t)-1) { + perror("mq_open"); + exit(1); + } + + // 发送消息 + for (i = 0;i<60 ; i++) { + sprintf(msg_buffer, "Message %d", i); + if (mq_send(mq, msg_buffer, strlen(msg_buffer) + 1, priority) == -1) { + perror("mq_send"); + break; + } + printf("Sent: %s\n", msg_buffer); + sleep(1); + } + + // 关闭消息队列 + mq_close(mq); + mq_unlink(QUEUE_NAME); + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c new file mode 100644 index 000000000..59bb2dcb6 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/test/test_cpuwatcher.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define gettid() syscall(__NR_gettid) + +static struct env { + bool sar_test; + bool cs_delay_test; + bool sc_delay_test; + bool mq_delay_test; + bool preempt_test; + bool schedule_test; + bool mutrace_test; +} env = { + .sar_test = false, + .cs_delay_test = false, + .sc_delay_test = false, + .mq_delay_test = false, + .preempt_test = false, + .schedule_test = false, + .mutrace_test = false, +}; + +const char argp_program_doc[] ="To test cpu_watcher.\n"; + +static const struct argp_option opts[] = { + { "sar", 's', NULL, 0, "To test sar", 0 }, + { "cs_delay", 'c', NULL, 0, "To test cs_delay", 0 }, + { "sc_delay", 'S', NULL, 0, "To test sc_delay", 0 }, + { "mq_delay", 'm', NULL, 0, "To test mq_delay", 0 }, + { "preempt_delay", 'p', NULL, 0, "To test preempt_delay", 0 }, + { "schedule_delay", 'd', NULL, 0, "To test schedule_delay", 0 }, + { "mu_trace", 'x', NULL, 0, "To test mutrace", 0 }, + { "all", 'a', NULL, 0, "To test all", 0 }, + { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help", 0 }, + {}, +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + (void)arg; + switch (key) { + case 'a': + env.sar_test = true; + env.cs_delay_test = true; + env.mq_delay_test = true; + env.preempt_test = true; + env.sc_delay_test = true; + env.schedule_test = true; + break; + case 's': + env.sar_test = true; + break; + case 'c': + env.cs_delay_test = true; + break; + case 'S': + env.sc_delay_test = true; + break; + case 'm': + env.mq_delay_test = true; + break; + case 'p': + env.preempt_test = true; + break; + case 'd': + env.schedule_test = true; + break; + case 'x': + env.mutrace_test = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +void *schedule_stress_test(void *arg) { + (void)arg; + while (1) { + sched_yield(); // 调度函数 + } + return NULL; +} + +void start_schedule_stress_test(int num_threads) { + pthread_t *threads = malloc(num_threads * sizeof(pthread_t)); + for (int i = 0; i < num_threads; i++) { + pthread_create(&threads[i], NULL, schedule_stress_test, NULL); + } + for (int i = 0; i < num_threads; i++) { + pthread_join(threads[i], NULL); + } + free(threads); +} + +void *func(void *arg) +{ + (void)arg; + int tpid; + tpid = gettid(); + printf("新线程pid:%d,睡眠3s后退出\n",tpid); + sleep(3); + printf("新线程退出\n"); + return NULL; +} + +void input_pid() { + int stop; + int pid = getpid(); + printf("test_proc进程的PID:【%d】\n", pid); + printf("输入任意数字继续程序的运行:"); + scanf("%d", &stop); // 使用时将其取消注释 + printf("程序开始执行...\n"); + printf("\n"); +} + +int main(int argc, char **argv){ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + if(env.sar_test){ + printf("SAR_TEST----------------------------------------------\n"); + //SAR功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程,持续15秒,观察加压前后的变化。 + char *argvv[] = { "/usr/bin/stress-ng", "--hdd", "4", "--hdd-opts", "wr-seq,rd-seq", "--io", "4", "--timeout", "15s", "--metrics-brief", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("SAR功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程和4个UDP网络操作线程,持续15秒,观察加压前后的变化\n"); + printf("执行指令 stress-ng --hdd 4 --hdd-opts wr-seq,rd-seq --io 4 --udp 4 --timeout 15s --metrics-brief\n"); + execve("/usr/bin/stress-ng", argvv, envp); + perror("execve"); + printf("\n"); + } + + if(env.cs_delay_test){ + printf("CS_DELAY_TEST----------------------------------------------\n"); + //CS_DELAY功能测试逻辑:无限循环的线程函数,不断调用 sched_yield() 来放弃 CPU 使用权,模拟高调度负载。 + start_schedule_stress_test(10); // 创建10个线程进行调度压力测试 + } + + if(env.sc_delay_test){ + printf("SC_DELAY_TEST----------------------------------------------\n"); + //SC_DELAY功能测试逻辑:创建多个系统调用,观察其变化 + const int num_iterations = 1000000; // 系统调用的迭代次数 + for (int i = 0; i < num_iterations; i++) { + getpid(); // 获取进程ID + getppid(); // 获取父进程ID + time(NULL); // 获取当前时间 + syscall(SYS_gettid); // 获取线程ID + } + printf("系统调用压力测试完成。\n"); + } + + if(env.mq_delay_test){ + /*mq_delay的测试代码*/ + input_pid(); // 在mq_delay_test中调用 + system("./sender & ./receiver"); + sleep(60); + system("^Z"); + } + + if(env.preempt_test){ + printf("PREEMPT_TEST----------------------------------------------\n"); + //PREEMPT功能测试逻辑:无限循环的线程函数,不断调用 sched_yield() 来放弃 CPU 使用权,模拟高调度负载。 + start_schedule_stress_test(10); // 创建10个线程进行调度压力测试 + } + + if(env.schedule_test){ + printf("SCHEDULE_TEST----------------------------------------------\n"); + // 调度延迟测试逻辑:创建线程执行 sysbench --threads=32 --time=10 cpu run,观察加压前后的变化 + char *argvv[] = { "/usr/bin/sysbench", "--threads=32", "--time=10", "cpu", "run", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("调度延迟测试逻辑:\n"); + printf("执行指令 sysbench --threads=32 --time=10 cpu run\n"); + execve("/usr/bin/sysbench", argvv, envp); + perror("execve"); + printf("\n"); + } + + if(env.mutrace_test){ + printf("MUTRACE_TEST----------------------------------------------\n"); + //MUTRACE功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程,持续15秒,观察加压前后的变化。 + char *argvv[] = { "/usr/bin/stress-ng", "--hdd", "4", "--hdd-opts", "wr-seq,rd-seq", "--io", "4", "--timeout", "15s", "--metrics-brief", NULL }; + char *envp[] = { "PATH=/bin", NULL }; + printf("MUTRACE功能测试逻辑:系统上执行混合压力测试,包括4个顺序读写硬盘线程、4个IO操作线程和4个UDP网络操作线程,持续15秒,观察加压前后的变化\n"); + printf("执行指令 stress-ng --hdd 4 --hdd-opts wr-seq,rd-seq --io 4 --udp 4 --timeout 15s --metrics-brief\n"); + execve("/usr/bin/stress-ng", argvv, envp); + perror("execve"); + printf("\n"); + } + + return 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile index 2c7d12a2a..d36f59600 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile @@ -142,7 +142,7 @@ $(CONTROLLER): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(WORKTOOL): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(call msg,BINARY,$@) - $(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + $(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -lpthread -o $@ @mkdir -p $(OUTPUT)/data @[ -f $(OUTPUT)/data/offcpu_stack.txt ] || touch $(OUTPUT)/data/offcpu_stack.txt @@ -162,4 +162,4 @@ SUCCESS_MESSAGE: .DELETE_ON_ERROR: # keep intermediate (.skel.h, .bpf.o, etc) targets -.SECONDARY: \ No newline at end of file +.SECONDARY: diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md index 4626ce028..5ec41e983 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md @@ -69,6 +69,14 @@ tools文件夹中的eBPF程序是按照进程生命周期中数据的类型分 | syscall_image | 对进程的系统调用序列进行画像 | | schedule_image | 对进程的调度信息进行画像 | +### 4.1 syscall_image使用说明 +[syscall_image使用说明](docs/Syscall_image工具使用说明.md) +
+ +### 4.2 keytime_image使用说明 +[keytime_image使用说明](docs/keytime_image工具使用说明.md) +
+ ## 五、基于 Prometheus 和 Grafana 的可视化平台 基于 Prometheus 和 Grafana 的可视化平台框架图: diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c index 68d8f29d5..b608d77be 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/bpf/syscall_image.bpf.c @@ -23,9 +23,12 @@ #include "proc_image.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; - +#define MAX_NODENAME_LEN 64 const volatile pid_t ignore_tgid = -1; +const volatile char hostname[MAX_NODENAME_LEN] = ""; const int key = 0; +pid_t pre_target_pid = -1;//上一个监测的进程; +int pre_target_tgid = -1;//上一个监测的进程组; struct { __uint(type, BPF_MAP_TYPE_ARRAY); @@ -46,14 +49,69 @@ struct { __uint(max_entries,256 * 10240); } syscall_rb SEC(".maps"); +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value,struct container_id); +}container_id_map SEC(".maps"); + +struct container_id{ + char container_id[20]; +}; + +struct data_t { + char nodename[MAX_NODENAME_LEN]; +}; + +static bool is_container_task(const volatile char hostname[MAX_NODENAME_LEN]){ + struct task_struct *task; + struct nsproxy *ns; + struct uts_namespace *uts; + struct data_t data = {}; + // 获取当前任务的 task_struct + task = (struct task_struct *)bpf_get_current_task(); + // 获取 nsproxy + bpf_probe_read_kernel(&ns, sizeof(ns), &task->nsproxy); + if (!ns) { + return false; + } + // 获取 uts_namespace + bpf_probe_read_kernel(&uts, sizeof(uts), &ns->uts_ns); + if (!uts) { + return false; + } + // 读取主机名 + bpf_probe_read_kernel_str(&data.nodename, sizeof(data.nodename), uts->name.nodename); + // 打印主机名 + bool is_equal = true; + for(int i = 0;isc_func) + if(!sc_ctrl || !sc_ctrl->sc_func) return 0; - + if(sc_ctrl->is_container) + if(!is_container_task(hostname)) + return 0; pid_t pid = bpf_get_current_pid_tgid(); int tgid = bpf_get_current_pid_tgid() >> 32; @@ -80,14 +138,14 @@ int sys_enter(struct trace_event_raw_sys_enter *args) if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->record_syscall[syscall_seq->count] = (int)args->id; } - syscall_seq->count ++; + syscall_seq->count++; }else if (syscall_seq->count <= MAX_SYSCALL_COUNT-1 && syscall_seq->count > 0 && syscall_seq->record_syscall+syscall_seq->count <= syscall_seq->record_syscall+(MAX_SYSCALL_COUNT-1)){ if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->record_syscall[syscall_seq->count] = (int)args->id; } - syscall_seq->count ++; + syscall_seq->count++; } } } @@ -102,7 +160,9 @@ int sys_exit(struct trace_event_raw_sys_exit *args) sc_ctrl = bpf_map_lookup_elem(&sc_ctrl_map,&key); if(!sc_ctrl || !sc_ctrl->sc_func) return 0; - + if(sc_ctrl->is_container) + if(!is_container_task(hostname)) + return 0; pid_t pid = bpf_get_current_pid_tgid(); int tgid = bpf_get_current_pid_tgid() >> 32; @@ -132,6 +192,20 @@ int sys_exit(struct trace_event_raw_sys_exit *args) syscall_seq->max_delay = this_delay; if(syscall_seq->min_delay==0 || this_delaymin_delay) syscall_seq->min_delay = this_delay; + //策略切换,首次数据不记录; + if(sc_ctrl->target_tgid ==-1 && sc_ctrl->target_pid ==pid && sc_ctrl->target_pid != pre_target_pid){ + syscall_seq->sum_delay = 0; + syscall_seq->count = 0; + pre_target_pid = sc_ctrl->target_pid;//更改pre_target_pid; + return 0; + } + if(sc_ctrl->target_tgid !=-1 && sc_ctrl->target_tgid ==tgid && sc_ctrl->target_tgid != pre_target_tgid){ + syscall_seq->sum_delay = 0; + syscall_seq->count = 0; + pre_target_tgid = sc_ctrl->target_tgid;//更改pre_target_pid; + return 0; + } + if((sc_ctrl->target_tgid==-1 && (sc_ctrl->target_pid==-1 || pid==sc_ctrl->target_pid)) || (sc_ctrl->target_tgid!=-1 && tgid == sc_ctrl->target_tgid)){ syscall_seq->proc_count += syscall_seq->count; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c index 121a8fdb1..718feda25 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/controller.c @@ -49,6 +49,7 @@ static struct env { bool enable_lock; bool enable_syscall; bool enable_schedule; + bool is_container; } env = { .usemode = 0, .pid = -1, @@ -68,6 +69,7 @@ static struct env { .enable_lock = false, .enable_syscall = false, .enable_schedule = false, + .is_container = false, }; const char argp_program_doc[] ="Trace process to get process image.\n"; @@ -78,6 +80,7 @@ static const struct argp_option opts[] = { { "finish", 'f', NULL, 0, "Finish to run eBPF tool" }, { "pid", 'p', "PID", 0, "Process ID to trace" }, { "tgid", 'P', "TGID", 0, "Thread group to trace" }, + { "containerproc", 'o', NULL, 0, "Thread of containerproc to trace" }, { "cpuid", 'c', "CPUID", 0, "Set For Tracing per-CPU Process(other processes don't need to set this parameter)" }, { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, { "myproc", 'm', NULL, 0, "Trace the process of the tool itself (not tracked by default)" }, @@ -143,6 +146,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'm': env.enable_myproc = true; break; + case 'o': + env.is_container = true; + break; case 'r': env.enable_resource = true; break; @@ -201,7 +207,7 @@ int deactivate_mode(){ } if(env.enable_syscall){ - struct sc_ctrl sc_ctrl = {false,false,-1,-1,0}; + struct sc_ctrl sc_ctrl = {false,false,false,-1,-1,0}; err = update_sc_ctrl_map(sc_ctrl); if(err < 0) return err; } @@ -257,7 +263,7 @@ int main(int argc, char **argv) } if(env.enable_syscall){ - struct sc_ctrl sc_ctrl = {true,env.enable_myproc,env.pid,env.tgid,env.syscalls}; + struct sc_ctrl sc_ctrl = {true,env.enable_myproc, env.is_container,env.pid,env.tgid,env.syscalls}; err = update_sc_ctrl_map(sc_ctrl); if(err < 0) return err; } diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" new file mode 100644 index 000000000..6eb296a60 --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/Syscall_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -0,0 +1,162 @@ +# Syscall_image工具使用说明: + +syscall_image工具是用于监测系统中系统调用时延的工具, 该工具可以监测特定线程或线程组调用系统调用情况,统计输出该线程系统调用时延(系统调用平均时延、系统调用最大时延、系统调用最大时延),并将系统调用序列号顺序输出,可用于解决进程出现异常后的问题排查; + +## 1.代码逻辑图: + +![](images/syscall_image.jpg) + +## 2.使用方法: + +### 2.1.编译 + +首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作; + +```shell +sudo make +``` + +编译成功后,会生成两个可执行文件`proc_image`,`controller`, 后面的数据监测均围绕这两个可执行文件; + +### 2.2.挂载 + +在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件, + +通过`proc_image -h`命令可查看进程画像的使用方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -h +Usage: proc_image [OPTION...] +Trace process to get process image. + + -a, --all Attach all eBPF functions(but do not start) + -k, --keytime Attach eBPF functions about keytime(but do not + start) + -l, --lock Attach eBPF functions about lock(but do not start) + + -r, --resource Attach eBPF functions about resource usage(but do + not start) + -s, --syscall Attach eBPF functions about syscall sequence(but + do not start) + -S, --schedule Attach eBPF functions about schedule (but do not + start) + -?, --help Give this help list + --usage Give a short usage message +``` + +挂载syscall_image工具相关挂载点; + +```shell +sudo ./proc_image -s +``` + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -s +libbpf: loading object 'syscall_image_bpf' from buffer +libbpf: elf: section(2) .symtab, size 1128, link 1, flags 0, type=2 +libbpf: elf: section(3) tracepoint/raw_syscalls/sys_enter, size 1064, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/raw_syscalls/sys_enter': found program 'sys_enter' at insn offset 0 (0 bytes), code size 133 insns (1064 bytes) +libbpf: elf: section(4) tracepoint/raw_syscalls/sys_exit, size 1216, link 0, flags 6, type=1 +.... +libbpf: prog 'sched_process_exit': relo #0: [51] struct task_struct.pid (0:85 @ offset 2456) +libbpf: prog 'sched_process_exit': relo #0: matching candidate #0 [80] struct task_struct.pid (0:85 @ offset 2456) +libbpf: prog 'sched_process_exit': relo #0: patched insn #9 (ALU/ALU64) imm 2456 -> 2456 +libbpf: unpinned map 'sc_ctrl_map' from '/sys/fs/bpf/proc_image_map/sc_ctrl_map' +libbpf: pinned map '/sys/fs/bpf/proc_image_map/sc_ctrl_map' + + +``` + +### 2.3.控制策略: + +可使用`controller`工具进行策略控制,策略切换。 + +重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +通过`controller -h`命令可查看进程画像策略切换方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./controller -h +Usage: controller [OPTION...] +Trace process to get process image. + + -a, --activate Set startup policy of proc_image tool + -c, --cpuid=CPUID Set For Tracing per-CPU Process(other processes + don't need to set this parameter) + -d, --deactivate Initialize to the original deactivated state + -f, --finish Finish to run eBPF tool + -k, --keytime=KEYTIME Collects keytime information about + processes(0:except CPU kt_info,1:all kt_info,any 0 + or 1 when deactivated) + -l, --lock Collects lock information about processes + -m, --myproc Trace the process of the tool itself (not tracked + by default) + -p, --pid=PID Process ID to trace + -P, --tgid=TGID Thread group to trace + -r, --resource Collects resource usage information about + processes + -s, --syscall=SYSCALLS Collects syscall sequence (1~50) information about + processes(any 1~50 when deactivated) + -S, --schedule Collects schedule information about processes + (trace tool process) + -t, --time=TIME-SEC Max Running Time(0 for infinite) + -?, --help Give this help list + --usage Give a short usage message + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. +``` + +使用syscall_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| ---- | ------------------------------------------------------------ | +| -s | syscall_image工具 后加参数(syscalls)用于控制每次输出系统调用次数;例:-s 10; | +| -a | 激活 syscall_image工具; | +| -p | 指定目标线程; | +| -P | 指定目标线程组; | +| -c | 指定检测cpu; | +| -t | 指定检测时间; | + +通过以下指令更改控制策略: + +* 激活对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -p 1111 -c 0 -a + ``` + +* 激活对线程组1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -P 1111 -c 0 -a + ``` + +* 关闭对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -p 1111 -c 0 -d + ``` + +* 关闭对线程1111的系统调用进行监测; + + ```shell + sudo ./controller -s 10 -P 1111 -c 0 -d + ``` + +* 关闭进程画像: + + ```shell + sudo ./controller -f + ``` + +### 2.3.数据监测: + +当更改了使用策略后,将对数据进行检测: + +![](images/syscall_image数据监测.png) + + + + + diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png new file mode 100644 index 000000000..0544a55d5 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720660193068.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png new file mode 100644 index 000000000..fb1a5f91a Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720683501899.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png new file mode 100644 index 000000000..28d36bb4c Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720692895341.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png new file mode 100644 index 000000000..fd1e31a5f Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693018279.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png new file mode 100644 index 000000000..219da5fcb Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693526887.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png new file mode 100644 index 000000000..d22bb6259 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693579439.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png new file mode 100644 index 000000000..787086983 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720693616700.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png new file mode 100644 index 000000000..741003f63 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720694420680.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png new file mode 100644 index 000000000..7afa54628 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/1720696786685.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png new file mode 100644 index 000000000..f2a286dc3 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime2.png differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..9e88e6db5 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\345\217\257\350\247\206\345\214\226.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" new file mode 100644 index 000000000..a5a2be566 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/keytime_image\346\225\260\346\215\256\347\233\221\346\265\213.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..89f8ddbad Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/lock_image\345\217\257\350\247\206\345\214\226.png" differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png new file mode 100644 index 000000000..1cfd38889 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_output.png differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png new file mode 100644 index 000000000..9d989c8ea Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_delay_process.png differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..6bdfa6564 Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/schedule_image\345\217\257\350\247\206\345\214\226.png" differ diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg new file mode 100644 index 000000000..3b4ed2082 Binary files /dev/null and b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image.jpg differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" new file mode 100644 index 000000000..a049df1db Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\345\217\257\350\247\206\345\214\226.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" new file mode 100644 index 000000000..e80dba39a Binary files /dev/null and "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/images/syscall_image\346\225\260\346\215\256\347\233\221\346\265\213.png" differ diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" new file mode 100644 index 000000000..5b49b20ef --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/keytime_image\345\267\245\345\205\267\344\275\277\347\224\250\350\257\264\346\230\216.md" @@ -0,0 +1,185 @@ +# keytime_image工具使用说明: + +keytime_image主要是用于采集进程创建与退出、进程以什么方式创建(fork、vfork、clone)、进程何时上cpu何时下cpu等信息。本工具可用于监测进程全生命周期,并获取对应的数据; + +## 1.代码逻辑图: + +![](images/keytime2.png) + +## 2.使用方法: + +### 2.1.编译 + +首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作; + +```shell +sudo make +``` + +编译成功后,会生成两个可执行文件`proc_image`,`controller`, 后面的数据监测均围绕这两个可执行文件; + +### 2.2.挂载 + +在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件, + +通过`proc_image -h`命令可查看进程画像的使用方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -h +Usage: proc_image [OPTION...] +Trace process to get process image. + + -a, --all Attach all eBPF functions(but do not start) + -k, --keytime Attach eBPF functions about keytime(but do not + start) + -l, --lock Attach eBPF functions about lock(but do not start) + + -r, --resource Attach eBPF functions about resource usage(but do + not start) + -s, --syscall Attach eBPF functions about syscall sequence(but + do not start) + -S, --schedule Attach eBPF functions about schedule (but do not + start) + -?, --help Give this help list + --usage Give a short usage message +``` + +挂载syscall_image工具相关挂载点; + +```shell +sudo ./proc_image -k +``` + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./proc_image -k +libbpf: loading object 'keytime_image_bpf' from buffer +libbpf: elf: section(2) .symtab, size 2736, link 1, flags 0, type=2 +libbpf: elf: section(3) tracepoint/syscalls/sys_enter_execve, size 17888, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/syscalls/sys_enter_execve': found program 'tracepoint__syscalls__sys_enter_execve' at insn offset 0 (0 bytes), code size 2236 insns (17888 bytes) +libbpf: elf: section(4) tracepoint/syscalls/sys_exit_execve, size 496, link 0, flags 6, type=1 +libbpf: sec 'tracepoint/syscalls/sys_exit_execve': found program 'tracepoint__syscalls__sys_exit_execve' at insn offset 0 (0 bytes), code size 62 insns (496 bytes) +... +libbpf: prog 'sched_switch': relo #3: patched insn #37 (ALU/ALU64) imm 2460 -> 2460 +libbpf: unpinned map 'kt_ctrl_map' from '/sys/fs/bpf/proc_image_map/kt_ctrl_map' +libbpf: pinned map '/sys/fs/bpf/proc_image_map/kt_ctrl_map' +libbpf: elf: symbol address match for 'fork' in '/usr/lib/x86_64-linux-gnu/libc.so.6': 0xefac0 + + +``` + +### 2.3.控制策略: + +可使用`controller`工具进行策略控制,策略切换。 + +重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +通过`controller -h`命令可查看进程画像策略切换方法: + +```shell +xhb@1:~/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image$ sudo ./controller -h +Usage: controller [OPTION...] +Trace process to get process image. + + -a, --activate Set startup policy of proc_image tool + -c, --cpuid=CPUID Set For Tracing per-CPU Process(other processes + don't need to set this parameter) + -d, --deactivate Initialize to the original deactivated state + -f, --finish Finish to run eBPF tool + -k, --keytime=KEYTIME Collects keytime information about + processes(0:except CPU kt_info,1:all kt_info,any 0 + or 1 when deactivated) + -l, --lock Collects lock information about processes + -m, --myproc Trace the process of the tool itself (not tracked + by default) + -p, --pid=PID Process ID to trace + -P, --tgid=TGID Thread group to trace + -r, --resource Collects resource usage information about + processes + -s, --syscall=SYSCALLS Collects syscall sequence (1~50) information about + processes(any 1~50 when deactivated) + -S, --schedule Collects schedule information about processes + (trace tool process) + -t, --time=TIME-SEC Max Running Time(0 for infinite) + -?, --help Give this help list + --usage Give a short usage message + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. +``` + +使用keytime_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| ---- | ------------------------------------------------------------ | +| -k | keytime_image工具 后加参数(0或1)用于控制是否监测进程上下CPU的时间点;例:-k 0; | +| -a | 激活 keytime_image工具; | +| -p | 指定目标线程; | +| -P | 指定目标线程组; | +| -t | 指定检测时间; | + +通过以下指令更改控制策略: + +- 激活对线程3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 0 -p 3184 -a + KEYTIME ------------------------------------------- + TIME PID EVENT ARGS/RET/OTHERS + 10:02:44 3184 forkP_enter child_pid:208024 + 10:02:44 3184 vforkP_enter child_pid:208024 + 10:02:45 3184 vforkP_exit child_pid:208024 + 10:02:45 3184 forkP_enter child_pid:208026 + 10:02:45 3184 vforkP_enter child_pid:208026 + 10:02:45 3184 vforkP_exit child_pid:208026 + 10:02:45 3184 forkP_enter child_pid:208028 + ``` + +- 激活对线程组3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 1 -P 3184 -a + KEYTIME ----------------------------------------------------------------- + TIME TGID PID EVENT ARGS/RET/OTHERS + 10:23:39 3184 3184 offCPU offcpu_time:253480629547342 + 10:23:39 3184 3184 onCPU oncpu_time:253480629595213 + 10:23:39 3184 3188 onCPU oncpu_time:253480630262378 + 10:23:39 3184 3188 offCPU offcpu_time:253480630274211 + 10:23:39 3184 3186 onCPU oncpu_time:253480630292716 + 10:23:39 3184 3188 onCPU oncpu_time:253480630301954 + 10:23:39 3184 3186 offCPU offcpu_time:253480630302214 + 10:23:39 3184 3188 offCPU offcpu_time:253480630307033 + 10:23:39 3184 3187 onCPU oncpu_time:253480630342231 + 10:23:39 3184 3186 onCPU oncpu_time:253480630350606 + 10:23:39 3184 3186 offCPU offcpu_time:253480630357990 + 10:23:39 3184 3188 onCPU oncpu_time:253480630397210 + + ``` + +- 关闭对线程3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 0 -p 3184 -d + ``` + +- 关闭对线程组3184关键时间点的数据收集; + + ```shell + sudo ./controller -k 1 -P 3184 -d + ``` + +- 关闭进程画像: + + ```shell + sudo ./controller -f + ``` + +### 2.3.数据监测: + +当更改了使用策略后,将对数据进行检测: + +![](images/keytime_image数据监测.png) + + + + + diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md index b43b75b16..2ed10bd7e 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/proc_image_vis_guide.md @@ -105,7 +105,10 @@ TIME READ/s WRITE/s FSYNC/s OPEN/s CREATE/s - 执行如下指令开始采集数据以及相关处理: ``` - ./data-visual collect /home/zhang/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image -r -p 16279 + // 终端1:挂载eBPF内核态函数,但是处于失活状态(不进行数据的采集) + ./data-visual collect /home/zhang/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image -r + // 终端2:激活eBPF内核态函数 + ./controller -a -r -p 16279 ``` - 个人喜欢在主机上进行可视化设置,这样方便快捷,所以在主机上打开网址http://192.168.109.150:8090/metrics (其中192.168.109.150是虚拟机网络接口的IPv4地址),可以看到暴露在http网页中的数据: diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md new file mode 100644 index 000000000..84cee2c22 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/schedule_delay_usemethod.md @@ -0,0 +1,152 @@ +# Schedule_delay工具使用说明 + +​ Schedule_delay工具是用来检测系统中调度延时的工具,该工具可以监测特定线程或线程组、以及整个系统的调度延迟(最大、最小、平均)。即从一个任务具备运行的条件,到真正执行(获得 CPU 的执行权)的这段时间。实时观测该指标可以帮助我们了解到当前操作系统的负载。 + +## 原理讲解 + +​ 该工具使用六个map来控制工具的行为和存储数据与用户态交互。分别为: + +- **ARRAY MAP**`sched_ctrl_map`:作为调度控制信息的存储,策略上将该map pin到bpf文件系统,即可在用户态更新该map,与内核态交互。其value为`struct sched_ctrl`: + +```c +struct sched_ctrl { + bool sched_func; // 控制是否记录调度相关事件 + pid_t target_pid; // 目标进程的 PID + int target_cpu_id; // 目标 CPU 的 ID + pid_t target_tgid; // 目标线程组的 TGID +}; +``` + +​ 通过这些控制信息,可以控制该工具在内核态的行为。 + +- **HASH MAP**`enable add`:用来记录进程是否被调度过,以避免重复计数或错误计数。 + +- **HASH MAP**`proc_schedule`:用于存储和跟踪每个进程的调度事件信息,key为进程的id和cpu_id,value为` struct schedule_event`: + +```c +struct schedule_event { + pid_t pid; // 进程ID + int tgid; // 线程组ID + int prio; // 进程优先级 + u32 count; // 调度次数 + u64 enter_time; // 进入调度的时间 + u64 sum_delay; // 累计调度延迟 + u64 max_delay; // 最大调度延迟 + u64 min_delay; // 最小调度延迟 +}; +``` + +- **HASH MAP**`target_schedule`:用于存储指定**线程**的调度信息,value为` struct schedule_event`,在用户态通过更新`sched_ctrl_map`指定要记录的线程。 +- **HASH MAP**`tg_schedule`:用于存储指定**线程组**的调度信息,value为` struct schedule_event`,在用户态通过更新`sched_ctrl_map`指定要记录的线程组。 + +- **ARRAY MAP**`sys_schedule`:用于记录整个系调度延时,value为`struct sum_schedule:` + +```c +struct sum_schedule { + long long unsigned int sum_count; // 总调度次数 + long long unsigned int sum_delay; // 总调度延迟 + long long unsigned int max_delay; // 最大调度延迟 + long long unsigned int min_delay; // 最小调度延迟 +}; +``` + +**代码逻辑图如下:** + +![](images/schedule_delay_process.png) + +## 2.使用方法 + +### 2.1编译 + +​ 首先在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下进行编译操作。 + +```shell +make +``` + +​ 编译成功后,会生成两个可执行文件`proc_image`,`controller`。 + +### 2.2挂载 + +​ 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `proc_image`可执行文件。 + +​ 使用-S选项挂载检测调度延迟的几个挂载点: + +```shell +sudo ./proc_image -S +``` + +``` +sudo ./proc_image -S +...... +libbpf: map 'sched_ctrl_map': created successfully, fd=3 +libbpf: map 'proc_schedule': created successfully, fd=4 +libbpf: map 'enable_add': created successfully, fd=5 +libbpf: map 'target_schedule': created successfully, fd=6 +libbpf: map 'tg_schedule': created successfully, fd=7 +libbpf: map 'sys_schedule': created successfully, fd=8 +libbpf: map 'schedule.rodata': created successfully, fd=9 +libbpf: pinned map '/sys/fs/bpf/proc_image_map/sched_ctrl_map' +``` + +​ 此时用户态内核态交互的**sched_ctrl_map**已经pin上,现在我们可以通过controller执行文件来更改输出策略。 + +### 2.3用户态控制交互 + +​ 可使用`controller`工具和内核态进行交互,控制输出。 + +​ 重启一个终端, 在`lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image`目录下运行 `controller`可执行文件。 + +​ 使用schedule_image工具的不同参数,控制该工具的使用策略: + +| 参数 | | +| :--: | ---------------------- | +| -S | 使用schedule_image工具 | +| -a | 激活schedule_image工具 | +| -p | 指定目标线程 | +| -P | 指定目标线程组 | +| -c | 指定检测CPU | +| -t | 指定检测时间 | + +通过以下指令更改控制策略: + +- 激活对线程21416的调度延迟进行监测: + + ``` + sudo ./controller -S -a -p 21416 + ``` + +- 激活对线程组21416的调度延迟进行监测: + + ``` + sudo ./controller -s -a -p 21416 + ``` + +- 关闭对线程21416的调度延迟进行监测: + + ``` + sudo ./controller -S -d -p 21416 + ``` + +- 关闭对线程组21416的调度延迟进行监测: + + ``` + sudo ./controller -S -d -P 21416 + ``` + +- 检测整个系统的调度延迟: + + ``` + sudo ./controller -S -a + ``` + +- 关闭进程画像工具: + + ``` + sudo ./controller -f + ``` + +#### 2.4 输出 + +​ 对线程21416的调度延迟进行监测,可以看到指定进程的调度延迟信息,包括该线程最大、最小、平均的调度延迟(P_xxx_DELAY),以及整个系统的最大、最小、平均的调度延迟(S_xxx_DELAY)。 +![](images/schedule_delay_output.png) \ No newline at end of file diff --git "a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" new file mode 100644 index 000000000..982dc08a1 --- /dev/null +++ "b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/docs/\346\265\213\350\257\225\344\270\216\346\225\260\346\215\256\345\205\263\350\201\224.md" @@ -0,0 +1,650 @@ +# 数据真实性测试 + +本次测试工作主要包括: + +* 针对进程画像的每个功能,涉及测试用例,在逻辑上验证ebpf程序的正确性; +* 学习使用LTTng工具,通过该工具实现相同或相似功能,将测试用例在使用在LTTng工具上,验证进程画像相应工具数据的正确性; +* 学习使用trace compass可视化性能分析工具,将LTTng监测出的数据放在trace compass上查看进程流程,并根据LTTng工具提供的技术文档来梳理不同数据见的关系; +* 输出数据关联性文档; + +## 0.准备工作: + +学习使用LTTng 来监测数据; + +学习将LTTng生成的文件在compass上跑出来,并分析; + +### 0.1.LTTng学习使用: + +[linux下 LTTng使用详细说明_no package 'liburcu' found-CSDN博客](https://blog.csdn.net/mao_hui_fei/article/details/120654095) + +```c +lttng create my-session +lttng enable-event --kernel sched_switch +lttng add-context --kernel --type pid --type tid +lttng track --kernel --pid +lttng track --kernel --tid +lttng start +# 运行你想监控的进程或线程 +lttng stop +lttng view +lttng destroy +``` + +### 0.2.trace compass + +[Trace Compass User Guide - LTTng Kernel Analysis (eclipse.org)](https://archive.eclipse.org/tracecompass/doc/stable/org.eclipse.tracecompass.doc.user/LTTng-Kernel-Analysis.html#Control_Flow_View) + +![1720660193068](./images/1720660193068.png) + +## 1.keytime_image + +keytime_image 是一个用于捕获进程关键时间点的工具,聚焦于进程何时创建(fork,vfork, pthread_create),何时上CPU,何时下CPU,何时执行execve等。通过ebpf技术将其挂载固定的挂载点上,并在进程发生以上几种行为时进行数据收集,并对数据进行处理,可以通过数据描绘出进程在关键时间点上的行为; + +本次测试以及数据关联工作分为三部分: + +* keytime_image工具逻辑正确性的测试; +* keytime_image工具数据正确性的测试; +* keytime_image工具数据的关联关系,以及原理梳理; + +### 1.1 测试用例 test_keytime + +为了完成以上三步测试与数据关联工作,需要涉及一个测试用例,使用该测试用例可以测试keytime_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断keytime_image数据正确性; + +#### 1.1.1 测试用例设计思路 + +测试用例主要目的是为了模拟一个进程的一组行为,包括keytime_image所检测的关键时间点,从而查看keytime_image在进程行为观测上是否正确无误; + +测试用例主要思路 + +* 对测试用例进程进行cpu绑核,提高优先级,保证当前cpu仅运行一个或一组进程; +* 分别通过fork,vfork创建新进程; +* 通过pthread_create创建3个不同的线程,并将三个线程绑核,保证整个线程组在同一个cpu上运行; + * 线程1:执行cpu密集型任务; + * 线程2:执行mem密集型任务; + * 线程3:执行io密集型任务; + * 注意:这三个线程会存在抢占cpu现象,这是由于三者的优先级一致; +* 通过sleep(3),让当前进程从绑定的cpu上下来,当睡眠结束,进程则会上cpu; +* 通过execve();,让测试程序执行指定的操作,方便测试keytime_image能否监测到进程执行execve; +* 如果execve()执行失败,则执行exit来终止进程; + +### 1.1.2 测试脚本: + +在通过测试用例对keytime_image工具进行逻辑正确性的验证之后,需要验证keytime_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现keytime_image工具和lttng同时监测测试用例的数据,并将数据导出; + +### 1.2 结果分析: + +通过测试用例和测试脚本,对keytime_image工具的逻辑正确性和数据正确性进行了评估,本小节将针对输出的数据进行正确性检测; + +#### 1.2.1.逻辑正确性: + +通过脚本,可以获取到keytime_image监测测试用例进程的全过程;以下是输出的数据: + +```shell +KEYTIME ------------------------------------------------------------------------------------------------- +TIME PID EVENT ARGS/RET/OTHERS +14:46:20 125532 onCPU oncpu_time:50032701454776 +14:46:20 125532 offCPU offcpu_time:50032701499005 +14:46:22 125532 onCPU oncpu_time:50035264147470 +14:46:22 125532 forkP_enter child_pid:127606 +14:46:22 125532 offCPU offcpu_time:50035264461066 +14:46:24 125532 forkP_exit child_pid:127606 +14:46:24 125532 onCPU oncpu_time:50037265018542 +14:46:24 125532 offCPU offcpu_time:50037265183095 +14:46:24 125532 vforkP_enter child_pid:128805 +14:46:26 125532 vforkP_exit child_pid:128805 +14:46:26 125532 onCPU oncpu_time:50039273327870 +14:46:26 125532 createT_enter child_pid:129392 +14:46:26 125532 createT_enter child_pid:129393 +14:46:26 125532 createT_enter child_pid:129394 +14:46:26 125532 offCPU offcpu_time:50039273813650 +14:46:29 125532 createT_exit child_pid:129393 +14:46:29 125532 createT_exit child_pid:129392 +14:46:29 125532 onCPU oncpu_time:50042307545368 +14:46:29 125532 offCPU offcpu_time:50042307567649 +14:46:29 125532 onCPU oncpu_time:50042411845305 +14:46:29 125532 offCPU offcpu_time:50042411869110 +14:46:29 125532 createT_exit child_pid:129394 +14:46:29 125532 onCPU oncpu_time:50042412175342 +14:46:29 125532 offCPU offcpu_time:50042412370717 +14:46:32 125532 onCPU oncpu_time:50045412482772 +14:46:32 125532 exec_enter /bin/ls -l +14:46:32 125532 offCPU offcpu_time:50045412900824 +14:46:32 125532 onCPU oncpu_time:50045413164136 +14:46:32 125532 offCPU offcpu_time:50045413644970 +14:46:32 125532 onCPU oncpu_time:50045414001685 +14:46:32 125532 exec_exit 0 +14:46:32 125532 exit 0 +14:46:32 125532 offCPU offcpu_time:50045415933416 + +``` + +结合测试用例在执行特定操作时的时间节点输出,可以验证在特定时间点,keytime_image是否正确捕获测试进程的该行为: + +```shell +#PID125532绑定CPU4:√ +#PID125532 被绑定在以下cpu上: 4 +test_proc进程的TGID:125532 PID:125532 CPU_id:4 +输入任意数字继续程序的运行: +程序开始执行... + +KEYTIME_TEST----------------------------------------------- + +fork逻辑------------------------: +fork开始时间2024-07-11 14:46:22 +(fork)子进程pid:127606,睡眠2s后退出 +fork结束时间2024-07-11 14:46:24 +(fork)pid为127606的子进程退出 + +输入任意数字开始测试vfork:vfork逻辑------------------------: +vfork开始时间2024-07-11 14:46:24 +(vfork)子进程pid:128805,睡眠2s后退出 +vfork退出时间2024-07-11 14:46:26 +(vfork)pid为128805的子进程退出 + +输入任意数字开始测试 pthread_create:pthread_create逻辑------------------------: +pthread_create开始时间2024-07-11 14:46:26 + + #3.PID:129394 TGID:125532 + #PID129394绑定CPU4:√ + #PID129394 被绑定在以下cpu上: 4 + #IO密集型 + + #2.PID:129393 TGID:125532 + #PID129393绑定CPU4:√ + #PID129393 被绑定在以下cpu上: 4 + #MEM 密集型 + + #1.PID:129392 TGID:125532 + #PID129392绑定CPU4:√ + #PID129392 被绑定在以下cpu上: 4 + #CPU密集型 + #129393退出 + #129392退出 + #129394退出 +pthread_create结束时间2024-07-11 14:46:29 + +输入任意数字开始测试 上下cpu:进程上下CPU逻辑------------------------: +CPU sleep 开始时间2024-07-11 14:46:29 +CPU sleep 结束时间2024-07-11 14:46:32 +程序睡眠3s! + +输入任意数字开始测试 execve:execve开始时间2024-07-11 14:46:32 +execve逻辑------------------------: +total 60 +-rw-rw-r-- 1 xhb xhb 194 Jul 9 10:51 Makefile +drwxrwxr-x 5 xhb xhb 4096 Jul 11 14:46 ebpf +-rwxr-xr-x 1 xhb xhb 995 Jul 10 23:10 ebpf_test.sh +drwxrwx--- 18 xhb xhb 4096 Jul 11 14:46 lttng-traces +-rwxr-xr-x 1 xhb xhb 1913 Jul 10 23:02 test_keytime.sh +-rwxr-xr-x 1 root root 26256 Jul 11 04:43 test_proc_image +-rw-rw-r-- 1 xhb xhb 9493 Jul 11 04:45 test_proc_image.c +``` + +![1720683501899](./images/1720683501899.png) + +通过对比测试用例关键点的时间和行为,与keytime_image关键点的时间完全一致,且keytime_image可以观测到进程执行cpu、mem、io等行为;至此我们可以得出结论,keytime_image逻辑正确,功能可用; + +#### 1.2.2 数据正确性 + +通过将keytime_image获取的数据同lttng作对比,便可得到 进程的整体行为是否正确,数据获取是否正确,描述该行为需要哪些数据进行关联; + +这里通过多次测试(包括但不限于指定某一进程进行数据采集,指定某一个线程组进行数据采集,不指定固定进程或线程采集全部数据信息),均证明可以证明keytime_image获取到的数据是真实可靠且可以描绘进程关键时间点行为的。原始数据在这里: + +keytime_image测出的数据:[keytime_image测试数据详细](../data/output.log) + +lttng 测出的数据: +- [lttng 测试数据表](../data/keytime_test_data.csv) +- [lttng 测试数据详细](../data/trace_data.txt) + +### 1.3.关键数据点的关联关系 + +通过lttng检测到的数据以及在trace compass上可以看到进程的行为跟踪图,我们这里可以根据lttng所提供的关键数据点指导进程行为分析,从而将这些数据关联到一起。本小节将找到这些关键数据点,并依据内核中的香相关原理来指导进程行为分析。 + +keytime_image关注点在一个进程或线程,我们以125532进程为例,分析keytime_image观测到该进程的具体行为,该进程的行为众多,这里值分析典型行为:创建进程,上下cpu,执行execve,以及进程退出: + +#### 1.3.1 创建进程: + +由于lttng关于进程创建的跟踪点为 sched_process_fork 只能识别进程创建了子进程,不能识别到通过什么方式创建的,而keytime_image可以通过uprobe和uretpobe识别到时是fork、vfork还是pythreat_create创建的子进程或线程; + +##### 1.3.1.1 fork + +**【22:811111842】** + +- 行为:主体进程125532,fork出子进程127606 + +- 数据点:父进程的信息,子进程的信息; + + * parent_comm = "test_proc_image", parent_tid = 125532, parent_pid = 125532; + + * child_comm = "test_proc_image", child_tid = 127606,child_pid = 127606; + * cpu_id = 4; + +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态fork上,可以看到父进程fork开始和fork结束的时间;这一点相较于lttng关注到了用户态,更细节; + +![1720693018279](./images/1720693018279.png) + +##### 1.3.1.2 vfork + +**【24:811853001】** + +- 行为:主体进程125532 vfork 出子进程128805; + +- 数据点:父进程的信息,子进程的信息; + + - prev_comm = "test_proc_image", prev_tid = 125532,prev_prio = 20, prev_state = 1; + + - next_comm = "test_proc_image", next_tid = 127606, next_prio = 20; + +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态vfork上,可以看到父进程vfork开始和fork结束的时间;并且可以知道父进程是如何创建了子进程,这一点相较于lttng关注到了用户态,更细节; + +![1720692895341](./images/1720692895341.png) + +##### 1.3.1.3 pthread_create + +**【26:820321972】** + +- 行为:主体进程125532创建线程129392, +- 数据点:父进程的信息,子进程的信息; + * parent_comm = "test_proc_image", parent_tid = 125532, parent_pid = 125532; + * child_comm = "test_proc_image", child_tid = 129392,child_pid = 125532 +- 原理:lttng的挂载点在sched_process_fork上,记录了父进程的信息,子进程的信息,以及在哪个cpu上进行的fork;而我们的keytime_image则将挂载点放在了用户态上pthread_create,可以看到父进程pthread_create开始和fork结束的时间;并且可以知道父进程是如何创建了子进程,这一点相较于lttng关注到了用户态,更细节; + +![1720693526887](./images/1720693526887.png) + +![1720693579439](./images/1720693579439.png) + +![1720693616700](./images/1720693616700.png) + + + +#### 1.3.2 上下CPU: + +上下CPU是CPU调度时产生的进程行为,由于不同的调度策略会使不同的进程上下CPU,一般进程上下CPU是在linux内核函数中的`__schedule()`中实现的,具体是通过`__schedule()–>pick_next_task()`选择下一个要上CPU的进程,`__schedule()–>context_switch()`进行上下文切换,完成进程上下CPU操作; + +keytime_image和lttng都关注到`__schedule()`中的一个静态挂载点:`sched_switch`,此时cpu知道了哪个进程下cpu,哪个进程上cpu,故而该关键点处的数据便可以描绘出进程上下cpu的行为; + +![1720694420680](./images/1720694420680.png) + +测试用例中涉及到了很多上下CPU的情况,此处将分析主体进程创建的三个线程129392,129393,129394三者之间在CPU4上的切换情况。 + +![1720696786685](./images/1720696786685.png) + +**【26:820503574】** + +- 行为:主体进程125532让出cpu,子进程 129394上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 125532, prev_prio = 20, prev_state = 1,; + - next_comm = "test_proc_image", next_tid = 129394, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:823397729】** + +- 行为:线程129394让出cpu,子进程 129393上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 2; + - next_comm = "test_proc_image", next_tid = 129393, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:826032494】** + +- 行为:线程129393让出cpu,子进程 129394上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129393, prev_prio = 20, prev_state = 1; + - next_comm = "test_proc_image", next_tid = 129394, next_prio = 20 ; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:826933280】** + +- 行为:线程129394让出cpu,线程 111470上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 256; + - next_comm = "kworker/4:1", next_tid = 111470, next_prio = 20; +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +**【26:835640901】** + +- 行为:线程129394让出cpu,线程129392上CPU; +- 数据点:上CPU进程信息,下CPU进程信息; + - prev_comm = "test_proc_image", prev_tid = 129394, prev_prio = 20, prev_state = 2; + - next_comm = "test_proc_image", next_tid = 129392, next_prio = 20 +- 原理:挂载点在sched_switch上,记录了进程在cpu上切换的数据,哪个进程下cpu,哪个进程上cpu; + +通过以上数据就可将keytime_image工具所监测到的数据所描述的进程行为描述出来,做到上下CPU这个行为的数据关联; + + + +#### 1.3.3 execve执行: + +execve是进程所执行的一个程序,它是进程的一种行为,会替换当前进程的地址空间,并加载并执行一个新的程序,这里`execve` 不创建新进程,而是替换当前进程。 + +keytime_image工具通过挂载点sys_enter_execve、sys_exit_execve对进程execve行为进行监测,而lttng通过sched_process_exec实现相同的数据监测。 + +**【32:960747436】** + +- 行为:线程 125532,执性程序/bin/ls; +- 数据点:哪个进程/线程在执行,执行的命令,下CPU进程信息; + - filename = "/bin/ls", tid = 125532, old_tid = 125532; + +* 原理:挂载点在sched_process_exec,或sys_enter_execve、sys_exit_execve上,用户态进程执行execve时会通过系统调用在内核中进行相关操作,通过sys_enter_execve、sys_exit_execve便可以获取到用户态进程执行的execve信息; + +#### 1.3.4 exit 执行: + +exit可以用于终止进程, 会导致当前进程的执行终止,并返回一个退出状态码给操作系统;同execve一样,exit会通过系统调用到内核中实现终止进程的操作;所以我们在sys_enter_exit、sys_enter_exit_group两处进行插装,获取到关于进程退出时的相关数据,这里主要是进程退出时间的统计; + +**【32:962637158】** + +- 行为:进程 125532, 退出; +- 数据点:哪个进程/线程,在什么时间点退出; + - pid : 125532 exit ret :0; + +- 原理:挂载点在sys_enter_exit、sys_enter_exit_group上,用户态进程执行exit时会通过系统调用在内核中进行相关操作,通过sys_enter_exit、sys_enter_exit_group便可以获取到用户态进程终止时的信息; + + + +## 2.lock_image + +lock_image 是一个用于捕获进程持有用户态锁的工具,聚焦于进程持有的所有用户态锁(互斥锁、自旋锁、读写锁)。通过ebpf技术将其挂载固定的挂载点上,并在进程持有以上几种锁时进行数据收集,并对数据进行处理; + +本次测试以及数据关联工作分为三部分: + +- lock_image工具逻辑正确性的测试; +- lock_image工具数据正确性的测试; +- lock_image工具数据的关联关系,以及原理梳理; + +### 2.1 测试用例 test_lock + +为了完成以上三步测试与数据关联工作,需要设计一个测试用例,使用该测试用例可以测试lock_image工具的逻辑正确性,并通过和lttng工具测试的结果进行对比,判断lock_image数据正确性; + +lock_image工具关注的是进程持有用户态锁的情况,故需要用到lttng-ust,我们需要在lttng中自定义跟踪函数用以跟踪测试用例中涉及到的进程对锁的相关行为; + +#### 2.1.1 测试用例设计思路 + +测试用例主要目的是为了模拟进程或线程成功持有锁、未成功持有锁以及争用一个锁的情景。 + +测试用例主要思路 + +- 对测试用例进程进行cpu绑核,提高优先级,保证当前cpu仅运行一个或一组进程; +- 分别定义一个自旋锁、互斥锁以及读写锁并初始化,用于线程争用持有这些锁,来查看lock_image是否能准确的获取到线程持有锁的状态以及锁的信息; +- 创建两个线程分别去申请持有提前定义好的锁,并在特殊点插入lttng-ust跟踪函数,用来和lock_image进行数据对比; + - 线程1分别去持有互斥锁、解锁互斥锁,持有读写锁-写锁、释放读写锁,持有自旋锁、释放自旋锁; + - 考虑到创建线程2的时间稍晚于线程1,故通过pthread_mutex_trylock等函数去尝试持有互斥锁1(这里涉及到pthread_mutex_lock,pthread_mutex_trylock实现方式差别,后面会提到),若尝试失败,则通过pthread_mutex_trylock去持有互斥锁2;读写锁和自旋锁同理; + +#### 2.1.2 lttng-ust跟踪用户应用事件 + +由于lttng不能像ebpf一样方便的使用uprobe和uretprobe来跟踪用户态的函数,所以我们要自己定义适合本次测试用例的跟踪点定义文件,并将`lttng_ust_tracepoint`加在测试用例相应的跟踪位置; + +我们以mutex互斥锁为例,展示线程触发互斥锁时相关跟踪点的定义,其余锁类似: + +- 请求互斥锁时的跟踪点; + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_start, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 获取互斥锁成功的跟踪点: + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_acquired, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 互斥锁解锁成功的跟踪点: + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_lock_released, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +- 尝试申请互斥锁的跟踪点,对应`pthread_mutex_trylock`函数 + +```c +TRACEPOINT_EVENT( + lock_monitor, + mutex_trylock_start, + TP_ARGS(int, thread_id, void*, lock_ptr, long long unsigned int, time), + TP_FIELDS( + ctf_integer(int, thread_id, thread_id) + ctf_integer_dec(void*, lock_ptr, lock_ptr) + ctf_integer(long long unsigned int, time, time) + ) +) +``` + +通过以下指令,将跟踪点定义文件生成跟踪点头文件和实现文件: + +```shell +lttng-gen-tp lock_tracepoint.tp +``` + +这将生成 `lock_tracepoint.h` 和 `lock_tracepoint.c` 文件,便可以将我们定义的跟踪点用在测试用例中,以下是一个示例: + +```c +#include +#include "lock_tracepoint.h" +... +void *thread_lock_func1(void *arg) { + ... + /*准备开始申请互斥锁*/ + tracepoint(lock_monitor, mutex_lock_start, tid,&mutex1,(long long unsigned int)(ts.tv_sec*1000000000+ts.tv_nsec)); + pthread_mutex_lock(&mutex1); + /*锁申请成功*/ + tracepoint(lock_monitor, mutex_lock_acquired, tid,&mutex1,(long long unsigned int)(ts.tv_sec*1000000000+ts.tv_nsec)); + ... +} +``` + +编译时将我们定义好的lttng跟踪点头文件包含在测试用例中,并进行编译和链接: + +```shell +gcc -o test_lock lock_monitor.c lock_tracepoint.c -lpthread -llttng-ust +``` + +#### 2.1.3 测试脚本: + +在通过测试用例对lock_image工具进行逻辑正确性的验证之后,需要验证lock_image工具采集到的数据是否是可靠的; + +这里通过一个测试脚本实现lock_image工具和lttng同时监测测试用例的数据,并将数据导出; + +```shell +#!/bin/bash + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +# 指定输出目录 +OUTPUT_DIR="$SCRIPT_DIR/lttng-traces/lock_test_$(date +'%Y%m%d_%H:%M:%S')" +CSV_FILE="$OUTPUT_DIR/lock_test_data.csv" +mkdir -p "$SCRIPT_DIR/ebpf/ebpf_output_$(date +'%Y%m%d_%H:%M:%S')" +EBPF_OUTPUT_DIR="$SCRIPT_DIR/ebpf/ebpf_output_$(date +'%Y%m%d_%H:%M:%S')" + +# 获取目标进程的 PID +TARGET_PID=$(pidof test_proc_image) + +if [ -z "$TARGET_PID" ]; then + echo "目标进程未运行,请先启动目标进程。" + exit 1 +fi +echo "测试程序 PID: $TARGET_PID" + +# 创建会话并指定输出目录 +sudo lttng create xhb_lock --output=$OUTPUT_DIR + +# 启用内核事件,仅针对特定 PID +sudo lttng enable-event -u 'lock_monitor:*' + +# 添加上下文信息 +sudo lttng add-context --kernel --type pid + +sudo lttng track --kernel --pid $TARGET_PID + +# 运行proc_image监测工具 +cd /home/xhb/lmp2/lmp/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/ +sudo ./proc_image -l > $EBPF_OUTPUT_DIR/lock_output.log & +PROC_IMAGE_PID=$! +sleep 1 +sudo ./controller -l -P $TARGET_PID -a +# 启动会话 +sudo lttng start +read + +sudo lttng stop +sudo lttng view +sudo lttng destroy +echo "追踪数据已保存到 $OUTPUT_DIR 目录中" +# 将 LTTng 跟踪数据转换为文本格式 +babeltrace2 $OUTPUT_DIR > $OUTPUT_DIR/trace_data.txt + +echo "lttng、eBPF 程序数据收集完毕,请 Ctrl+C 结束" +read +echo "数据已导出到 $CSV_FILE" +``` + +### 2 .2 结果分析: + +通过测试用例和测试脚本,对lock_image工具的逻辑正确性和数据正确性进行了评估,本小节将针对输出的数据进行正确性检测; + +#### 1.2.1.逻辑正确性: + +通过脚本,可以获取到lock_image监测测试用例进程的全过程;以下是输出的数据: + +``` + #PID24685绑定CPU4:√ + #PID24685 被绑定在以下cpu上: 4 +test_proc进程的TGID:24685 PID:24685 CPU_id:4 +输入任意数字继续程序的运行:1 +程序开始执行... + +LOCK_TEST-------------------------------------------------- +用户态自旋锁地址:94014792843640 +用户态互斥锁地址:94014792843456 , 94014792843520 +用户态读写锁地址:94014792843584 +线程1 tid:24994 ... +tid :24994 申请互斥锁成功,并将持有1s +线程2 tid:24995 ... +tid :24995 申请互斥锁1失败 +tid :24995 尝试申请读写锁2... +tid :24995 申请互斥锁2成功,并将持有1s +tid :24994 互斥锁解锁成功 +tid:24994 尝试申请读写锁... +tid :24994 申请读写锁-读锁成功,并将持有1s +tid :24995 互斥锁解锁2成功 +tid:24995 尝试申请读写锁... +tid :24995 申请读写锁-写锁失败 +tid:24995 尝试申请自旋锁... +tid :24995 申请自旋锁成功,并将持有1s +tid :24994 读写锁-读锁解锁成功 +tid:24994 尝试申请自旋锁... +tid :24995 自旋锁解锁成功 +tid :24994 申请自旋锁成功,并将持有1s +tid :24994 自旋锁解锁成功 +``` + +**【测试用例逻辑分析】**:我们可以根据测试用例输出的信息,对该进程争用锁的整体画像进行梳理,以此为基准,对照lock_image工具查看其逻辑是否正确; + +- 目标锁:【用户态自旋锁: 94014792843640】、【用户态互斥锁1:94014792843456]】,【用户态互斥锁2: 94014792843520】、【用户态读写锁:94014792843584】。 +- 目标线程:线程1:24994 ;线程2:24995; +- 主要行为: + - 1.主体进程24685 初始化所有锁; + - 2.线程1被创建后,立即申请【用户态互斥锁1:94014792843456】,并将持有该锁1s; + - 3.线程2被创建后,立即尝试申请【用户态互斥锁1:94014792843456】,由于该锁被线程1持有,线程2申请失败,并尝试申请【用户态互斥锁2: 94014792843520】,申请成功并持有该锁1s; + - 4.线程1持有【用户态互斥锁1:94014792843456】1s后将其释放,解锁成功; + - 5.线程1申请持有【用户态读写锁:94014792843584】,并将持有1s; + - 6.线程2解锁【用户态互斥锁2: 94014792843520】成功; + - 7.线程2尝试持有【用户态读写锁:94014792843584】,由于该锁被线程1持有,线程2申请失败,则放弃申请该锁; + - 8.线程2尝试持有【用户态自旋锁: 94014792843640】,由于该锁未被持有,故线程2申请成功,并将持有1s + - 9.线程1持有【用户态读写锁:94014792843584】1s后解锁成功; + - 10.线程1申请持有【用户态自旋锁: 94014792843640】,由于该锁被线程2持有,故线程1等待该锁被释放后再申请持有; + - 11.线程2持有【用户态自旋锁: 94014792843640】1s并解锁成功; + - 12.线程1申请持有【用户态自旋锁: 94014792843640】并将持有1s; + - 13.线程1解锁【用户态自旋锁: 94014792843640】; + +**【lock_image跟踪结果】** + +lock_image工具跟踪的是整个进程组的数据,故会有父进程24685关于非指定锁的一些使用情况,这里选择性的删除,仅关注我们提前定义好可以跟踪的锁。 + +lock_image跟踪到的数据: + +```shell +USERLOCK ---------------------------------------------------------- +TIME TGID PID LockAddr LockStatus +10139926674250 24685 24685 94014792843640 spinlock_unlock +10139926898189 24685 24994 94014792843456 mutex_req +10139926902097 24685 24994 94014792843456 mutex_lock-0 +10140927410524 24685 24995 94014792843456 mutex_req +10140927416075 24685 24995 94014792843456 mutex_lock-16 +10140927418850 24685 24995 94014792843520 mutex_req +10140927423449 24685 24995 94014792843520 mutex_lock-0 +10140927462995 24685 24994 94014792843456 mutex_unlock +10140927489736 24685 24994 94014792843584 rdlock_req +10140927496720 24685 24994 94014792843584 rdlock_lock-0 +10141927546414 24685 24995 94014792843520 mutex_unlock +10141927603794 24685 24995 94014792843584 wrlock_req +10141927610998 24685 24995 94014792843584 wrlock_lock-16 +10141927621508 24685 24995 94014792843640 spinlock_req +10141927627018 24685 24995 94014792843640 spinlock_lock-0 +10141927646065 24685 24994 94014792843584 rdlock_unlock +10141927658509 24685 24994 94014792843640 spinlock_req +10142927736555 24685 24995 94014792843640 spinlock_unlock +10142927920498 24685 24994 94014792843640 spinlock_lock-0 +10143928473997 24685 24994 94014792843640 spinlock_unlock +``` + +针对lock_image跟踪到的数据,对其进行逻辑行为分析: + +- 目标锁:【用户态自旋锁: 94014792843640】、【用户态互斥锁1:94014792843456]】,【用户态互斥锁2: 94014792843520】、【用户态读写锁:94014792843584】。 + +- 目标线程:线程1:24994 ;线程2:24995 + +- 行为分析: + + - **[时间:10139926674250 ]**:由于`pthread_spin_init()`会调用`pthread_spin_unlock()`故主体进程触发pthread_spin_unlock()被检测到,`spinlock_unlock`; + - **[时间:10139926898189]~[时间:10139926902097 ]**: 线程1发出申请【互斥锁1:94014792843456】请求,并申请成功,`mutex_lock-0`; + - **[时间:10140927410524]~[时间:10140927416075]**: 线程2发出申请【互斥锁1:94014792843456】请求,申请失败,返回值-16,`mutex_lock-16`; + - **[时间:10140927418850]~[时间:10140927423449]**:线程2尝试申请用户态互斥锁2: 94014792843520】,申请成功,`mutex_lock-0`; + - **[时间:10140927462995]**:线程1持有锁1s(10140927462995-10139926902097 =1s),并解锁成功`mutex_unlock`; + - **[时间:10140927489736]~[时间:10140927496720]**:线程1申请持有【用户态读写锁:94014792843584】,申请成功,`rdlock_lock-0`; + - **[时间:10141927546414]** :线程2持有锁1s (10140927423449-10141927546414=1s)并解锁成功`mutex_unlock`; + - **[时间:10141927603794]~[时间:10141927610998]**:线程2尝试申请【用户态读写锁:94014792843584】写锁,失败,返回值-16,`wrlock_lock-16`; + - **[时间:10141927621508]~[时间:10141927627018]**:线程2尝试申请自旋锁【用户态自旋锁: 94014792843640】,申请成功,`spinlock_lock-0`; + - **[时间:10141927646065 ]**:线程1持有【用户态读写锁:94014792843584】1s(10140927496720-10141927646065=1s),并解锁成功,`rdlock_unlock`; + - **[时间:10141927658509 ]**:线程1尝试申请【用户态自旋锁: 94014792843640】,`spinlock_req` + - **[时间:10142927736555]**:线程2持有【用户态自旋锁: 94014792843640】1s,并释放成功,`spinlock_unlock`; + - **[时间:10142927920498]~[时间:10143928473997]**:线程1持有【用户态自旋锁: 94014792843640】,从发出持有申请到实际持有,共等待了10142927920498-10141927658509 =1.00s,并在持有1s后解锁; + + 通过将lock_image对进程关于锁的行为分析的逻辑同测试用例的逻辑对比,发现lock_image可以完美的观测到测试用的全部行为,包括一些细微的行为; + + + +#### 2.2.2 数据正确 + +通过将lock_image获取的数据同lttng作对比,便可得到 进程的整体行为是否正确,数据获取是否正确,描述该行为需要哪些数据进行关联; + +由于lttng-ust仅仅是将自定义的跟踪点挂到了测试用例上,而不是像uprobe一样挂载到libc用户态函数中,在时间上会存在一定的误差,从实现原理上讲,我们的lock_image所采集到的时间点更贴近真实时间; + diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h index ecfc1905f..589ca5063 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/include/proc_image.h @@ -65,6 +65,7 @@ struct total_rsc{ struct sc_ctrl { bool sc_func; bool enable_myproc; + bool is_container; pid_t target_pid; pid_t target_tgid; int syscalls; diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c index b52a77712..9577b3482 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/proc_image.c @@ -65,6 +65,7 @@ static struct env { int lock_prev_tgid; int sched_prev_tgid; int sc_prev_tgid; + char hostname[64]; } env = { .output_resourse = false, .output_schedule = false, @@ -88,6 +89,7 @@ static struct env { .lock_prev_tgid = 0, .sched_prev_tgid = 0, .sc_prev_tgid = 0, + .hostname = "", }; struct hashmap *map = NULL; @@ -723,6 +725,16 @@ static void sig_handler(int signo) exiting = true; } +void get_hostname() { + char hostname[64]; + int result = gethostname(hostname, sizeof(hostname)); + if (result == 0) { + strcpy(env.hostname,hostname); + } else { + perror("gethostname"); + } +} + int main(int argc, char **argv) { struct resource_image_bpf *resource_skel = NULL; @@ -802,7 +814,9 @@ int main(int argc, char **argv) } syscall_skel->rodata->ignore_tgid = env.ignore_tgid; - + get_hostname(); + strcpy(syscall_skel->rodata->hostname,env.hostname); + err = syscall_image_bpf__load(syscall_skel); if (err) { fprintf(stderr, "Failed to load and verify BPF syscall skeleton\n"); diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile index ae93b43c6..af43eaec8 100644 --- a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/Makefile @@ -42,7 +42,7 @@ INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(L CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = lifecycle_image lock_image newlife_image keytime_image +APPS = lifecycle_image lock_image newlife_image keytime_image migrate_image CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c new file mode 100644 index 000000000..bff87d0ac --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.bpf.c @@ -0,0 +1,115 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com + +#include "vmlinux.h" +#include //包含了BPF 辅助函数 +#include +#include +#include "migrate_image.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(pid_t)); + __uint(value_size, sizeof(struct migrate_event)); + __uint(max_entries, 1024); +} migrate SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(struct minfo_key)); + __uint(value_size, sizeof(struct per_migrate)); + __uint(max_entries, 1024); +} migrate_info SEC(".maps"); + +SEC("tracepoint/sched/sched_migrate_task") +int tracepoint_sched_migrate_task(struct trace_event_raw_sched_migrate_task *args){ + u64 time = bpf_ktime_get_ns();//当前转移时间点; + pid_t pid = args->pid; + struct migrate_event *migrate_event; + struct task_struct *task = (struct task_struct *)bpf_get_current_task(); + struct rq *orig_rq = BPF_CORE_READ(task,se.cfs_rq,rq); + struct cfs_rq *orig_cfs = BPF_CORE_READ(task,se.cfs_rq); + + bpf_printk("[se]:Pload_avg:%llu\tPutil_avg:%llu\n",BPF_CORE_READ(task,se.avg.load_avg),BPF_CORE_READ(task,se.avg.util_avg)); + bpf_printk("[rq]: nr_running :%d cpu_capacity : %ld cpu_capacity_orig : %ld\n", + BPF_CORE_READ(orig_rq,cpu),BPF_CORE_READ(orig_rq,nr_running), + BPF_CORE_READ(orig_rq,cpu_capacity),BPF_CORE_READ(orig_rq,cpu_capacity_orig)); + bpf_printk("Cload_avg:%ld\n",BPF_CORE_READ(orig_cfs,avg.runnable_avg)); + migrate_event = bpf_map_lookup_elem(&migrate,&pid); + if(!migrate_event){ + struct migrate_event migrate_event = {}; + struct per_migrate per_migrate = {}; + struct minfo_key mkey = {}; + mkey.pid = pid; + mkey.count = 1; + migrate_event.pid = pid; + migrate_event.prio = args->prio; + migrate_event.count = 1; + migrate_event.rear = 1; + per_migrate.time = time; + per_migrate.orig_cpu = args->orig_cpu; + per_migrate.dest_cpu = args->dest_cpu; + + per_migrate.cpu_capacity = BPF_CORE_READ(orig_rq,cpu_capacity); + per_migrate.cpu_capacity_orig = BPF_CORE_READ(orig_rq,cpu_capacity_orig); + per_migrate.cpu_load_avg = BPF_CORE_READ(orig_cfs,avg.runnable_avg); + + + per_migrate.pload_avg = BPF_CORE_READ(task,se.avg.load_avg);//进程的量化负载; + per_migrate.putil_avg = BPF_CORE_READ(task,se.avg.util_avg);//进程的实际算力; + per_migrate.mem_usage = BPF_CORE_READ(task,mm,total_vm) << PAGE_SHIFT; + + + per_migrate.read_bytes = BPF_CORE_READ(task,ioac.read_bytes); + per_migrate.write_bytes = BPF_CORE_READ(task,ioac.write_bytes); + + per_migrate.context_switches = BPF_CORE_READ(task,nvcsw) + BPF_CORE_READ(task,nivcsw); + // per_migrate.runtime = BPF_CORE_READ(task,se.sum_exec_runtime); + bpf_map_update_elem(&migrate_info, &mkey, &per_migrate, BPF_ANY); + bpf_map_update_elem(&migrate, &pid, &migrate_event, BPF_ANY); + } + /*&& (migrate_event->migrate_info + migrate_event->count) < (migrate_event->migrate_info + MAX_MIGRATE)*/ + else if(migrate_event->count>0 && migrate_event->countcount++; + mkey.pid = pid; + mkey.count = migrate_event->count; + per_migrate.time = time; + per_migrate.orig_cpu = args->orig_cpu; + per_migrate.dest_cpu = args->dest_cpu; + + per_migrate.cpu_capacity = BPF_CORE_READ(orig_rq,cpu_capacity); + per_migrate.cpu_capacity_orig = BPF_CORE_READ(orig_rq,cpu_capacity_orig); + per_migrate.cpu_load_avg = BPF_CORE_READ(orig_cfs,avg.runnable_avg); + + per_migrate.pload_avg = BPF_CORE_READ(task,se.avg.load_avg);//进程的量化负载; + per_migrate.putil_avg = BPF_CORE_READ(task,se.avg.util_avg);//进程的实际算力; + per_migrate.mem_usage = BPF_CORE_READ(task,mm,total_vm) << PAGE_SHIFT; + + + per_migrate.read_bytes = BPF_CORE_READ(task,ioac.read_bytes); + per_migrate.write_bytes = BPF_CORE_READ(task,ioac.write_bytes); + + per_migrate.context_switches = BPF_CORE_READ(task,nvcsw) + BPF_CORE_READ(task,nivcsw); + // per_migrate.runtime = BPF_CORE_READ(task,se.sum_exec_runtime); + + bpf_map_update_elem(&migrate_info, &mkey, &per_migrate, BPF_ANY); + } + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c new file mode 100644 index 000000000..af2958210 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.c @@ -0,0 +1,256 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: albert_xuu@163.com + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "migrate_image.skel.h" +#include "migrate_image.h" +#include "trace_helpers.h" + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static volatile bool exiting = false; +struct migrate_image_bpf *skel; +// static struct env { +// int pid; +// int time; +// int cpu_id; +// int stack_count; +// bool set_stack; +// bool enable_cs; +// } env = { +// .pid = 0, +// .time = 0, +// .cpu_id = 0, +// .stack_count = 0, +// .set_stack = false, +// .enable_cs = false, +// }; + +static struct ksyms *ksyms = NULL; + +const char argp_program_doc[] ="Trace process to get process life cycle image.\n"; + +// static const struct argp_option opts[] = { +// { "pid", 'p', "PID", 0, "Process ID to trace" }, +// { "cpuid", 'C', "CPUID", 0, "Set For Tracing Process 0(other processes don't need to set this parameter)" }, +// { "time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)" }, +// { "cs-reason", 'r', NULL, 0, "Process context switch reasons annotation" }, +// { "stack", 's', "STACK-COUNT", 0, "The number of kernel stacks printed when the process is under the CPU" }, +// { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, +// {}, +// }; + +// static error_t parse_arg(int key, char *arg, struct argp_state *state) +// { +// long pid; +// long cpu_id; +// long stack; +// switch (key) { +// case 'p': +// errno = 0; +// pid = strtol(arg, NULL, 10); +// if (errno || pid < 0) { +// warn("Invalid PID: %s\n", arg); +// // 调用argp_usage函数,用于打印用法信息并退出程序 +// argp_usage(state); +// } +// env.pid = pid; +// break; +// case 'C': +// cpu_id = strtol(arg, NULL, 10); +// if(cpu_id < 0){ +// warn("Invalid CPUID: %s\n", arg); +// argp_usage(state); +// } +// env.cpu_id = cpu_id; +// break; +// case 't': +// env.time = strtol(arg, NULL, 10); +// if(env.time) alarm(env.time); +// break; +// case 'r': +// env.enable_cs = true; +// break; +// case 's': +// stack = strtol(arg, NULL, 10); +// if (stack < 0) { +// warn("Invalid STACK-COUNT: %s\n", arg); +// // 调用argp_usage函数,用于打印用法信息并退出程序 +// argp_usage(state); +// } +// env.stack_count = stack; +// env.set_stack = true; +// break; +// case 'h': +// argp_state_help(state, stderr, ARGP_HELP_STD_HELP); +// break; +// default: +// return ARGP_ERR_UNKNOWN; +// } + +// return 0; +// } + +static void sig_handler(int sig) +{ + exiting = true; +} + + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + return vfprintf(stderr, format, args); +} + +static int migrate_print(){ + time_t now = time(NULL);// 获取当前时间 + struct tm *localTime = localtime(&now);// 将时间转换为本地时间结构 + printf("\nTime: %02d:%02d:%02d\n",localTime->tm_hour, localTime->tm_min, localTime->tm_sec); + printf("---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n"); + int err,migrate_fd =bpf_map__fd(skel->maps.migrate),migrate_info_fd =bpf_map__fd(skel->maps.migrate_info); + + pid_t lookup_key = -1 ,next_key; + struct migrate_event migrate_event; + while(!bpf_map_get_next_key(migrate_fd, &lookup_key, &next_key)){//遍历打印hash map + err = bpf_map_lookup_elem(migrate_fd,&next_key,&migrate_event); + if (err < 0) { + fprintf(stderr, "failed to lookup infos2: %d\n", err); + return -1; + } + if(migrate_event.count <= migrate_event.rear) { + lookup_key = next_key; + continue; + } + u64 last_time_stamp = 0; + printf("\npid:%d\tprio:%d\tcount:%d\trear:%d\n",migrate_event.pid,migrate_event.prio,migrate_event.count,migrate_event.rear); + printf("---------------------------------------------------------------------------------\n"); + for(int i=migrate_event.rear;i<=migrate_event.count;i++){ + struct per_migrate migrate_info; + struct minfo_key mkey; + mkey.pid = migrate_event.pid; + mkey.count = i; + err = bpf_map_lookup_elem(migrate_info_fd,&mkey,&migrate_info); + if (err < 0) { + fprintf(stderr, "failed to lookup infos err %d mkey_pid: %d mkey_count: %d\n", err,mkey.pid,i); + continue; + } + printf("time_stamp:%llu\t%d->%d \t PROC_LOAD:%llu \t PROC_UTIL:%llu\t", + migrate_info.time,migrate_info.orig_cpu,migrate_info.dest_cpu,migrate_info.pload_avg,migrate_info.putil_avg); + printf("CPU_LOAD: %ld \t Cpu_Capacity:[%ld:%ld] \t ",migrate_info.cpu_load_avg,migrate_info.cpu_capacity,migrate_info.cpu_capacity_orig); + printf("mmem_usage:%llu kb \t\t read:%llu kb \t\t wite:%llu kb \t\t context_switch:%llu\t", + migrate_info.mem_usage/1024,migrate_info.read_bytes/1024,migrate_info.write_bytes/1024, + migrate_info.context_switches); + + if(i==migrate_event.rear && last_time_stamp == 0) { + last_time_stamp = migrate_info.time; + printf("delay: /\n"); + }else{ + printf("delay: %d us\n",(migrate_info.time - last_time_stamp)/1000); + last_time_stamp = migrate_info.time; + } + bpf_map_delete_elem(migrate_info_fd,&mkey);//删除已经打印了的数据 + + } + migrate_event.rear = migrate_event.count + 1; + bpf_map_update_elem(migrate_fd,&next_key,&migrate_event,BPF_ANY); + lookup_key = next_key; + } + printf("---------------------------------------------------------------------------------\n\n"); + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + int err; + // static const struct argp argp = { + // .options = opts, + // .parser = parse_arg, + // .doc = argp_program_doc, + // }; + + // err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + // if (err) + // return err; + + + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + /* 设置libbpf错误和调试信息回调 */ + libbpf_set_print(libbpf_print_fn); + + /* 更干净地处理Ctrl-C + SIGINT:由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 + SIGTERM:请求中止进程,kill命令发送 + */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM,sig_handler); + + /* 打开BPF应用程序 */ + skel = migrate_image_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + + /* 加载并验证BPF程序 */ + err = migrate_image_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + ksyms = ksyms__load(); + if (!ksyms) { + fprintf(stderr, "failed to load kallsyms\n"); + goto cleanup; + } + + /* 附加跟踪点处理程序 */ + err = migrate_image_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* 处理事件 */ + while (!exiting) { + sleep(1); + err = migrate_print(); + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error: %d\n", err); + break; + } + } + +/* 卸载BPF程序 */ +cleanup: + // ring_buffer__free(rb); + migrate_image_bpf__destroy(skel); + return err < 0 ? -err : 0; +} diff --git a/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h new file mode 100644 index 000000000..685cf0374 --- /dev/null +++ b/eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/tools/migrate_image.h @@ -0,0 +1,45 @@ +#include +#include + +typedef unsigned long long u64; +typedef unsigned int u32; +/*----------------------------------------------*/ +/* migrate_event结构体 */ +/*----------------------------------------------*/ +#define MAX_MIGRATE 1024 +#define PAGE_SHIFT 13 +// #define ARRY_OVERFLOW -1 +struct minfo_key{ + pid_t pid; + int count; +}; +struct per_migrate{//每次迁移,记录该次迁移信息; + u64 time; + u32 orig_cpu; + u32 dest_cpu; + + u64 orig_cpu_load;//cfs->avg.runnale_avg 就绪队列所有调度实体;量化负载总和 + u64 dest_cpu_load; + int cpu_capacity;//计算机算力 + int cpu_capacity_orig;//额定算力 + u64 cpu_load_avg; + u64 pload_avg; + u64 putil_avg; + + int on_cpu; + u64 mem_usage; + u64 read_bytes; + u64 write_bytes; + // u64 syscr; + // u64 syscw; + u64 context_switches; + u64 runtime; +}; +//每个进程的迁移信息; +struct migrate_event{ + int erro; + pid_t pid; + int prio; + int count,rear;//迁移频率 + //struct per_migrate *migrate_info;//该进程每次迁移信息; +}; diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md new file mode 100644 index 000000000..1e0eb6f70 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/README.md @@ -0,0 +1,32 @@ +# DIFUSE + +## 代码结构 + +``` +. +├── README.md +├── src +│ ├── difuse.c 源码 +│ ├── Makefile +│ └── mountpoints 挂载目录 +└── test + └── test.sh 测试脚本 +``` + + + +## Build + +在`src`目录下执行`make` + +```bash +cd src +make +``` + +在`test`目录下执行`test`脚本 + +```bash +cd test +./test.sh +``` \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile new file mode 100644 index 000000000..aa3c52834 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/Makefile @@ -0,0 +1,29 @@ +# Makefile for FUSE difuse example + +# Compiler +CC = gcc + +# Compiler flags +CFLAGS = -Wall `pkg-config fuse3 --cflags` + +# Linker flags +LDFLAGS = `pkg-config fuse3 --libs` + +# Source files +SRCS = difuse.c + +# Output executable +TARGET = difuse + +# Build target +all: $(TARGET) + +# Rule to build the target +$(TARGET): $(SRCS) + $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS) + +# Clean rule +clean: + rm -f $(TARGET) + +.PHONY: all clean diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse new file mode 100755 index 000000000..8b255be73 Binary files /dev/null and b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse differ diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c new file mode 100644 index 000000000..03cdec001 --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/src/difuse.c @@ -0,0 +1,443 @@ +#define FUSE_USE_VERSION 31 + +#include +#include +#include +#include +#include +#include +#include +#include + +/*相关数据结构*/ + +#define FILE_TYPE 1 +#define DIRECTORY_TYPE 2 +#define MAX_INODES 1000 //最大 inode 数量 +#define HASH_SIZE 1024 + +uint32_t next_ino = 1; + +struct dfs_data +{ + int chunk_size; + size_t size; + struct dfs_data *next; +}; + +struct dfs_inode +{ + uint32_t ino; //inode编号 + int size; //文件大小 + int dir_cnt; // 如果是目录类型文件,下面有几个目录项 + struct dfs_data *data_pointer; //指向数据块的指针 + struct dfs_inode *prev; // LRU 链表前驱指针 + struct dfs_inode *next; // LRU 链表后继指针 +}; + +struct dfs_dentry +{ + char fname[255]; + int ftype; + struct dfs_dentry *parent; + struct dfs_dentry *brother; + struct dfs_dentry *child; + struct dfs_inode *inode; //指向对应的inode + struct dfs_dentry *prev; //LRU 链表前驱指针 + struct dfs_dentry *next; //LRU 链表后继指针 +}; + +struct dfs_dentry *root; //根节点 +struct dfs_dentry *lru_head = NULL; //LRU 链表头 +struct dfs_dentry *lru_tail = NULL; //LRU 链表尾 +struct dfs_dentry *hash_table[HASH_SIZE]; //哈希表 + +/*缓存管理*/ + +static unsigned int hash(const char *path) +{ + unsigned int hash = 0; + while (*path) + { + hash = (hash << 5) + *path++; + } + return hash % HASH_SIZE; +} + +static void lru_remove(struct dfs_dentry *dentry) +{ + if (dentry->prev) + { + dentry->prev->next = dentry->next; + } + else + { + lru_head = dentry->next; + } + if (dentry->next) + { + dentry->next->prev = dentry->prev; + } + else + { + lru_tail = dentry->prev; + } +} + +static void lru_insert(struct dfs_dentry *dentry) +{ + dentry->next = lru_head; + dentry->prev = NULL; + if (lru_head) + { + lru_head->prev = dentry; + } + lru_head = dentry; + if (!lru_tail) + { + lru_tail = dentry; + } +} + +static void lru_access(struct dfs_dentry *dentry) +{ + lru_remove(dentry); + lru_insert(dentry); +} + +static void lru_evict() +{ + if (lru_tail) + { + struct dfs_dentry *evict = lru_tail; + lru_remove(evict); + unsigned int index = hash(evict->fname); + hash_table[index] = NULL; + free(evict->inode); + free(evict); + } +} + +/*过程函数*/ +static struct dfs_inode *new_inode(int size, int dir_cnt) +{ + struct dfs_inode *inode = (struct dfs_inode *)malloc(sizeof(struct dfs_inode)); + inode->ino = next_ino++; + inode->size = size; + inode->dir_cnt = dir_cnt; + inode->data_pointer = NULL; + inode->prev = NULL; + inode->next = NULL; + return inode; +} + +static struct dfs_dentry *new_dentry(char *fname, int ftype, struct dfs_dentry *parent, struct dfs_inode *inode) +{ + struct dfs_dentry *dentry = (struct dfs_dentry *)malloc(sizeof(struct dfs_dentry)); + strcpy(dentry->fname, fname); + dentry->inode = inode; + dentry->brother = NULL; + dentry->parent = parent; + dentry->child = NULL; + dentry->ftype = ftype; + dentry->prev = NULL; + dentry->next = NULL; + return dentry; +} + +void add_child_dentry(struct dfs_dentry *parent, struct dfs_dentry *child) +{ + child->brother = parent->child; + parent->child = child; +} + +static int remove_child_dentry(struct dfs_dentry *parent, struct dfs_dentry *child) +{ + struct dfs_dentry *prev_child = NULL; + struct dfs_dentry *cur_child = parent->child; + + while (cur_child != NULL && cur_child != child) + { + prev_child = cur_child; + cur_child = cur_child->brother; + } + if (cur_child == NULL) + return 0; + + if (prev_child == NULL) + parent->child = cur_child->brother; + else prev_child->brother = cur_child->brother; + return 1; +} + +struct dfs_dentry *traverse_path(struct dfs_dentry *start_dentry, const char *path, int ftype, int create) +{ + struct dfs_dentry *dentry = start_dentry; + char *path_copy = strdup(path); + char *token = strtok(path_copy, "/"); + + while (token != NULL) + { + struct dfs_dentry *child = dentry->child; + while (child != NULL && strcmp(child->fname, token) != 0) + { + child = child->brother; + } + + if (child == NULL) + { + if (create) + { + struct dfs_inode *new_inodes = new_inode(0, 0); // 创建新的 inode + child = new_dentry(token, ftype, dentry, new_inodes); // 创建新的目录项 + add_child_dentry(dentry, child); // 将新目录项添加到父目录项的子目录列表中 + } + else + { + free(path_copy); + return NULL; + } + } + + dentry = child; + token = strtok(NULL, "/"); + } + + free(path_copy); + return dentry; +} + +struct dfs_dentry *look_up(struct dfs_dentry *dentrys, const char *path) +{ + return traverse_path(dentrys, path, 0, 0); +} + +struct dfs_dentry *lookup_or_create_dentry(const char *path, struct dfs_dentry *start_dentry, int ftype) +{ + unsigned int index = hash(path); + struct dfs_dentry *dentry = hash_table[index]; + + if (dentry) + { + lru_access(dentry); + return dentry; + } + + dentry = traverse_path(start_dentry, path, ftype, 1); + if (dentry) + { + lru_insert(dentry); + hash_table[index] = dentry; + if (next_ino > MAX_INODES) + { + lru_evict(); + } + } + + return dentry; +} + + +/*功能函数*/ +static int di_unlink(const char *path) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + if (remove_child_dentry(dentry->parent, dentry)) + { + lru_remove(dentry); + unsigned int index = hash(dentry->fname); + hash_table[index] = NULL; + free(dentry->inode); + free(dentry); + return 0; + } + return -ENOENT; +} + +static int di_rmdir(const char *path) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + if (dentry->ftype != DIRECTORY_TYPE) + return -ENOTDIR; + if (dentry->child != NULL) + return -ENOTEMPTY; + + // 移除子目录项 + if (remove_child_dentry(dentry->parent, dentry)) + { + lru_remove(dentry); + unsigned int index = hash(dentry->fname); + hash_table[index] = NULL; + free(dentry->inode); + free(dentry); + return 0; + } + return -ENOENT; +} + +static int di_utimens(const char *path, const struct timespec ts[2], struct fuse_file_info *fi) +{ + (void)fi; + struct dfs_dentry *dentry = look_up(root, path); + if (dentry == NULL) + { + return -ENOENT; + } + + return 0; +} + +static int di_mkdir(const char *path, mode_t mode) +{ + (void)mode; + struct dfs_dentry *dentry = lookup_or_create_dentry(path, root, DIRECTORY_TYPE); + if (dentry == NULL) + { + return -ENOENT; + } + + return 0; +} + +static int dfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + (void)mode; + (void)fi; + struct dfs_dentry *existing = look_up(root, path); + if (existing != NULL) + { + return -EEXIST; // 文件已存在,返回错误 + } + struct dfs_dentry *dentry = lookup_or_create_dentry(path, root, FILE_TYPE); + if (dentry == NULL) + { + return -ENOENT; + } + + return 0; +} + +static int di_getattr(const char *path, struct stat *di_stat, struct fuse_file_info *fi) +{ + (void)fi; + int ret = 0; + memset(di_stat, 0, sizeof(struct stat)); + + struct dfs_dentry *dentry = look_up(root, path); + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype == DIRECTORY_TYPE) + { + di_stat->st_mode = S_IFDIR | 0755; + di_stat->st_nlink = 2; + } + else if (dentry->ftype == FILE_TYPE) + { + di_stat->st_mode = S_IFREG | 0644; + di_stat->st_nlink = 1; + di_stat->st_size = dentry->inode->size; + } + + return ret; +} + +/*遍历目录项*/ +static int di_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) +{ + (void)fi; + (void)offset; + (void)flags; + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != DIRECTORY_TYPE) + return -ENOTDIR; + + filler(buf, ".", NULL, 0, 0); + filler(buf, "..", NULL, 0, 0); + + struct dfs_dentry *child = dentry->child; + while (child != NULL) + { + filler(buf, child->fname, NULL, 0, 0); + child = child->brother; + } + + return 0; +} + +static int di_open(const char *path, struct fuse_file_info *fi) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + return 0; +} + +static int di_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) +{ + struct dfs_dentry *dentry = look_up(root, path); + + if (dentry == NULL) + return -ENOENT; + + if (dentry->ftype != FILE_TYPE) + return -EISDIR; + + if (offset < dentry->inode->size) + { + if (offset + size > dentry->inode->size) + size = dentry->inode->size - offset; + memcpy(buf, "dummy_content", size); + } + else + size = 0; + + return size; +} + +static void *di_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +{ + (void)conn; + + // 创建并初始化根目录的 inode 和 dentry + struct dfs_inode *root_inode = new_inode(0, 0); + root = new_dentry("/", DIRECTORY_TYPE, NULL, root_inode); + + return 0; +} + +static struct fuse_operations difs_ops = { + .init = di_init, + .readdir = di_readdir, + .getattr = di_getattr, + .open = di_open, + .read = di_read, + .mkdir = di_mkdir, + .create = dfs_create, + .utimens = di_utimens, + .unlink = di_unlink, + .rmdir = di_rmdir, +}; + +int main(int argc, char *argv[]) +{ + return fuse_main(argc, argv, &difs_ops, NULL); +} \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh new file mode 100755 index 000000000..5dbb6234f --- /dev/null +++ b/eBPF_Supermarket/Filesystem_Subsystem/fast_fuse/difuse/test/test.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# 挂载点目录 +MOUNT_POINT="../src/mountpoints" +SRC_DIR="../src" + +# FUSE 可执行文件 +FUSE_EXEC="../src/difuse" + +echo "Compiling FUSE filesystem..." +make -C "$SRC_DIR" clean +make -C "$SRC_DIR" all + +# 检查编译是否成功 +if [ ! -f "$FUSE_EXEC" ]; then + echo "Compilation failed. Exiting." + exit 1 +fi + +# 创建挂载点目录(如果不存在) +if [ ! -d "$MOUNT_POINT" ]; then + mkdir -p "$MOUNT_POINT" +fi + +# 挂载 FUSE 文件系统(前台运行并显示调试信息) +echo "Mounting FUSE filesystem..." +$FUSE_EXEC -f -d "$MOUNT_POINT" & +FUSE_PID=$! +sleep 2 # 等待文件系统完全挂载 + +<<<<<<< HEAD +# 确保脚本退出时卸载文件系统 +trap "fusermount -u $MOUNT_POINT" EXIT + +======= +>>>>>>> e040be4f7b8f162b7a4c9b0c5f5bb40158e2b6fb +# 创建目录 +mkdir $MOUNT_POINT/dir1 +mkdir $MOUNT_POINT/dir2 + +# 创建文件 +touch $MOUNT_POINT/dir1/file1 +touch $MOUNT_POINT/dir1/file2 +touch $MOUNT_POINT/dir2/file3 + +# 验证结构 +echo "创建的目录和文件结构:" +ls -l $MOUNT_POINT + +<<<<<<< HEAD +# 删除文件 +echo "删除文件 $MOUNT_POINT/dir1/file1 和 $MOUNT_POINT/dir2/file3..." +rm $MOUNT_POINT/dir1/file1 +rm $MOUNT_POINT/dir2/file3 + +# 验证文件删除 +echo "验证文件删除后的结构:" +ls -l $MOUNT_POINT/dir1 +ls -l $MOUNT_POINT/dir2 + +# 尝试删除非空目录 +echo "尝试删除非空目录 $MOUNT_POINT/dir1 (应失败)..." +rmdir $MOUNT_POINT/dir1 || echo "无法删除非空目录 $MOUNT_POINT/dir1, 操作成功。" + +# 删除剩余文件 +rm $MOUNT_POINT/dir1/file2 + +# 删除空目录 +echo "删除空目录 $MOUNT_POINT/dir1 和 $MOUNT_POINT/dir2..." +rmdir $MOUNT_POINT/dir1 +rmdir $MOUNT_POINT/dir2 + +# 验证目录删除 +echo "验证目录删除后的结构:" +ls -l $MOUNT_POINT +======= +# 确保脚本退出时卸载文件系统 +trap "fusermount -u $MOUNT_POINT" EXIT +>>>>>>> e040be4f7b8f162b7a4c9b0c5f5bb40158e2b6fb diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/fs_watcher.c b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/fs_watcher.c index fe36747f4..8a2f6ab8a 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/fs_watcher.c +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/fs_watcher.c @@ -117,7 +117,6 @@ static const struct argp_option opts[] = { {"write", 'w', 0, 0, "Print write system call report"}, {"disk_io_visit", 'd', 0, 0, "Print disk I/O visit report"}, {"block_rq_issue", 'b', 0, 0, "Print block I/O request submission events. Reports when block I/O requests are submitted to device drivers."}, - {0} // 结束标记,用于指示选项列表的结束 }; @@ -186,13 +185,12 @@ int main(int argc,char **argv){ libbpf_set_strict_mode(LIBBPF_STRICT_ALL); - - /* Set up libbpf errors and debug info callback */ - libbpf_set_print(libbpf_print_fn); + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); /* Cleaner handling of Ctrl-C */ - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); signal(SIGALRM, sig_handler); @@ -217,7 +215,7 @@ int main(int argc,char **argv){ } } -static int handle_event_open(void *ctx, void *data, size_t data_sz) + static int handle_event_open(void *ctx, void *data, size_t data_sz) { struct event_open *e = (struct event_open *)data; char *filename = strrchr(e->path_name_, '/'); @@ -228,7 +226,7 @@ static int handle_event_open(void *ctx, void *data, size_t data_sz) char comm[TASK_COMM_LEN]; int i = 0; int map_fd = *(int *)ctx;//传递map得文件描述符 - + for (; i < e->n_; ++i) { snprintf(fd_path, sizeof(fd_path), "/proc/%d/fd/%d", e->pid_, i); @@ -280,6 +278,7 @@ static int handle_event_write(void *ctx, void *data, size_t data_sz) return 0; } + static int handle_event_disk_io_visit(void *ctx, void *data,unsigned long data_sz) { const struct event_disk_io_visit *e = data; @@ -376,4 +375,4 @@ static int process_block_rq_issue(struct block_rq_issue_bpf *skel_block_rq_issue return err; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h index da78cc0dd..94e7e4350 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/include/fs_watcher.h @@ -1,11 +1,10 @@ #ifndef __FS_WATCHER_H #define __FS_WATCHER_H - +/*open*/ #define path_size 256 +#define TASK_COMM_LEN 16 -#define TASK_COMM_LEN 256 -/*open*/ struct event_open { int pid_; char path_name_[path_size]; diff --git a/eBPF_Supermarket/Memory_Subsystem/README.md b/eBPF_Supermarket/Memory_Subsystem/README.md deleted file mode 100644 index 2175886a4..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/README.md +++ /dev/null @@ -1,442 +0,0 @@ -# mem_watcher - -## mem_watcher介绍 - -memwatcher是一款基于eBPF的内存监测工具,其设计的目的就是为了可以让用户能够在主机环境上可以快捷的检测到Linux内存的详细信息。 -通过高效的数据收集和精准的监控能力,帮助用户可以有效的监控主机内存情况。 -使用了eBPF(Extended Berkeley Packet Filter)来监控内核中的几个关键事件,主要涉及到内存管理方面的几个功能: -第一:`get_page_from_freelist:` - -- 监控页面分配过程中的某些关键参数。 -- 捕获了页面分配时的一些重要信息,比如所用的gfp_mask、order、alloc_flags等。 - -第二:`shrink_page_list:` - -- 监控页面收缩(shrink)过程中的一些参数。 -- 捕获了页面收缩时的关键参数,如nr_to_reclaim、nr_reclaimed等。 - -第三:`finish_task_switch:` - -- 监控进程切换完成时的内存相关参数。 -- 捕获了进程切换时的一些内存使用情况,如pid、vsize、rss等。 - -第四:`get_page_from_freelist:` - -- 监控页面分配的另一方面,可能是为了提供更全面的内存分配情况。 -- 捕获了更多与页面分配相关的内存统计信息,如anon_inactive、file_inactive等。 - -通过收集这些信息,可以用于监控系统内存的使用情况、诊断内存相关的性能问题以及进行性能优化。 - -第五: - -内存泄露是指程序在申请内存后,无法释放或未能及时释放,从而导致系统内存的不断消耗,最终导致程序的崩溃或性能的下降。这种现象一般发生在程序中有大量的动态内存分配和释放操作,如果程序员忘记或者疏忽了释放内存,就有可能导致内存泄露。 - -eBPF 提供了一种高效的机制来监控和追踪系统级别的事件,包括内存的分配和释放。通过 eBPF,可以跟踪内存分配和释放的请求,并收集每次分配的调用堆栈。然后,分析这些信息,找出执行了内存分配但未执行释放操作的调用堆栈,这有助于程序员找出导致内存泄漏的源头。 - ------- -## 背景意义 - -内存子系统是Linux内核中是一个相对复杂的模块,内核中几乎所有的数据、缓存、程序指令都有内存模块参与管理。在内存不足的情况下,这些数据就会被存储在磁盘的交换空间中,但是磁盘的处理速度相对与内存非常慢,当内存和磁盘频繁进行数据交换时,缓慢的磁盘读写速度非常影响系统性能。系统可能因内存不足从而终止那些占用内存较大的进程,导致程序运行故障。因此准确的监控分析内存性能状况就变得非常重要。 - -目前,传统的内存性能分析工具通过读取proc文件系统下的数据,经过简单的处理后呈现给用户,方便管理人员随时了解系统状况。然而这些工具的灵活性非常差,单个工具输出的信息有限。系统维护人员在分析性能问题时常常需要借助多个工具才能进行。步骤繁琐且工具本身对系统性能也有一定影响。随着ebpf技术在系统可观测上的发展,利于ebpf非侵入式的数据获取方式已被大多数企业、高校认可并取得了一定的研究成果。ebpf的可编程性可以让管理人员灵活的获取系统的运行数据,而且在数据的提取粒度上有着传统工具无法比拟的优势。现在,ebpf作为Linux内核顶级子系统,已经成为实现Linux内核可观测性、网络和内核安全的理想技术。 - ------- - -# mem_watcher据体代码的分析。 - -## procstat - -### 采集信息: - -| 参数 | 含义 | -| -------- | ------------------------ | -| vsize | 进程使用的虚拟内存 | -| size | 进程使用的最大物理内存 | -| rssanon | 进程使用的匿名页面 | -| rssfile | 进程使用的文件映射页面 | -| rssshmem | 进程使用的共享内存页面 | -| vswap | 进程使用的交换分区大小 | -| vdata | 进程使用的私有数据段大小 | -| vpte | 进程页表大小 | -| vstk | 进程用户栈大小 | -### 功能 - -主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息,并将这些信息写入环形缓冲区中。 - -### 分析 - -`BPF_KPROBE`标记了一个内核探针函数,挂载在`finish_task_switch`结束时,这个函数用于捕获进程切换事件。 -内核探针函数首先通过`bpf_get_current_pid_tgid()`获取当前进程的PID,然后通过prev参数获取切换前的进程结构体指针。 -然后判断当前进程的PID是否是要跟踪的用户进程的PID,如果是则直接返回,不做任何处理。 -接着获取切换前进程的PID,并判断是否是要跟踪的用户进程的PID,如果是则直接返回。 -如果不是要跟踪的进程,则从`last_val`哈希表中查找上一次记录的内存状态。 -如果没有找到,则更新`last_val`哈希表,将该进程的PID作为键,将值设置为1。 -如果找到了上一次的记录,并且上一次的值与当前值相同,则说明内存状态没有变化,直接返回。 -如果上一次的记录与当前值不同,则说明内存状态发生了变化,需要记录内存信息。 -通过`bpf_ringbuf_reserve()`函数在环形缓冲区中分配空间,并填充内存事件信息。 -最后通过`bpf_ringbuf_submit()`函数将填充好的内存事件信息提交到环形缓冲区中。 -### 载点及挂载原因 - -挂载点:finish_task_switch - -挂载原因: - -首先,获取进程级别内存使用信息首先需要获取到进程的task_struct结构体,其中在mm_struct成员中存在一个保存进程当前内存使用状态的数组结构,因此有关进程的大部分内存使用信息都可以通过这个数组获得。其次,需要注意函数的插入点,插入点的选取关系到数据准确性是否得到保证,而在进程的内存申请,释放,规整等代码路径上都存在页面状态改变,但是数量信息还没有更新的相关结构中的情况,如果插入点这两者中间,数据就会和实际情况存在差异,所有在确保可以获取到进程PCB的前提下,选择在进程调度代码路径上考虑。而finish_task_switch函数是新一个进程第一个执行的函数,做的事却是给上一个被调度出去的进程做收尾工作,所有这个函数的参数是上一个进程的PCB,从这块获得上一个进程的内存信息就可以确保在它没有再次被调度上CPU执行的这段时间内的内存数据稳定性。因此最后选择将程序挂载到finish_task_switch函数上。以下是调度程序处理过程: - -![](./image/6.png) - -数据来源有两部分,一个是mm_struc结构本身存在的状态信息,另一个是在mm_rss_stat结构中,它总共统计四部分信息,内核定义如下: - -![](./image/7.png) - -## sysstat - -### 采集信息: - -| 参数 | 含义 | -| -------------- | -------------------------------- | -| active | LRU活跃内存大小 | -| inactive | LRU不活跃内存大小 | -| anon_active | 活跃匿名内存大小 | -| anon_inactive | 不活跃匿名内存大小 | -| file_active | 活跃文件映射内存大小 | -| file_inactive | 不活跃文件映射内存大小 | -| unevictable | 不可回收内存大小 | -| dirty | 脏页大小 | -| writeback | 正在回写的内存大小 | -| anonpages | RMAP页面 | -| mapped | 所有映射到用户地址空间的内存大小 | -| shmem | 共享内存 | -| kreclaimable | 内核可回收内存 | -| slab | 用于slab的内存大小 | -| sreclaimable | 可回收slab内存 | -| sunreclaim | 不可回收slab内存 | -| NFS_unstable | NFS中还没写到磁盘中的内存 | -| writebacktmp | 回写所使用的临时缓存大小 | -| anonhugepages | 透明巨页大小 | -| shmemhugepages | shmem或tmpfs使用的透明巨页 | -### 功能 - -提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。 - -### 分析 - -分别用last_val1、last_val2、last_val3 三个哈希表记录上次统计的值,这里的键是不同类型页面的数量。 -`BPF_KPROBE(get_page_from_freelist_second, ...)` 是一个 Kprobe 函数,用于跟踪从空闲页列表获取页面的情况。 -在函数内部,首先获取当前进程的 PID,并与 `user_pid` 进行比较,如果相同则直接返回。 -通过 `BPF_CORE_READ` 读取内存分配相关的统计信息,包括匿名页面的活动状态和非活动状态下的数量、文件页面的活动状态和非活动状态下的数量、不可驱逐页面的数量等。 -通过三个哈希表 last_val1、last_val2、last_val3 查找上次统计的值,如果不存在则将新的统计值更新到哈希表中,如果存在且与当前值相同,则表示重复统计,直接返回。 -在环形缓冲区中预留空间,用于记录系统统计事件,并将获取的内存分配相关的统计信息填充到事件结构体中。 -最后,提交系统统计事件到环形缓冲区中。 -这个代码的挂载函数和paf代码相同在这里不再进行二次分析。 -### 挂载点及挂载原因 - -挂载点:get_page_from_freelist - -原因: - -首先,内存状态数据的提取需要获取到内存节点pglist_data数据结构,这个结构是对内存的总体抽象。pglist_data数据结构末尾有个vm_stat的数组,里面包含了当前内存节点所有的状态信息。所有只需要获取到pglist_data结构就能拿到当前的内存状态信息。但是物理内存分配在选择内存节点是通过mempolicy结构获取,无法获得具体的节点结构。选择内存节点的函数处理流程如下: - -```c -struct mempolicy *get_task_policy(struct task_struct *p) -{ - struct mempolicy *pol = p->mempolicy;//根据当前task_struct取得 - int node; - - if (pol) - return pol; - - node = numa_node_id(); - if (node != NUMA_NO_NODE) {//存在其他节点 - pol = &preferred_node_policy[node]; - /* preferred_node_policy is not initialised early in boot */ - if (pol->mode) - return pol; - } - - return &default_policy;//不存在其他节点返回本地节点 -} -``` - -经过对内存申请的内部结构alloc_context分析(这是内存申请过程中临时保存相关参数的结构),当前内存节点是可以通过:alloc_context——>zoneref——>zone——>pglist_data的路径访问到。 - -其次,因为函数执行申请内存的过程对获取内存节点数据的影响不大,所以只要可以获得alloc_context数据结构,在整个申请路径上挂载函数都是可以的。sysstat工具选择的挂载点是get_page_from_freelist函数。这个函数是快速物理内存分配的入口函数。因为内核在进行物理内存分配时,都会进入快速路径分配,只有当失败时才会进入慢速路径,所以get_page_from_freelist函数是必经函数。整个处理过程以及函数关系如下: - -![](./image/1.png) - -但是,经过对proc文件系统的打印函数meminfo_proc_show函数的分析得知,影响内存性能的参数在vm_stat中无法全部获得。一部分数据需要遍历当前内存节点包含的所有内存管理区zone结构中vm_stat数组获得,一部分需要读取全局变量vm_node_stat获得。但是内核的全局变量不会作为函数参数参与数据处理,目前还没具体方法获得这部分数据。 - -### 存在问题 - -■ 部分性能数据存在于内核全局变量中,而这些数据不会作为函数参数存储在栈中,因此这些数据目前还没实现统计 - -■ 因为内核对内存管理不会是物理内存的全部容量,而且最大管理内存的数据结构是内存结点,所以以上统计数据是以当前内存结点实际管理的内存容量为基准。 - -■ 当前剩余内存总量的统计需要遍历所有内存管理区来统计,但是由于内存管理区的空闲页面信息存储在数组第一个位置,使用指针指向时,统计到的数据不准确,使用变量统计会出现数据类型错误的报告。 - -## paf - -### 采集信息 - -| 参数 | 含义 | -| ------- | ------------------------------------ | -| min | 内存管理区处于最低警戒水位的页面数量 | -| low | 内存管理区处于低水位的页面数量 | -| high | 内存管理区处于高水位的页面数量 | -| present | 内存管理区实际管理的页面数量 | -| flag | 申请页面时的权限(标志) | -### 功能 - -主要是监控内核中的`get_page_from_freelist`函数。这个函数在内核中用于从内存空闲页列表中获取一个页面。 -程序主要是输出present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 - -### 分析 - -该程序挂载在内核函数`get_page_from_freelist`的前面。 -首先,获取当前进程的PID,并检查是否等于`user_pid`,如果相等,则直接返回,表示不监控当前进程。 -然后,通过`bpf_map_lookup_elem()`查找last_val哈希表中是否已经存在当前`gfp_mask`的值,如果不存在,则将当前`gfp_mask`的值插入哈希表中。 -如果已经存在,并且与上一次观察到的值相同,则直接返回,表示不需要重复记录相同的`gfp_mask`值。 -接着,通过`bpf_ringbuf_reserve()`尝试在环形缓冲区中预留一段空间,用于存储事件数据。如果预留失败,则直接返回。 -之后,通过`BPF_CORE_READ()`读取给定的内核数据结构`alloc_context`中的一些字段的值,并将这些值存储在定义好的事件结构体e中。 -最后,通过`bpf_ringbuf_submit()`将填充好的事件结构体提交到环形缓冲区中。 - -内存申请失败一般集中在申请权限不够或者是权限冲突导致,申请权限不够是当内核申请优先级较低的页面时,虽然内存管理区有足够的页面满足这次申请数量,但是当前剩余空闲页面少于最低警戒水位,因此导致内核无法成功分配页面的情况。权限冲突,例如内核在开启CMA机制下导致的页面页面申请失败的情况,这种情况下管理区空闲页面需要减去CMA机制占用内存才是当前可分配内存。相关权限判断代码如下: - -添加CMA权限代码路径mm/page_alloc.c - -```c -static inline unsigned int -gfp_to_alloc_flags(gfp_t gfp_mask) -{ - unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; - ... - alloc_flags |= (__force int) (gfp_mask & __GFP_HIGH); - ... - if (gfp_mask & __GFP_KSWAPD_RECLAIM) - alloc_flags |= ALLOC_KSWAPD; - -#ifdef CONFIG_CMA - if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) - alloc_flags |= ALLOC_CMA; -#endif - return alloc_flags; -} -``` - -CMA机制内存处理代码: - -```c -bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, - int classzone_idx, unsigned int alloc_flags, long free_pages) -{ - ... -#ifdef CONFIG_CMA - if (!(alloc_flags & ALLOC_CMA)) - free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES); -#endif - - if (free_pages <= min + z->lowmem_reserve[classzone_idx]) - return false; - ... -#ifdef CONFIG_CMA - if ((alloc_flags & ALLOC_CMA) && - !list_empty(&area->free_list[MIGRATE_CMA])) { - return true; - } -#endif -} -``` -挂载点:get_page_from_freelist - -原因: - -经过对内核源码的分析,页面申请失败分析工具的理想挂载点应该是慢速路径的入口函数(__alloc_pages_slowpath)。但是这个函数不允许ebpf程序挂载,而且这个函数内部也不存在合理的挂载点,所有将函数挂载点选在快速路径的入口函数get_page_from_freelist上。因为页面申请的控制结构体ac在这两个函数之间不存在信息更改,所以可以确保这两个函数传递的ac结构体是相同的,不会对提取出来的数据产生影响。为了确保数据确实是在页面申请失败的情况下才会打印数据,需要对alloc_pages_nodemask函数的返回值进行挂载,当alloc_pages_nodemask函数没有返回页面结构体page时,也就是页面申请失败的情况下单元提取的数据。 - -### 存在问题 - -■ 打印出来的内存申请标志与申请内存传递进去的标志不符,分析原因可能内核在进行alloc_pages函数之前有对标志位进行处理。 - -■ 因为内存管理区的剩余内存空间处在vm_stat数组第一位,经过分析,使用指针提取的数组第一个数据总是存在差异,需要调整。 - -■ 对打印的标志位需要进一步解析,方便快速确认当前申请页面类型。 - - -## pr - -### 采集信息 - -| 参数 | 含义 | -| ------------- | -------------------------------------------- | -| reclaim | 要回收的页面数量 | -| reclaimed | 已经回收的页面数量 | -| unqueue_dirty | 还没开始回写和还没在队列等待的脏页 | -| congested | 正在块设备上回写的页面,含写入交换空间的页面 | -| writeback | 正在回写的页面 | -### 功能 - -主要用于监控内核中的`shrink_page_list`函数。 -整个BPF程序的功能是监控`shrink_page_list`函数的调用,当函数被调用时,记录特定的内核数据(包括`nr_reclaimed`等值),并将这些数据存储在环形缓冲区中,以供用户空间程序使用。 -跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 - -### 分析 - -在这个函数内部,首先通过`bpf_get_current_pid_tgid()`获取当前进程的PID,并与`user_pid`比较,如果相等,则直接返回,表示不监控当前进程。 -通过`BPF_CORE_READ()`读取给定的内核数据结构`scan_control`中的一些字段的值,并将这些值存储在定义好的事件结构体中。 -通过`bpf_ringbuf_reserve()`尝试在环形缓冲区中预留一段空间,用于存储事件数据。如果预留失败,则直接返回。 -通过bpf_ringbuf_submit()将填充好的事件结构体提交到环形缓冲区中。 -### 挂载点与挂载原因 - -挂载点 - -shrink_page_list - -挂载原因 - -shrink_page_list函数是页面回收后期指向函数,主要操作是遍历链表中每一个页面,根据页面的属性确定将页面添加到回收队列、活跃链表还是不活跃链表.这块遍历的链表是在上一级函数 shrink_inactive_list中定义的临时链表,因为一次最多扫描32个页面,所有链表最多含有32个页面。在shrink_page_list这个函数中还有一个重要操作是统计不同状态的页面数量并保存在scan_control结构体中。而工具数据提取的位置就是找到这个结构体并获取有关性能指标。因为这个提取的数据都是内核函数实时更改的,所有具有较高准确性。 - -scan_control结构体是每次进行内存回收时都会被回收进程重新定义,所有会看到数据是一个增长状态,之后有回归0,这和挂载点也有一定关系。 -## memleak - -### 功能 - -代码主要用于跟踪内核内存分配和释放的情况,并记录相关的统计信息。 - -### 分析 - -首先定义了几个 BPF 映射: -`sizes`:用于存储与每个进程 ID(PID)关联的内存分配大小。 -`allocs`:用于存储与内存分配相关信息,以分配的返回地址为索引。 -`combined_allocs`:另一个哈希映射,用于根据堆栈跟踪 ID 聚合内存分配信息。 -`stack_traces`:堆栈跟踪映射,用于将堆栈跟踪转换为堆栈跟踪 ID。 - -定义了两个 `uprobes`(用户空间探测点): -`malloc_enter`:此探测点附加到 `malloc` 函数的入口点。它将当前进程 ID(pid)的内存分配大小记录到 `sizes` 映射中。 -`malloc_exit`:此探测点附加到 `malloc` 函数的退出点。它从 `sizes` 映射中检索先前记录的大小,将其与分配的返回地址关联,并使用此信息更新 `allocs` 映射。此外,它根据堆栈跟踪更新 `combined_allocs` 映射以聚合分配信息。 - -定义了一个 `uretprobe`(用户空间返回探测点): -`malloc_exit`:此探测点附加到 `malloc` 函数的退出点。它从 sizes 映射中检索先前记录的大小,将其与分配的返回地址关联,并使用此信息更新 `allocs` 映射。此外,它根据堆栈跟踪更新 `combined_allocs` 映射以聚合分配信息。 - -定义了一个 `uprobe`(用户空间探测点): -`free_ente`r:此探测点附加到 `free` 函数的入口点。它从 allocs 映射中检索与正在释放的地址相关的分配信息。然后,它更新 `combined_allocs` 映射以反映释放。 - -# 工具的使用方法说明 - -## 功能介绍 - -mem_watcher工具可以通过一系列的命令控制参数来控制其具体的检测行为:我们可以通过sudo ./mem_watcher -h来查看工具支持的功能 - -``` - select function: - -a, --paf print paf (内存页面状态报告) - -p, --pr print pr (页面回收状态报告) - -r, --procstat print procstat (进程内存状态报告) - -s, --sysstat print sysstat (系统内存状态报告) - -l, --memleak=PID print memleak (内存泄漏检测) -``` - -- -a 输出的信息包括时间戳、进程ID、虚拟内存大小、物理内存等。输出的内容根据用户的选择(特定PID、是否显示RSS等)而变化。除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,主要是present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 -- -p 跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 -- -r 主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息。 -- -s 提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。 -- -l 输出了用户态造成内存泄漏的位置,包括内存泄漏指令地址对应符号名、文件名、行号,程序中尚未被释放的内存总量,未被释放的分配次数。 - -## 使用方法和结果展示 - -## paf - -```c -sudo ./mem_watcher -a -MIN LOW HIGH PRESENT FLAG -262144 5100 6120 262144 1100dca -262144 5100 6120 262144 2800 -262144 5100 6120 262144 cc0 -262144 5100 6120 262144 d00 -262144 5100 6120 262144 2dc2 -...... -``` - -## pr - -```c -sudo ./mem_watcher -p -RECLAIM RECLAIMED UNQUEUE CONGESTED WRITEBACK -16893 0 0 0 0 -16893 24 0 0 0 -16893 24 0 0 0 -16893 40 0 0 0 -16893 64 0 0 0 -16893 64 0 0 0 -16893 66 0 0 0 -...... -``` - -## procstat - -```c -sudo ./mem_watcher -r -...... -01:08:50 334 0 0 0 0 -01:08:50 2984 13194 10242 2952 0 -01:08:50 0 0 0 0 0 -01:08:50 334 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 2984 13194 10242 2952 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 334 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 0 0 0 0 0 -01:08:50 5427 0 0 0 0 -01:08:50 0 0 0 0 0 -...... -``` - -## sysstat - -```c -sudo ./mem_watcher -s -...... -ACTIVE INACTVE ANON_ACT ANON_INA FILE_ACT FILE_INA UNEVICT DIRTY WRITEBK ANONPAG MAP SHMEM -327644 2747936 1988 2278752 325656 469184 0 216 0 563728 249116 7832 -327652 2747616 1996 2278432 325656 469184 0 240 0 563844 249164 7832 -327652 2747616 1996 2278432 325656 469184 0 240 0 563864 249164 7832 -327652 2747844 1996 2278656 325656 469188 0 252 0 563864 249164 7832 -327652 2747844 1996 2278656 325656 469188 0 252 0 563884 249164 7832 -...... -``` - -## memleak - -``` -sudo ./mem_watcher -l 2429 -...... -stack_id=0x3c14 with outstanding allocations: total_size=4 nr_allocs=1 -000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 -000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 -000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 -000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 -00007f1ca1d66609: start_thread @ 0x8530+0xd9 -stack_id=0x3c14 with outstanding allocations: total_size=8 nr_allocs=2 -000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 -000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 -000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 -000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 -00007f1ca1d66609: start_thread @ 0x8530+0xd9 -...... -``` - ------- -## 测试环境 - -deepin20.6,Linux-5.17; - -libbpf:[libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) - diff --git a/eBPF_Supermarket/Memory_Subsystem/bpftool b/eBPF_Supermarket/Memory_Subsystem/bpftool deleted file mode 160000 index 06c61eccd..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/bpftool +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 06c61eccd3b8a6ff3df3e451a2a93058913124fc diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png new file mode 100644 index 000000000..7ca4bdf64 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/10.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png new file mode 100644 index 000000000..e35dd9d0c Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/11.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png new file mode 100644 index 000000000..dd79f597f Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/12.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png new file mode 100644 index 000000000..6f9423fee Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/13.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png new file mode 100644 index 000000000..5012e8e47 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/14.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png b/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png new file mode 100644 index 000000000..3cb9da295 Binary files /dev/null and b/eBPF_Supermarket/Memory_Subsystem/docs/image/9.png differ diff --git a/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md b/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md new file mode 100644 index 000000000..b64ce6a24 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md @@ -0,0 +1,43 @@ +# mem_watcher可视化 +## 配置环境 +在使用mem_watcher可视化之前,请先配置docker、go的环境,具体配置方法可参考: +### 配置docker +[在 Ubuntu 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/ubuntu/#install-docker-desktop) +[在 Linux 上安装 Docker Desktop |Docker 文档](https://docs.docker.com/desktop/install/linux-install/) +### go环境 +配置好dacker环境后接着配置go的环境 +我们首先对我们现有的工具进行编译 +- 首先先进入lmp目录下的`lmp/eBPF_Supermarket/Memory_Subsystem/mem_watcher`文件夹 +然后进行编译,其实这一步也可以忽略掉 +```c + make -j 20 +``` +- 在lmp目录下的`eBPF_Visualization/eBPF_prometheus`文件夹下 +- 执行make指令,编译可视化的go语言工具 +- 执行`make start_service`指令,配置下载docker镜像并启动grafana和prometheus服务 +- 执行如下指令开始采集数据以及相关处理: +```c + ./data-visual collect lmp/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher -p +``` +切记根据自己的文件所在目录进行修改,如果不知道或者不确定的可以在自己的程序文件下输入`pwd`命令进行查看,如果目录出现错误会失败。 + +在网页打开网址:http://81.70.204.7:8090/metrics 此处为localhost:8090/metrics,便可以看到http网页中的数据; +这个网址需要根据你自己的虚拟机的ip进行修改。 +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a2aab03835bf44ca8a27023c8c445bad.png) +- 在网页打开网址:http://81.70.204.7:3000/ 即可进入grafana服务,使用初始密码登录(user:admin pswd: admin)进入管理界面: +- 点击【Home-Connection-Add new connection】,选择Prometheus,建立与Prometheus服务器的连接: +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/1e41ec78f5fa4923a5a1e18cab233c62.png) + +这个172.17.0.1表示docker0网桥的 IPv4 地址。在 Docker 中,通常会将docker0的第一个 IP 地址分配给Docker主机自身。因此,172.17.0.1是 Docker主机上Docker守护进程的 IP 地址,所以在Grafana数据源这块设置成http://172.17.0.1:9090 ,然后点击下面的【Save & test】按钮 +- 进入可视化配置界面: +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6e37ecb4c62245b2a68a80dc07643ac9.png) +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/fbdb65ef1cc54c128594a96cad5e2386.png) +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4f3a33b65929429b96b683e573593938.png) +## mem_watcher子工具可视化输出(样例) +### mem_watcher -p +- reclaimed +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/eed35e475ba6422ca176f966b011902c.png) +可以进行压力测试 +首先使用`sudo apt-get install stress`安装压力测试工具。 +使用下面的命令对内存进行施压 `stress -m 3 --vm-bytes 300M` +会生成3个进程,每个进程占用300M内存。 diff --git a/eBPF_Supermarket/Memory_Subsystem/libbpf b/eBPF_Supermarket/Memory_Subsystem/libbpf deleted file mode 160000 index 46eafba62..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/libbpf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 46eafba62ee380a1f8230e2d5e81f65e0e030f15 diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile index 5847750c7..7238a8af4 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/Makefile @@ -1,13 +1,13 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool -LIBBLAZESYM_SRC := $(abspath ../../blazesym/) -LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/capi/include) +LIBBLAZESYM_SRC := $(abspath ../blazesym/) +LIBBLAZESYM_INC := $(abspath ../blazesym/capi/include) LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym_c.a) ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ @@ -16,22 +16,23 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h +VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) +INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = paf pr procstat sysstat memleak +APPS = paf pr procstat sysstat memleak fraginfo vmasnap CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) BZS_APPS := else -BZS_APPS := mem_watcher -APPS += $(BZS_APPS) +BZS_APPS := +TARGETS= mem_watcher + # Required by libblazesym ALL_LDFLAGS += -lrt -ldl -lpthread -lm endif @@ -69,12 +70,12 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(APPS) +all: $(TARGETS) .PHONY: clean clean: $(call msg,CLEAN) - $(Q)rm -rf $(OUTPUT) $(APPS) + $(Q)rm -rf $(OUTPUT) $(TARGETS) $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) @@ -102,7 +103,7 @@ $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a | $(OUTPUT $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a $@ # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -110,25 +111,39 @@ $(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) # Generate BPF skeletons -$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) +.PHONY: $(APPS) +$(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(call msg,GEN-SKEL,$@) - $(Q)$(BPFTOOL) gen skeleton $< > $@ + $(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h # Build user-space code -$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h - $(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) + $(call msg,CC,$@) + $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ -$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_OBJ) +$(patsubst %,$(OUTPUT)/%.o,$(TARGETS)): $(LIBBLAZESYM_OBJ) -$(BZS_APPS): $(LIBBLAZESYM_OBJ) +$(TARGETS): $(LIBBLAZESYM_OBJ) # Build application binary -$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) +$(TARGETS): %: $(OUTPUT)/%.o $(COMMON_OBJ) $(LIBBPF_OBJ) | $(OUTPUT) $(call msg,BINARY,$@) - $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ + $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ + +SUCCESS_MESSAGE: + @echo "\e[38;2;255;102;204m _ _ \e[0m" + @echo "\e[38;2;255;153;204m _ __ ___ ___ _ __ ___ __ ____ _| |_ ___| |__ ___ _ __ \e[0m" + @echo "\e[38;2;255;204;204m | '_ \` _ \ / _ \ '_ \` _ \ \ \ /\ / / _\` | __/ __| '_ \ / _ \ '__|\e[0m" + @echo "\e[38;2;204;229;255m | | | | | | __/ | | | | | \ V V / (_| | || (__| | | | __/ | \e[0m" + @echo "\e[38;2;153;204;255m |_| |_| |_|\___|_| |_| |_| \_/\_/ \__ _|\__\___|_| |_|\___|_| \e[0m" + + @echo "\e[38;2;0;255;255mSuccessful to compile mem_watcher tools: \e[0m" + @echo "\e[38;2;0;255;255mPlease start your use ~ \e[0m" +all: $(TARGETS) SUCCESS_MESSAGE # delete failed targets .DELETE_ON_ERROR: diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md new file mode 100644 index 000000000..fe18a83cc --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/README.md @@ -0,0 +1,457 @@ +# mem\_watcher + +## mem\_watcher介绍 + +**memwatcher是一款基于eBPF的内存监测工具,其设计的目的就是为了可以让用户能够在主机环境上可以快捷的检测到Linux内存的详细信息。** **通过高效的数据收集和精准的监控能力,帮助用户可以有效的监控主机内存情况。** **使用了eBPF(Extended Berkeley Packet Filter)来监控内核中的几个关键事件,主要涉及到内存管理方面的几个功能:** + +**本项目是内存性能分析工具集合,采用libbpf编写。现有工具procstat(进程内存状态报告),sysstat(系统内存状态报告),paf(内存页面状态报告),pr(内存回收状态报告)和memleak(内存泄漏检测)。** + +**eBPF 提供了一种高效的机制来监控和追踪系统级别的事件,包括内存的分配和释放。通过 eBPF,可以跟踪内存分配和释放的请求,并收集每次分配的调用堆栈。然后,分析这些信息,找出执行了内存分配但未执行释放操作的调用堆栈,这有助于程序员找出导致内存泄漏的源头。** + +--- + +## 背景意义 + +**内存子系统是Linux内核中是一个相对复杂的模块,内核中几乎所有的数据、缓存、程序指令都有内存模块参与管理。在内存不足的情况下,这些数据就会被存储在磁盘的交换空间中,但是磁盘的处理速度相对与内存非常慢,当内存和磁盘频繁进行数据交换时,缓慢的磁盘读写速度非常影响系统性能。系统可能因内存不足从而终止那些占用内存较大的进程,导致程序运行故障。因此准确的监控分析内存性能状况就变得非常重要。** + +**目前,传统的内存性能分析工具通过读取proc文件系统下的数据,经过简单的处理后呈现给用户,方便管理人员随时了解系统状况。然而这些工具的灵活性非常差,单个工具输出的信息有限。系统维护人员在分析性能问题时常常需要借助多个工具才能进行。步骤繁琐且工具本身对系统性能也有一定影响。随着ebpf技术在系统可观测上的发展,利于ebpf非侵入式的数据获取方式已被大多数企业、高校认可并取得了一定的研究成果。ebpf的可编程性可以让管理人员灵活的获取系统的运行数据,而且在数据的提取粒度上有着传统工具无法比拟的优势。现在,ebpf作为Linux内核顶级子系统,已经成为实现Linux内核可观测性、网络和内核安全的理想技术。** + +## 准备工作 + +**环境:Ubuntu 22.04, 内核版本 5.15.0-107-generic及以上** + +**注:由于 eBPF 的 kprobe 逻辑与内核数据结构定义高度相关,而现在 BTF 的应用(可消除不同内核版本间数据结构的不兼容)还不是很成熟,因此在使用此例程前,需首先适配内核版本。** + +**软件:** + +* **go SDK(安装cilium库)** +* **llvm,clang,rust** +* **bpftrace** + +## 环境搭建 + +1. **rust 语言编译环境安装** + **`blazesym` 使用 `rust` 语言编写,使用前需要安装 `rust` 语言的编译环境** + ``` + # 安装前先配置国内镜像源,可以加速下载 + # 设置环境变量 RUSTUP_DIST_SERVER (用于更新 toolchain): + export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static + # RUSTUP_UPDATE_ROOT (用于更新 rustup): + export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup + + # 安装 https://www.rust-lang.org/tools/install + # 请不要使用Ubuntu的安装命令: sudo apt install cargo,否则可能会出现莫名其妙的问题 + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + + # 修改 ~/.cargo/config 文件,配置 rust 使用的国内镜像源 + [source.crates-io] + registry = "https://github.com/rust-lang/crates.io-index" + replace-with = 'ustc' + + [source.ustc] + registry = "git://mirrors.ustc.edu.cn/crates.io-index" + ``` + +# 工具的使用方法说明 + +## 功能介绍 + +**mem\_watcher工具可以通过一系列的命令控制参数来控制其具体的检测行为:我们可以通过sudo ./mem\_watcher -h来查看工具支持的功能** + +``` +mem_watcher is in use .... + + Select function: + + Memory Page Reports: + -a, --paf                 Print paf (内存页面状态报告) + + Page Reclaim Reports: + -p, --pr                   Print pr (页面回收状态报告) + + Process Memory Reports: + -P, --choose_pid=PID       选择进程号打印 + -r, --procstat             Print procstat (进程内存状态报告) + -R, --Rss                 打印进程页面 + + System Memory Reports: + -s, --sysstat             Print sysstat (系统内存状态报告) + -n, --part2               System Memory Reports (Part 2) + + Memory Leak Detection: + -l, --memleak             Print memleak (内核态内存泄漏检测) +     --choose_pid=PID       选择进程号打印, Print memleak (用户态内存泄漏检测) +``` + +* **-a 输出的信息跟踪内核中页面的回收行为。包括present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。** +* **-p 跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。** +* **-r 主要是用于跟踪用户空间进程的内存使用情况。具体功能是在用户空间进程切换时,记录切换前进程的内存信息。如果要选择进程进行跟踪,则输入进程的pid;输入参数R,显示进程使用的页面数量。** +* **-s 提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。该功能分为了两部分输出,输入参数“n”,输出第二部分性能指标。** +* **-l 输出了用户态造成内存泄漏的位置,包括内存泄漏指令地址对应符号名,程序中尚未被释放的内存总量,未被释放的分配次数。也可以输出内核态的内存泄漏。如跟踪内核态内存泄漏,只输入参数-l;如跟踪用户态内存泄漏,则在获取用户态进程的pid后,再输入要跟踪进程的pid。** + +--- + +# mem\_watcher具体功能分析 + +## procstat + +### 采集信息: + + +| **参数** | **含义** | +| ------------ | ---------------------------- | +| **vsize** | **进程使用的虚拟内存** | +| **size** | **进程使用的最大物理内存** | +| **rssanon** | **进程使用的匿名页面** | +| **rssfile** | **进程使用的文件映射页面** | +| **rssshmem** | **进程使用的共享内存页面** | +| **vswap** | **进程使用的交换分区大小** | +| **vdata** | **进程使用的私有数据段大小** | +| **vpte** | **进程页表大小** | +| **vstk** | **进程用户栈大小** | + +### 载点及挂载原因 + +**挂载点:finish\_task\_switch** + +**挂载原因:** + +**首先,获取进程级别内存使用信息首先需要获取到进程的task\_struct结构体,其中在mm\_struct成员中存在一个保存进程当前内存使用状态的数组结构,因此有关进程的大部分内存使用信息都可以通过这个数组获得。其次,需要注意函数的插入点,插入点的选取关系到数据准确性是否得到保证,而在进程的内存申请,释放,规整等代码路径上都存在页面状态改变,但是数量信息还没有更新的相关结构中的情况,如果插入点这两者中间,数据就会和实际情况存在差异,所有在确保可以获取到进程PCB的前提下,选择在进程调度代码路径上考虑。而finish\_task\_switch函数是新一个进程第一个执行的函数,做的事却是给上一个被调度出去的进程做收尾工作,所有这个函数的参数是上一个进程的PCB,从这块获得上一个进程的内存信息就可以确保在它没有再次被调度上CPU执行的这段时间内的内存数据稳定性。因此最后选择将程序挂载到finish\_task\_switch函数上。** + +### 可以解决的问题 + +**首先,通过监测这些指标,可以及时发现内存使用异常或泄漏问题,针对性地进行性能优化和内存管理,确保系统运行的高效性和稳定性。其次,了解进程的内存占用情况有助于有效管理系统资源,避免资源浪费和冲突,提高整体资源利用率。此外,当系统出现内存相关的故障或异常时,通过分析这些指标可以快速定位问题所在,有针对性地进行故障排除和修复,缩短系统恢复时间。结合这些指标可以进行性能调优,优化内存分配和释放策略,提升系统的响应速度和整体性能表现。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -r +...... +01:08:50 334      0        0        0        0   +01:08:50 2984     13194    10242    2952     0   +01:08:50 0        0        0        0        0   +01:08:50 334      0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 2984     13194    10242    2952     0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 334      0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 0        0        0        0        0   +01:08:50 5427     0        0        0        0   +01:08:50 0        0        0        0        0   +...... +``` + +## sysstat + +### 采集信息: + + +| **参数** | **含义** | +| ------------------ | ------------------------------------ | +| **active** | **LRU活跃内存大小** | +| **inactive** | **LRU不活跃内存大小** | +| **anon\_active** | **活跃匿名内存大小** | +| **anon\_inactive** | **不活跃匿名内存大小** | +| **file\_active** | **活跃文件映射内存大小** | +| **file\_inactive** | **不活跃文件映射内存大小** | +| **unevictable** | **不可回收内存大小** | +| **dirty** | **脏页大小** | +| **writeback** | **正在回写的内存大小** | +| **anonpages** | **RMAP页面** | +| **mapped** | **所有映射到用户地址空间的内存大小** | +| **shmem** | **共享内存** | +| **kreclaimable** | **内核可回收内存** | +| **slab** | **用于slab的内存大小** | +| **sreclaimable** | **可回收slab内存** | +| **sunreclaim** | **不可回收slab内存** | +| **NFS\_unstable** | **NFS中还没写到磁盘中的内存** | +| **writebacktmp** | **回写所使用的临时缓存大小** | +| **anonhugepages** | **透明巨页大小** | +| **shmemhugepages** | **shmem或tmpfs使用的透明巨页** | + +### 功能 + +**提取各种类型内存的活动和非活动页面数量,以及其他内存回收相关的统计数据,除了常规的事件信息外,程序还输出了与内存管理相关的详细信息,包括了不同类型内存的活动(active)和非活动(inactive)页面,未被驱逐(unevictable)页面,脏(dirty)页面,写回(writeback)页面,映射(mapped)页面,以及各种类型的内存回收相关统计数据。** + +### 挂载点及挂载原因 + +**挂载点:get\_page\_from\_freelist** + +**原因:** + +**首先,内存状态数据的提取需要获取到内存节点pglist\_data数据结构,这个结构是对内存的总体抽象。pglist\_data数据结构末尾有个vm\_stat的数组,里面包含了当前内存节点所有的状态信息。所有只需要获取到pglist\_data结构就能拿到当前的内存状态信息。但是物理内存分配在选择内存节点是通过mempolicy结构获取,无法获得具体的节点结构。选择内存节点的函数处理流程如下:** + +``` +struct mempolicy *get_task_policy(struct task_struct *p) +{ +        struct mempolicy *pol = p->mempolicy;//根据当前task_struct取得 +        int node; + +        if (pol) +                return pol; + +        node = numa_node_id(); +        if (node != NUMA_NO_NODE) {//存在其他节点 +                pol = &preferred_node_policy[node]; +                /* preferred_node_policy is not initialised early in boot */ +                if (pol->mode) +                        return pol; +       }   + +        return &default_policy;//不存在其他节点返回本地节点 +} +``` + +**经过对内存申请的内部结构alloc\_context分析(这是内存申请过程中临时保存相关参数的结构),当前内存节点是可以通过:alloc\_context——>zoneref——>zone——>pglist\_data的路径访问到。** + +**其次,因为函数执行申请内存的过程对获取内存节点数据的影响不大,所以只要可以获得alloc\_context数据结构,在整个申请路径上挂载函数都是可以的。sysstat工具选择的挂载点是get\_page\_from\_freelist函数。这个函数是快速物理内存分配的入口函数。因为内核在进行物理内存分配时,都会进入快速路径分配,只有当失败时才会进入慢速路径,所以get\_page\_from\_freelist函数是必经函数。** + +**但是,经过对proc文件系统的打印函数meminfo\_proc\_show函数的分析得知,影响内存性能的参数在vm\_stat中无法全部获得。一部分数据需要遍历当前内存节点包含的所有内存管理区zone结构中vm\_stat数组获得,一部分需要读取全局变量vm\_node\_stat获得。但是内核的全局变量不会作为函数参数参与数据处理,目前还没具体方法获得这部分数据。** + +### 存在问题 + +**■ 部分性能数据存在于内核全局变量中,而这些数据不会作为函数参数存储在栈中,因此这些数据目前还没实现统计** + +**■ 因为内核对内存管理不会是物理内存的全部容量,而且最大管理内存的数据结构是内存结点,所以以上统计数据是以当前内存结点实际管理的内存容量为基准。** + +**■ 当前剩余内存总量的统计需要遍历所有内存管理区来统计,但是由于内存管理区的空闲页面信息存储在数组第一个位置,使用指针指向时,统计到的数据不准确,使用变量统计会出现数据类型错误的报告。** + +### 可以解决的问题 + +**通过提取内存指标,可以及时发现潜在问题,如内存泄漏,从而采取相应措施。此外,了解各种内存类型的使用情况有助于合理分配资源,提高系统效率,并确保数据一致性。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -s +...... +ACTIVE   INACTVE  ANON_ACT ANON_INA FILE_ACT FILE_INA UNEVICT  DIRTY    WRITEBK  ANONPAG  MAP      SHMEM   +327644   2747936  1988     2278752  325656   469184   0        216      0        563728   249116   7832   +327652   2747616  1996     2278432  325656   469184   0        240      0        563844   249164   7832   +327652   2747616  1996     2278432  325656   469184   0        240      0        563864   249164   7832   +327652   2747844  1996     2278656  325656   469188   0        252      0        563864   249164   7832   +327652   2747844  1996     2278656  325656   469188   0        252      0        563884   249164   7832 +...... +``` + +## paf + +### 采集信息 + + +| **参数** | **含义** | +| ----------- | ---------------------------------------- | +| **min** | **内存管理区处于最低警戒水位的页面数量** | +| **low** | **内存管理区处于低水位的页面数量** | +| **high** | **内存管理区处于高水位的页面数量** | +| **present** | **内存管理区实际管理的页面数量** | +| **flag** | **申请页面时的权限(标志)** | + +### 功能 + +**主要是监控内核中的 **`get_page_from_freelist`函数。这个函数在内核中用于从内存空闲页列表中获取一个页面。** **程序主要是输出present(当前内存中可用的页面数量),min(在这个阈值下,系统可能会触发内存压缩),low(在这个阈值下,系统进行内存回收),high(在这个阈值上,认为内存资源充足),flag(用于内存分配的状态)。 + +### 注: + +**内存申请失败一般集中在申请权限不够或者是权限冲突导致,申请权限不够是当内核申请优先级较低的页面时,虽然内存管理区有足够的页面满足这次申请数量,但是当前剩余空闲页面少于最低警戒水位,因此导致内核无法成功分配页面的情况。权限冲突,例如内核在开启CMA机制下导致的页面页面申请失败的情况,这种情况下管理区空闲页面需要减去CMA机制占用内存才是当前可分配内存。相关权限判断代码如下:** + +**添加CMA权限代码路径mm/page\_alloc.c** + +``` +static inline unsigned int +gfp_to_alloc_flags(gfp_t gfp_mask) +{ + unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET; +   ... + alloc_flags |= (__force int) (gfp_mask & __GFP_HIGH); +   ... + if (gfp_mask & __GFP_KSWAPD_RECLAIM) + alloc_flags |= ALLOC_KSWAPD; +   +#ifdef CONFIG_CMA + if (gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) + alloc_flags |= ALLOC_CMA; +#endif + return alloc_flags; +} +``` + +**CMA机制内存处理代码:** + +``` +bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, +                         int classzone_idx, unsigned int alloc_flags, long free_pages) +{ +   ... +#ifdef CONFIG_CMA +        if (!(alloc_flags & ALLOC_CMA)) +                free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES); +#endif   +   +        if (free_pages <= min + z->lowmem_reserve[classzone_idx]) +                return false; + ... +#ifdef CONFIG_CMA +       if ((alloc_flags & ALLOC_CMA) && +                !list_empty(&area->free_list[MIGRATE_CMA])) { +                        return true; +               } +#endif +} +``` + +**挂载点:get\_page\_from\_freelist** + +**原因:** + +**经过对内核源码的分析,页面申请失败分析工具的理想挂载点应该是慢速路径的入口函数(\_\_alloc\_pages\_slowpath)。但是这个函数不允许ebpf程序挂载,而且这个函数内部也不存在合理的挂载点,所有将函数挂载点选在快速路径的入口函数get\_page\_from\_freelist上。因为页面申请的控制结构体ac在这两个函数之间不存在信息更改,所以可以确保这两个函数传递的ac结构体是相同的,不会对提取出来的数据产生影响。为了确保数据确实是在页面申请失败的情况下才会打印数据,需要对alloc\_pages\_nodemask函数的返回值进行挂载,当alloc\_pages\_nodemask函数没有返回页面结构体page时,也就是页面申请失败的情况下单元提取的数据。** + +### 存在问题 + +**■ 打印出来的内存申请标志与申请内存传递进去的标志不符,分析原因可能内核在进行alloc\_pages函数之前有对标志位进行处理。** + +**■ 因为内存管理区的剩余内存空间处在vm\_stat数组第一位,经过分析,使用指针提取的数组第一个数据总是存在差异,需要调整。** + +**■ 对打印的标志位需要进一步解析,方便快速确认当前申请页面类型。** + +### 可以解决的问题 + +**可以帮助监控系统内存的使用情况,预测内存不足的可能性,以及在必要时触发相应的内存回收机制来保证系统的稳定性和性能。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -a +MIN      LOW       HIGH     PRESENT  FLAG   +262144   5100      6120     262144   1100dca +262144   5100      6120     262144   2800   +262144   5100      6120     262144   cc0   +262144   5100      6120     262144   d00   +262144   5100      6120     262144   2dc2   +...... +``` + +## pr + +### 采集信息 + + +| **参数** | **含义** | +| ------------------ | ------------------------------------------------ | +| **reclaim** | **要回收的页面数量** | +| **reclaimed** | **已经回收的页面数量** | +| **unqueue\_dirty** | **还没开始回写和还没在队列等待的脏页** | +| **congested** | **正在块设备上回写的页面,含写入交换空间的页面** | +| **writeback** | **正在回写的页面** | + +### 功能 + +**主要用于监控内核中的 **`shrink_page_list`函数。** **整个BPF程序的功能是监控 `shrink_page_list`函数的调用,当函数被调用时,记录特定的内核数据(包括 `nr_reclaimed`等值),并将这些数据存储在环形缓冲区中,以供用户空间程序使用。** **跟踪内核中页面的回收行为,记录回收的各个阶段,例如要回收的页面,以回收的页面,等待回收的脏页数,要写回的页数(包括交换空间中的页数)以及当前正在写回的页数。 + +### 挂载点与挂载原因 + +**挂载点** + +**shrink\_page\_list** + +**挂载原因** + +**shrink\_page\_list函数是页面回收后期指向函数,主要操作是遍历链表中每一个页面,根据页面的属性确定将页面添加到回收队列、活跃链表还是不活跃链表.这块遍历的链表是在上一级函数 shrink\_inactive\_list中定义的临时链表,因为一次最多扫描32个页面,所有链表最多含有32个页面。在shrink\_page\_list这个函数中还有一个重要操作是统计不同状态的页面数量并保存在scan\_control结构体中。而工具数据提取的位置就是找到这个结构体并获取有关性能指标。因为这个提取的数据都是内核函数实时更改的,所有具有较高准确性。** **scan\_control结构体是每次进行内存回收时都会被回收进程重新定义,所有会看到数据是一个增长状态,之后有回归0,这和挂载点也有一定关系。** + +### 可以解决的问题 + +**监测系统的页面回收和写回情况,对系统内存使用和性能优化非常重要。整体来说,这些参数可以帮助系统管理员或开发人员了解系统内存管理的情况,包括页面回收的效率、脏页处理情况以及写回操作的进度。** + +### 使用方法和结果展示 + +``` +sudo ./mem_watcher -p +RECLAIM RECLAIMED UNQUEUE CONGESTED WRITEBACK +16893 0 0 0 0 +16893 24 0 0 0 +16893 24 0 0 0 +16893 40 0 0 0 +16893 64 0 0 0 +16893 64 0 0 0 +16893 66 0 0 0 +...... +``` + +## memleak + +### 计算方式 + +1. **当调用内存分配相关的函数(如malloc、calloc等)时,程序记录分配的大小并跟踪分配返回的地址。这涉及更新两个BPF映射,一个记录PID与分配大小的对应关系,另一个记录分配地址与分配详情的对应关系。** +2. **当调用free或相关函数时,程序查找之前记录的分配地址,若找到,则更新或删除对应映射中的记录。** +3. **对于每次分配,程序尝试获取调用堆栈的ID,这通过bpf\_get\_stackid()实现。堆栈ID用于识别特定的调用序列,帮助理解分配发生的上下文。** +4. **使用堆栈ID将相同堆栈上的分配合并,记录总分配大小和次数。这涉及到在BPF映射中累加新的分配或减去释放的分配。程序使用****sync\_fetch\_and\_add和**sync\_fetch\_and\_sub等原子操作来更新共享数据。这确保即使在高并发的环境下,数据更新也是安全的。 + +### 采集信息 + + +| **参数** | **含义** | +| --------------- | ---------------------------------- | +| **stack\_id** | **触发分配的的调用堆栈ID** | +| **total\_size** | **表示程序中尚未被释放的内存总量** | +| **nr\_allocs** | **所有未释放分配的总次数** | +| **input\_addr** | **堆栈中的指令地址** | +| **name** | **符号名** | + +### 功能 + +**代码主要用于跟踪内核内存分配和释放的情况,并记录相关的统计信息。** + +### 可以解决的问题 + +**可以帮助用户准确的找出内存泄露的位置,更好的排查出所存在的问题。** + +### 使用方法和结果展示 + +**用户态内存泄漏** + +``` +sudo ./mem_watcher -l -P 2429 +...... +stack_id=0x3c14 with outstanding allocations: total_size=4 nr_allocs=1 +000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 +000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 +000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 +000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 +00007f1ca1d66609: start_thread @ 0x8530+0xd9 +stack_id=0x3c14 with outstanding allocations: total_size=8 nr_allocs=2 +000055e032027205: alloc_v3 @ 0x11e9+0x1c /test_leak.c:11 +000055e032027228: alloc_v2 @ 0x120f+0x19 /test_leak.c:17 +000055e03202724b: alloc_v1 @ 0x1232+0x19 /test_leak.c:23 +000055e032027287: memory_leak @ 0x1255+0x32 /test_leak.c:35 +00007f1ca1d66609: start_thread @ 0x8530+0xd9 +...... +``` + +**内核态内存泄漏** + +``` +sudo ./mem_watcher -l +...... +[19:49:22] Top 10 stacks with outstanding allocations: +ffffffff95127b02: __alloc_pages @ 0xffffffff951278a0+0x262 +ffffffff95127b02: __alloc_pages @ 0xffffffff951278a0+0x262 +ffffffff95147bb0: alloc_pages @ 0xffffffff95147b20+0x90 +ffffffff950b40e7: __page_cache_alloc @ 0xffffffff950b4060+0x87 +ffffffff950b75f0: pagecache_get_page @ 0xffffffff950b74a0+0x150 +ffffffff951e1f92: __getblk_gfp @ 0xffffffff951e1ea0+0xf2 +ffffffff952d143c: jbd2_journal_get_descriptor_buffer @ 0xffffffff952d13e0+0x5c +ffffffff952c724b: journal_submit_commit_record.part.0 @ 0xffffffff952c7210+0x3b +ffffffff952c8839: jbd2_journal_commit_transaction @ 0xffffffff952c7540+0x12f9 +ffffffff952ce166: kjournald2 @ 0xffffffff952ce0b0+0xb6 +...... +``` diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c new file mode 100644 index 000000000..342411373 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/fraginfo.bpf.c @@ -0,0 +1,99 @@ +#include "vmlinux.h" +#include +#include +#include +#include "fraginfo.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key, u64); + __type(value, struct zone_info); +} zones SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key, u64); + __type(value, struct pgdat_info); +} nodes SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 102400); + __type(key,struct order_zone); + __type(value, struct ctg_info); +} orders SEC(".maps"); + +static void fill_contig_page_info(struct zone *zone, unsigned int suitable_order, + struct contig_page_info *info) +{ + unsigned int order; + info->free_pages = 0; + info->free_blocks_total = 0; + info->free_blocks_suitable = 0; + for (order = 0; order <= MAX_ORDER; order++) { + unsigned long blocks; + unsigned long nr_free; + nr_free = BPF_CORE_READ(&zone->free_area[order], nr_free); + blocks = nr_free; + info->free_blocks_total += blocks; + info->free_pages += blocks << order; + if (order >= suitable_order) + info->free_blocks_suitable += blocks << (order - suitable_order); + } +} + +SEC("kprobe/get_page_from_freelist") +int BPF_KPROBE(get_page_from_freelist, gfp_t gfp_mask, unsigned int order, int alloc_flags, + const struct alloc_context *ac) +{ + // bpf_printk("1111"); + struct pgdat_info node_info = {}; + struct zone_info zone_data = {}; + + struct pglist_data *pgdat; + struct zoneref *zref; + struct zone *z; + int i; + unsigned int a_order; + + pgdat = BPF_CORE_READ(ac, preferred_zoneref, zone, zone_pgdat); + node_info.node_id = BPF_CORE_READ(pgdat, node_id); + node_info.nr_zones = BPF_CORE_READ(pgdat, nr_zones); + node_info.pgdat_ptr = (u64)pgdat; + u64 key = (u64)pgdat; + + bpf_map_update_elem(&nodes, &key, &node_info, BPF_ANY); + + for (i = 0; i < __MAX_NR_ZONES; i++) { + zref = &pgdat->node_zonelists[0]._zonerefs[i]; + z = BPF_CORE_READ(zref, zone); + if ((u64)z == 0) break; + zone_data.zone_ptr = (u64)z; + u64 zone_key = (u64)z; + zone_data.zone_start_pfn = BPF_CORE_READ(z, zone_start_pfn); + zone_data.spanned_pages = BPF_CORE_READ(z, spanned_pages); + zone_data.present_pages = BPF_CORE_READ(z, present_pages); + bpf_probe_read_kernel_str(zone_data.comm, sizeof(zone_data.comm), BPF_CORE_READ(z, name)); + for (a_order = 0; a_order <= MAX_ORDER; ++a_order) { + zone_data.order = a_order; + struct order_zone order_key = {}; + order_key.order = a_order; + if ((u64)z == 0) break; + order_key.zone_ptr = (u64)z; + struct contig_page_info ctg_info = {}; + fill_contig_page_info(z, a_order, &ctg_info); + bpf_map_update_elem(&orders,&order_key,&ctg_info,BPF_ANY); + bpf_printk("Order: %d, Free pages: %lu, Free blocks total: %lu, Free blocks suitable: %lu", + a_order, ctg_info.free_pages, ctg_info.free_blocks_total, ctg_info.free_blocks_suitable); + bpf_printk("2"); + } + + bpf_map_update_elem(&zones, &zone_key, &zone_data, BPF_ANY); + } + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c similarity index 55% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c index d8b5d0d31..00ef2efe1 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c @@ -19,10 +19,10 @@ #include "vmlinux.h" #include #include +#include #include "mem_watcher.h" - -#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP) -#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) + +const volatile __u64 stack_flags = 0; struct { __uint(type, BPF_MAP_TYPE_HASH); @@ -62,6 +62,20 @@ struct { __type(value, u64); // 用户态指针变量 memptr } memptrs SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u64); /* alloc return address */ + __type(value, u64); /* timestamp */ + __uint(max_entries, 10240); +} addr_times SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u64); /* alloc return address */ + __type(value, u64); /* timestamp */ + __uint(max_entries, 10240); +} first_time SEC(".maps"); + char LICENSE[] SEC("license") = "Dual BSD/GPL"; static int gen_alloc_enter(size_t size) { @@ -88,10 +102,14 @@ static int gen_alloc_exit2(void *ctx, u64 address) { bpf_map_delete_elem(&sizes, &pid); if (0 != address) { - info.stack_id = bpf_get_stackid(ctx, &stack_traces, USER_STACKID_FLAGS); + info.stack_id = bpf_get_stackid(ctx, &stack_traces, stack_flags); bpf_map_update_elem(&allocs, &addr, &info, BPF_ANY); + // Initialize the addr_times map to 0 + __u64 zero_ts = 0; + bpf_map_update_elem(&addr_times, &addr, &zero_ts, BPF_ANY); + union combined_alloc_info add_cinfo = { .total_size = info.size, .number_of_allocs = 1 @@ -135,6 +153,10 @@ static int gen_free_enter(const void *address) { bpf_map_delete_elem(&allocs, &addr); + // Initialize the addr_times map to 0 + __u64 zero_ts = 0; + bpf_map_update_elem(&addr_times, &addr, &zero_ts, BPF_ANY); + return 0; } @@ -258,4 +280,191 @@ int BPF_KPROBE(pvalloc_enter, size_t size) { SEC("uretprobe") int BPF_KRETPROBE(pvalloc_exit) { return gen_alloc_exit(ctx); +} + +struct trace_event_raw_kmem_alloc_node___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kmem_alloc_node(void) { + if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc_node___x)) + return true; + return false; +} + +struct trace_event_raw_kmem_alloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmalloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmem_cache_alloc___x { + const void *ptr; + size_t bytes_alloc; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kmem_alloc(void) +{ + if (bpf_core_type_exists(struct trace_event_raw_kmem_alloc___x)) + return true; + return false; +} + +SEC("tracepoint/kmem/kmalloc") +int memleak__kmalloc(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc()) { + struct trace_event_raw_kmem_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } else { + struct trace_event_raw_kmalloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); +} + +SEC("tracepoint/kmem/kmalloc_node") +int memleak__kmalloc_node(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc_node()) { + struct trace_event_raw_kmem_alloc_node___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + + gen_alloc_enter( bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); + } else { + /* tracepoint is disabled if not exist, avoid compile warning */ + return 0; + } +} + +struct trace_event_raw_kmem_free___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kfree___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +struct trace_event_raw_kmem_cache_free___x { + const void *ptr; +} __attribute__((preserve_access_index)); + +static __always_inline bool has_kfree() +{ + if (bpf_core_type_exists(struct trace_event_raw_kfree___x)) + return true; + return false; +} + +static __always_inline bool has_kmem_cache_free() +{ + if (bpf_core_type_exists(struct trace_event_raw_kmem_cache_free___x)) + return true; + return false; +} + +SEC("tracepoint/kmem/kfree") +int memleak__kfree(void *ctx) +{ + const void *ptr; + + if (has_kfree()) { + struct trace_event_raw_kfree___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } else { + struct trace_event_raw_kmem_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } + + return gen_free_enter(ptr); +} + +SEC("tracepoint/kmem/kmem_cache_alloc") +int memleak__kmem_cache_alloc(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc()) { + struct trace_event_raw_kmem_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } else { + struct trace_event_raw_kmem_cache_alloc___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + } + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); +} + +SEC("tracepoint/kmem/kmem_cache_alloc_node") +int memleak__kmem_cache_alloc_node(void *ctx) +{ + const void *ptr; + size_t bytes_alloc; + + if (has_kmem_alloc_node()) { + struct trace_event_raw_kmem_alloc_node___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + bytes_alloc = BPF_CORE_READ(args, bytes_alloc); + + gen_alloc_enter(bytes_alloc); + + return gen_alloc_exit2(ctx, (u64)ptr); + } else { + /* tracepoint is disabled if not exist, avoid compile warning */ + return 0; + } +} + +SEC("tracepoint/kmem/kmem_cache_free") +int memleak__kmem_cache_free(void *ctx) +{ + const void *ptr; + + if (has_kmem_cache_free()) { + struct trace_event_raw_kmem_cache_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } else { + struct trace_event_raw_kmem_free___x *args = ctx; + ptr = BPF_CORE_READ(args, ptr); + } + + return gen_free_enter(ptr); +} + +SEC("tracepoint/kmem/mm_page_alloc") +int memleak__mm_page_alloc(struct trace_event_raw_mm_page_alloc *ctx) +{ + gen_alloc_enter(4096 << ctx->order); + + return gen_alloc_exit2(ctx, ctx->pfn); +} + +SEC("tracepoint/kmem/mm_page_free") +int memleak__mm_page_free(struct trace_event_raw_mm_page_free *ctx) +{ + return gen_free_enter((void *)ctx->pfn); } \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/paf.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/paf.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/pr.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/pr.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/procstat.bpf.c similarity index 96% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/procstat.bpf.c index 13d08e593..6d44f33ac 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.bpf.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/procstat.bpf.c @@ -33,7 +33,7 @@ pid_t user_pid = 0; SEC("kprobe/finish_task_switch") int BPF_KPROBE(finish_task_switch, struct task_struct *prev) { struct procstat_event *e; - struct percpu_counter rss = {}; + struct mm_rss_stat rss = {}; struct mm_struct *mms; long long *t; pid_t pid = bpf_get_current_pid_tgid() >> 32; @@ -65,7 +65,7 @@ int BPF_KPROBE(finish_task_switch, struct task_struct *prev) { e->nvcsw = BPF_CORE_READ(prev, nvcsw); e->nivcsw = BPF_CORE_READ(prev, nivcsw); - rss = *BPF_CORE_READ(prev, mm, rss_stat); + rss = BPF_CORE_READ(prev, mm, rss_stat); t = (long long *)(rss.count); e->rssfile = *t; e->rssanon = *(t + 1); diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/sysstat.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/sysstat.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c new file mode 100644 index 000000000..550aa2fe0 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/vmasnap.bpf.c @@ -0,0 +1,247 @@ +#include "vmlinux.h" +#include +#include +#include +#include "mem_watcher.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +// BPF Map 用于存储插入操作的事件数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, u64); + __type(value, struct insert_event_t); +} insert_events SEC(".maps"); + +// BPF Map 用于存储查找操作的事件数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, u64); + __type(value, struct find_event_t); +} find_events SEC(".maps"); + +// =============================== +// 查找操作的 Trace 函数 +// =============================== + +// Trace function for vmacache_find +SEC("kprobe/vmacache_find") +int BPF_KPROBE(trace_vmacache_find, struct mm_struct *mm, unsigned long addr) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + event.addr = addr; + + // Initialize event with default values + event.vmacache_hit = 0; + + // Update map with initial event details + bpf_map_update_elem(&find_events, &pid, &event, BPF_ANY); + return 0; +} + +SEC("kretprobe/vmacache_find") +int BPF_KRETPROBE(trace_vmacache_find_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t *event; + struct vm_area_struct *vma; + + // Look up the event for the current PID + event = bpf_map_lookup_elem(&find_events, &pid); + if (event) { + // Check the return value of vmacache_find + vma = (struct vm_area_struct *)PT_REGS_RC(ctx); + if (vma) { + // If vma is not NULL, set vmacache_hit to 1 + event->vmacache_hit = 1; + } else { + // If vma is NULL, set vmacache_hit to 0 + event->vmacache_hit = 0; + } + + // Update the event in the map + bpf_map_update_elem(&find_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Trace function for find_vma +SEC("kprobe/find_vma") +int BPF_KPROBE(trace_find_vma, struct mm_struct *mm, unsigned long addr) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + event.addr = addr; + + // Initialize event with default values + event.vmacache_hit = 0; + + // Update map with initial event details + bpf_map_update_elem(&find_events, &pid, &event, BPF_ANY); + return 0; +} + +SEC("kretprobe/find_vma") +int BPF_KRETPROBE(trace_find_vma_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct find_event_t *event; + struct vm_area_struct *vma; + + event = bpf_map_lookup_elem(&find_events, &pid); + if (event) { + event->duration = bpf_ktime_get_ns() - event->timestamp; + + // Retrieve vma and rb_subtree_last + vma = (struct vm_area_struct *)PT_REGS_RC(ctx); + if (vma) { + u64 rb_subtree_last = BPF_CORE_READ(vma, shared.rb_subtree_last); + u64 vm_start = BPF_CORE_READ(vma, vm_start); + u64 vm_end = BPF_CORE_READ(vma, vm_end); + + event->rb_subtree_last = rb_subtree_last; + event->vm_start = vm_start; + event->vm_end = vm_end; + bpf_printk("pid=%llu\n", pid); + bpf_printk("find_vma addr=%lu, duration=%llu ns, rb_subtree_last=%llu\n", + event->addr, event->duration, event->rb_subtree_last); + bpf_printk("vm_start=%llu, vm_end=%llu\n", + vm_start, vm_end); + bpf_printk("vmacache_hit=%d\n", event->vmacache_hit); + } + else { + bpf_printk("find_vma addr=%lu, duration=%llu ns, rb_subtree_last=not_available, vmacache_hit=%d\n", + event->addr, event->duration, event->vmacache_hit); + } + + // bpf_map_delete_elem(&find_events, &pid); + } + return 0; +} + +// =============================== +// 插入操作的 Trace 函数 +// =============================== + +// Trace function for insert_vm_struct +SEC("kprobe/insert_vm_struct") +int BPF_KPROBE(trace_insert_vm_struct, struct mm_struct *mm, struct vm_area_struct *vma) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t event = {}; + event.timestamp = bpf_ktime_get_ns(); + + // Initialize event with default values + event.inserted_to_list = 0; + event.inserted_to_rb = 0; + event.inserted_to_interval_tree = 0; + event.link_list_duration = 0; + event.link_rb_duration = 0; + event.interval_tree_duration = 0; + + // Update map with initial event details + bpf_map_update_elem(&insert_events, &pid, &event, BPF_ANY); + + return 0; +} + +// Kprobe for __vma_link_list +SEC("kprobe/__vma_link_list") +int BPF_KPROBE(trace_vma_link_list, struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_list = 1; + event->link_list_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Kprobe for __vma_link_rb +SEC("kprobe/__vma_link_rb") +int BPF_KPROBE(trace_vma_link_rb, struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev, struct rb_node **rb_link, struct rb_node *rb_parent) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_rb = 1; + event->link_rb_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Kprobe for vma_interval_tree_insert +SEC("kprobe/vma_interval_tree_insert") +int BPF_KPROBE(trace_vma_interval_tree_insert, struct vm_area_struct *vma, struct interval_tree_node *node) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + u64 timestamp = bpf_ktime_get_ns(); + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->inserted_to_interval_tree = 1; + event->interval_tree_start_time = timestamp; + bpf_map_update_elem(&insert_events, &pid, event, BPF_ANY); + } + return 0; +} + +// Return probe for insert_vm_struct +SEC("kretprobe/insert_vm_struct") +int BPF_KRETPROBE(trace_insert_vm_struct_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + event->duration = bpf_ktime_get_ns() - event->timestamp; + + // Output the result + bpf_printk("insert_vm_struct duration=%llu ns, list=%d, rb=%d\n", + event->duration, event->inserted_to_list, + event->inserted_to_rb); + bpf_printk("interval_tree=%d\n", event->inserted_to_interval_tree); + + // Remove the event after processing + // bpf_map_delete_elem(&insert_events, &pid); + } + return 0; +} + +// Return probe for __vma_link_list +SEC("kretprobe/__vma_link_list") +int BPF_KRETPROBE(trace_vma_link_list_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->link_list_duration = end_time - event->link_list_start_time; + bpf_printk("__vma_link_list duration=%llu ns\n", event->link_list_duration); + } + return 0; +} + +// Return probe for __vma_link_rb +SEC("kretprobe/__vma_link_rb") +int BPF_KRETPROBE(trace_vma_link_rb_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->link_rb_duration = end_time - event->link_rb_start_time; + bpf_printk("__vma_link_rb duration=%llu ns\n", event->link_rb_duration); + } + return 0; +} + +// Return probe for vma_interval_tree_insert +SEC("kretprobe/vma_interval_tree_insert") +int BPF_KRETPROBE(trace_vma_interval_tree_insert_ret) { + u64 pid = bpf_get_current_pid_tgid() >> 32; + struct insert_event_t *event = bpf_map_lookup_elem(&insert_events, &pid); + if (event) { + u64 end_time = bpf_ktime_get_ns(); + event->interval_tree_duration = end_time - event->interval_tree_start_time; + bpf_printk("vma_interval_tree_insert duration=%llu ns\n", event->interval_tree_duration); + } + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h new file mode 100644 index 000000000..530e62065 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/fraginfo.h @@ -0,0 +1,35 @@ +#ifndef FRAGINFO_H +#define FRAGINFO_H + +#define MAX_ORDER 10 +typedef __u64 u64; +struct order_zone{ + unsigned int order; + u64 zone_ptr; +}; +struct ctg_info { + long unsigned int free_pages; + long unsigned int free_blocks_total; + long unsigned int free_blocks_suitable; +}; + +struct zone_info +{ + u64 zone_ptr; + u64 zone_start_pfn; + //spanned_pages: 代表的是这个zone中所有的页,包含空洞,计算公式是: zone_end_pfn - zone_start_pfn + //present_pages: 代表的是这个zone中可用的所有物理页,计算公式是:spanned_pages-hole_pages + u64 spanned_pages; + u64 present_pages; + char comm[32]; + unsigned int order; +}; + +struct pgdat_info +{ + u64 pgdat_ptr; + int nr_zones; + int node_id; +}; + +#endif diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h new file mode 100644 index 000000000..1c69f4a1b --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h @@ -0,0 +1,165 @@ +#ifndef __MEM_WATCHER_H +#define __MEM_WATCHER_H + +#define TASK_COMM_LEN 16 +#define MAX_FILENAME_LEN 127 + +#define ___GFP_DMA 0x01u +#define ___GFP_HIGHMEM 0x02u +#define ___GFP_DMA32 0x04u +#define ___GFP_MOVABLE 0x08u +#define ___GFP_RECLAIMABLE 0x10u +#define ___GFP_HIGH 0x20u +#define ___GFP_IO 0x40u +#define ___GFP_FS 0x80u +#define ___GFP_ZERO 0x100u +#define ___GFP_ATOMIC 0x200u +#define ___GFP_DIRECT_RECLAIM 0x400u +#define ___GFP_KSWAPD_RECLAIM 0x800u +#define ___GFP_WRITE 0x1000u +#define ___GFP_NOWARN 0x2000u +#define ___GFP_RETRY_MAYFAIL 0x4000u +#define ___GFP_NOFAIL 0x8000u +#define ___GFP_NORETRY 0x10000u +#define ___GFP_MEMALLOC 0x20000u +#define ___GFP_COMP 0x40000u +#define ___GFP_NOMEMALLOC 0x80000u +#define ___GFP_HARDWALL 0x100000u +#define ___GFP_THISNODE 0x200000u +#define ___GFP_ACCOUNT 0x400000u +#define ___GFP_ZEROTAGS 0x800000u +#define ___GFP_SKIP_KASAN_POISON 0x1000000u + +#define GFP_ATOMIC (___GFP_HIGH|___GFP_ATOMIC|___GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (___GFP_RECLAIMABLE | ___GFP_IO | ___GFP_FS) +#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | ___GFP_ACCOUNT) +#define GFP_NOWAIT (___GFP_KSWAPD_RECLAIM) +#define GFP_NOIO (___GFP_RECLAIMABLE) +#define GFP_NOFS (___GFP_RECLAIMABLE | ___GFP_IO) +#define GFP_USER (___GFP_RECLAIMABLE | ___GFP_IO | ___GFP_FS | ___GFP_HARDWALL) +#define GFP_DMA ___GFP_DMA +#define GFP_DMA32 ___GFP_DMA32 +#define GFP_HIGHUSER (GFP_USER | ___GFP_HIGHMEM) +#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | ___GFP_MOVABLE | ___GFP_SKIP_KASAN_POISON) +#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | ___GFP_COMP | ___GFP_NOMEMALLOC | ___GFP_NOWARN) & ~___GFP_RECLAIMABLE) +#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | ___GFP_DIRECT_RECLAIM) + +struct paf_event { + unsigned long min; + unsigned long low; + unsigned long high; + unsigned long present; + unsigned long protection; + int flag; +}; + +struct pr_event { + unsigned long reclaim; + unsigned long reclaimed; + unsigned int unqueued_dirty; + unsigned int congested; + unsigned int writeback; +}; + +struct procstat_event { + /*进程内存状态报告*/ + pid_t pid; + long nvcsw; + long nivcsw; + long vsize; //虚拟内存 + long size; //物理内存 + long long rssanon; //匿名页面 + long long rssfile; //文件页面 + long long rssshmem; //共享页面 + long long vswap; //交换页面 + long long Hpages; //hugetlbPages + long Vdata; //Private data segments + long Vstk; //User stack + long long VPTE; +}; + +struct sysstat_event { + /*系统内存状态报告*/ + unsigned long present; + unsigned long anon_inactive; // 0 + unsigned long anon_active; // 1 + unsigned long file_inactive; // 2 + unsigned long file_active; // 3 + unsigned long unevictable; // 不可回收页面 + unsigned long slab_reclaimable; + unsigned long slab_unreclaimable; + unsigned long anon_isolated; // 匿名隔离页面 + unsigned long file_isolated; // 文件隔离页面 + + unsigned long working_nodes; // 12 + unsigned long working_refault; + unsigned long working_activate; + unsigned long working_restore; + unsigned long working_nodereclaim; + + unsigned long anon_mapped; // 17 + unsigned long file_mapped; + + unsigned long file_pages; // 19 + unsigned long file_dirty; + unsigned long writeback; + unsigned long writeback_temp; + + unsigned long shmem; // 共享内存23 + unsigned long shmem_thps; + unsigned long pmdmapped; + unsigned long anon_thps; + unsigned long unstable_nfs; + unsigned long vmscan_write; + unsigned long vmscan_immediate; + + unsigned long diried; + unsigned long written; + unsigned long kernel_misc_reclaimable; +}; + +/*memleak.h*/ +#define ALLOCS_MAX_ENTRIES 1000000 +#define COMBINED_ALLOCS_MAX_ENTRIES 10240 + +struct alloc_info { + __u64 size; + int stack_id; +}; + +union combined_alloc_info { + struct { + __u64 total_size : 40; + __u64 number_of_allocs : 24; + }; + __u64 bits; +}; + +/* vmasnap.h */ +// 记录插入操作的事件数据 +struct insert_event_t { + unsigned long long timestamp; + unsigned long long duration; + int inserted_to_list; + int inserted_to_rb; + int inserted_to_interval_tree; + unsigned long long link_list_start_time; + unsigned long long link_rb_start_time; + unsigned long long interval_tree_start_time; + unsigned long long link_list_duration; + unsigned long long link_rb_duration; + unsigned long long interval_tree_duration; +}; + +// 记录查找操作的事件数据 +struct find_event_t { + unsigned long long timestamp; + unsigned long long duration; + unsigned long addr; + int vmacache_hit; + unsigned long long rb_subtree_last; + unsigned long long vm_start; + unsigned long long vm_end; +}; + +#endif /* __MEM_WATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c deleted file mode 100644 index 2872e867e..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c +++ /dev/null @@ -1,14 +0,0 @@ - -/* 请一定注意vmlinux.h头文件是依赖于特定架构的,本机编译的时候需要自行生成, -生成方法: -1、切换至本代码../../vmlinux/你的架构目录下; -2、安装Linux开发工具包:sudo apt install linux-tools-$(uname -r) -3、删除那个vmlinux_数字.h文件(记住它的名字); -4、生成vmlinux.h文件:bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h -5、将生成的vmlinux.h文件名字改成刚刚删除的vmlinux_数字.h -如果编译不通过,提示找不到vmlinux.h文件,那么请在本代码同级目录下运行生成vmlinux.h命令 */ -#include "vmlinux.h" -#include -#include -#include -#include "mem_watcher.h" \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c index 8c5eb3dc6..f00b7ca05 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c @@ -21,72 +21,220 @@ #include #include #include +#include #include #include #include +#include #include #include #include "paf.skel.h" #include "pr.skel.h" #include "procstat.skel.h" #include "sysstat.skel.h" +#include "fraginfo.skel.h" #include "memleak.skel.h" +#include "vmasnap.skel.h" #include "mem_watcher.h" +#include "fraginfo.h" #include "blazesym.h" -static const int perf_max_stack_depth = 127; //stack id 对应的堆栈的深度 -static const int stack_map_max_entries = 10240; //最大允许存储多少个stack_id +// 定义标志结构体 +typedef struct { + int flag; + const char *name; +} Flag; + +// 定义所有组合修饰符和单独标志位 +Flag gfp_combined_list[] = { + {GFP_ATOMIC, "GFP_ATOMIC"}, + {GFP_KERNEL, "GFP_KERNEL"}, + {GFP_KERNEL_ACCOUNT, "GFP_KERNEL_ACCOUNT"}, + {GFP_NOWAIT, "GFP_NOWAIT"}, + {GFP_NOIO, "GFP_NOIO"}, + {GFP_NOFS, "GFP_NOFS"}, + {GFP_USER, "GFP_USER"}, + {GFP_DMA, "GFP_DMA"}, + {GFP_DMA32, "GFP_DMA32"}, + {GFP_HIGHUSER, "GFP_HIGHUSER"}, + {GFP_HIGHUSER_MOVABLE, "GFP_HIGHUSER_MOVABLE"}, + {GFP_TRANSHUGE_LIGHT, "GFP_TRANSHUGE_LIGHT"}, + {GFP_TRANSHUGE, "GFP_TRANSHUGE"}, +}; + +Flag gfp_separate_list[] = { + {___GFP_DMA, "___GFP_DMA"}, + {___GFP_HIGHMEM, "___GFP_HIGHMEM"}, + {___GFP_DMA32, "___GFP_DMA32"}, + {___GFP_MOVABLE, "___GFP_MOVABLE"}, + {___GFP_RECLAIMABLE, "___GFP_RECLAIMABLE"}, + {___GFP_HIGH, "___GFP_HIGH"}, + {___GFP_IO, "___GFP_IO"}, + {___GFP_FS, "___GFP_FS"}, + {___GFP_ZERO, "___GFP_ZERO"}, + {___GFP_ATOMIC, "___GFP_ATOMIC"}, + {___GFP_DIRECT_RECLAIM, "___GFP_DIRECT_RECLAIM"}, + {___GFP_KSWAPD_RECLAIM, "___GFP_KSWAPD_RECLAIM"}, + {___GFP_WRITE, "___GFP_WRITE"}, + {___GFP_NOWARN, "___GFP_NOWARN"}, + {___GFP_RETRY_MAYFAIL, "___GFP_RETRY_MAYFAIL"}, + {___GFP_NOFAIL, "___GFP_NOFAIL"}, + {___GFP_NORETRY, "___GFP_NORETRY"}, + {___GFP_MEMALLOC, "___GFP_MEMALLOC"}, + {___GFP_COMP, "___GFP_COMP"}, + {___GFP_NOMEMALLOC, "___GFP_NOMEMALLOC"}, + {___GFP_HARDWALL, "___GFP_HARDWALL"}, + {___GFP_THISNODE, "___GFP_THISNODE"}, + {___GFP_ACCOUNT, "___GFP_ACCOUNT"}, + {___GFP_ZEROTAGS, "___GFP_ZEROTAGS"}, + {___GFP_SKIP_KASAN_POISON, "___GFP_SKIP_KASAN_POISON"}, +}; + +static const int perf_max_stack_depth = 127; // stack id 对应的堆栈的深度 +static const int stack_map_max_entries = 10240; // 最大允许存储多少个stack_id static __u64 *g_stacks = NULL; static size_t g_stacks_size = 0; +#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP) +#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) + static struct blaze_symbolizer *symbolizer; static int attach_pid; +pid_t own_pid; static char binary_path[128] = {0}; - -#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ - do { \ - LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ - .func_name = #sym_name, \ - .retprobe = is_retprobe); \ - skel->links.prog_name = bpf_program__attach_uprobe_opts( \ - skel->progs.prog_name, \ - attach_pid, \ - binary_path, \ - 0, \ - &uprobe_opts); \ - } while (false) - -#define __CHECK_PROGRAM(skel, prog_name) \ - do { \ - if (!skel->links.prog_name) { \ - perror("no program attached for " #prog_name); \ - return -errno; \ - } \ - } while (false) - + +struct allocation +{ + int stack_id; + __u64 size; + size_t count; +}; +// ============================= fraginfo==================================== +struct order_entry { + struct order_zone okey; + struct ctg_info oinfo; +}; + +int compare_entries(const void *a, const void *b) { + struct order_entry *entryA = (struct order_entry *)a; + struct order_entry *entryB = (struct order_entry *)b; + + if (entryA->okey.zone_ptr != entryB->okey.zone_ptr) { + return (entryA->okey.zone_ptr < entryB->okey.zone_ptr) ? -1 : 1; + } else { + return (entryA->okey.order < entryB->okey.order) ? -1 : 1; + } +} + +// ============================= fraginfo==================================== +static struct allocation *allocs; + +static volatile bool exiting = false; + +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do \ + { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \ + .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, \ + attach_pid, \ + binary_path, \ + 0, \ + &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do \ + { \ + if (!skel->links.prog_name) \ + { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + #define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ - do { \ - __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ - __CHECK_PROGRAM(skel, prog_name); \ - } while (false) + do \ + { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) #define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) #define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true) - + #define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) #define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) +#define PROCESS_SKEL(skel, func) \ + skel = func##_bpf__open(); \ + if (!skel) \ + { \ + fprintf(stderr, "Failed to open and load BPF skeleton\n"); \ + return 1; \ + } \ + process_##func(skel) + +#define POLL_RING_BUFFER(rb, timeout, err) \ + while (!exiting) \ + { \ + err = ring_buffer__poll(rb, timeout); \ + if (err == -EINTR) \ + { \ + err = 0; \ + break; \ + } \ + if (err < 0) \ + { \ + printf("Error polling perf buffer: %d\n", err); \ + break; \ + } \ + } -static struct env { +#define LOAD_AND_ATTACH_SKELETON(skel, event) \ + do \ + { \ + skel->bss->user_pid = own_pid; \ + err = event##_bpf__load(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + err = event##_bpf__attach(skel); \ + if (err) \ + { \ + fprintf(stderr, "Failed to attach BPF skeleton\n"); \ + goto event##_cleanup; \ + } \ + \ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event_##event, NULL, NULL); \ + if (!rb) \ + { \ + fprintf(stderr, "Failed to create ring buffer\n"); \ + goto event##_cleanup; \ + } \ + } while (0) + +static struct env +{ int time; bool paf; bool pr; bool procstat; bool sysstat; bool memleak; - + bool fraginfo; + bool vmasnap; + bool kernel_trace; + bool print_time; + int interval; + int duration; bool part2; long choose_pid; @@ -98,8 +246,15 @@ static struct env { .procstat = false, .sysstat = false, .memleak = false, + .fraginfo = false, + .vmasnap = false, + .kernel_trace = true, + .print_time = false, .rss = false, .part2 = false, + .choose_pid = 0, + .interval = 1, + .duration = 10, }; const char argp_program_doc[] = "mem_watcher is in use ....\n"; @@ -107,28 +262,44 @@ const char argp_program_doc[] = "mem_watcher is in use ....\n"; static const struct argp_option opts[] = { {0, 0, 0, 0, "select function:", 1}, - {"paf", 'a', 0, 0, "print paf (内存页面状态报告)", 2}, + {0, 0, 0, 0, "par:", 2}, + {"paf", 'a', 0, 0, "print paf (内存页面状态报告)"}, - {"pr", 'p', 0, 0, "print pr (页面回收状态报告)", 3}, + {0, 0, 0, 0, "pr:", 3}, + {"pr", 'p', 0, 0, "print pr (页面回收状态报告)"}, - {"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)", 4}, - {0, 0, 0, 0, "procstat additional function:"}, + {0, 0, 0, 0, "procstat:", 4}, + {"procstat", 'r', 0, 0, "print procstat (进程内存状态报告)"}, {"choose_pid", 'P', "PID", 0, "选择进程号打印"}, {"Rss", 'R', NULL, 0, "打印进程页面", 5}, - {"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)", 6}, - {0, 0, 0, 0, "sysstat additional function:"}, + {0, 0, 0, 0, "sysstat:", 6}, + {"sysstat", 's', 0, 0, "print sysstat (系统内存状态报告)"}, + {"part2", 'n', NULL, 0, "系统内存状态报告2", 7}, - {"memleak", 'l', "PID", 0, "print memleak (内存泄漏检测)", 8}, + {0, 0, 0, 0, "memleak:", 8}, + {"memleak", 'l', 0, 0, "print memleak (内核态内存泄漏检测)", 8}, + {"choose_pid", 'P', "PID", 0, "选择进程号打印, print memleak (用户态内存泄漏检测)", 9}, + {"print_time", 'm', 0, 0, "打印申请地址时间 (用户态)", 10}, + {"print_time", 'f', 0, 0, "打印申请地址时间 (用户态)", 10}, + {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 11}, + + {0, 0, 0, 0, "fraginfo:", 12}, + {"fraginfo", 'f', 0, 0, "print fraginfo",12}, + {"interval", 'i', "INTERVAL", 0, "Print interval in seconds (default 1)"}, + {"duration", 'd', "DURATION", 0, "Total duration in seconds to run (default 10)"}, - {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 9}, + {0, 0, 0, 0, "vmasnap:", 13}, + {"vmasnap", 'v', 0, 0, "print vmasnap (虚拟内存区域信息)"}, {NULL, 'h', NULL, OPTION_HIDDEN, "show the full help"}, {0}, }; -static error_t parse_arg(int key, char *arg, struct argp_state *state) { - switch (key) { +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + switch (key) + { case 't': env.time = strtol(arg, NULL, 10); if (env.time) @@ -143,15 +314,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'r': env.procstat = true; break; + case 'f': + env.fraginfo = true; + break; + case 'v': + env.vmasnap = true; + break; case 's': env.sysstat = true; break; case 'n': env.part2 = true; break; - case 'h': - argp_state_help(state, stderr, ARGP_HELP_STD_HELP); - break; case 'P': env.choose_pid = strtol(arg, NULL, 10); break; @@ -161,6 +335,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'l': env.memleak = true; break; + case 'm': + env.print_time = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + case 'i': + env.interval = atoi(arg); + break; + case 'd': + env.duration = atoi(arg); + break; default: return ARGP_ERR_UNKNOWN; } @@ -173,35 +359,165 @@ static const struct argp argp = { .doc = argp_program_doc, }; -static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info) { +// Function prototypes +// static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args); +static void sig_handler(int sig); +static void setup_signals(void); +static void disable_kernel_tracepoints(struct memleak_bpf *skel); +static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info); +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid); +static int print_outstanding_allocs(struct memleak_bpf *skel); +static int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid); +static int handle_event_paf(void *ctx, void *data, size_t data_sz); +static int handle_event_pr(void *ctx, void *data, size_t data_sz); +static int handle_event_procstat(void *ctx, void *data, size_t data_sz); +static int handle_event_sysstat(void *ctx, void *data, size_t data_sz); +static int attach_uprobes(struct memleak_bpf *skel); +static void print_flag_modifiers(int flag); +static int process_paf(struct paf_bpf *skel_paf); +static int process_pr(struct pr_bpf *skel_pr); +static int process_procstat(struct procstat_bpf *skel_procstat); +static int process_sysstat(struct sysstat_bpf *skel_sysstat); +static int process_memleak(struct memleak_bpf *skel_memleak, struct env); +static int process_fraginfo(struct fraginfo_bpf *skel_fraginfo); +static int process_vmasnap(struct vmasnap_bpf *skel_vmasnap); +static __u64 adjust_time_to_program_start_time(__u64 first_query_time); +static int update_addr_times(struct memleak_bpf *skel_memleak); +static int print_time(struct memleak_bpf *skel_memleak); +static void print_find_event_data(int map_fd); +static void print_insert_event_data(int map_fd); + +// Main function +int main(int argc, char **argv) +{ + int err; + struct paf_bpf *skel_paf; + struct pr_bpf *skel_pr; + struct procstat_bpf *skel_procstat; + struct sysstat_bpf *skel_sysstat; + struct memleak_bpf *skel_memleak; + struct fraginfo_bpf *skel_fraginfo; + struct vmasnap_bpf *skel_vmasnap; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + own_pid = getpid(); + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + // libbpf_set_print(libbpf_print_fn); + + setup_signals(); + + if (env.paf) + { + PROCESS_SKEL(skel_paf, paf); + } + else if (env.pr) + { + PROCESS_SKEL(skel_pr, pr); + } + else if (env.procstat) + { + PROCESS_SKEL(skel_procstat, procstat); + } + else if (env.fraginfo) + { + PROCESS_SKEL(skel_fraginfo, fraginfo); + } + else if (env.vmasnap) + { + PROCESS_SKEL(skel_vmasnap, vmasnap); + } + else if (env.sysstat) + { + PROCESS_SKEL(skel_sysstat, sysstat); + } + else if (env.memleak) + { + if (env.choose_pid != 0) + { + printf("用户态内存泄漏\n"); + env.kernel_trace = false; + attach_pid = env.choose_pid; + } + else + attach_pid = 0; + + strcpy(binary_path, "/lib/x86_64-linux-gnu/libc.so.6"); + + allocs = calloc(ALLOCS_MAX_ENTRIES, sizeof(*allocs)); + + /* Set up libbpf errors and debug info callback */ + // libbpf_set_print(libbpf_print_fn); + + /* Load and verify BPF application */ + skel_memleak = memleak_bpf__open(); + if (!skel_memleak) + { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } + process_memleak(skel_memleak, env); + } + return 0; +} + +int alloc_size_compare(const void *a, const void *b) +{ + const struct allocation *x = (struct allocation *)a; + const struct allocation *y = (struct allocation *)b; + + // descending order + + if (x->size > y->size) + return -1; + + if (x->size < y->size) + return 1; + + return 0; +} + +static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info *code_info) +{ // If we have an input address we have a new symbol. - if (input_addr != 0) { + if (input_addr != 0) + { printf("%016lx: %s @ 0x%lx+0x%lx", input_addr, name, addr, offset); - if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { + if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) + { printf(" %s/%s:%u\n", code_info->dir, code_info->file, code_info->line); } - else if (code_info != NULL && code_info->file != NULL) { + else if (code_info != NULL && code_info->file != NULL) + { printf(" %s:%u\n", code_info->file, code_info->line); } - else { + else + { printf("\n"); } } - else { + else + { printf("%16s %s", "", name); - if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { + if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) + { printf("@ %s/%s:%u [inlined]\n", code_info->dir, code_info->file, code_info->line); } - else if (code_info != NULL && code_info->file != NULL) { + else if (code_info != NULL && code_info->file != NULL) + { printf("@ %s:%u [inlined]\n", code_info->file, code_info->line); } - else { + else + { printf("[inlined]\n"); } } } -static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) +{ const struct blaze_symbolize_inlined_fn *inlined; const struct blaze_result *result; const struct blaze_sym *sym; @@ -209,23 +525,26 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { assert(sizeof(uintptr_t) == sizeof(uint64_t)); - if (pid) { + if (pid) + { struct blaze_symbolize_src_process src = { .type_size = sizeof(src), .pid = pid, }; result = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); } - else { + else + { struct blaze_symbolize_src_kernel src = { .type_size = sizeof(src), }; result = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); } - - for (i = 0; i < stack_sz; i++) { - if (!result || result->cnt <= i || result->syms[i].name == NULL) { + for (i = 0; i < stack_sz; i++) + { + if (!result || result->cnt <= i || result->syms[i].name == NULL) + { printf("%016llx: \n", stack[i]); continue; } @@ -233,7 +552,8 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { sym = &result->syms[i]; print_frame(sym->name, stack[i], sym->addr, sym->offset, &sym->code_info); - for (j = 0; j < sym->inlined_cnt; j++) { + for (j = 0; j < sym->inlined_cnt; j++) + { inlined = &sym->inlined[j]; print_frame(sym->name, 0, 0, 0, &inlined->code_info); } @@ -242,102 +562,335 @@ static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { blaze_result_free(result); } -int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) { - const size_t combined_allocs_key_size = bpf_map__key_size(skel->maps.combined_allocs); - const size_t stack_traces_key_size = bpf_map__key_size(skel->maps.stack_traces); +int print_outstanding_allocs(struct memleak_bpf *skel) +{ + const size_t allocs_key_size = bpf_map__key_size(skel->maps.allocs); - for (__u64 prev_key = 0, curr_key = 0; ; prev_key = curr_key) { + time_t t = time(NULL); + struct tm *tm = localtime(&t); - if (bpf_map__get_next_key(skel->maps.combined_allocs, - &prev_key, &curr_key, combined_allocs_key_size)) { - if (errno == ENOENT) { - break; //no more keys, done! - } - perror("map get next key failed!"); + size_t nr_allocs = 0; - return -errno; - } + // for each struct alloc_info "alloc_info" in the bpf map "allocs" + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + struct alloc_info alloc_info = {}; + memset(&alloc_info, 0, sizeof(alloc_info)); + + if (bpf_map__get_next_key(skel->maps.allocs, &prev_key, &curr_key, allocs_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done + } - // stack_id = curr_key - union combined_alloc_info cinfo; - memset(&cinfo, 0, sizeof(cinfo)); + perror("map get next key error"); - if (bpf_map__lookup_elem(skel->maps.combined_allocs, - &curr_key, combined_allocs_key_size, &cinfo, sizeof(cinfo), 0)) { - if (errno == ENOENT) { - continue; - } + return -errno; + } - perror("map lookup failed!"); - return -errno; - } + if (bpf_map__lookup_elem(skel->maps.allocs, &curr_key, allocs_key_size, &alloc_info, sizeof(alloc_info), 0)) + { + if (errno == ENOENT) + continue; - if (bpf_map__lookup_elem(skel->maps.stack_traces, - &curr_key, stack_traces_key_size, g_stacks, g_stacks_size, 0)) { - perror("failed to lookup stack traces!"); - return -errno; - } + perror("map lookup error"); - printf("stack_id=0x%llx with outstanding allocations: total_size=%llu nr_allocs=%llu\n", - curr_key, (__u64)cinfo.total_size, (__u64)cinfo.number_of_allocs); + return -errno; + } - int stack_sz = 0; - for (int i = 0; i < perf_max_stack_depth; i++) { - if (g_stacks[i] == 0) { - break; - } - stack_sz++; - //printf("[%3d] 0x%llx\n", i, g_stacks[i]); - } + // filter invalid stacks + if (alloc_info.stack_id < 0) + { + continue; + } - show_stack_trace(g_stacks, stack_sz, pid); - } + // when the stack_id exists in the allocs array, + // increment size with alloc_info.size + bool stack_exists = false; - return 0; + for (size_t i = 0; !stack_exists && i < nr_allocs; ++i) + { + struct allocation *alloc = &allocs[i]; + + if (alloc->stack_id == alloc_info.stack_id) + { + alloc->size += alloc_info.size; + alloc->count++; + + stack_exists = true; + break; + } + } + + if (stack_exists) + continue; + + // when the stack_id does not exist in the allocs array, + // create a new entry in the array + struct allocation alloc = { + .stack_id = alloc_info.stack_id, + .size = alloc_info.size, + .count = 1, + }; + + memcpy(&allocs[nr_allocs], &alloc, sizeof(alloc)); + nr_allocs++; + } + + // sort the allocs array in descending order + qsort(allocs, nr_allocs, sizeof(allocs[0]), alloc_size_compare); + + // get min of allocs we stored vs the top N requested stacks + size_t nr_allocs_to_show = nr_allocs < 10 ? nr_allocs : 10; + + printf("[%d:%d:%d] Top %zu stacks with outstanding allocations:\n", + tm->tm_hour, tm->tm_min, tm->tm_sec, nr_allocs_to_show); + + for (size_t i = 0; i < nr_allocs_to_show; i++) + { + if (bpf_map__lookup_elem(skel->maps.stack_traces, + &allocs[i].stack_id, sizeof(allocs[i].stack_id), g_stacks, g_stacks_size, 0)) + { + perror("failed to lookup stack traces!"); + return -errno; + } + } + + show_stack_trace(g_stacks, nr_allocs_to_show, 0); + + return 0; +} + +int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) +{ + const size_t combined_allocs_key_size = bpf_map__key_size(skel->maps.combined_allocs); + const size_t stack_traces_key_size = bpf_map__key_size(skel->maps.stack_traces); + + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + + if (bpf_map__get_next_key(skel->maps.combined_allocs, + &prev_key, &curr_key, combined_allocs_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + perror("map get next key failed!"); + + return -errno; + } + + // stack_id = curr_key + union combined_alloc_info cinfo; + memset(&cinfo, 0, sizeof(cinfo)); + + if (bpf_map__lookup_elem(skel->maps.combined_allocs, + &curr_key, combined_allocs_key_size, &cinfo, sizeof(cinfo), 0)) + { + if (errno == ENOENT) + { + continue; + } + + perror("map lookup failed!"); + return -errno; + } + + if (bpf_map__lookup_elem(skel->maps.stack_traces, + &curr_key, stack_traces_key_size, g_stacks, g_stacks_size, 0)) + { + perror("failed to lookup stack traces!"); + return -errno; + } + + printf("stack_id=0x%llx with outstanding allocations: total_size=%llu nr_allocs=%llu\n", + curr_key, (__u64)cinfo.total_size, (__u64)cinfo.number_of_allocs); + + int stack_sz = 0; + for (int i = 0; i < perf_max_stack_depth; i++) + { + if (g_stacks[i] == 0) + { + break; + } + stack_sz++; + // printf("[%3d] 0x%llx\n", i, g_stacks[i]); + } + + show_stack_trace(g_stacks, stack_sz, pid); + } + + return 0; } -static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { - return vfprintf(stderr, format, args); +// 在更新时间之前获取当前时间并调整为相对于程序启动时的时间 +static __u64 adjust_time_to_program_start_time(__u64 first_query_time) +{ + struct timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + // printf("current_time: %ld\n", current_time.tv_sec); + __u64 adjusted_time; + adjusted_time = current_time.tv_sec - first_query_time; + + // printf("adjusted_time: %lld\n", adjusted_time); + return adjusted_time; } -static volatile bool exiting = false; +// 在更新时间时,先将时间调整为相对于程序启动的时间 +static int update_addr_times(struct memleak_bpf *skel) +{ + const size_t addr_times_key_size = bpf_map__key_size(skel->maps.addr_times); + const size_t first_time_key_size = bpf_map__key_size(skel->maps.first_time); + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + if (bpf_map__get_next_key(skel->maps.addr_times, &prev_key, &curr_key, addr_times_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + + perror("map get next key failed!"); + return -errno; + } -static void sig_handler(int sig) { + // Check if the address exists in the first_time map + __u64 first_query_time; + if (bpf_map__lookup_elem(skel->maps.first_time, &curr_key, first_time_key_size, &first_query_time, sizeof(first_query_time), 0)) + { + // Address doesn't exist in the first_time map, add it with the current time + struct timespec first_time_alloc; + clock_gettime(CLOCK_MONOTONIC, &first_time_alloc); + if (bpf_map__update_elem(skel->maps.first_time, &curr_key, first_time_key_size, &first_time_alloc.tv_sec, sizeof(first_time_alloc.tv_sec), 0)) + { + perror("map update failed!"); + return -errno; + } + } + else + { + // Address exists in the first_time map + // This is the first time updating timestamp + __u64 adjusted_time = adjust_time_to_program_start_time(first_query_time); + // printf("update_addr_times adjusted_time: %lld\n", adjusted_time); + + // Save the adjusted time to addr_times map + __u64 timestamp = adjusted_time; + + // write the updated timestamp back to the map + if (bpf_map__update_elem(skel->maps.addr_times, &curr_key, addr_times_key_size, ×tamp, sizeof(timestamp), 0)) + { + perror("map update failed!"); + return -errno; + } + } + } + return 0; +} + +// 在打印时间时,先将时间调整为相对于程序启动的时间 +int print_time(struct memleak_bpf *skel) +{ + const size_t addr_times_key_size = bpf_map__key_size(skel->maps.addr_times); + + printf("%-16s %12s\n", "AL_ADDR", "AL_Time(s)"); + + // Iterate over the addr_times map to print address and time + for (__u64 prev_key = 0, curr_key = 0;; prev_key = curr_key) + { + if (bpf_map__get_next_key(skel->maps.addr_times, &prev_key, &curr_key, addr_times_key_size)) + { + if (errno == ENOENT) + { + break; // no more keys, done! + } + perror("map get next key failed!"); + return -errno; + } + + // Read the timestamp for the current address + __u64 timestamp; + if (bpf_map__lookup_elem(skel->maps.addr_times, &curr_key, addr_times_key_size, ×tamp, sizeof(timestamp), 0) == 0) + { + printf("0x%-16llx %lld\n", curr_key, timestamp); + } + else + { + perror("map lookup failed!"); + return -errno; + } + } + return 0; +} + +void disable_kernel_tracepoints(struct memleak_bpf *skel) +{ + bpf_program__set_autoload(skel->progs.memleak__kmalloc, false); + bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false); + bpf_program__set_autoload(skel->progs.memleak__kfree, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false); + bpf_program__set_autoload(skel->progs.memleak__kmem_cache_free, false); + bpf_program__set_autoload(skel->progs.memleak__mm_page_alloc, false); + bpf_program__set_autoload(skel->progs.memleak__mm_page_free, false); +} + +// static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +// { +// return vfprintf(stderr, format, args); +// } + +static void sig_handler(int sig) +{ exiting = true; - exit(EXIT_SUCCESS); + exit(EXIT_SUCCESS); } -/* -static char* flags(int flag) +static void setup_signals(void) { - if(flag & GFP_ATOMIC) - return "GFP_ATOMIC"; - if(flag & GFP_KERNEL) - return "GFP_KERNEL"; - if(flag & GFP_KERNEL_ACCOUNT) - return "GFP_KERNEL_ACCOUNT"; - if(flag & GFP_NOWAIT) - return "GFP_NOWAIT"; - if(flag & GFP_NOIO ) - return "GFP_NOIO "; - if(flag & GFP_NOFS) - return "GFP_NOFS"; - if(flag & GFP_USER) - return "GFP_USER"; - if(flag & GFP_DMA) - return "GFP_DMA"; - if(flag & GFP_DMA32) - return "GFP_DMA32"; - if(flag & GFP_HIGHUSER) - return "GFP_HIGHUSER"; - if(flag & GFP_HIGHUSER_MOVABLE) - return "GFP_HIGHUSER_MOVABLE"; - if(flag & GFP_TRANSHUGE_LIGHT) - return "GFP_TRANSHUGE_LIGHT"; - return; + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGALRM, sig_handler); } -*/ -static int handle_event_paf(void *ctx, void *data, size_t data_sz) { + +static void print_flag_modifiers(int flag) { + char combined[512] = {0}; // 用于保存组合修饰符 + char separate[512] = {0}; // 用于保存单独标志位 + + // 检查组合修饰符 + for (int i = 0; i < sizeof(gfp_combined_list) / sizeof(gfp_combined_list[0]); ++i) { + if ((flag & gfp_combined_list[i].flag) == gfp_combined_list[i].flag) { + strcat(combined, gfp_combined_list[i].name); + strcat(combined, " | "); + } + } + + // 移除最后一个 " | " 字符串的末尾 + if (strlen(combined) > 3) { + combined[strlen(combined) - 3] = '\0'; + } + + // 检查单独标志位 + for (int i = 0; i < sizeof(gfp_separate_list) / sizeof(gfp_separate_list[0]); ++i) { + if (flag & gfp_separate_list[i].flag) { + strcat(separate, gfp_separate_list[i].name); + strcat(separate, " | "); + } + } + + // 移除最后一个 " | " 字符串的末尾 + if (strlen(separate) > 3) { + separate[strlen(separate) - 3] = '\0'; + } + + // 打印组合修饰符和单独标志位 + printf("%-50s %-100s\n", combined, separate); +} + +static int handle_event_paf(void *ctx, void *data, size_t data_sz) +{ const struct paf_event *e = data; struct tm *tm; char ts[32]; @@ -347,13 +900,16 @@ static int handle_event_paf(void *ctx, void *data, size_t data_sz) { tm = localtime(&t); strftime(ts, sizeof(ts), "%H:%M:%S", tm); - printf("%-8lu %-8lu %-8lu %-8lu %-8x\n", + printf("%-8lu %-8lu %-8lu %-8lu %-8x ", e->min, e->low, e->high, e->present, e->flag); + print_flag_modifiers(e->flag); + printf("\n"); return 0; } -static int handle_event_pr(void *ctx, void *data, size_t data_sz) { +static int handle_event_pr(void *ctx, void *data, size_t data_sz) +{ const struct pr_event *e = data; struct tm *tm; char ts[32]; @@ -364,12 +920,13 @@ static int handle_event_pr(void *ctx, void *data, size_t data_sz) { strftime(ts, sizeof(ts), "%H:%M:%S", tm); printf("%-8lu %-8lu %-8u %-8u %-8u\n", - e->reclaim, e->reclaimed, e->unqueued_dirty, e->congested, e->writeback); + e->reclaim, e->reclaimed, e->unqueued_dirty, e->congested, e->writeback); return 0; } -static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { +static int handle_event_procstat(void *ctx, void *data, size_t data_sz) +{ const struct procstat_event *e = data; struct tm *tm; char ts[32]; @@ -378,15 +935,18 @@ static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { time(&t); tm = localtime(&t); strftime(ts, sizeof(ts), "%H:%M:%S", tm); - if (env.choose_pid) { - if (e->pid == env.choose_pid) { + if (env.choose_pid) + { + if (e->pid == env.choose_pid) + { if (env.rss == true) printf("%-8s %-8d %-8ld %-8ld %-8ld %-8lld %-8lld\n", ts, e->pid, e->vsize, e->Vdata, e->Vstk, e->VPTE, e->vswap); else printf("%-8s %-8d %-8ld %-8lld %-8lld %-8lld\n", ts, e->pid, e->size, e->rssanon, e->rssfile, e->rssshmem); } } - else { + else + { if (env.rss == true) printf("%-8s %-8d %-8ld %-8ld %-8ld %-8lld %-8lld\n", ts, e->pid, e->vsize, e->Vdata, e->Vstk, e->VPTE, e->vswap); else @@ -396,7 +956,8 @@ static int handle_event_procstat(void *ctx, void *data, size_t data_sz) { return 0; } -static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) { +static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) +{ const struct sysstat_event *e = data; struct tm *tm; char ts[32]; @@ -414,342 +975,411 @@ static int handle_event_sysstat(void *ctx, void *data, size_t data_sz) { return 0; } -pid_t own_pid; +int attach_uprobes(struct memleak_bpf *skel) +{ + ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter); + ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit); + ATTACH_UPROBE_CHECKED(skel, free, free_enter); -int attach_uprobes(struct memleak_bpf *skel) { - ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter); - ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit); - ATTACH_UPROBE_CHECKED(skel, free, free_enter); + ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter); + ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit); - ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter); - ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit); + ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter); + ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit); - ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter); - ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit); + ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter); + ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit); - ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter); - ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit); + ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter); + ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit); - ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter); - ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit); + ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter); + ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit); - ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter); - ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit); + ATTACH_UPROBE_CHECKED(skel, free, free_enter); + ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter); - ATTACH_UPROBE_CHECKED(skel, free, free_enter); - ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter); + // the following probes are intentinally allowed to fail attachment - // the following probes are intentinally allowed to fail attachment + // deprecated in libc.so bionic + ATTACH_UPROBE(skel, valloc, valloc_enter); + ATTACH_URETPROBE(skel, valloc, valloc_exit); - // deprecated in libc.so bionic - ATTACH_UPROBE(skel, valloc, valloc_enter); - ATTACH_URETPROBE(skel, valloc, valloc_exit); + // deprecated in libc.so bionic + ATTACH_UPROBE(skel, pvalloc, pvalloc_enter); + ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit); - // deprecated in libc.so bionic - ATTACH_UPROBE(skel, pvalloc, pvalloc_enter); - ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit); + // added in C11 + ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter); + ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit); - // added in C11 - ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter); - ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit); + return 0; +} +// Functions to process different BPF programs +static int process_paf(struct paf_bpf *skel_paf) +{ + int err; + struct ring_buffer *rb; - return 0; + LOAD_AND_ATTACH_SKELETON(skel_paf, paf); + + printf("%-8s %-8s %-8s %-8s %-8s\n", "MIN", "LOW", "HIGH", "PRESENT", "FLAG"); + + POLL_RING_BUFFER(rb, 1000, err); + +paf_cleanup: + ring_buffer__free(rb); + paf_bpf__destroy(skel_paf); + return err; } -int main(int argc, char **argv) { - struct ring_buffer *rb = NULL; - struct paf_bpf *skel_paf; - struct pr_bpf *skel_pr; - struct procstat_bpf *skel_procstat; - struct sysstat_bpf *skel_sysstat; - struct memleak_bpf *skel; +static int process_pr(struct pr_bpf *skel_pr) +{ + int err; + struct ring_buffer *rb; - int err, i; - LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); + LOAD_AND_ATTACH_SKELETON(skel_pr, pr); - err = argp_parse(&argp, argc, argv, 0, NULL, NULL); - if (err) - return err; - own_pid = getpid(); - libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + printf("%-8s %-8s %-8s %-8s %-8s\n", "RECLAIM", "RECLAIMED", "UNQUEUE", "CONGESTED", "WRITEBACK"); - /* Set up libbpf errors and debug info callback */ - libbpf_set_print(libbpf_print_fn); + POLL_RING_BUFFER(rb, 1000, err); - /* Cleaner handling of Ctrl-C */ - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); - signal(SIGALRM, sig_handler); +pr_cleanup: + ring_buffer__free(rb); + pr_bpf__destroy(skel_pr); + return err; +} - if (env.paf) { - /* Load and verify BPF application */ - skel_paf = paf_bpf__open(); - if (!skel_paf) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } +static int process_procstat(struct procstat_bpf *skel_procstat) +{ + int err; + struct ring_buffer *rb; - skel_paf->bss->user_pid = own_pid; + LOAD_AND_ATTACH_SKELETON(skel_procstat, procstat); - /* Load & verify BPF programs */ - err = paf_bpf__load(skel_paf); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto paf_cleanup; - } + if (env.rss) + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "VMSIZE", "VMDATA", "VMSTK", "VMPTE", "VMSWAP"); + } + else + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "SIZE", "RSSANON", "RSSFILE", "RSSSHMEM"); + } - /* Attach tracepoints */ - err = paf_bpf__attach(skel_paf); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto paf_cleanup; - } + POLL_RING_BUFFER(rb, 1000, err); - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_paf->maps.rb), handle_event_paf, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto paf_cleanup; - } +procstat_cleanup: + ring_buffer__free(rb); + procstat_bpf__destroy(skel_procstat); + return err; +} + +static int process_sysstat(struct sysstat_bpf *skel_sysstat) +{ + int err; + struct ring_buffer *rb; + + LOAD_AND_ATTACH_SKELETON(skel_sysstat, sysstat); - /* Process events */ - printf("%-8s %-8s %-8s %-8s %-8s\n", "MIN", "LOW", "HIGH", "PRESENT", "FLAG"); + if (env.part2) + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "KRECLM", "SLAB", "SRECLM", "SUNRECL", "NFSUNSTB", "WRITEBACKTMP", "KMAP", "UNMAP", "PAGE"); } - else if (env.pr) { - /* Load and verify BPF application */ - skel_pr = pr_bpf__open(); - if (!skel_pr) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } + else + { + printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "CPU", "MEM", "READ", "WRITE", "IOWAIT", "SWAP"); + } + POLL_RING_BUFFER(rb, 1000, err); - skel_pr->bss->user_pid = own_pid; +sysstat_cleanup: + ring_buffer__free(rb); + sysstat_bpf__destroy(skel_sysstat); + return err; +} - /* Load & verify BPF programs */ - err = pr_bpf__load(skel_pr); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto pr_cleanup; - } +static int process_memleak(struct memleak_bpf *skel_memleak, struct env env) +{ + skel_memleak->rodata->stack_flags = env.kernel_trace ? KERN_STACKID_FLAGS : USER_STACKID_FLAGS; - /* Attach tracepoints */ - err = pr_bpf__attach(skel_pr); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto pr_cleanup; - } + bpf_map__set_value_size(skel_memleak->maps.stack_traces, perf_max_stack_depth * sizeof(__u64)); + bpf_map__set_max_entries(skel_memleak->maps.stack_traces, stack_map_max_entries); - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_pr->maps.rb), handle_event_pr, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto pr_cleanup; - } + if (!env.kernel_trace) + disable_kernel_tracepoints(skel_memleak); - /* Process events */ - printf("%-8s %-8s %-8s %-8s %-8s\n", "RECLAIM", "RECLAIMED", "UNQUEUE", "CONGESTED", "WRITEBACK"); + int err = memleak_bpf__load(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to load BPF skeleton\n"); + goto memleak_cleanup; } - else if (env.procstat) { - /* Load and verify BPF application */ - skel_procstat = procstat_bpf__open(); - if (!skel_procstat) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; + if (!env.kernel_trace) + { + err = attach_uprobes(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to attach uprobes\n"); + goto memleak_cleanup; } + } - skel_procstat->bss->user_pid = own_pid; + err = memleak_bpf__attach(skel_memleak); + if (err) + { + fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); + goto memleak_cleanup; + } - /* Load & verify BPF programs */ - err = procstat_bpf__load(skel_procstat); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto procstat_cleanup; - } + g_stacks_size = perf_max_stack_depth * sizeof(*g_stacks); + g_stacks = (__u64 *)malloc(g_stacks_size); + if (!g_stacks) + { + fprintf(stderr, "Failed to allocate memory\n"); + err = -1; + goto memleak_cleanup; + } + memset(g_stacks, 0, g_stacks_size); + + symbolizer = blaze_symbolizer_new(); + if (!symbolizer) + { + fprintf(stderr, "Fail to create a symbolizer\n"); + err = -1; + goto memleak_cleanup; + } - /* Attach tracepoints */ - err = procstat_bpf__attach(skel_procstat); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto procstat_cleanup; - } + for (;;) + { + if (!env.kernel_trace) + if (env.print_time) + { + system("clear"); + update_addr_times(skel_memleak); + print_time(skel_memleak); + } + else + print_outstanding_combined_allocs(skel_memleak, attach_pid); + else + print_outstanding_allocs(skel_memleak); - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_procstat->maps.rb), handle_event_procstat, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto procstat_cleanup; - } + sleep(1); + } - /* Process events */ - if (env.rss == true) { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "VMSIZE", "VMDATA", "VMSTK", "VMPTE", "VMSWAP"); + while (!exiting) + { + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) + { + err = 0; + break; } - else { - printf("%-8s %-8s %-8s %-8s %-8s %-8s\n", "TIME", "PID", "SIZE", "RSSANON", "RSSFILE", "RSSSHMEM"); + if (err < 0) + { + printf("Error polling perf buffer: %d\n", err); + break; } } - else if (env.sysstat) { - /* Load and verify BPF application */ - skel_sysstat = sysstat_bpf__open(); - if (!skel_sysstat) { - fprintf(stderr, "Failed to open and load BPF skeleton\n"); - return 1; - } +memleak_cleanup: + memleak_bpf__destroy(skel_memleak); + if (symbolizer) + blaze_symbolizer_free(symbolizer); + if (g_stacks) + free(g_stacks); + if (allocs) + free(allocs); + return err; +} - skel_sysstat->bss->user_pid = own_pid; +// ================================================== fraginfo==================================================================== +void print_nodes(int fd) { + struct pgdat_info pinfo; + __u64 key = 0, next_key; + printf(" Node ID PGDAT_PTR NR_ZONES \n"); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + bpf_map_lookup_elem(fd, &next_key, &pinfo); + printf(" %5d 0x%llx %5d\n", + pinfo.node_id, pinfo.pgdat_ptr, pinfo.nr_zones); + key = next_key; + } +} - /* Load & verify BPF programs */ - err = sysstat_bpf__load(skel_sysstat); - if (err) { - fprintf(stderr, "Failed to load and verify BPF skeleton\n"); - goto sysstat_cleanup; - } +void print_zones(int fd) { + struct zone_info zinfo; + __u64 key = 0, next_key; + printf("%-20s %-20s %-25s %-20s %-20s"," COMM" , "ZONE_PTR" , "ZONE_PFN " , " SUM_PAGES" ,"FACT_PAGES "); + printf("\n"); + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { + bpf_map_lookup_elem(fd, &next_key, &zinfo); + printf(" %-15s 0x%-25llx %-25llu %-20llu %-15llu\n", zinfo.comm, zinfo.zone_ptr, zinfo.zone_start_pfn,zinfo.spanned_pages,zinfo.present_pages); + key = next_key; + } - /* Attach tracepoints */ - err = sysstat_bpf__attach(skel_sysstat); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto sysstat_cleanup; - } +} +void print_orders(int fd) { + struct order_zone okey = {}; + struct ctg_info oinfo; + struct order_entry entries[256]; + int entry_count = 0; + + while (bpf_map_get_next_key(fd, &okey, &okey) == 0) { + if (bpf_map_lookup_elem(fd, &okey, &oinfo) == 0) { + entries[entry_count].okey = okey; + entries[entry_count].oinfo = oinfo; + entry_count++; + } + } - /* Set up ring buffer polling */ - rb = ring_buffer__new(bpf_map__fd(skel_sysstat->maps.rb), handle_event_sysstat, NULL, NULL); - if (!rb) { - err = -1; - fprintf(stderr, "Failed to create ring buffer\n"); - goto sysstat_cleanup; - } + //排序 + qsort(entries, entry_count, sizeof(struct order_entry), compare_entries); - /* Process events */ - if (env.part2 == true) { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "KRECLM", "SLAB", "SRECLM", "SUNRECLMA", "UNSTABLE", "WRITEBK_T", "ANONHUGE", "SHMEMHUGE", "PMDMAPP"); - } - else { - printf("%-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s\n", "ACTIVE", "INACTVE", "ANON_ACT", "ANON_INA", "FILE_ACT", "FILE_INA", "UNEVICT", "DIRTY", "WRITEBK", "ANONPAG", "MAP", "SHMEM"); - } + // 打印排序后的 + printf(" Order Zone_PTR Free Pages Free Blocks Total Free Blocks Suitable\n"); + for (int i = 0; i < entry_count; i++) { + printf(" %-8u 0x%-25llx %-20lu %-20lu %-20lu\n", + entries[i].okey.order, entries[i].okey.zone_ptr, entries[i].oinfo.free_pages, + entries[i].oinfo.free_blocks_total, entries[i].oinfo.free_blocks_suitable); + } +} + + +static int process_fraginfo(struct fraginfo_bpf *skel_fraginfo) +{ + + int err = fraginfo_bpf__load(skel_fraginfo); + if (err) + { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto fraginfo_cleanup; + } + + err = fraginfo_bpf__attach(skel_fraginfo); + if (err) + { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto fraginfo_cleanup; + } + while(1){ + sleep(env.interval); + print_nodes(bpf_map__fd(skel_fraginfo->maps.nodes)); + printf("\n"); + print_zones(bpf_map__fd(skel_fraginfo->maps.zones)); + printf("\n"); + print_orders(bpf_map__fd(skel_fraginfo->maps.orders)); + printf("\n"); } - else if (env.memleak) { - if (argc != 3) { - printf("usage:%s attach_pid\n", argv[0]); - return -1; - } +fraginfo_cleanup: + fraginfo_bpf__destroy(skel_fraginfo); + return -err; +} - attach_pid = atoi(argv[2]); +// ================================================== vmasnap ==================================================================== +static void print_find_event_data(int map_fd) { + __aligned_u64 key = 0; + __aligned_u64 next_key; + struct find_event_t event; - strcpy(binary_path, "/lib/x86_64-linux-gnu/libc.so.6"); + printf("Reading find events...\n"); - /* Set up libbpf errors and debug info callback */ - libbpf_set_print(libbpf_print_fn); + // Print header + printf("%-10s %-20s %-15s %-20s %-20s %-20s %-20s\n", + "PID", "Address", "Duration", "VMACache Hit", "RB Subtree Last", "VM Start", "VM End"); - /* Load and verify BPF application */ - skel = memleak_bpf__open(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } + while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) { + if (bpf_map_lookup_elem(map_fd, &next_key, &event) == 0) { + printf("%-10llu %-20lu %-15llu %-20d %-20llu %-20llu %-20llu\n", + next_key, event.addr, event.duration, event.vmacache_hit, + event.rb_subtree_last, event.vm_start, event.vm_end); - bpf_map__set_value_size(skel->maps.stack_traces, perf_max_stack_depth * sizeof(__u64)); - bpf_map__set_max_entries(skel->maps.stack_traces, stack_map_max_entries); + // Delete the element from the map after printing + if (bpf_map_delete_elem(map_fd, &next_key) != 0) { + perror("Failed to delete element from map"); + } + } - /* Load & verify BPF programs */ - err = memleak_bpf__load(skel); - if (err) { - fprintf(stderr, "Failed to load BPF skeleton\n"); - goto memleak_cleanup; - } + // Use a temporary variable to handle key update + __aligned_u64 temp_key = next_key; + key = temp_key; + } +} - err = attach_uprobes(skel); - if (err) { - fprintf(stderr, "failed to attach uprobes\n"); - goto memleak_cleanup; - } - /* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub - * NOTICE: we provide path and symbol info in SEC for BPF programs - */ - err = memleak_bpf__attach(skel); - if (err) { - fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); - goto memleak_cleanup; - } +static void print_insert_event_data(int map_fd) { + __aligned_u64 key = 0; + __aligned_u64 next_key; + struct insert_event_t event; - g_stacks_size = perf_max_stack_depth * sizeof(*g_stacks); - g_stacks = (__u64 *)malloc(g_stacks_size); - memset(g_stacks, 0, g_stacks_size); + printf("Reading insert events...\n"); - symbolizer = blaze_symbolizer_new(); - if (!symbolizer) { - fprintf(stderr, "Fail to create a symbolizer\n"); - err = -1; - goto memleak_cleanup; - } + // Print header + printf("%-10s %-15s %-15s %-20s %-20s %-20s %-20s %-20s\n", + "PID", "Duration", "List", "RB", "Interval Tree", "List Time", "RB Time", "Interval Tree Time"); - for (i = 0;; i++) { - /* trigger our BPF programs */ - print_outstanding_combined_allocs(skel, attach_pid); - sleep(1); - } - } + while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) { + if (bpf_map_lookup_elem(map_fd, &next_key, &event) == 0) { + printf("%-10llu %-15llu %-15d %-20d %-20d %-20llu %-20llu %-20llu\n", + next_key, event.duration, event.inserted_to_list, event.inserted_to_rb, + event.inserted_to_interval_tree, event.link_list_duration, + event.link_rb_duration, event.interval_tree_duration); - while (!exiting) { - if (env.paf || env.pr || env.procstat || env.sysstat) { - err = ring_buffer__poll(rb, 1000 /* timeout, ms */); - /* Ctrl-C will cause -EINTR */ - if (err == -EINTR) { - err = 0; - break; - } - if (err < 0) { - printf("Error polling perf buffer: %d\n", err); - break; - } - } - else if (env.memleak) { - /* Ctrl-C will cause -EINTR */ - if (err == -EINTR) { - err = 0; - break; - } - if (err < 0) { - printf("Error polling perf buffer: %d\n", err); - break; - } - } - else { - printf("请输入要使用的功能...\n"); - break; - } - } + // Delete the element from the map after printing + if (bpf_map_delete_elem(map_fd, &next_key) != 0) { + perror("Failed to delete element from map"); + } + } -paf_cleanup: - ring_buffer__free(rb); - paf_bpf__destroy(skel_paf); - return err < 0 ? -err : 0; + // Use a temporary variable to handle key update + __aligned_u64 temp_key = next_key; + key = temp_key; + } +} -pr_cleanup: - ring_buffer__free(rb); - pr_bpf__destroy(skel_pr); - return err < 0 ? -err : 0; +static int process_vmasnap(struct vmasnap_bpf *skel_vmasnap) { + int err; -procstat_cleanup: - ring_buffer__free(rb); - procstat_bpf__destroy(skel_procstat); - return err < 0 ? -err : 0; + // Load and verify BPF application + skel_vmasnap = vmasnap_bpf__open(); + if (!skel_vmasnap) { + fprintf(stderr, "Failed to open BPF skeleton\n"); + return 1; + } -sysstat_cleanup: - ring_buffer__free(rb); - sysstat_bpf__destroy(skel_sysstat); - return err < 0 ? -err : 0; + // Load & verify BPF programs + err = vmasnap_bpf__load(skel_vmasnap); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto vmasnap_cleanup; + } -memleak_cleanup: - memleak_bpf__destroy(skel); - blaze_symbolizer_free(symbolizer); - free(g_stacks); - return err < 0 ? -err : 0; + // Attach tracepoints + err = vmasnap_bpf__attach(skel_vmasnap); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto vmasnap_cleanup; + } + + printf("Successfully started! Press Ctrl-C to exit.\n"); + + // Get the file descriptors for the maps + int find_map_fd = bpf_map__fd(skel_vmasnap->maps.find_events); + int insert_map_fd = bpf_map__fd(skel_vmasnap->maps.insert_events); + + if (find_map_fd < 0 || insert_map_fd < 0) { + fprintf(stderr, "Failed to get file descriptor for maps\n"); + goto vmasnap_cleanup; + } + + // Main loop + while (!exiting) { + // Print events data every second + print_find_event_data(find_map_fd); + print_insert_event_data(insert_map_fd); + sleep(1); + } + +vmasnap_cleanup: + vmasnap_bpf__destroy(skel_vmasnap); + return 0; } \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h deleted file mode 100644 index 3b65256eb..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef __MEM_WATCHER_H -#define __MEM_WATCHER_H - -#define TASK_COMM_LEN 16 -#define MAX_FILENAME_LEN 127 - -#define ___GFP_DMA 0x01u -#define ___GFP_HIGHMEM 0x02u -#define ___GFP_DMA32 0x04u -#define ___GFP_MOVABLE 0x08u -#define ___GFP_RECLAIMABLE 0x10u -#define ___GFP_HIGH 0x20u -#define ___GFP_IO 0x40u -#define ___GFP_FS 0x80u -#define ___GFP_WRITE 0x100u -#define ___GFP_NOWARN 0x200u -#define ___GFP_RETRY_MAYFAIL 0x400u -#define ___GFP_NOFAIL 0x800u -#define ___GFP_NORETRY 0x1000u -#define ___GFP_MEMALLOC 0x2000u -#define ___GFP_COMP 0x4000u -#define ___GFP_ZERO 0x8000u -#define ___GFP_NOMEMALLOC 0x10000u -#define ___GFP_HARDWALL 0x20000u -#define ___GFP_THISNODE 0x40000u -#define ___GFP_ATOMIC 0x80000u -#define ___GFP_ACCOUNT 0x100000u -#define ___GFP_DIRECT_RECLAIM 0x200000u -#define ___GFP_KSWAPD_RECLAIM 0x400000u - -#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) -#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) -#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT) -#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) -#define GFP_NOIO (__GFP_RECLAIM) -#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) -#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL) -#define GFP_DMA __GFP_DMA -#define GFP_DMA32 __GFP_DMA32 -#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) -#define GFP_HIGHUSER_MOVABLE (GFP_HIGHUSER | __GFP_MOVABLE) -#define GFP_TRANSHUGE_LIGHT ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \ - __GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM) -#define GFP_TRANSHUGE (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM) - -struct paf_event { - unsigned long min; - unsigned long low; - unsigned long high; - unsigned long present; - unsigned long protection; - int flag; -}; - -struct pr_event { - unsigned long reclaim; - unsigned long reclaimed; - unsigned int unqueued_dirty; - unsigned int congested; - unsigned int writeback; -}; - -struct procstat_event { - /*进程内存状态报告*/ - pid_t pid; - long nvcsw; - long nivcsw; - long vsize; //虚拟内存 - long size; //物理内存 - long long rssanon; //匿名页面 - long long rssfile; //文件页面 - long long rssshmem; //共享页面 - long long vswap; //交换页面 - long long Hpages; //hugetlbPages - long Vdata; //Private data segments - long Vstk; //User stack - long long VPTE; -}; - -struct sysstat_event { - /*系统内存状态报告*/ - unsigned long present; - unsigned long anon_inactive; // 0 - unsigned long anon_active; // 1 - unsigned long file_inactive; // 2 - unsigned long file_active; // 3 - unsigned long unevictable; // 不可回收页面 - unsigned long slab_reclaimable; - unsigned long slab_unreclaimable; - unsigned long anon_isolated; // 匿名隔离页面 - unsigned long file_isolated; // 文件隔离页面 - - unsigned long working_nodes; // 12 - unsigned long working_refault; - unsigned long working_activate; - unsigned long working_restore; - unsigned long working_nodereclaim; - - unsigned long anon_mapped; // 17 - unsigned long file_mapped; - - unsigned long file_pages; // 19 - unsigned long file_dirty; - unsigned long writeback; - unsigned long writeback_temp; - - unsigned long shmem; // 共享内存23 - unsigned long shmem_thps; - unsigned long pmdmapped; - unsigned long anon_thps; - unsigned long unstable_nfs; - unsigned long vmscan_write; - unsigned long vmscan_immediate; - - unsigned long diried; - unsigned long written; - unsigned long kernel_misc_reclaimable; -}; - -/*memleak.h*/ -#define ALLOCS_MAX_ENTRIES 1000000 -#define COMBINED_ALLOCS_MAX_ENTRIES 10240 - -struct alloc_info { - __u64 size; - int stack_id; -}; - -union combined_alloc_info { - struct { - __u64 total_size : 40; - __u64 number_of_allocs : 24; - }; - __u64 bits; -}; - -#endif /* __MEM_WATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c deleted file mode 100644 index fa926527c..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.c +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 The LMP Authors. -// -// Licensed 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// 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. -// -// author: zai953879556@163.com -// -// mem_watcher libbpf user mode code - -#include -#include -#include -#include -#include -#include -#include -#include "memleak.skel.h" -#include "mem_watcher.h" - -#include "blazesym.h" - -int main(int argc, char **argv) { - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c deleted file mode 100644 index 3057fae61..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/paf.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "paf.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c deleted file mode 100644 index c4dcd6f3f..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/pr.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "pr.skel.h" -#include -#include - -int main(int argc, char **argv){ - -} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c deleted file mode 100644 index 90876c125..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/procstat.c +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -/* Copyright (c) 2020 Facebook */ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "procstat.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c deleted file mode 100644 index 448210d90..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/sysstat.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "mem_watcher.h" -#include "sysstat.skel.h" -#include -#include - -int main(int argc, char **argv) { - -} \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile new file mode 100644 index 000000000..cbe5710b9 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/Makefile @@ -0,0 +1,12 @@ +CC = gcc +CFLAGS = -g + +.PHONY: all clean + +all: test_mem + +test_mem: test_mem.c + $(CC) $(CFLAGS) -o test_mem test_mem.c + +clean: + rm -f test_mem diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile new file mode 100644 index 000000000..55d78fca0 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/Makefile @@ -0,0 +1,22 @@ +# Compiler and flags +CC = gcc +CFLAGS = -Wall + +# Source files +C_SRC = pr_test.c + +# Output binary +C_OUT = pr_test + +.PHONY: all clean run + +all: $(C_OUT) + +$(C_OUT): $(C_SRC) + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f $(C_OUT) + +run: $(C_OUT) + sudo ./$(C_OUT) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c new file mode 100644 index 000000000..e10e25bf5 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/pr_test/pr_test.c @@ -0,0 +1,36 @@ +#include +#include +#include + +#define ALLOC_SIZE 1024*1024*1024 // 分配 512 MB 内存 + +int main() { + void *memory; + printf("Allocating memory...\n"); + memory = malloc(ALLOC_SIZE); + if (!memory) { + perror("Failed to allocate memory"); + return -1; + } + + // 填充内存以确保页面被分配 + printf("Filling memory...\n"); + for (size_t i = 0; i < ALLOC_SIZE; ++i) { + ((char*)memory)[i] = (char)i; + } + + printf("Freeing memory...\n"); + free(memory); + + // 给内核更多时间处理回收 + printf("Sleeping for 10 seconds...\n"); + sleep(10); // 增加等待时间到 10 秒 + + printf("Memory management demo finished.\n"); + return 0; +} + +// 分配大量内存。 +//填充内存以确保页面分配。 +//释放内存。 +//等待一段时间以允许内核处理任何可能的内存回收。 \ No newline at end of file diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile new file mode 100644 index 000000000..9e3fbbded --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wall -O2 +TARGET = sysstat_test + +all: $(TARGET) + +$(TARGET): sysstat_test.c + $(CC) $(CFLAGS) -o $(TARGET) sysstat_test.c + +clean: + rm -f $(TARGET) diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c new file mode 100644 index 000000000..c0e7e64b5 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/sysstat_test/sysstat_test.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#define ALLOC_SIZE_SMALL 512*1024*1024 // 分配 512 MB 内存 +#define ALLOC_SIZE_LARGE 1024*1024*1024 // 分配 1 GB 内存 + +void allocate_memory(size_t size) { + void *memory; + + printf("Allocating %lu MB memory...\n", size / (1024*1024)); + memory = malloc(size); + if (!memory) { + perror("Failed to allocate memory"); + return; + } + + // 填充内存以确保页面被分配 + printf("Filling memory...\n"); + for (size_t i = 0; i < size; ++i) { + ((char*)memory)[i] = (char)i; + } + + printf("Freeing memory...\n"); + free(memory); + + // 给内核更多时间处理回收 + printf("Sleeping for 10 seconds...\n"); + sleep(10); // 增加等待时间到 10 秒 + + printf("Memory management operation finished.\n"); +} + +int main() { + printf("Starting memory management demo...\n"); + + // 分配和释放多次小块内存 + for (int i = 0; i < 5; ++i) { + allocate_memory(ALLOC_SIZE_SMALL); + } + + // 分配和释放多次大块内存 + for (int i = 0; i < 5; ++i) { + allocate_memory(ALLOC_SIZE_LARGE); + } + + printf("Memory management demo finished.\n"); + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c new file mode 100644 index 000000000..9396ace1f --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/test/test_mem.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +#define ALLOC_SIZE_SMALL 4 +#define ALLOC_SIZE_MEDIUM 64 +#define ALLOC_SIZE_LARGE 1024 + +static struct env { + bool overall_leak_test; + bool mem_leak; + bool mem_unleak; +} env = { + .overall_leak_test = false, + .mem_leak = false, + .mem_unleak = false, +}; + +const char argp_program_doc[] ="mem_watcher test.\n"; + +static const struct argp_option opts[] = { + { NULL, 0, NULL, 0, "Memory Management Options:", 1 }, + { "overall-test", 'o', NULL, 0, "Perform overall memory test", 2 }, + { "detect-leak", 'l', NULL, 0, "Detect memory leaks", 3 }, + { "no-leak", 'n', NULL, 0, "No memory leaks expected", 3 }, + { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help", 0 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + (void)arg; + switch (key) { + case 'o': + env.overall_leak_test = true; + break; + case 'l': + env.mem_leak = true; + break; + case 'n': + env.mem_unleak = true; + break; + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +// 模拟一些处理,通过写入分配的内存 +static void process_data(void *ptr, int size) { + memset(ptr, 0, size); +} + +// 分配内存并处理数据 +static void * alloc_v3(int alloc_size) { + void *ptr = malloc(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 3); + } + return ptr; +} + +// 分配内存并处理数据 +static void * alloc_v2(int alloc_size) { + void *ptr = alloc_v3(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 4); + } + return ptr; +} + +// 分配内存并处理数据 +static void * alloc_v1(int alloc_size) { + void *ptr = alloc_v2(alloc_size); + if (ptr) { + process_data(ptr, alloc_size / 5); + } + return ptr; +} + +// 演示内存泄漏 +static void leak_memory() { + void *ptr = malloc(ALLOC_SIZE_LARGE); + // 故意不释放 ptr 以制造内存泄漏 + process_data(ptr, ALLOC_SIZE_LARGE); +} + +static void mem_leak_process() { + // 引入一些间歇性的内存泄漏 + void *ptr = NULL; + int i = 0; + for (i = 0; ; i++) { + if (i % 5 == 0) { + leak_memory(); + } + sleep(1); + } +} + +static void mem_unleak_process(){ + void *ptr = NULL; + int i = 0; + + for (i = 0; ; i++) { + int alloc_size = (i % 3 == 0) ? ALLOC_SIZE_SMALL : (i % 3 == 1) ? ALLOC_SIZE_MEDIUM : ALLOC_SIZE_LARGE; + + ptr = alloc_v1(alloc_size); + if (!ptr) { + perror("alloc_v1 失败"); + exit(EXIT_FAILURE); + } + + void *ptr2 = malloc(alloc_size); + if (!ptr2) { + perror("malloc 失败"); + free(ptr); + exit(EXIT_FAILURE); + } + + process_data(ptr2, alloc_size); + + sleep(1); + free(ptr); + + sleep(2); + free(ptr2); + } +} + +int main(int argc, char **argv){ + int err; + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc, + }; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + if (env.overall_leak_test) { + // 打印当前进程的进程号(PID) + pid_t pid = getpid(); + printf("当前进程的进程号(PID): %d\n", pid); + if (env.mem_leak) { + printf("正在进行内存泄漏检测...\n"); + mem_leak_process(); + } + if (env.mem_unleak) { + printf("正在进行无内存泄漏测试...\n"); + mem_unleak_process(); + } + } + + return 0; +} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile index c3a2cd4fc..4bee190f7 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/cma/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile index e9e8ef726..154ab723b 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/ion/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile index 041fcb3ba..9e45bb23a 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/Makefile @@ -2,8 +2,8 @@ OUTPUT := .output CLANG ?= clang LLVM_STRIP ?= llvm-strip -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c deleted file mode 100644 index dd18dd50c..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.bpf.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "vmlinux.h" -#include -#include -#include -#include "cma_monitor.h" - -#define INTERVAL_MAX 6U -char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, unsigned); - __type(value, u64); -} count_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, u32); - __type(value, u64); -} time_map SEC(".maps"); - - -SEC("kretprobe/cma_alloc") -int BPF_KRETPROBE(cma_alloc) -{ - u32 pid = bpf_get_current_pid_tgid(); - u64 ts = bpf_ktime_get_ns(); - - bpf_map_update_elem(&time_map, &pid, &ts, BPF_ANY); - - return 0; -} - -SEC("kprobe/alloc_contig_range") -int BPF_KRETPROBE(alloc_contig_range) -{ - u32 pid = bpf_get_current_pid_tgid(); - u64 tm = bpf_ktime_get_ns(); - u64 *tsp = bpf_map_lookup_elem(&time_map, &pid); - - if (tsp) - tm -= *tsp; - else - return 1; - - unsigned key = tm / 10000000; - if (key > INTERVAL_MAX - 1) - key = INTERVAL_MAX - 1; - - u64 *value = bpf_map_lookup_elem(&count_map, &key); - if (value) - *value += 1; - else { - u64 init_value = 1; - bpf_map_update_elem(&count_map, &key, &init_value, BPF_ANY); - } - - bpf_map_delete_elem(&time_map, &pid); - - return 0; -} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c deleted file mode 100644 index cecf5bc82..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -//#include -#include "cma_monitor.h" -#include "cma_monitor.skel.h" - -#define INTERVAL_MAX 6U - -int main(int argc, char **argv) -{ - /* - char file_name[200]; - - snprintf(file_name, sizeof(file_name), "%s_kern.o", argv[0]); - if (load_bpf_file(file_name)) { - printf("%s", bpf_log_buf); - - return 1; - }*/ - struct cma_monitor_bpf *skel = cma_monitor_bpf__open_and_load(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - int fd = bpf_map__fd(skel->maps.time_map); - int key; - - for (;;) { - sleep(5); - - for (key = 0; key < INTERVAL_MAX; key++) { - unsigned long long value = 0; - bpf_map_lookup_elem(fd, &key, &value); - - if (key < INTERVAL_MAX - 1) - printf("Range %dms - %dms\tCount:%llu\n", - key * 10, (key + 1) * 10, value); - else - printf("Over 50ms\t\tCount:%llu\n", value); - } - - printf("=========================================\n"); - } - - return 0; -} diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h deleted file mode 100644 index b9b3eddb1..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/cma/cma_monitor.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* Copyright (c) 2022 Jacky Yin */ -#ifndef __CMA_MONITOR_H -#define __CMA_MONTOR_H - - - - - -#endif /* __CMA_MONTOR_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c deleted file mode 100644 index e91936618..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.bpf.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "vmlinux.h" -#include -#include -#include -#include "ion_monitor.h" - -#define INTERVAL_MAX 6U - -char LICENSE[] SEC("license") = "Dual BSD/GPL"; -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, unsigned); - __type(value, u64); -} count_map SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 8192); - __type(key, u32); - __type(value, u64); -} time_map SEC(".maps"); - -SEC("kprobe/ion_alloc") -int bpf_prog1(void *ctx) -{ - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 time = bpf_ktime_get_ns(); - u64 ts = bpf_ktime_get_ns(); - bpf_map_update_elem(&time_map, &pid, &ts, BPF_ANY); - - return 0; -} - -SEC("kprobe/ion_ioctl") -int bpf_prog2(void *ctx) -{ - u32 pid = bpf_get_current_pid_tgid() >> 32; - u64 tm = bpf_ktime_get_ns(); - - u64 *tsp = bpf_map_lookup_elem(&time_map, &pid); - if (tsp) - tm -= *tsp; - else - return -1; - - unsigned key = tm / 10000000;//10ms为区间单位 - if (key > INTERVAL_MAX - 1) - key = INTERVAL_MAX - 1; - u64 *value = bpf_map_lookup_elem(&count_map,&key); - if (value) { - *value += 1; - } else { - u64 init_value = 1; - bpf_map_update_elem(&count_map, &key, &init_value, BPF_ANY); - } - - bpf_map_delete_elem(&time_map, &pid); - - return 0; -} - -char _license[] SEC("license") = "GPL"; diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c deleted file mode 100644 index d22a7e004..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.c +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -//#include -#include "ion_monitor.h" -#include "ion_monitor.skel.h" -#include - - - -#define INTERVAL_MAX 6U -int main(int argc, char **argv) -{ - /* - char file_name[200]; - - snprintf(file_name, sizeof(file_name), "%s_kern.o", argv[0]); - if (load_bpf_file(file_name)) { - printf("%s", bpf_log_buf); - - return 1; - }*/ - struct ion_monitor_bpf *skel = ion_monitor_bpf__open_and_load(); - if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); - return 1; - } - - int fd = bpf_map__fd(skel->maps.time_map); - int key; - - for(;;) { - sleep(10); - - for (key = 0; key < INTERVAL_MAX; key++) { - unsigned long long value = 0; - bpf_map_lookup_elem(fd, &key, &value); - if (key < INTERVAL_MAX - 1) - printf("Range %dms - %dms\tCount:%llu\n", - key * 10, (key + 1) * 10, value); - else - printf("Over 50ms\t\tCount:%llu\n", value); - } - - printf("==========================================\n"); - } - - return 0; -} - - - - diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h b/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h deleted file mode 100644 index e6712713e..000000000 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/ion_monitor.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* Copyright (c) 2022 Jacky Yin */ -#ifndef __ION_MONITOR_H -#define __ION_MONITOR_H - - - - - -#endif /* __ION_MONTOR_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile index 04f7a181a..75cfbb57c 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/paf/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile similarity index 93% rename from eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile index e9e8ef726..6fc92d3a9 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/mem_watcher/page_fault/ion/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile @@ -1,10 +1,10 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) -BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) +BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/../bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ @@ -14,7 +14,7 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h +VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated @@ -22,7 +22,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = ion_monitor +APPS = time # Get Clang's default includes on this system. We'll explicitly add these dirs # to the includes list when compiling with `-target bpf` because otherwise some diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c new file mode 100644 index 000000000..073dd01b4 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c @@ -0,0 +1,67 @@ +#include "vmlinux.h" +#include +#include +#include +#include "time.h" + + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +} exec_start SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +//动态挂载 +SEC("kprobe/handle_mm_fault") +int BPF_KPROBE(handle_mm_fault_enter) +{ + unsigned int pid; + u64 current_time; + pid=bpf_get_current_pid_tgid();//pid + + current_time= bpf_ktime_get_ns()/1000;//挂载前进程时间 + bpf_map_update_elem(&exec_start, &pid, ¤t_time, BPF_ANY);//更新map元素 + + //bpf_printk("KPROBE ENTRY pid=%d,comm=%s,state=%d,current_time=%d\n",pid,comm,state,current_time); + return 0; +} + +SEC("kretprobe/handle_mm_fault") +int BPF_KRETPROBE(handle_mm_fault_exit) +{ + struct task_struct *t = (struct task_struct *)bpf_get_current_task();//获取task_struct结构体数据 + struct event *e; + unsigned int pid; + u64 *start_ts; + u64 duration_ns = 0; + char comm[20]; + + /* get PID and TID of exiting thread/process */ + + pid = bpf_get_current_pid_tgid(); + + start_ts=bpf_map_lookup_elem(&exec_start,&pid); + if (!start_ts) + return 0; + duration_ns = bpf_ktime_get_ns()/1000 - *start_ts;//获取系统进程执行时间 + bpf_map_delete_elem(&exec_start,&pid);//删除当前进程pid对应键值对 + //ringbuf提交 + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);//malloc + if (!e) + return 0; + + e->duration_ns=duration_ns; + e->pid=pid; + bpf_get_current_comm(&e->comm,sizeof(e->comm)); + e->state=BPF_CORE_READ(t,__state); + bpf_ringbuf_submit(e, 0); + return 0; +} + diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c new file mode 100644 index 000000000..570ce5e7e --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include "time.h" +#include "time.skel.h" + + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + //if (level == LIBBPF_DEBUG && !env.verbose) + // return 0; + return vfprintf(stderr, format, args); +} + +static volatile bool exiting = false; + +static void sig_handler(int sig) +{ + exiting = true; +} + +static int handle_event(void *ctx, void *data, size_t data_sz) +{ + const struct event *e = data; + printf("%-8d %-5d %-16lld %-7s\n", e->pid, e->state, e->duration_ns,e->comm); + return 0; +} + +int main(int argc, char **argv) +{ + struct ring_buffer *rb = NULL; + struct time_bpf *skel; + int err; + + + + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + /* Load and verify BPF application */ + skel = time_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = time_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoints */ + err = time_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + /* Set up ring buffer polling */ + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); + if (!rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer\n"); + goto cleanup; + } + + /* Process events */ + + while (!exiting) { + err = ring_buffer__poll(rb, 100 /* timeout, ms */); + /* Ctrl-C will cause -EINTR */ + if (err == -EINTR) { + err = 0; + break; + } + if (err < 0) { + printf("Error polling perf buffer: %d\n", err); + break; + } + } + +cleanup: + /* Clean up */ + ring_buffer__free(rb); + time_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} + + + diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h new file mode 100644 index 000000000..bf9de6845 --- /dev/null +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2020 Facebook */ +#ifndef __BOOTSTRAP_H +#define __BOOTSTRAP_H + +#define TASK_COMM_LEN 16 +#define MAX_FILENAME_LEN 127 + +struct event { + int pid; + unsigned int state; + unsigned long long duration_ns; + bool exit_event; + char comm[TASK_COMM_LEN]; +}; + +#endif /* __BOOTSTRAP_H */ diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile index 5b76fdb3e..fe15282f5 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/pr/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile index 28dcdc12c..a28526400 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/procstat/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile index 422ff2fc3..5f33e0a2a 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/sysstat/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../../libbpf/src) -BPFTOOL_SRC := $(abspath ../../bpftool/src) +LIBBPF_SRC := $(abspath ../../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool diff --git a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod index 2c47343f5..6fba00706 100644 --- a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod +++ b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.mod @@ -3,7 +3,7 @@ module lmp/eTrafficManager go 1.20 require ( - github.com/cilium/cilium v1.14.8 + github.com/cilium/cilium v1.14.14 github.com/cilium/ebpf v0.11.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.42.0 @@ -18,7 +18,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.4 // indirect @@ -30,11 +30,11 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.22.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -54,7 +54,7 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil/v3 v3.23.5 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -68,22 +68,22 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect + k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum index 101946f56..073b75e00 100644 --- a/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum +++ b/eBPF_Supermarket/Network_Subsystem/TrafficManager/go.sum @@ -52,8 +52,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/checkmate v1.0.3 h1:CQC5eOmlAZeEjPrVZY3ZwEBH64lHlx9mXYdUehEwI5w= -github.com/cilium/cilium v1.14.8 h1:Jm54iA7XxWJn+GdZrfzcEyeoNUb58w8y0g8ZB2lVcJw= -github.com/cilium/cilium v1.14.8/go.mod h1:7cWSSEl+dU9Q5CqnEWT//c0w8yLSazShw287Uop6xVs= +github.com/cilium/cilium v1.14.14 h1:KYxWuIcibhR2ad1IjAoUT1ofQt93vtSEQwkYRujjgus= +github.com/cilium/cilium v1.14.14/go.mod h1:Av3AtSEaR3A93p4saMzhNBCdCnmyvUGQYu8oueKmKIA= github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -81,8 +81,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= @@ -176,8 +176,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -194,8 +194,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -216,8 +217,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -323,8 +324,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -352,7 +353,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -376,6 +377,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= @@ -398,6 +400,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -435,6 +438,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -468,8 +472,9 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -479,8 +484,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -493,8 +498,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -539,16 +545,19 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.1-0.20230616193735-e0c3b6e6ae3b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -558,8 +567,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -618,7 +628,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -648,8 +659,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -715,8 +727,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -756,8 +768,8 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY= +k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile b/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile index 7bbe2ad6b..3f0306b8d 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/Makefile @@ -12,7 +12,7 @@ MAKEFLAGS += --no-print-directory -s Q = @ endif -PROJ := xacl_ip router xacl_mac xstate +PROJ := xacl_ip router xacl_mac xstate net_manager diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h index 167822a1f..4d7e9e8b9 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_defines.h @@ -1,11 +1,12 @@ #ifndef __COMMON_DEFINES_H #define __COMMON_DEFINES_H -#include -#include -#include #include + +#define FILE_MAXSIZE 128 +#define IF_NAMESIZE 16 + struct config { enum xdp_attach_mode attach_mode; __u32 xdp_flags; @@ -27,6 +28,19 @@ struct config { int xsk_if_queue; bool xsk_poll_mode; bool unload_all; + bool show_stats; // 数据统计 + bool ip_filter; //ip过滤 + bool mac_filter; //mac过滤 + bool router; //路由 + bool state; //会话保持 + bool clear; //清理 + char *ip_filter_file; + char ip_filter_file_buf[FILE_MAXSIZE]; + char *mac_filter_file; + char mac_filter_file_buf[FILE_MAXSIZE]; + char *router_file; + char router_file_buf[FILE_MAXSIZE]; + bool print_info; }; /* Defined in common_params.o */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c index 44d07f39e..0bfafedff 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/common/common_params.c @@ -16,66 +16,124 @@ int verbose = 1; #define BUFSIZE 30 +/** + * @brief 打印选项的帮助信息 + * + * @param long_options 包含所有长选项的结构体数组 + * @param required 标志位,用于指示是否打印必需的选项 + */ void _print_options(const struct option_wrapper *long_options, bool required) { - int i, pos; - char buf[BUFSIZE]; - - for (i = 0; long_options[i].option.name != 0; i++) { - if (long_options[i].required != required) - continue; - - if (long_options[i].option.val > 64) /* ord('A') = 65 */ - printf(" -%c,", long_options[i].option.val); - else - printf(" "); - pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name); - if (long_options[i].metavar) - snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar); - printf("%-22s", buf); - printf(" %s", long_options[i].help); - printf("\n"); - } + int i, pos; + char buf[BUFSIZE]; + + // 遍历所有的长选项 + for (i = 0; long_options[i].option.name != 0; i++) { + // 如果选项的必需性与参数不符,则跳过 + if (long_options[i].required != required) + continue; + + // 如果选项的短名称为大写字母,打印其短名称 + if (long_options[i].option.val > 64) /* ord('A') = 65 */ + printf(" -%c,", long_options[i].option.val); + else + // 否则不打印短名称,保留空白对齐 + printf(" "); + + // 将选项的长名称及其元变量(如果有)格式化到 buf 中 + pos = snprintf(buf, BUFSIZE, " --%s", long_options[i].option.name); + if (long_options[i].metavar) + snprintf(&buf[pos], BUFSIZE-pos, " %s", long_options[i].metavar); + + // 打印格式化后的选项名称和帮助信息 + printf("%-22s", buf); + printf(" %s", long_options[i].help); + printf("\n"); + } } + +/** + * @brief 打印程序的用法信息 + * + * @param prog_name 程序名 + * @param doc 程序的文档说明 + * @param long_options 包含所有长选项的结构体数组 + * @param full 标志位,是否打印完整的帮助信息 + */ void usage(const char *prog_name, const char *doc, const struct option_wrapper *long_options, bool full) { - printf("Usage: %s [options]\n", prog_name); + // 打印用法信息的基本格式 + printf("Usage: %s [options]\n", prog_name); - if (!full) { - printf("Use --help (or -h) to see full option list.\n"); - return; - } + // 如果不需要打印完整的帮助信息 + if (!full) { + // 提示用户使用 --help 或 -h 查看完整的选项列表 + printf("Use --help (or -h) to see full option list.\n"); + return; + } - printf("\nDOCUMENTATION:\n %s\n", doc); - printf("Required options:\n"); - _print_options(long_options, true); - printf("\n"); - printf("Other options:\n"); - _print_options(long_options, false); - printf("\n"); + // 打印文档说明 + printf("\nDOCUMENTATION:\n %s\n", doc); + + // 打印必需选项 + printf("Required options:\n"); + _print_options(long_options, true); + printf("\n"); + + // 打印其他选项 + printf("Other options:\n"); + _print_options(long_options, false); + printf("\n"); } + +/** + * @brief 将 option_wrapper 结构体数组转换为标准的 option 结构体数组 + * + * @param wrapper 包含所有长选项的结构体数组 + * @param options 输出参数,用于存储转换后的 option 结构体数组 + * @return int 成功返回0,失败返回-1 + */ int option_wrappers_to_options(const struct option_wrapper *wrapper, struct option **options) { int i, num; struct option *new_options; + + // 计算 wrapper 数组中的选项数量 for (i = 0; wrapper[i].option.name != 0; i++) {} num = i; + // 分配新的 option 数组内存 new_options = malloc(sizeof(struct option) * num); if (!new_options) + // 如果内存分配失败,返回 -1 return -1; + + // 将 wrapper 数组中的每个 option 复制到新的 option 数组中 for (i = 0; i < num; i++) { memcpy(&new_options[i], &wrapper[i], sizeof(struct option)); } + // 将新分配并填充的 option 数组赋值给输出参数 *options *options = new_options; + + // 成功返回 0 return 0; } + +/** + * @brief 解析命令行参数 + * + * @param argc 参数个数 + * @param argv 参数值数组 + * @param options_wrapper 包含所有长选项的结构体数组 + * @param cfg 配置结构体,用于存储解析结果 + * @param doc 程序的文档说明 + */ void parse_cmdline_args(int argc, char **argv, const struct option_wrapper *options_wrapper, struct config *cfg, const char *doc) @@ -86,22 +144,26 @@ void parse_cmdline_args(int argc, char **argv, char *dest; int opt; + // 将 option_wrapper 结构体数组转换为标准的 option 结构体数组 if (option_wrappers_to_options(options_wrapper, &long_options)) { fprintf(stderr, "Unable to malloc()\n"); exit(EXIT_FAIL_OPTION); } - /* Parse commands line args */ - while ((opt = getopt_long(argc, argv, "hd:r:L:R:ASNFU:MQ:czpq", + /* 解析命令行参数 */ + while ((opt = getopt_long(argc, argv, "hd:r:L:R:ASNFUMQ:czpq:i:m:k:g:n:tT", long_options, &longindex)) != -1) { switch (opt) { case 'd': + // 检查设备名称长度是否超出限制 if (strlen(optarg) >= IF_NAMESIZE) { fprintf(stderr, "ERR: --dev name too long\n"); goto error; } + // 设置设备名称 cfg->ifname = (char *)&cfg->ifname_buf; strncpy(cfg->ifname, optarg, IF_NAMESIZE); + // 获取设备索引 cfg->ifindex = if_nametoindex(cfg->ifname); if (cfg->ifindex == 0) { fprintf(stderr, @@ -111,12 +173,15 @@ void parse_cmdline_args(int argc, char **argv, } break; case 'r': + // 检查重定向设备名称长度是否超出限制 if (strlen(optarg) >= IF_NAMESIZE) { fprintf(stderr, "ERR: --redirect-dev name too long\n"); goto error; } + // 设置重定向设备名称 cfg->redirect_ifname = (char *)&cfg->redirect_ifname_buf; strncpy(cfg->redirect_ifname, optarg, IF_NAMESIZE); + // 获取重定向设备索引 cfg->redirect_ifindex = if_nametoindex(cfg->redirect_ifname); if (cfg->redirect_ifindex == 0) { fprintf(stderr, @@ -125,73 +190,140 @@ void parse_cmdline_args(int argc, char **argv, goto error; } break; + case 't': + cfg->show_stats = true; + break; + case 'i': + cfg->ip_filter = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --ip_filter_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->ip_filter_file = (char *)&cfg->ip_filter_file_buf; //初始化ip_filter_file + strncpy(cfg->ip_filter_file, optarg, FILE_MAXSIZE); + //printf("%s %s\n",optarg,cfg->ip_filter_file); + break; + case 'm': + cfg->mac_filter = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --mac_filter_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->mac_filter_file = (char *)&cfg->mac_filter_file_buf; //初始化ip_filter_file + strncpy(cfg->mac_filter_file, optarg, FILE_MAXSIZE); + break; + case 'k': + cfg->router = true; + // 检查文件路径长度是否超出限制 + if (strlen(optarg) >= FILE_MAXSIZE) { + fprintf(stderr, "ERR: --router_file name too long\n"); + goto error; + } + // 设置文件路径 + cfg->router_file = (char *)&cfg->router_file_buf; //初始化ip_filter_file + strncpy(cfg->router_file, optarg, FILE_MAXSIZE); + break; + case 'g': + cfg->state = true; + break; + case 'n': + cfg->clear = true; + break; case 'A': + // 设置附加模式为未指定模式 cfg->attach_mode = XDP_MODE_UNSPEC; break; case 'S': + // 设置附加模式为 SKB 模式 cfg->attach_mode = XDP_MODE_SKB; cfg->xsk_bind_flags &= ~XDP_ZEROCOPY; cfg->xsk_bind_flags |= XDP_COPY; break; case 'N': + // 设置附加模式为原生模式 cfg->attach_mode = XDP_MODE_NATIVE; break; case 3: /* --offload-mode */ + // 设置附加模式为硬件模式 cfg->attach_mode = XDP_MODE_HW; break; case 'M': + // 启用重用地图 cfg->reuse_maps = true; break; case 'U': + // 设置卸载标志 cfg->do_unload = true; cfg->unload_all = true; - //cfg->prog_id = atoi(optarg); + // cfg->prog_id = atoi(optarg); break; case 'p': + // 启用轮询模式 cfg->xsk_poll_mode = true; break; case 'q': + // 设置为非详细模式 verbose = false; break; case 'Q': + // 设置接口队列 cfg->xsk_if_queue = atoi(optarg); break; case 1: /* --filename */ + // 设置文件名 dest = (char *)&cfg->filename; strncpy(dest, optarg, sizeof(cfg->filename)); break; case 2: /* --progname */ + // 设置程序名称 dest = (char *)&cfg->progname; strncpy(dest, optarg, sizeof(cfg->progname)); break; case 'L': /* --src-mac */ + // 设置源 MAC 地址 dest = (char *)&cfg->src_mac; strncpy(dest, optarg, sizeof(cfg->src_mac)); break; case 'R': /* --dest-mac */ + // 设置目的 MAC 地址 dest = (char *)&cfg->dest_mac; strncpy(dest, optarg, sizeof(cfg->dest_mac)); break; case 'c': + // 设置绑定标志为复制模式 cfg->xsk_bind_flags &= ~XDP_ZEROCOPY; cfg->xsk_bind_flags |= XDP_COPY; break; case 'z': + // 设置绑定标志为零拷贝模式 cfg->xsk_bind_flags &= ~XDP_COPY; cfg->xsk_bind_flags |= XDP_ZEROCOPY; break; case 4: /* --unload-all */ + // 设置卸载所有标志 cfg->unload_all = true; break; case 'h': + // 设置显示完整帮助信息的标志 full_help = true; /* fall-through */ + break; + case 'T': + // 设置打印的标志 + cfg->print_info = true; + break; error: default: + // 打印使用信息并退出 usage(argv[0], doc, options_wrapper, full_help); free(long_options); exit(EXIT_FAIL_OPTION); } } + // 释放分配的内存 free(long_options); } diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md new file mode 100644 index 000000000..1d6b7bb7d --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/ip_filter.md @@ -0,0 +1,149 @@ +## 黑白名单 + +### 概述 + +​ 本工具通过XDP技术,在内核层面实现高效的网络流量监控和过滤。通过配置规则文件,可以灵活地定义黑白名单策略,对特定IP、端口和协议的报文进行过滤。结合实际网络环境,这种工具能够有效提升网络安全性,减少恶意流量,优化网络性能,特别适用于需要高性能数据包处理的场景,如大型数据中心和云计算平台。 + +### 实现 + +![image-20240726104526418](./pic/ip_filter1.png) + +#### 输入参数优化 + +在原先的黑白名单中,名单的路径参数十分固定 + +```c + if (cfg.ip_filter) { + load_bpf_map(); + err = load_handler_ipv4(argc - 3, argv + 6); + if (err) { + fprintf(stderr, "ERR: loading IP filter config file\n"); + return err; + } +``` + +因此其使用的方法十分固定,要求路径必须在相应的位置,如: + +``` +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +将其绑定到-i参数上 + +```c +cfg->ip_filter_file = (char *)&cfg->ip_filter_file_buf; //初始化ip_filter_file +strncpy(cfg->ip_filter_file, optarg, FILE_MAXSIZE); +``` + +通过先将 `cfg->ifname` 指向 `cfg->ifname_buf`,确保了 `cfg->ifname` 指向的是一个有效的缓冲区,然后再使用 `strncpy` 将字符串复制到该缓冲区中,从而避免了潜在的内存访问错误。 + +去掉这种限制 + +``` +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +#### 增添输出格式 + +原先的程序中,除了对所有策略的统计输出,并无其余输出,对可读性有一定的限制,现在通过增添filter_info将数据打印在日志中,使用户可以清晰看到相应规则所匹配到的条目信息。 + +### 使用方法 + +本功能的使用命令为 + +``` +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf +``` + +也可以在其中加入-t/T选项,t参数会定时统计所有策略对应的报文数,T参数会输出所有匹配条目与策略 + +之后可以使用xdp-loader查看挂载程序及卸载 + +```shell +sudo xdp-loader status +``` + +![image-20240726114124092](./pic/ip_filter2.png) + +其中可以看到对应网卡上挂载的XDP程序 + +当不使用时进行卸载 + +```shell +sudo xdp-loader unload ens33 --all +``` + +在 ./conf.d 目录里有样例规则文件 black_ipv4.conf 别代表条目名单。程序会按顺序逐行加载进BPF Map,同样,XDP程序执行时也会逐行匹配规则,所以写在前面的规则具有更高的优先级。每行规则的格式为: + +``` +[SIP/MASK] [DIP/MASK] [SPORT] [DPORT] [PROTO] [ALLOW/DENY] +``` + +其中分别为源地址/源码、目的地址/源码、源端口、目的端口、协议类型、条目策略。 + +需要注意,**XDP只对收包路径上的数据有效,因此此处的源地址/端口为另一端,而目的地址/端口为本机**。 + +**当某段字段为0时,代表不进行此处的过滤,为全部匹配**。如需要匹配所有的ICMP报文,则为 + +``` +0.0.0.0/0 0.0.0.0/0 0 0 ICMP DENY +``` + +若要实现黑名单,根据匹配的优先级顺序,则需要在规则的最后⼀条写上(也可不加),默认为ALLOW,当匹配不到其余规则时会默认进行PASS策略(但仍建议增添) + +```c +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +若要实现白名单,需要将最后⼀条规则写为(必须增添,否则没有实际效果) + +``` +0.0.0.0/0 0.0.0.0/0 0 0 0 DENY +``` + +如需禁止源IP为 172.17.0.0/24 ,目的IP为任意 0.0.0.0/0 ,源端⼝号为任意 0 ,⽬的端⼝号为 80 的 TCP 协议 + +``` +172.17.0.2/32 0.0.0.0/0 0 80 TCP DENY +``` + +对于掩码,本功能用以过滤同一IP组的报文,如 + +``` +192.168.239.0/24 192.168.239.132/32 0 0 ICMP DENY +``` + +此处的192.168.239.0/24表示掩码为24位,及仅留取前24位的IP用以匹配,同样的,**收到的实际报文也会按照此掩码屏蔽掉相应字节**, 所以此句将会屏蔽192.168.239.*地址发来的ICMP报文,所以此处192.168.239.0/24 中的0并无实际意义,其可以写为任意实际ip,但需注意掩码位数,尤其掩码不为8的整数倍时,但是本功能**建议屏蔽掉的相应字节归零**,便于阅读,如192.168.239.194/26应写为192.168.239.192/26,此处的192为194屏蔽掉后后6位的实际值。 + +最终给出示例,我们在配置文件中写入,用以配置规则,其实现类似黑名单的功能,及禁止所有目的地地址为本机的ICMP及TCP报文 + +```c +0.0.0.0/0 192.168.239.132/32 0 0 ICMP DENY +0.0.0.0/0 192.168.239.132/32 80 0 TCP DENY +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW //此条目可省略 +``` + +之后加载到程序中 + +```bash +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +可以发现已经drop了所有报文 + +![image-20240726110021447](./pic/ip_filter3.png) + + +### 输出分析 + +本功能将输出所有匹配到的报文,此为无其余输出,由于用户态程序只用于激活,不作为循环监听的载体,故在内核态进行输出,当XDP程序挂载到相应网卡并且与相应条目匹配后输出。 + +需要使用相关命令查看 + +``` +sudo cat /sys/kernel/debug/tracing/trace_pipe +``` + +![image-20240726153912838](./pic/ip_filter4.png) + +其中可以看到符合匹配的相关报文信息,其中包括四元组、协议类型、XDP策略行为以及匹配条目的序号。 \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md new file mode 100644 index 000000000..b6ff8fd10 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/net_manager.md @@ -0,0 +1,209 @@ +## netmanager + +netmanager是一款基于 eBPF 技术的高效网络管理工具,核心技术是 eBPF 的 XDP模块。eBPF 是一种强大的内核技术,允许在不修改内核源代码的情况下执行自定义代码,而 XDP 则是 eBPF 的一个子集,专门用于高速数据包处理。NetManager 利用 XDP 的高速数据包处理能力,实现了过滤、转发、统计信息和会话保持等主要功能。这款工具通过在网络数据包进入内核堆栈之前处理它们,显著提升了数据包处理的性能和效率。在高流量环境下,NetManager 能够保持卓越的性能和低延迟,帮助用户优化网络管理,提升系统效率。 + +## 框架 + +目前netmanager的整体框架为 + +![image-20240711091259144](./pic/net_manager1.png) + + + +netmanager 的整体框架从主程序入口(main)开始,首先进行启动程序并解析命令行参数(parse_cmdline_args)。解析得到的参数会被解译,并用于挂载相应的处理函数。接下来,程序会根据参数进行进一步操作,如加载对应的配置文件,初始化处理程序,包括加载IPv4、MAC地址和路由器的处理函数(load_handler_ipv4, load_handler_mac, load_handler_router)。 + +在用户态与内核态的交互中,用户态通过map与内核态进行数据交换。内核态的默认处理入口包括多个XDP处理函数,如xdp_entry_state、xdp_entry_ipv4、xdp_entry_router和xdp_entry_mac,这些函数分别负责不同类型数据包的解析和处理。xdp_entry_state负责筛选TCP报文,并根据新的报文标志位修改连接状态;xdp_entry_ipv4处理逐层解析报文,并匹配相应的IPv4过滤规则;xdp_entry_router则解析报文并进行标准匹配,通过bpf_fib_lookup实现路由转发;xdp_entry_mac则负责MAC报文的解析和过滤。 + +最终,所有的处理结果都会通过xdp_stats_record_action进行统计和记录,处理后的报文和字节数也会被记录下来。这一系列操作确保了NetManager能够高效地执行过滤、转发、统计信息和会话保持等主要功能,为用户提供了强大的网络管理能力。 + +## 功能 + +netmanager目前实现了过滤、转发、统计信息和会话保持四大功能 + +**xdp_entry_state** + +通过解析进入网络设备的数据包,跟踪和记录 IPv4 连接的状态变化,特别是 TCP 连接的状态。 + +1. **数据包解析**: + - 通过 `parse_ethhdr` 函数解析以太网头部,检查是否为 IPv4 数据包。 + - 如果是 IPv4 数据包,则继续解析 IP 头部,获取源地址(saddr)、目的地址(daddr)和协议(proto)。 +2. **TCP 连接处理**: + - 如果协议是 TCP,解析 TCP 头部,获取源端口(sport)和目的端口(dport)。 + - 创建或查找一个包含源地址、目的地址、源端口、目的端口和协议的连接键(conn_k)。 + - 在 `conn_ipv4_map` 哈希表中查找该连接键对应的连接值(p_conn_v)。如果找不到,交换源和目的地址端口后再次查找。 + - 如果依然找不到,并且是 TCP SYN 包(表示新连接请求),则创建一个新的连接项并插入到哈希表中,同时记录日志。 + - 如果找到现有连接项,处理 TCP 标志位以更新连接状态(如 SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT1, CLOSE 等)。 + - 根据不同的连接状态,更新哈希表或删除连接项,并记录日志。 +3. **UDP 连接处理**: + - 如果协议是 UDP,解析 UDP 头部,获取源端口和目的端口。 + - 打印除 SSH(端口 22)协议以外的所有连接信息(在调试模式下)。 +4. **日志记录**: + - 对新创建的连接项和状态变化的连接项记录日志,便于后续分析和调试。 +5. **状态记录**: + - 使用 `xdp_stats_record_action` 函数记录操作状态。 + +**xdp_entry_mac** + +其功能是根据源 MAC 地址查找映射表,决定是否放行数据包。如果源 MAC 地址在映射表中存在,则根据映射表中的值设置相应的操作(例如,放行、丢弃或重定向);如果不存在,则默认放行数据包。 + +**xdp_entry_router** + +功能是根据数据包的目标地址进行查找和转发处理,支持 IPv4 和 IPv6 两种协议。通过查找转发表和最长前缀匹配表,决定如何处理数据包(例如,放行、丢弃或重定向)。 + +​ 其核心功能是对路由进行匹配:解析 IP 头部,获取源地址 `ip4_saddr`。检查 IP 头部的 TTL创建并初始化路由表项 `nitem`,将源地址赋值给 `nitem.saddr`。调用 `match_rules` 函数进行精确查找转发表。 + +​ 如果找到匹配项,进行数据包的转发处理:减少 IP 头部的 TTL。更新以太网头部的目的地址和源地址。用 `bpf_redirect_map` 函数进行数据包重定向。 + +如果精确查找未找到匹配项,执行最长前缀匹配查找:初始化 `bpf_fib_lookup` 结构体 `ifib`,设置相关字段(如 IP 头部信息、接口索引等)。调用 bpf_fib_lookup函数进行查找,检查返回值并根据结果进行处理: + +- `BPF_FIB_LKUP_RET_SUCCESS`:查找成功,减少 IP 头部的 TTL,更新以太网头部的目的地址和源地址,调用 `bpf_redirect` 函数进行数据包重定向。 +- `BPF_FIB_LKUP_RET_BLACKHOLE`、`BPF_FIB_LKUP_RET_UNREACHABLE`、`BPF_FIB_LKUP_RET_PROHIBIT`:目标地址不可达,丢弃数据包。 +- 其他返回值:默认放行数据包。 + +**xdp_entry_ipv4** + +其功能是解析 IPv4 数据包的头部信息,记录连接信息,并根据预定义的规则对数据包进行处理。代码支持对 TCP 和 UDP 数据包的解析和处理,通过匹配规则函数 `match_rules_ipv4` 来决定数据包的最终处理方式。 + +## 代码运行实例 + +目前功能的具体使用操作如下, + +```c +黑白名单:加载到本地链路 ens33 上 + sudo ./xdp_loader -d ens33 --progname=xdp_entry_state -S +会话保持: + sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf + +``` + +**以会话保持为例** + +![image-20240711110643737](./pic/net_manager2.png) + +可以借助xdp-loader工具进行操作 + +![image-20240711110713174](./pic/net_manager3.png) + +可以看到我们的程序被挂载到相应的端口上 + +![image-20240711110826395](./pic/net_manager4.png) + +可以看到其能抓到本机网络通信的各个报文,并获取其中的基本信息和连接状态。 + +#### 黑白名单 + +在 ./conf.d ⽬录里有样例规则⽂件 black_ipv4.conf 别代表黑白名单。程序会按顺序逐行加载进BPF Map,同样,XDP程序执行时也会逐⾏匹配规则,所以写在前面的规则具有更高的优先级。每行规则的格式为: + +``` +[SIP/MASK] [DIP/MASK] [SPORT] [DPORT] [PROTO] [ALLOW/DENY] +``` + +若要实现⿊名单,根据匹配的优先级顺序,则需要在规则的最后⼀条写上 + +```c +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +同理,若要实现⽩名单,需要将最后⼀条规则写为 + +``` +0.0.0.0/0 0.0.0.0/0 0 0 0 DENY +``` + +如需禁⽌源IP为 172.17.0.0/24 ,⽬的IP为任意 0.0.0.0/0 ,源端⼝号为任意 0 ,⽬的端⼝号为 80 的 TCP 协议 + +``` +172.17.0.2/32 0.0.0.0/0 0 80 TCP DENY +``` + +如需禁⽌任意来源的PING请求,可以写为: + +``` +0.0.0.0/0 0.0.0.0/0 0 0 ICMP DENY +``` + +我们在配置文件中写入,用以配置规则 + +```c +192.168.239.132/24 0.0.0.0/0 0 0 ICMP DENY +192.168.239.132/24 0.0.0.0/0 0 80 TCP DENY +0.0.0.0/0 0.0.0.0/0 0 0 0 ALLOW +``` + +之后加载到程序中 + +```bash +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +之后加载到程序中 + +```bash +sudo ./xdp_loader -d ens33 -S --progname=xdp_entry_ipv4 -i conf.d/black_ipv4.conf -t +``` + +统计信息如下 + +![image-20240715202110387](./pic/net_manager5.png) + +可以发现已经drop了所有icmp报文 + +卸载命令 + +```bash +sudo xdp-loader unload ens33 --all +``` + +## ## 代码分析 + +现阶段的目录结构如下 + +```c +. +├── bpf_use_errno_test.o +├── common +│ ├── ... +├── config.mk +├── configure +├── lib +│ ├── ... +├── Makefile +├── net_manager +│ ├── common_kern_user.h +│ ├── conf.d +│ │ ├── black_ipv4.conf +│ │ ├── mac_load.conf +│ │ └── router_load.conf +│ ├── Makefile +│ ├── xdp_loader +│ ├── xdp_loader.c +│ ├── xdp_prog_kern.c +│ ├── xdp_prog_kern.ll +│ └── xdp_prog_kern.o +├── README.md +//以下为子项目 +├── router +│ ├── ... +├── testenv +│ ├── ... +├── xacl_ip +│ ├── ... +├── xacl_mac +│ ├── ... +``` + +### Makefile结构 + +![image-20240711102520620](./pic/net_manager6.png) + +为递归make,尝试修改,较为困难 + +### ./net_manager/xdp_loader.c + +主用户态代码 + +![image-20240711105855149](./pic/net_manager7.png) + +### ./net_manager/xdp_prog_kern.c + +![image-20240711110039806](./pic/net_manager8.png) \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter1.png new file mode 100644 index 000000000..5cccd9127 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter2.png new file mode 100644 index 000000000..084a5e233 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter3.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter3.png new file mode 100644 index 000000000..cb4331a8e Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter3.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter4.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter4.png new file mode 100644 index 000000000..1dea44779 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/ip_filter4.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager1.png new file mode 100644 index 000000000..0051b6295 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager2.png new file mode 100644 index 000000000..eed5d0fc1 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager3.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager3.png new file mode 100644 index 000000000..2655c5f4e Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager3.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager4.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager4.png new file mode 100644 index 000000000..e749ff86d Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager4.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager5.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager5.png new file mode 100644 index 000000000..e56504f20 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager5.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager6.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager6.png new file mode 100644 index 000000000..75a714ac8 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager6.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager7.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager7.png new file mode 100644 index 000000000..8cec2c143 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager7.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager8.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager8.png new file mode 100644 index 000000000..902fb1981 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/net_manager8.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep1.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep1.png new file mode 100644 index 000000000..31e1e5965 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep1.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep2.png b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep2.png new file mode 100644 index 000000000..e392bc825 Binary files /dev/null and b/eBPF_Supermarket/Network_Subsystem/net_manager/document/pic/session_keep2.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md b/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md new file mode 100644 index 000000000..eacfdbf26 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/document/session_keep.md @@ -0,0 +1,72 @@ +## 会话保持 + +### 概述 + +​ 工具能够检测多种类型的网络报文,包括TCP、UDP和ICMP。通过对TCP连接状态的监控,了解连接的建立和关闭过程;UDP数据包的监测提供了数据传输的详细信息,如源和目标端口以及数据包大小;而ICMP报文则用于网络诊断,帮助确认主机在线状态和测量网络延迟。可以帮助全面掌握网络流量状况和性能。 + +### 实现 + +在先前的功能中,仅能实现对lo端口的监测,不能实现对通用网卡的监测,且ip输出格式不易懂 + +其在lo网卡上监测的信息 + +![image-20240719173203213](./pic/session_keep1.png) + +增加对通用网卡的监测,UDP、ICMP的监测,输出格式的转化,但由于XDP仅在收包路径,所以发送报文/相关状态获取不到 + +### 使用方法 + +使用命令将程序挂载到相应网卡 + +```c +sudo ./xdp_loader -d ens33 --progname=xdp_entry_state -S +``` + +查看挂载程序 + +``` +sudo ./xdp_loader status +``` + +查看输出 + +```c +sudo cat /sys/kernel/debug/tracing/trace_pipe +``` + +实例截图 + +![image-20240719173840908](./pic/session_keep2.png) + +进行卸载 + +```c +sudo xdp-loader unload ens33 --all +``` + +### 输出分析 + +**tcp连接** + +​ 程序可以监测源IP地址、目标IP地址、端口号、连接状态以及连接的角色(C/S),通过分析和统计其连接状态,可以获取到其连接的相应信息 + +```c +tcp(96.91.189.91:80->132.239.168.192:36676),state:ESTABLISHED,client +``` + +**udp** + +​ 程序可以监测源IP地址、目标IP地址、端口号、数据包长度等信息。通过分析此日志,可以了解网络中的DNS查询活动,并监控数据传输的细节,故障排除以及检测异常流量。 + +```c +udp(192.168.239.2:53->192.168.239.132:36874),len=247 +``` + +**ICMP** + +​ 程序可以监测源IP地址、目标IP地址、icmp报文的类型和代码等信息,用于确认目标主机是否在线或检查网络连接的延迟,有助于检查网络连接的正常性和响应时间。 + +```c +icmp(1.1.1.1->192.168.239.132),type=0,code=0 +``` + diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/Makefile b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/Makefile new file mode 100644 index 000000000..e29a040e9 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +XDP_TARGETS := xdp_prog_kern +USER_TARGETS := xdp_loader + +COMMON_DIR = ../common + +# Extend with another COMMON_OBJS +COMMON_OBJS += $(COMMON_DIR)/common_user_bpf_xdp.o + + +EXTRA_DEPS := $(COMMON_DIR)/parsing_helpers.h + +include $(COMMON_DIR)/common.mk diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/common_kern_user.h b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/common_kern_user.h new file mode 100644 index 000000000..c146b8528 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/common_kern_user.h @@ -0,0 +1,94 @@ +/* This common_kern_user.h is used by kernel side BPF-progs and + * userspace programs, for sharing common struct's and DEFINEs. + */ +#ifndef __COMMON_KERN_USER_H +#define __COMMON_KERN_USER_H + +#include + +typedef __u32 xdp_act; +#define ETH_ALEN 6 + +#define ALERT_ERR_STR "[XACL] ERROR:" + + +#define MAX_RULES 256 + + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + + +struct datarec { + __u64 rx_packets; + __u64 rx_bytes; +}; + +struct conn_ipv4 { + __u32 saddr; + __u32 daddr; + __u16 sport; + __u16 dport; + __u16 ip_proto; +}; + +struct rules_ipv4 { + __u32 saddr; + __u32 daddr; + __u8 saddr_mask; + __u8 daddr_mask; + __u16 sport; + __u16 dport; + __u16 ip_proto; + __u16 action; + __u16 prev_rule; + __u16 next_rule; +}; + + +// 转发表项 +struct rt_item { + __u32 saddr; + __u8 eth_source[ETH_ALEN]; // 封装帧的源MAC地址。 + __u8 eth_dest[ETH_ALEN]; // 封装帧的目标MAC地址。 +}; + +// mac 过滤 +struct mac_addr { + __u8 addr[ETH_ALEN]; +}; + + +// 会话保持 +struct conn_ipv4_key { + __u32 saddr; + __u32 daddr; + __u16 sport; + __u16 dport; + __u16 proto; +}; + +struct conn_ipv4_val { + __u32 tcp_state; + __u32 rid; +}; + +enum { + TCP_S_NONE = 0U, + TCP_S_ESTABLISHED, + TCP_S_SYN_SENT, + TCP_S_SYN_RECV, + TCP_S_FIN_WAIT1, + TCP_S_FIN_WAIT2, + TCP_S_CLOSE_WAIT, + TCP_S_CLOSE, +}; + + + +#ifndef XDP_ACTION_MAX +#define XDP_ACTION_MAX (XDP_REDIRECT + 1) +#endif + +#endif /* __COMMON_KERN_USER_H */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/black_ipv4.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/black_ipv4.conf new file mode 100644 index 000000000..ea5f7ac8c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/black_ipv4.conf @@ -0,0 +1,2 @@ +0.0.0.0/0 192.168.239.132/32 0 0 ICMP DENY +0.0.0.0/0 192.168.239.132/32 80 0 TCP DENY \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/mac_load.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/mac_load.conf new file mode 100644 index 000000000..3b41e21a5 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/mac_load.conf @@ -0,0 +1,2 @@ +00:0c:29:dd:17:2c DENY +00:0c:29:fd:69:58 DENY \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/router_load.conf b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/router_load.conf new file mode 100644 index 000000000..ac552338c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/conf.d/router_load.conf @@ -0,0 +1,2 @@ +0.0.0.0 00:0c:29:7b:a6:d9 00:0c:29:fd:69:58 +1.2.3.4 00:0c:29:7b:a6:d9 00:0c:29:dd:17:2c \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/xdp_loader.c b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/xdp_loader.c new file mode 100644 index 000000000..288c1f178 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/net_manager/xdp_loader.c @@ -0,0 +1,831 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +static const char *__doc__ = "XDP loader\n" + " - Allows selecting BPF program --progname name to XDP-attach to --dev\n" + " - Collects and displays stats from XDP program\n"; + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include /* depend on kernel-headers installed */ + +#include "../common/common_params.h" +#include "../common/common_user_bpf_xdp.h" +#include "../common/common_libbpf.h" +#include "common_kern_user.h" + +static const char *default_filename = "xdp_prog_kern.o"; +static const char *default_progname = "xdp_entry_state"; + +static const struct option_wrapper long_options[] = { + + {{"help", no_argument, NULL, 'h' }, + "Show help", false}, + + {{"dev", required_argument, NULL, 'd' }, + "Operate on device ", "", true}, + + {{"skb-mode", no_argument, NULL, 'S' }, + "Install XDP program in SKB (AKA generic) mode"}, + + {{"native-mode", no_argument, NULL, 'N' }, + "Install XDP program in native mode"}, + + {{"auto-mode", no_argument, NULL, 'A' }, + "Auto-detect SKB or native mode"}, + + {{"force", no_argument, NULL, 'F' }, + "Force install, replacing existing program on interface"}, + + {{"unload", no_argument, NULL, 'U' }, + "Unload XDP program instead of loading"}, + + {{"quiet", no_argument, NULL, 'q' }, + "Quiet mode (no output)"}, + + {{"progname", required_argument, NULL, 2 }, + "Load program from function in the ELF file", ""}, + + {{"stats", no_argument, NULL, 't' }, + "Show XDP stats"}, + + {{"ip-filter", required_argument, NULL, 'i' }, + "ip_filter"}, + + {{"mac_filter", required_argument, NULL, 'm' }, + "mac_filter"}, + + {{"router", required_argument, NULL, 'k' }, + "package_router"}, + + {{"clear", no_argument, NULL, 'n' }, + "clear_map"}, + + {{"config", no_argument, NULL, 'T' }, + "config from user to kernel"}, + {{0, 0, NULL, 0 }, NULL, false} +}; + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +const char *pin_basedir = "/sys/fs/bpf"; +const char *map_name = "xdp_stats_map"; +char pin_dir[PATH_MAX]; +char map_filename[PATH_MAX]; + + +static void list_avail_progs(struct bpf_object *obj) +{ + struct bpf_program *pos; + + printf("BPF object (%s) listing available XDP functions\n", + bpf_object__name(obj)); + + bpf_object__for_each_program(pos, obj) { + if (bpf_program__type(pos) == BPF_PROG_TYPE_XDP) + printf(" %s\n", bpf_program__name(pos)); + } +} + + +#define NANOSEC_PER_SEC 1000000000 /* 10^9 */ +static __u64 gettime(void) +{ + struct timespec t; + int res; + + res = clock_gettime(CLOCK_MONOTONIC, &t); + if (res < 0) { + fprintf(stderr, "Error with gettimeofday! (%i)\n", res); + exit(EXIT_FAIL); + } + return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec; +} + +struct record { + __u64 timestamp; + struct datarec total; /* defined in common_kern_user.h */ +}; + +struct stats_record { + struct record stats[XDP_ACTION_MAX]; +}; + +static double calc_period(struct record *r, struct record *p) +{ + double period_ = 0; + __u64 period = 0; + + period = r->timestamp - p->timestamp; + if (period > 0) + period_ = ((double) period / NANOSEC_PER_SEC); + + return period_; +} + +static void stats_print_header() +{ + /* Print stats "header" */ + printf("%-12s\n", "XDP-action"); +} + +static void stats_print(struct stats_record *stats_rec, + struct stats_record *stats_prev) +{ + struct record *rec, *prev; + __u64 packets, bytes; + double period; + double pps; /* packets per sec */ + double bps; /* bits per sec */ + int i; + + stats_print_header(); /* Print stats "header" */ + + /* Print for each XDP actions stats */ + for (i = 0; i < XDP_ACTION_MAX; i++) + { + char *fmt = "%-12s %'11lld pkts (%'10.0f pps)" + " %'11lld Kbytes (%'6.0f Mbits/s)" + " period:%f\n"; + const char *action = action2str(i); + + rec = &stats_rec->stats[i]; + prev = &stats_prev->stats[i]; + + period = calc_period(rec, prev); + if (period == 0) + return; + + packets = rec->total.rx_packets - prev->total.rx_packets; + pps = packets / period; + + bytes = rec->total.rx_bytes - prev->total.rx_bytes; + bps = (bytes * 8)/ period / 1000000; + + printf(fmt, action, rec->total.rx_packets, pps, + rec->total.rx_bytes / 1000 , bps, + period); + } + printf("\n"); +} + + +/* BPF_MAP_TYPE_ARRAY */ +void map_get_value_array(int fd, __u32 key, struct datarec *value) +{ + if ((bpf_map_lookup_elem(fd, &key, value)) != 0) { + fprintf(stderr, + "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); + } +} + +/* BPF_MAP_TYPE_PERCPU_ARRAY */ +void map_get_value_percpu_array(int fd, __u32 key, struct datarec *value) +{ + /* For percpu maps, userspace gets a value per possible CPU */ + unsigned int nr_cpus = libbpf_num_possible_cpus(); + struct datarec values[nr_cpus]; + __u64 sum_bytes = 0; + __u64 sum_pkts = 0; + int i; + + if ((bpf_map_lookup_elem(fd, &key, values)) != 0) { + fprintf(stderr, + "ERR: bpf_map_lookup_elem failed key:0x%X\n", key); + return; + } + + /* Sum values from each CPU */ + for (i = 0; i < nr_cpus; i++) { + sum_pkts += values[i].rx_packets; + sum_bytes += values[i].rx_bytes; + } + value->rx_packets = sum_pkts; + value->rx_bytes = sum_bytes; +} + +static bool map_collect(int fd, __u32 map_type, __u32 key, struct record *rec) +{ + struct datarec value; + + /* Get time as close as possible to reading map contents */ + rec->timestamp = gettime(); + + switch (map_type) { + case BPF_MAP_TYPE_ARRAY: + map_get_value_array(fd, key, &value); + break; + case BPF_MAP_TYPE_PERCPU_ARRAY: + map_get_value_percpu_array(fd, key, &value); + break; + default: + fprintf(stderr, "ERR: Unknown map_type(%u) cannot handle\n", + map_type); + return false; + break; + } + + rec->total.rx_packets = value.rx_packets; + rec->total.rx_bytes = value.rx_bytes; + return true; +} + +static void stats_collect(int map_fd, __u32 map_type, + struct stats_record *stats_rec) +{ + /* Collect all XDP actions stats */ + __u32 key; + + for (key = 0; key < XDP_ACTION_MAX; key++) { + map_collect(map_fd, map_type, key, &stats_rec->stats[key]); + } +} + +static void stats_poll(int map_fd, __u32 map_type, int interval) +{ + struct stats_record prev, record = { 0 }; + + /* Trick to pretty printf with thousands separators use %' */ + setlocale(LC_NUMERIC, "en_US"); + + /* Get initial reading quickly */ + stats_collect(map_fd, map_type, &record); + usleep(1000000/4); + + while (1) { + prev = record; /* struct copy */ + stats_collect(map_fd, map_type, &record); + stats_print(&record, &prev); + sleep(interval); + } +} + + + +int unpin_maps(struct bpf_object *bpf_obj) +{ + int err; + /* Existing/previous XDP prog might not have cleaned up */ + if (access(map_filename, F_OK ) != -1 ) { + if (verbose) + printf(" - Unpinning (remove) prev maps in %s/\n", + pin_dir); + + /* Basically calls unlink(3) on map_filename */ + err = bpf_object__unpin_maps(bpf_obj, pin_dir); + if (err) { + fprintf(stderr, "ERR: UNpinning maps in %s\n", pin_dir); + return EXIT_FAIL_BPF; + } + } + return 0; +} + +/* Pinning maps under /sys/fs/bpf in subdir */ +int pin_maps_in_bpf_object(struct bpf_object *bpf_obj) +{ + int err; + unpin_maps(bpf_obj); + if (verbose) + printf(" - Pinning maps in %s/\n", pin_dir); + + /* This will pin all maps in our bpf_object */ + err = bpf_object__pin_maps(bpf_obj, pin_dir); + if (err) + return EXIT_FAIL_BPF; + + return 0; +} + + +char *ifname; +int rules_ipv4_map; +int rtcache_map4; +int src_macs; + +int print_usage(int id){ + switch(id){ + case 0: + fprintf(stderr, "Usage: \n"); + break; + default: + break; + }; + + return 0; +} + + +int open_map(const char *ifname, const char *map_name){ + int len; + char pin_dir[PATH_MAX]; + const char *pin_basedir = "/sys/fs/bpf"; + struct bpf_map_info info = { 0 }; + + /* Use the --dev name as subdir for finding pinned maps */ + len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, ifname); + if (len < 0) { + fprintf(stderr, "ERR: creating pin dirname\n"); + return -1; + } + + int fd = open_bpf_map_file(pin_dir, map_name, &info); + if (fd < 0) { + fprintf(stderr, "ERR: Failed to open map file: %s\n", map_name); + return -1; + } + printf("Opened BPF map\n"); + printf(" - BPF map (bpf_map_type:%d) fd: %d id:%d name:%s" + " key_size:%d value_size:%d max_entries:%d\n", + info.type, fd, info.id, info.name, + info.key_size, info.value_size, info.max_entries); + + return fd; +} + + +int load_bpf_map(){ + rules_ipv4_map = open_map(ifname, "rules_ipv4_map"); + rtcache_map4 = open_map(ifname, "rtcache_map4"); + src_macs = open_map(ifname, "src_macs"); + // Check if any map failed to open + if (rules_ipv4_map < 0) { + fprintf(stderr, "Failed to open rules_ipv4_map\n"); + } + if (rtcache_map4 < 0) { + fprintf(stderr, "Failed to open rtcache_map4\n"); + } + if (src_macs < 0) { + fprintf(stderr, "Failed to open src_macs\n"); + } + + if (rules_ipv4_map < 0 || rtcache_map4 < 0 || src_macs < 0) { + fprintf(stderr, "load bpf map error, check device name\n"); + return -1; + } + + return 0; +} + + +static __u32 ip_to_u32(__u8 *ip_u8) { + __u32 ip_u32 = 0; + ip_u32 = (ip_u8[0]<<24) | (ip_u8[1]<<16) | (ip_u8[2]<<8) | (ip_u8[3]); + //printf("%hhu.%hhu.%hhu.%hhu,%u\n",ip_u8[0],ip_u8[1],ip_u8[2],ip_u8[3],ip_u32); + return ip_u32; +} + +int clear_map(){ + __u16 keys[MAX_RULES]; + for(int i=0; i +#include +#include +#include + +#include "common_kern_user.h" +#include "../common/parsing_helpers.h" + +#ifndef memcpy +#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) +#endif + +//重定义 +#undef AF_INET +#define AF_INET 2 +#undef AF_INET6 +#define AF_INET6 10 +#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF) + +// config map +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, __u16); + __uint(max_entries, 1); +} print_info_map SEC(".maps"); + +// 数据包统计 +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, __u32); + __type(value, struct datarec); + __uint(max_entries, XDP_ACTION_MAX); +} xdp_stats_map SEC(".maps"); + +// ipv4—filter +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u16); + __type(value, struct rules_ipv4); + __uint(max_entries, MAX_RULES); +} rules_ipv4_map SEC(".maps"); + + +// router +struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __type(key, int); + __type(value, int); + __uint(max_entries, 256); +} tx_port SEC(".maps"); + +// 路由转发表缓存 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u32); + __type(value, struct rt_item); + __uint(max_entries, MAX_RULES); +} rtcache_map4 SEC(".maps"); + + +/*filter-pass-drop*/ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, ETH_ALEN); + __type(value, __u32); + __uint(max_entries, MAX_RULES); +} src_macs SEC(".maps"); + + +// 会话保持 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, struct conn_ipv4_key); + __type(value, struct conn_ipv4_val); + __uint(max_entries, MAX_RULES); +} conn_ipv4_map SEC(".maps"); + +static __always_inline +__u32 xdp_stats_record_action(struct xdp_md *ctx, __u32 action) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + + if (action >= XDP_ACTION_MAX) + return XDP_ABORTED; + + /* Lookup in kernel BPF-side return pointer to actual data record */ + struct datarec *rec = bpf_map_lookup_elem(&xdp_stats_map, &action); + if (!rec) + return XDP_ABORTED; + + /* Calculate packet length */ + __u64 bytes = data_end - data; + + /* BPF_MAP_TYPE_PERCPU_ARRAY returns a data record specific to current + * CPU and XDP hooks runs under Softirq, which makes it safe to update + * without atomic operations. + */ + rec->rx_packets++; + rec->rx_bytes += bytes; + + return action; +} + +/*路由功能*/ + +/* from include/net/ip.h */ +static __always_inline int ip_decrease_ttl(struct iphdr *iph) +{ + __u32 check = iph->check; + check += bpf_htons(0x0100); + iph->check = (__u16)(check + (check >= 0xFFFF)); + return --iph->ttl; +} + +static __always_inline +int mac_zero(const __u8 *mac_addr) { + // 检查MAC地址是否不全为零 + for (int i = 0; i < ETH_ALEN; i++) { + if (mac_addr[i] != 0) + return 1; // 如果有一个字节不为零,返回1表示不为零 + } + return 0; // 如果所有字节都为零,返回0表示全为零 +} + + +static __always_inline +int ipv4_match(__u32 conn_addr, __u32 rule_addr) { + // 直接比较IPv4地址和网络地址 + if( (!rule_addr) || (conn_addr == rule_addr) ) //0 , match all + return 1; + return 0; +} + +static int match_rules_loop(__u32 index, void *ctx) +{ + struct rt_item *p_ctx = (struct rt_item *)ctx; + + + struct rt_item *p_r = bpf_map_lookup_elem(&rtcache_map4, &index); + if(!p_r){ + return 1; //out of range + } + + + if( ipv4_match(p_ctx->saddr, p_r->saddr) ) { + + memcpy(p_ctx->eth_source, p_r->eth_source, ETH_ALEN); + memcpy(p_ctx->eth_dest, p_r->eth_dest, ETH_ALEN); + + + return 1; + } + + + return 1; +} + +static __always_inline +int match_rules(struct rt_item *conn) +{ + struct rt_item *ctx = conn; + + bpf_loop(MAX_RULES, match_rules_loop, ctx, 0); + + return 1; +} + +/*使用 IP 进行过滤*/ + +struct match_rules_loop_ctx{ + __u16 action; + __u16 next_rule; + struct conn_ipv4 *conn; +}; + +static __always_inline +int ipv4_cidr_match(__u32 ip_addr, __u32 network_addr, __u8 cidr) { + if(network_addr == 0 && cidr == 0) + return 1; + + __u32 subnet_mask = (0xFFFFFFFFU << (32 - cidr)) & 0xFFFFFFFFU; + + __u32 masked_ip = ip_addr & subnet_mask; + __u32 masked_network = network_addr & subnet_mask; + return masked_ip == masked_network; +} + +static __always_inline +int port_match(__u16 conn_port, __u16 rule_port){ + if( (!rule_port) || (rule_port == conn_port) ) //0 , match all + return 1; + return 0; +} + +static int match_rules_ipv4_loop(__u32 index, void *ctx) +{ + int i = 0; + unsigned char *saddr; + unsigned char *daddr; + struct match_rules_loop_ctx *p_ctx = (struct match_rules_loop_ctx *)ctx; + if(index != p_ctx->next_rule) + return 0; + + struct rules_ipv4 *p_r = bpf_map_lookup_elem(&rules_ipv4_map, &index); + if(!p_r){ + return 1; //out of range + } + + p_ctx->next_rule = p_r->next_rule; + + if(index == 0) + goto out_match_rules_ipv4_loop; + //bpf_printk("match_rules_ipv4_loop %d",index); + if( ipv4_cidr_match(p_ctx->conn->saddr, p_r->saddr, p_r->saddr_mask) && + ipv4_cidr_match(p_ctx->conn->daddr, p_r->daddr, p_r->daddr_mask) && + port_match(p_ctx->conn->sport, p_r->sport) && + port_match(p_ctx->conn->dport, p_r->dport) && + port_match(p_ctx->conn->ip_proto, p_r->ip_proto) ) + { + p_ctx->action = p_r->action; + __u8 *print_info=(__u8*)bpf_map_lookup_elem(&print_info_map,&i); + if(!print_info) return 1; + if(print_info){ + saddr = (unsigned char *)&p_ctx->conn->saddr; + daddr = (unsigned char *)&p_ctx->conn->daddr; + //bpf_printk("%s ,%s",saddr,daddr); + bpf_printk("src: %lu.%lu.%lu.%lu:%d" ,(unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0],p_ctx->conn->sport); + bpf_printk("dst: %lu.%lu.%lu.%lu:%d" ,(unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0],p_ctx->conn->dport); + bpf_printk("prot:%d ,action:%d ,index:%d" ,p_ctx->conn->ip_proto,p_ctx->action, index); + bpf_printk("-----------------------------------"); + } + + return 1; + } + +out_match_rules_ipv4_loop: + if(p_r->next_rule == 0) + return 1; //go out loop + + return 0; +} + +static __always_inline +xdp_act match_rules_ipv4(struct conn_ipv4 *conn) +{ + struct match_rules_loop_ctx ctx = {.action = XDP_PASS, .conn = conn, .next_rule = 0}; + + + for(int i=0; idata_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + struct conn_ipv4 conn = {.saddr = 0, .daddr = 0, .sport = 0, .dport = 0, .ip_proto = 0}; + + //bpf_printk("xdp_entry_ipv4"); + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + if (nh_type == IPPROTO_TCP) { + if(parse_tcphdr(&nh, data_end, &tcph) < 0) + goto out; + + conn.sport = bpf_ntohs(tcph -> source); + conn.dport = bpf_ntohs(tcph -> dest); + + } + else if(nh_type == IPPROTO_UDP){ + if(parse_udphdr(&nh, data_end, &udph) < 0){ + goto out; + } + conn.sport = bpf_ntohs(udph -> source); + conn.dport = bpf_ntohs(udph -> dest); + } + else if(nh_type == IPPROTO_ICMP){ + conn.sport = 0; + conn.dport = 0; + } + conn.saddr = bpf_ntohl(iph -> saddr); + conn.daddr = bpf_ntohl(iph -> daddr); + conn.ip_proto = nh_type; + + // #ifdef DEBUG_PRINT_EVERY + // if(conn.dport != 22) + // bpf_printk("conn(%u:%u to %u:%u)", conn.saddr, conn.sport, conn.daddr, conn.dport); + // #endif + + action = match_rules_ipv4(&conn); + + } + + +out: + return xdp_stats_record_action(ctx, action); +} + + + + +/* Solution to packet03/assignment-4 */ +SEC("xdp") +int xdp_entry_router(struct xdp_md *ctx) +{ + xdp_act action = XDP_PASS; + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct bpf_fib_lookup ifib = {}; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth = data; + struct ipv6hdr *ip6h; + struct iphdr *iph; + unsigned int ip4_saddr = 0; + //unsigned ifindex = 2; + int rc; + struct rt_item nitem = {.saddr = 0, .eth_source = {0}, .eth_dest = {0}}; + + + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + + if (iph->ttl <= 1) + goto out; + + + ip4_saddr = iph->saddr; + + nitem.saddr = ip4_saddr; + + // 首先精确查找转发表,如果找到就直接转发,不必再经历最长前缀匹配的慢速通配查找 + match_rules(&nitem); + + + + if (mac_zero(nitem.eth_dest)) { + ip_decrease_ttl(iph); + memcpy(eth->h_dest, nitem.eth_dest, ETH_ALEN); + memcpy(eth->h_source, nitem.eth_source, ETH_ALEN); + action = bpf_redirect_map(&tx_port, 0, 0);//这里可能需要改 + + goto out; + } + + // 否则执行最长前缀匹配了 + ifib.family = AF_INET; + ifib.tos = iph->tos; + ifib.l4_protocol = iph->protocol; + ifib.sport = 0; + ifib.dport = 0; + ifib.tot_len = bpf_ntohs(iph->tot_len); + ifib.ipv4_src = iph->saddr; + ifib.ipv4_dst = iph->daddr; + ifib.ifindex = ctx->ingress_ifindex; + + + rc = bpf_fib_lookup(ctx, &ifib, sizeof(ifib), 0); + switch (rc) { + case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */ + ip_decrease_ttl(iph); + + memcpy(eth->h_dest, ifib.dmac, ETH_ALEN); + memcpy(eth->h_source, ifib.smac, ETH_ALEN); + action = bpf_redirect(ifib.ifindex, 0); + goto out; + break; + case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */ + case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */ + case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */ + action = XDP_DROP; + goto out; + break; + case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */ + case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */ + case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */ + case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */ + case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */ + /* PASS */ + goto out; + break; + } + + } else if (nh_type == bpf_htons(ETH_P_IPV6)) { + nh_type = parse_ip6hdr(&nh, data_end, &ip6h); + + struct in6_addr *src = (struct in6_addr *) ifib.ipv6_src; + struct in6_addr *dst = (struct in6_addr *) ifib.ipv6_dst; + + if(nh_type < 0) + goto out; + + if (ip6h->hop_limit <= 1) + goto out; + + ifib.family = AF_INET6; + ifib.flowinfo = *(__be32 *) ip6h & IPV6_FLOWINFO_MASK; + ifib.l4_protocol = ip6h->nexthdr; + ifib.sport = 0; + ifib.dport = 0; + ifib.tot_len = bpf_ntohs(ip6h->payload_len); + *src = ip6h->saddr; + *dst = ip6h->daddr; + ifib.ifindex = ctx->ingress_ifindex; + + rc = bpf_fib_lookup(ctx, &ifib, sizeof(ifib), 0); + switch (rc) { + case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */ + ip6h->hop_limit--; + + memcpy(eth->h_dest, ifib.dmac, ETH_ALEN); + memcpy(eth->h_source, ifib.smac, ETH_ALEN); + action = bpf_redirect(ifib.ifindex, 0); + goto out; + break; + case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */ + case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */ + case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */ + action = XDP_DROP; + break; + case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */ + case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */ + case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */ + case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */ + case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */ + /* PASS */ + break; + } + + } + else { + goto out; + } + + + +out: + return xdp_stats_record_action(ctx, action); +} + + +/* accept ethernet addresses and filter everything else */ +SEC("xdp") +int xdp_entry_mac(struct xdp_md *ctx) +{ + xdp_act action = XDP_PASS; + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + __u32 *value; + + + nh.pos = data; + + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + //action = match_rules_ipv4(ð->h_source); + + /* check if src mac is in src_macs map */ + value = bpf_map_lookup_elem(&src_macs, eth->h_source); + if (value) { + action = *value; + goto out; + } + + +out: + return xdp_stats_record_action(ctx, action); +} + +SEC("xdp") +int xdp_entry_state(struct xdp_md *ctx) +{ + __u32 action = XDP_PASS; + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct hdr_cursor nh; + int nh_type; //next header type + struct ethhdr *eth; + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + struct icmphdr *icmph; + unsigned char *saddr; + unsigned char *daddr; + + // 定义IPv4连接关键信息 + struct conn_ipv4_key conn_k = {.saddr = 0, .daddr = 0, .sport = 0, .dport = 0, .proto = 0}; + nh.pos = data; + + // 如果下一个头部类型为IPv4 + nh_type = parse_ethhdr(&nh, data_end, ð); + + if(nh_type < 0) + goto out; + + if (nh_type == bpf_htons(ETH_P_IP)) { + + nh_type = parse_iphdr(&nh, data_end, &iph); + + if(nh_type < 0) + goto out; + + conn_k.saddr = bpf_ntohl(iph -> saddr); + conn_k.daddr = bpf_ntohl(iph -> daddr); + + conn_k.proto = nh_type; + + saddr = (unsigned char *)&conn_k.saddr; + daddr = (unsigned char *)&conn_k.daddr; + + // 如果下一个头部类型为TCP + if (nh_type == IPPROTO_TCP) { + + if(parse_tcphdr(&nh, data_end, &tcph) < 0) + goto out; + // 获取TCP连接信息 + conn_k.sport = bpf_ntohs(tcph -> source); + conn_k.dport = bpf_ntohs(tcph -> dest); + // bpf_printk("conn(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u)", + // (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + // (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport); + // 查找IPv4连接映射表中的值 + // 如果找到,就说明该连接已经存在,可以在原有连接信息的基础上进行处理。 + // 如果没有找到,可能是首次遇到这个连接,可以进行一些初始化操作,例如创建新的连接信息并添加到哈希表中。 + struct conn_ipv4_val *p_conn_v = bpf_map_lookup_elem(&conn_ipv4_map, &conn_k); + if(!p_conn_v){ + if(tcph->syn && tcph->ack){ //客户端 + struct conn_ipv4_val conn_v = {.tcp_state = TCP_S_ESTABLISHED,.rid=1}; + // 将新的连接项插入到 IPv4 连接映射中 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, &conn_v, BPF_ANY); + // 输出日志信息,表示创建了一个新的连接项 + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "ESTABLISHED",conn_v.rid?"client":"service"); + } + else if(tcph->syn){ //客户端 + struct conn_ipv4_val conn_v = {.tcp_state = TCP_S_SYN_RECV,.rid=0}; + // 将新的连接项插入到 IPv4 连接映射中 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, &conn_v, BPF_ANY); + // 输出日志信息,表示创建了一个新的连接项 + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "SYN-RECV",conn_v.rid?"client":"service"); + } + goto out; + } + // 如果查找成功,继续处理连接项 + // 如果TCP报文的标志位包含RST(复位),则删除连接项并输出相应的日志信息 + if(tcph->rst){ + bpf_map_delete_elem(&conn_ipv4_map, &conn_k); + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + "RST",p_conn_v->rid?"client":"service"); + goto out; + } + if(p_conn_v->rid) //客户端 + { + // 如果连接项的TCP状态为ESTABLISHED并且收到了ack,将TCP状态更新为FIN_WAIT2 + if(p_conn_v->tcp_state == TCP_S_ESTABLISHED && tcph->ack){ + p_conn_v->tcp_state = TCP_S_FIN_WAIT2; + goto out_tcp_conn; + } + } + if(!p_conn_v->rid)//服务端 + { + // 如果连接项的TCP状态为SYN_RECV并且收到了ACK,将TCP状态更新为ESTABLISHED + if(p_conn_v->tcp_state == TCP_S_SYN_RECV && tcph->ack){ + p_conn_v->tcp_state = TCP_S_ESTABLISHED; + goto out_tcp_conn; + } + // 如果连接项的TCP状态为ESTABLISHED并且收到了FIN,将TCP状态更新为FIN_WAIT1 + if(p_conn_v->tcp_state == TCP_S_ESTABLISHED && tcph->fin){ + p_conn_v->tcp_state = TCP_S_CLOSE_WAIT; + goto out_tcp_conn; + } + // 如果连接项的TCP状态为CLOSE_WAIT且收到了FIN和ACK,将TCP状态更新为FIN_WAIT2 + if(p_conn_v->tcp_state == TCP_S_CLOSE_WAIT && tcph->ack){ + p_conn_v->tcp_state = TCP_S_CLOSE; + goto out_tcp_conn; + } + } + const char *tcp_state_str; + + // 根据连接状态设置对应的字符串 + out_tcp_conn: + if(p_conn_v->tcp_state == TCP_S_CLOSE||p_conn_v->tcp_state == TCP_S_FIN_WAIT2){ + // 如果是CLOSE状态,从映射表中删除连接信息 + bpf_map_delete_elem(&conn_ipv4_map, &conn_k); + }else{ + // 否则更新映射表中的连接信息 + bpf_map_update_elem(&conn_ipv4_map, &conn_k, p_conn_v, BPF_EXIST); + } + // 根据连接状态打印日志 + switch(p_conn_v->tcp_state) { + case TCP_S_SYN_SENT: + tcp_state_str = "SYN_SENT"; + break; + case TCP_S_SYN_RECV: + tcp_state_str = "SYN_RECV"; + break; + case TCP_S_ESTABLISHED: + tcp_state_str = "ESTABLISHED"; + break; + case TCP_S_FIN_WAIT1: + tcp_state_str = "FIN_WAIT1"; + break; + case TCP_S_FIN_WAIT2: + tcp_state_str = "FIN_WAIT2"; + break; + case TCP_S_CLOSE_WAIT: + tcp_state_str = "CLOSE_WAIT"; + break; + case TCP_S_CLOSE: + tcp_state_str = "CLOSE"; + break; + default: + tcp_state_str = ""; + } + bpf_printk("tcp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),state:%s,%s", + (unsigned long)saddr[0], (unsigned long)saddr[1], (unsigned long)saddr[2], (unsigned long)saddr[3],conn_k.sport, + (unsigned long)daddr[0], (unsigned long)daddr[1], (unsigned long)daddr[2], (unsigned long)daddr[3],conn_k.dport, + tcp_state_str,p_conn_v->rid?"client":"service"); + goto out; + } + else if(nh_type == IPPROTO_UDP){ + // 如果是UDP包,解析UDP头部并获取端口信息 + if(parse_udphdr(&nh, data_end, &udph) < 0){ + goto out; + } + conn_k.sport = bpf_ntohs(udph -> source); + conn_k.dport = bpf_ntohs(udph -> dest); + bpf_printk("udp(%lu.%lu.%lu.%lu:%u->%lu.%lu.%lu.%lu:%u),len=%lu", + (unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0],conn_k.sport, + (unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0],conn_k.dport, + __bpf_ntohs(udph -> len)); + } + else if(nh_type == IPPROTO_ICMP){ + // 如果是ICMP + if(parse_icmphdr(&nh, data_end, &icmph) < 0){ + goto out; + } + bpf_printk("icmp(%lu.%lu.%lu.%lu->%lu.%lu.%lu.%lu),type=%u,code=%u", + (unsigned long)saddr[3], (unsigned long)saddr[2], (unsigned long)saddr[1], (unsigned long)saddr[0], + (unsigned long)daddr[3], (unsigned long)daddr[2], (unsigned long)daddr[1], (unsigned long)daddr[0], + icmph->type,icmph->code); + } + #ifdef DEBUG_PRINT_EVERY + // 打印除SSH协议以外的所有连接信息 + if(conn.dport != 22) + bpf_printk("icmp(%u:%u to %u:%u)", conn.saddr, conn.sport, conn.daddr, conn.dport); + #endif + + } + + +out: + return xdp_stats_record_action(ctx, action); + +} + +SEC("xdp") +int test(struct xdp_md *ctx) +{ + bpf_printk("1111111111 test\n"); + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org new file mode 100644 index 000000000..7bae95ebc --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/README.org @@ -0,0 +1,79 @@ +# -*- fill-column: 76; -*- +#+TITLE: Test environment script +#+OPTIONS: ^:nil + +This directory contains a setup script that you can use to create test +environments for testing your XDP programs. It works by creating virtual +ethernet (veth) interface pairs and moving one end of each pair to another +network namespace. You can load the XDP program in the other namespace and +send traffic to it through the interface that is visible in the root +namespace. + +Run =./testenv.sh= with no parameter to get a list of available commands, or +run =./testenv.sh --help= to get the full help listing with all options. The +script can maintain several environments active at the same time, and you +can switch between them using the =--name= option. + +If you don't specify a name, the most recently used environment will be +used. If you don't specify a name when setting up a new environment, a +random name will be generated for you. + +Examples: + +Setup new environment named "test": +=./testenv.sh setup --name=test= + +Create a shell alias for easy use of script from anywhere: +=eval $(./testenv.sh alias)= + +See the currently active environment, and a list of all active environment +names (with alias defined as above): +=t status= + +Enter the currently active environment: +=t enter= + +Execute a command inside the environment: +=t exec -- ip a= + +Teardown the environment: +=t teardown= + +* Understanding the network topology + +When setting up a test environment, there will be a virtual link between the +environment inside the new namespace, and the interface visible from the +host system root namespace. The new namespace will be named after the +environment name passed to the script, as will the interface visible in the +outer namespace. The interface *inside* the namespace will always be named +'veth0'. + +To illustrate this, creating a test environment with the name 'test01' (with +=t setup --name test01= will result in the following environment being set +up: + +#+begin_example ++-----------------------------+ +-----------------------------+ +| Root namespace | | Testenv namespace 'test01' | +| | From 'test01' | | +| +--------+ TX-> RX-> +--------+ | +| | test01 +--------------------------+ veth0 | | +| +--------+ <-RX <-TX +--------+ | +| | From 'veth0' | | ++-----------------------------+ +-----------------------------+ +#+end_example + +The 'test01' interface visible in the root namespace is the one we will be +installing XDP programs on in the tutorial lessons. The XDP program will see +packets being *received* on this interface; as you can see from the diagram, +this means all packets being transmitted from inside the new namespace. + +The setup is created this way to simulate the case where the host machine +have physical interfaces; but instead of the traffic arriving from outside +hosts on physical interfaces, they will arrive from inside the namespace on +the virtual interface. This also means that when you generate traffic to +test your XDP programs, you need to generate it from *inside* the test +environment. The =t ping= command will start the ping inside the test +environment by default, and you can run arbitrary programs inside the +environment by using =t exec -- =, or simply spawning a shell with +=t enter=. diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh new file mode 100644 index 000000000..4b1853b76 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/config.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# These are the config options for the testlab + + +SETUP_SCRIPT="$(dirname "$0")/setup-env.sh" +STATEDIR="${TMPDIR:-/tmp}/xdp-tutorial-testlab" +IP6_SUBNET=fc00:dead:cafe # must have exactly three :-separated elements +IP6_PREFIX_SIZE=64 # Size of assigned prefixes +IP6_FULL_PREFIX_SIZE=48 # Size of IP6_SUBNET +IP4_SUBNET=10.11 +IP4_PREFIX_SIZE=24 # Size of assigned prefixes +IP4_FULL_PREFIX_SIZE=16 # Size of IP4_SUBNET +VLAN_IDS=(1 2) +GENERATED_NAME_PREFIX="xdptut" diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh new file mode 100755 index 000000000..b3db42537 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/setup-env.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script to setup things inside a test environment, used by testenv.sh for +# executing commands. +# +# Author: Toke Høiland-Jørgensen (toke@redhat.com) +# Date: 7 March 2019 +# Copyright (c) 2019 Red Hat + + +die() +{ + echo "$1" >&2 + exit 1 +} + +[ -n "$TESTENV_NAME" ] || die "TESTENV_NAME missing from environment" +[ -n "$1" ] || die "Usage: $0 " + +set -o nounset + +mount -t bpf bpf /sys/fs/bpf/ || die "Unable to mount /sys/fs/bpf inside test environment" + +exec "$@" diff --git a/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh new file mode 100755 index 000000000..34016b744 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_manager/testenv/testenv.sh @@ -0,0 +1,619 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script to setup and manage test environment for the XDP tutorial. +# See README.org for instructions on how to use. +# +# Author: Toke Høiland-Jørgensen (toke@redhat.com) +# Date: 6 March 2019 +# Copyright (c) 2019 Red Hat + +set -o errexit +set -o nounset +umask 077 + +source "$(dirname "$0")/config.sh" + +NEEDED_TOOLS="ethtool ip tc ping" +MAX_NAMELEN=15 + +# Global state variables that will be set by options etc below +GENERATE_NEW=0 +CLEANUP_FUNC= +STATEFILE= +CMD= +NS= +XDP_LOADER=./xdp_loader +XDP_STATS=./xdp_stats +LEGACY_IP=0 +USE_VLAN=0 +RUN_ON_INNER=0 + +# State variables that are written to and read from statefile +STATEVARS=(IP6_PREFIX IP4_PREFIX + INSIDE_IP6 INSIDE_IP4 INSIDE_MAC + OUTSIDE_IP6 OUTSIDE_IP4 OUTSIDE_MAC + ENABLE_IPV4 ENABLE_VLAN) +IP6_PREFIX= +IP4_PREFIX= +INSIDE_IP6= +INSIDE_IP4= +INSIDE_MAC= +OUTSIDE_IP6= +OUTSIDE_IP4= +OUTSIDE_MAC= +ENABLE_IPV4=0 +ENABLE_VLAN=0 + +die() +{ + echo "$1" >&2 + exit 1 +} + +check_prereq() +{ + local max_locked_mem=$(ulimit -l) + + for t in $NEEDED_TOOLS; do + which "$t" > /dev/null || die "Missing required tools: $t" + done + + if [ "$EUID" -ne "0" ]; then + die "This script needs root permissions to run." + fi + + [ -d "$STATEDIR" ] || mkdir -p "$STATEDIR" || die "Unable to create state dir $STATEDIR" + + if [ "$max_locked_mem" != "unlimited" ]; then + ulimit -l unlimited || die "Unable to set ulimit" + fi +} + +get_nsname() +{ + local GENERATE=${1:-0} + + if [ -z "$NS" ]; then + [ -f "$STATEDIR/current" ] && NS=$(< "$STATEDIR/current") + + if [ "$GENERATE" -eq "1" ] && [ -z "$NS" -o "$GENERATE_NEW" -eq "1" ]; then + NS=$(printf "%s-%04x" "$GENERATED_NAME_PREFIX" $RANDOM) + fi + fi + + if [ "${#NS}" -gt "$MAX_NAMELEN" ]; then + die "Environment name '$NS' is too long (max $MAX_NAMELEN)" + fi + + STATEFILE="$STATEDIR/${NS}.state" +} + +ensure_nsname() +{ + [ -z "$NS" ] && die "No environment selected; use --name to select one or 'setup' to create one" + [ -e "$STATEFILE" ] || die "Environment for $NS doesn't seem to exist" + + echo "$NS" > "$STATEDIR/current" + + read_statefile +} + +get_num() +{ + local num=1 + if [ -f "$STATEDIR/highest_num" ]; then + num=$(( 1 + $(< "$STATEDIR/highest_num" ))) + fi + + echo $num > "$STATEDIR/highest_num" + printf "%x" $num +} + +write_statefile() +{ + [ -z "$STATEFILE" ] && return 1 + echo > "$STATEFILE" + for var in "${STATEVARS[@]}"; do + echo "${var}='$(eval echo '$'$var)'" >> "$STATEFILE" + done +} + +read_statefile() +{ + local value + for var in "${STATEVARS[@]}"; do + value=$(source "$STATEFILE"; eval echo '$'$var) + eval "$var=\"$value\"" + done +} + +cleanup_setup() +{ + echo "Error during setup, removing partially-configured environment '$NS'" >&2 + set +o errexit + ip netns del "$NS" 2>/dev/null + ip link del dev "$NS" 2>/dev/null + rm -f "$STATEFILE" +} + +cleanup_teardown() +{ + echo "Warning: Errors during teardown, partial environment may be left" >&2 +} + + +cleanup() +{ + [ -n "$CLEANUP_FUNC" ] && $CLEANUP_FUNC + + [ -d "$STATEDIR" ] || return 0 + + local statefiles=("$STATEDIR"/*.state) + + if [ "${#statefiles[*]}" -eq 1 ] && [ ! -e "${statefiles[0]}" ]; then + rm -f "${STATEDIR}/highest_num" "${STATEDIR}/current" + rmdir "$STATEDIR" + fi +} + +iface_macaddr() +{ + local iface="$1" + local ns="${2:-}" + local output + + if [ -n "$ns" ]; then + output=$(ip -br -n "$ns" link show dev "$iface") + else + output=$(ip -br link show dev "$iface") + fi + echo "$output" | awk '{print $3}' +} + +set_sysctls() +{ + local iface="$1" + local in_ns="${2:-}" + local nscmd= + + [ -n "$in_ns" ] && nscmd="ip netns exec $in_ns" + local sysctls=(accept_dad + accept_ra + mldv1_unsolicited_report_interval + mldv2_unsolicited_report_interval) + + for s in ${sysctls[*]}; do + $nscmd sysctl -w net.ipv6.conf.$iface.${s}=0 >/dev/null + done +} + +wait_for_dev() +{ + local iface="$1" + local in_ns="${2:-}" + local retries=5 # max retries + local nscmd= + + [ -n "$in_ns" ] && nscmd="ip netns exec $in_ns" + while [ "$retries" -gt "0" ]; do + if ! $nscmd ip addr show dev $iface | grep -q tentative; then return 0; fi + sleep 0.5 + retries=$((retries -1)) + done +} + +get_vlan_prefix() +{ + # Split the IPv6 prefix, and add the VLAN ID to the upper byte of the fourth + # element in the prefix. This will break if the global prefix config doesn't + # have exactly three elements in it. + local prefix="$1" + local vid="$2" + (IFS=:; set -- $prefix; printf "%s:%s:%s:%x::" "$1" "$2" "$3" $(($4 + $vid * 4096))) +} + +setup() +{ + get_nsname 1 + + echo "Setting up new environment '$NS'" + + [ -e "$STATEFILE" ] && die "Environment for '$NS' already exists" + + local NUM=$(get_num "$NS") + local PEERNAME="testl-ve-$NUM" + [ -z "$IP6_PREFIX" ] && IP6_PREFIX="${IP6_SUBNET}:${NUM}::" + [ -z "$IP4_PREFIX" ] && IP4_PREFIX="${IP4_SUBNET}.$((0x$NUM))." + + INSIDE_IP6="${IP6_PREFIX}2" + INSIDE_IP4="${IP4_PREFIX}2" + OUTSIDE_IP6="${IP6_PREFIX}1" + OUTSIDE_IP4="${IP4_PREFIX}1" + + CLEANUP_FUNC=cleanup_setup + + if ! mount | grep -q /sys/fs/bpf; then + mount -t bpf bpf /sys/fs/bpf/ + fi + + ip netns add "$NS" + ip link add dev "$NS" type veth peer name veth0 netns "$NS" + + set_sysctls $NS + ip link set dev "$NS" up + ip addr add dev "$NS" "${OUTSIDE_IP6}/${IP6_PREFIX_SIZE}" + ethtool -K "$NS" rxvlan off txvlan off + # Prevent neighbour queries on the link + INSIDE_MAC=$(iface_macaddr veth0 "$NS") + ip neigh add "$INSIDE_IP6" lladdr "$INSIDE_MAC" dev "$NS" nud permanent + + set_sysctls veth0 "$NS" + ip -n "$NS" link set dev lo up + ip -n "$NS" link set dev veth0 up + ip -n "$NS" addr add dev veth0 "${INSIDE_IP6}/${IP6_PREFIX_SIZE}" + ip netns exec "$NS" ethtool -K veth0 rxvlan off txvlan off + # Prevent neighbour queries on the link + OUTSIDE_MAC=$(iface_macaddr "$NS") + ip -n "$NS" neigh add "$OUTSIDE_IP6" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent + # Add route for whole test subnet, to make it easier to communicate between + # namespaces + ip -n "$NS" route add "${IP6_SUBNET}::/$IP6_FULL_PREFIX_SIZE" via "$OUTSIDE_IP6" dev veth0 + + if [ "$LEGACY_IP" -eq "1" ]; then + ip addr add dev "$NS" "${OUTSIDE_IP4}/${IP4_PREFIX_SIZE}" + ip -n "$NS" addr add dev veth0 "${INSIDE_IP4}/${IP4_PREFIX_SIZE}" + ip neigh add "$INSIDE_IP4" lladdr "$INSIDE_MAC" dev "$NS" nud permanent + ip -n "$NS" neigh add "$OUTSIDE_IP4" lladdr "$OUTSIDE_MAC" dev veth0 nud permanent + ip -n "$NS" route add "${IP4_SUBNET}/${IP4_FULL_PREFIX_SIZE}" via "$OUTSIDE_IP4" dev veth0 + ENABLE_IPV4=1 + else + ENABLE_IPV4=0 + fi + + if [ "$USE_VLAN" -eq "1" ]; then + ENABLE_VLAN=1 + for vid in "${VLAN_IDS[@]}"; do + local vlpx="$(get_vlan_prefix "$IP6_PREFIX" "$vid")" + local inside_ip="${vlpx}2" + local outside_ip="${vlpx}1" + ip link add dev "${NS}.$vid" link "$NS" type vlan id "$vid" + ip link set dev "${NS}.$vid" up + ip addr add dev "${NS}.$vid" "${outside_ip}/${IP6_PREFIX_SIZE}" + ip neigh add "$inside_ip" lladdr "$INSIDE_MAC" dev "${NS}.$vid" nud permanent + set_sysctls "${NS}/$vid" + + ip -n "$NS" link add dev "veth0.$vid" link "veth0" type vlan id "$vid" + ip -n "$NS" link set dev "veth0.$vid" up + ip -n "$NS" addr add dev "veth0.$vid" "${inside_ip}/${IP6_PREFIX_SIZE}" + ip -n "$NS" neigh add "$outside_ip" lladdr "$OUTSIDE_MAC" dev "veth0.$vid" nud permanent + set_sysctls "veth0/$vid" "$NS" + done + else + ENABLE_VLAN=0 + fi + + write_statefile + + CLEANUP_FUNC= + + echo -n "Setup environment '$NS' with peer ip ${INSIDE_IP6}" + [ "$ENABLE_IPV4" -eq "1" ] && echo " and ${INSIDE_IP4}." || echo "." + echo "Waiting for interface configuration to settle..." + echo "" + wait_for_dev "$NS" && wait_for_dev veth0 "$NS" + + LEGACY_IP=0 USE_VLAN=0 run_ping -c 1 + + echo "$NS" > "$STATEDIR/current" +} + +teardown() +{ + get_nsname && ensure_nsname "$NS" + + echo "Tearing down environment '$NS'" + + CLEANUP_FUNC=cleanup_teardown + + ip link del dev "$NS" + ip netns del "$NS" + rm -f "$STATEFILE" + [ -d "/sys/fs/bpf/$NS" ] && rmdir "/sys/fs/bpf/$NS" || true + + if [ -f "$STATEDIR/current" ]; then + local CUR=$(< "$STATEDIR/current" ) + [[ "$CUR" == "$NS" ]] && rm -f "$STATEDIR/current" + fi + + CLEANUP_FUNC= +} + +reset() +{ + teardown && setup +} + +ns_exec() +{ + get_nsname && ensure_nsname "$NS" + + ip netns exec "$NS" env TESTENV_NAME="$NS" "$SETUP_SCRIPT" "$@" +} + +enter() +{ + ns_exec "${SHELL:-bash}" +} + +run_ping() +{ + local PING + local IP + + get_nsname && ensure_nsname "$NS" + + echo "Running ping from inside test environment:" + echo "" + + if [ "$LEGACY_IP" -eq "1" ]; then + PING=$(which ping) + IP="${OUTSIDE_IP4}" + [ "$USE_VLAN" -eq "0" ] || die "Can't use --legacy-ip and --vlan at the same time." + [ "$ENABLE_IPV4" -eq "1" ] || die "No legacy IP addresses configured in environment." + else + PING=$(which ping6 2>/dev/null || which ping) + if [ "$USE_VLAN" -eq "0" ]; then + IP="${OUTSIDE_IP6}" + else + [ "$ENABLE_VLAN" -eq "1" ] || die "No VLANs configured in environment." + IP="$(get_vlan_prefix "$IP6_PREFIX" "${VLAN_IDS[0]}")1" + fi + fi + + ns_exec "$PING" "$IP" "$@" +} + +run_tcpdump() +{ + get_nsname && ensure_nsname "$NS" + + if [ "$RUN_ON_INNER" -eq "1" ]; then + ns_exec tcpdump -nei veth0 "$@" + else + tcpdump -nei "$NS" "$@" + fi +} + +status() +{ + get_nsname + + echo "Currently selected environment: ${NS:-None}" + if [ -n "$NS" ] && [ -e "$STATEFILE" ]; then + read_statefile + echo -n " Namespace: "; ip netns | grep "^$NS" + echo " Prefix: ${IP6_PREFIX}/${IP6_PREFIX_SIZE}" + [ "$ENABLE_IPV4" -eq "1" ] && echo " Legacy prefix: ${IP4_PREFIX}0/${IP4_PREFIX_SIZE}" + echo -n " Iface: "; ip -br a show dev "$NS" | sed 's/\s\+/ /g' + fi + echo "" + + echo "All existing environments:" + for f in "$STATEDIR"/*.state; do + if [ ! -e "$f" ]; then + echo " No environments exist" + break + fi + NAME=$(basename "$f" .state) + echo " $NAME" + done +} + +print_alias() +{ + local scriptname="$(readlink -e "$0")" + local sudo= + + [ -t 1 ] && echo "Eval this with \`eval \$($0 alias)\` to create shell alias" >&2 + + if [ "$EUID" -ne "0" ]; then + sudo="sudo " + echo "WARNING: Creating sudo alias; be careful, this script WILL execute arbitrary programs" >&2 + fi + + echo "" >&2 + + + echo "alias t='$sudo$scriptname'" +} + +# +# This command can be used to populate maps for the assignment 3 of the +# packet03-redirecting lesson. It takes two arguments: the source and the +# destination environment names. +# +populate_redirect_map() +{ + local src="$1" + local dest="$2" + local src_mac=$(ip netns exec $src cat /sys/class/net/veth0/address) + local dest_mac=$(ip netns exec $dest cat /sys/class/net/veth0/address) + + # set bidirectional forwarding + ./xdp_prog_user -d $src -r $dest --src-mac $src_mac --dest-mac $dest_mac + ./xdp_prog_user -d $dest -r $src --src-mac $dest_mac --dest-mac $src_mac +} + +xdp_load() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_LOADER" ] || die "Loader '$XDP_LOADER' is not executable" + $XDP_LOADER --dev "$NS" "$@" +} + +xdp_unload() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_LOADER" ] || die "Loader '$XDP_LOADER' is not executable" + $XDP_LOADER --dev "$NS" --unload "$@" +} + +xdp_stats() +{ + get_nsname && ensure_nsname + + [ -x "$XDP_STATS" ] || die "Stats tool '$XDP_STATS' is not executable" + $XDP_STATS --dev "$NS" "$@" +} + +usage() +{ + local FULL=${1:-} + + echo "Usage: $0 [options] [param]" + echo "" + echo "Commands:" + echo "setup Setup and initialise new environment" + echo "teardown Tear down existing environment" + echo "reset Reset environment to original state" + echo "exec Exec inside test environment" + echo "enter Execute shell inside test environment" + echo "ping Run ping inside test environment" + echo "alias Print shell alias for easy access to this script" + echo "status (or st) Show status of test environment" + echo "load Load XDP program on outer interface" + echo "unload Unload XDP program on outer interface" + echo "tcpdump Run on outer interface (or inner with --inner)" + echo "stats Run the XDP statistics program" + echo "redirect Setup redirects for packet03 lessons" + echo "" + + if [ -z "$FULL" ] ; then + echo "Use --help to see the list of options." + exit 1 + fi + + echo "Options:" + echo "-h, --help Show this usage text" + echo "" + echo "-n, --name Set name of test environment. If not set, the last used" + echo " name will be used, or a new one generated." + echo "" + echo "-g, --gen-new Generate a new test environment name even though an existing" + echo " environment is selected as the current one." + echo "" + echo "-l, --loader Specify program to use for loading XDP programs." + echo " Device name will be passed to it, along with any additional" + echo " command line options passed after --." + echo " Default: '$XDP_LOADER'" + echo "" + echo "-s, --stats Specify program to use for getting statistics ('stats' command)." + echo " Device name will be passed to it, along with any additional" + echo " command line options passed after --." + echo " Default: '$XDP_STATS'" + echo "" + echo " --legacy-ip Enable legacy IP (IPv4) support." + echo " For setup and reset commands this enables configuration of legacy" + echo " IP addresses on the interface, for the ping command it switches to" + echo " legacy ping." + echo "" + echo " --vlan Enable VLAN support." + echo " When used with the setup and reset commands, these VLAN IDs will" + echo " be configured: ${VLAN_IDS[*]}. The VLAN interfaces are named as" + echo " .." + echo " When used with the ping command, the pings will be sent on the" + echo " first VLAN ID (${VLAN_IDS[0]})." + echo "" + echo " --inner Use with tcpdump command to run on inner interface." + echo "" + exit 1 +} + + +OPTS="hn:gl:s:" +LONGOPTS="help,name:,gen-new,loader:,stats:,legacy-ip,vlan,inner" + +OPTIONS=$(getopt -o "$OPTS" --long "$LONGOPTS" -- "$@") +[ "$?" -ne "0" ] && usage >&2 || true + +eval set -- "$OPTIONS" + + +while true; do + arg="$1" + shift + + case "$arg" in + -h | --help) + usage full >&2 + ;; + -n | --name) + NS="$1" + shift + ;; + -l | --loader) + XDP_LOADER="$1" + shift + ;; + -s | --stats) + XDP_STATS="$1" + shift + ;; + -g | --gen-new) + GENERATE_NEW=1 + ;; + --legacy-ip) + LEGACY_IP=1 + ;; + --vlan) + USE_VLAN=1 + ;; + --inner) + RUN_ON_INNER=1 + ;; + -- ) + break + ;; + esac +done + +[ "$#" -eq 0 ] && usage >&2 + +case "$1" in + st|sta|status) + CMD=status + ;; + setup|teardown|reset|enter) + CMD="$1" + ;; + load|unload|stats) + CMD="xdp_$1" + ;; + "exec") + CMD=ns_exec + ;; + ping|tcpdump) + CMD="run_$1" + ;; + redirect) + CMD=populate_redirect_map + ;; + "alias") + print_alias + exit 0 + ;; + "help") + usage full >&2 + ;; + *) + usage >&2 + ;; +esac + +shift +trap cleanup EXIT +check_prereq +$CMD "$@" diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile b/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile index 38ff8307f..c8c6e30fe 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/Makefile @@ -1,12 +1,19 @@ # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) OUTPUT := .output CLANG ?= clang -LIBBPF_SRC := $(abspath ../libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath ../libbpf-bootstrap/bpftool/src) +LIBBPF_SRC := $(abspath ../../lib/libbpf/src) +BPFTOOL_SRC := $(abspath ../../lib/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool - +VERSION_INFO := $(shell uname -r | cut -d'-' -f1) +VERSION_MAJOR := $(shell echo $(VERSION_INFO) | cut -d'.' -f1) +VERSION_MINOR := $(shell echo $(VERSION_INFO) | cut -d'.' -f2) +VERSION_PATCH := $(shell echo $(VERSION_INFO) | cut -d'.' -f3) +export VERSION_INFO +export VERSION_MAJOR +export VERSION_MINOR +export VERSION_PATCH ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ | sed 's/aarch64/arm64/' \ @@ -14,11 +21,14 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := ../libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h +LOGSRC := ./data/doc +VMDIR := ../../lib/vmlinux +VMLINUXSRC := $(VMDIR)/$(ARCH) +VMLINUX := $(VMLINUXSRC)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) +INCLUDES := -I$(OUTPUT) -I ../../lib/libbpf/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) @@ -57,13 +67,62 @@ $(call allow-override,CC,$(CROSS_COMPILE)cc) $(call allow-override,LD,$(CROSS_COMPILE)ld) .PHONY: all -all: $(APPS) +all: deps $(APPS) +#更新vmlinux.h文件 +.PHONY: deps +deps:check_bpftool check_clang check_multilib + @echo "Kernel version is $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)" + mkdir -p $(VMLINUXSRC) + bpftool btf dump file /sys/kernel/btf/vmlinux format c > $(VMLINUX) + +.PHONY: check_bpftool +check_bpftool: + @if ! command -v bpftool &> /dev/null; then \ + echo "bpftool 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y linux-tools-$(shell uname -r); \ + else \ + echo "bpftool 已经安装"; \ + fi +.PHONY: check_clang + +check_clang: + @if ! command -v clang &> /dev/null; then \ + echo "clang 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y clang; \ + else \ + echo "clang 已经安装"; \ + fi + +# 检查 multilib 是否安装,如果没有则安装 +.PHONY: check_multilib +check_multilib: + @if [ ! -f /usr/include/x86_64-linux-gnu/gnu/stubs-32.h ]; then \ + echo "multilib 未安装,正在安装..."; \ + sudo apt-get update; \ + sudo apt-get install -y gcc-multilib g++-multilib; \ + else \ + echo "multilib 已经安装"; \ + fi .PHONY: clean clean: $(call msg,CLEAN) $(Q)rm -rf $(OUTPUT) $(APPS) - + + rm -rf $(LOGSRC) + rm -rf $(VMDIR) + +.PHONY: clean2 +clean2: + $(call msg,CLEAN) + rm -f $(APPS) + rm -f $(OUTPUT)/*.skel.h + rm -f $(OUTPUT)/*.o + rm -rf $(LOGSRC) + rm -rf $(VMDIR) + $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) $(Q)mkdir -p $@ @@ -72,7 +131,7 @@ $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf $(call msg,LIB,$@) $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ - OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ + OBJDIR=$(dir $@)libbpf DESTDIR=$(dir $@) \ INCLUDEDIR= LIBDIR= UAPIDIR= \ install @@ -82,9 +141,13 @@ $(BPFTOOL): | $(BPFTOOL_OUTPUT) $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap # Build BPF code -$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard *.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ + -D__TARGET_ARCH_$(ARCH) \ + -DVERSION_MAJOR=$(VERSION_MAJOR) \ + -DVERSION_MINOR=$(VERSION_MINOR) \ + -DVERSION_PATCH=$(VERSION_PATCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) @@ -97,7 +160,7 @@ $(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) # Build user-space code $(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h -$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) +$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) $(wildcard %.h) $(call msg,CC,$@) $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md b/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md index 7cf63cb0f..355cf2f18 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/README.md @@ -1,6 +1,7 @@ # netwatcher - 网络检测工具 ## 一、工具介绍 ### 1.1 背景 + lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这些工具功能零散并且冗余: | 工具目录 | 功能 | 监控信息 | | --- | --- | --- | @@ -18,18 +19,26 @@ lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这 ### 1.2 功能介绍 -`netwatcher`是一款基于eBPF的网络检测工具,其旨在使用户方便快捷的获取主机环境下linux网络协议栈的各种信息。 +`netwatcher`是一款基于eBPF的高效网络检测工具,其目的是为了让用户能够轻松快捷地获取到网络协议栈的详细信息,通过高效的数据采集和精准的监控能力,帮助用户深入了解网络行为,确保网络安全和性能的优化。其在车辆智能导航、自动驾驶等关键领扮演着重要角色,面对网络异常或延迟时,`netwatcher`能够提供强大的网络监测和优化支持,帮助企业及时诊断并解决网络障碍,提升系统稳定性和服务可靠性。 + +netwatcher作为一款基于eBPF的网络检测工具,其设计初衷是帮助用户能够在主机环境中,轻松快捷地获取到 Linux 网络协议栈的详细信息,通过高效的数据收集和精准的监控能力,深入了解网络行为,确保网络安全和性能的优化,其应用范围涉及较广。 + +netwatcher能够追踪TCP、UDP、ICMP协议数据包从应用程序发出开始,经过内核协议栈到驱动、最终发出过程(发包路径)的时延数据和流量信息,以及数据包从驱动到内核协议栈到用户态程序的过程(收包路径)中的时延数据。对获取到的时延数据采用算法进行阈值比较捕获异常时延数据并给予警告信息。同时监测TCP连接的状态信息(seq、ack、连接状态、重传信息、错误信息、rwnd、cwnd、sndbuf等关键指标),并且可以监控丢包事件(包括skb_drop_reason中定义的77种原因)。 + +其有助于及时解决各种网络问题,提高系统稳定性和服务可靠性。无论是云计算所需要的确保其基础设施的稳定性和安全性,还是金融机构需要保障交易的及时性和安全性,亦或是电子商务企业追求用户体验和网站性能的优化,netwatcher都提供了强大的网络监测和优化能力,满足不同行业的需求,助力企业顺利应对数字化时代的挑战。 目前,其实现的功能包括: + - TCP相关的信息监测:主机环境下对tcp/ip协议的分析,可以统计流量,延时,错误,链接信息等主要信息 - HTTP1/1.1相关信息检测:通过截取相应TCP包的HTTP头实现主机环境下对用户态http1的分析 +- TCP、UDP、ICMP相关信息监测:追踪TCP、UDP、ICMP协议数据包,并实现对主机接收和发送的所有相关数据包的时延数据和流量信息 +- 监测TCP连接状态信息:包括三次握手以及四次挥手的状态转变和时延数据 +- 丢包事件的监控:分析导致丢包的地址以及丢包原因 +- DNS协议相关信息监控:通过截取UDP包,对DNS协议包进行解析,获取事务ID、标志字段、问题部分计数、应答记录计数、授权记录计数、附加记录计数、域名等信息 +- 主机环境下对用户态mysql的分析:uprobe实现对mysql的监测,其监测内容有进程pid、进程名、sql语句、sql语句执行时间。 #### TODO -- [ ] ICMP数据包信息的监控 - - 实现对ICMP协议报文的监控,输出到达的ICMP数据包的消息类型 -- [ ] UDP数据包信息的监控 - - 实现对主机接收和发送的所有UDP数据包的监控,输出各个UDP数据包的地址-端口4元组 -- [ ] 应用层协议的支持 +- [ ] 应用层协议的支持 - 在各个底层协议之上,提供对以下应用层信息的监控 - [ ] HTTP协议相关 - [ ] HTTP1 @@ -47,19 +56,33 @@ lmp现有许多用于监控linux网络协议栈相关信息的小工具,但这 ### 1.3 组织结构 -- netwatcher.bpf.c:在各个内核探针点对TCP包信息、TCP连接信息以及各个包的HTTP1/1.1信息进行记录 -- netwatcher.c: 对bpf.c文件中记录的信息进行输出 -- netwatcher.h: 定义内核态与用户态程序共用的结构体 - doc/: - implement.md:详细描述本项目的实现细节 -- data/: +- data/:文件夹存放打印的日志信息 - connects.log:符合Prometheus格式的连接信息 - err.log:符合Prometheus格式的错误包信息 - packets.log:符合Prometheus格式的包信息 +- udp.loh:符合Prometheus格式的udp包信息 - visual.py:暴露metrics接口给Prometheus,输出data文件夹下的所有信息 +- netwatcher.c :对bpf.c文件中记录的信息进行输出 +- netwatcher.bpf.c:封装内核探针点。 +- tcp.bpf.h:网络数据包处理以及tcp连接状态等信息具体实现细节。 +- udp.bpf.h :udp数据包时延、流量的具体处理逻辑。 +- packet.bpf.h :网络数据包的处理、时间戳的记录、数据包信息的提取等指标具体处理逻辑。 +- netfilter.bpf.h:处理netfilter时延的具体逻辑,`submit_nf_time`函数将时延信息提交到用户态,`store_nf_time`函数存储经过每个`HOOK`点的时延。 +- drop.bpf.h :数据包丢弃原因的具体处理逻辑。 +- dropreason.h :skb_drop_reason定义77种丢包原因。 +- icmp.bpf.h: icmp时延具体实现细节。 +- comm.bpf.h :辅助函数、宏、BPF映射、以及内核中使用到的结构体。 +- mysql,bpf.h : 处理mysql的具体实现逻辑。 +- mysql_helper.bpf : mysql相关数据结构。 + ## 二、快速开始 ### 2.1 安装依赖 OS: Ubuntu 22.04LTS + +Kernel:Linux 6.2 + ```bash sudo apt update sudo apt install libbpf-dev clang llvm libelf-dev libpcap-dev gcc-multilib build-essential @@ -78,22 +101,41 @@ sudo make test # 测试 ```bash Usage: netwatcher [OPTION...] +Watch tcp/ip in network subsystem +Usage: netwatcher [OPTION...] Watch tcp/ip in network subsystem -a, --all set to trace CLOSED connection + -A, --stack set to trace of stack -d, --dport=DPORT trace this destination port only + -D, --dns set to trace dns information info include Id + 事务ID、Flags 标志字段、Qd + 问题部分计数、An 应答记录计数、Ns + 授权记录计数、Ar 附加记录计数、Qr + 域名、rx 收发包 -e, --err set to trace TCP error packets -i, --http set to trace http info + -I, --icmptime set to trace layer time of icmp + -k, --drop_reason trace kfree + -L, --timeload analysis time load + -M, --mysql set to trace mysql information info include Pid + 进程id、Comm 进程名、Size + sql语句字节大小、Sql 语句 + -n, --net_filter trace ipv4 packget filter -r, --retrans set to trace extra retrans info -s, --sport=SPORT trace this source port only + -S, --tcpstate set to trace tcpstate -t, --time set to trace layer time of each packet + -T, --addr_to_func translation addr to func and offset + -u, --udp trace the udp message -x, --extra set to trace extra conn info -?, --help Give this help list - --usage Give a short usage messages. + --usage Give a short usage message + ``` - 参数`-d`,`-s`用于指定监控某个源端口/目的端口 - 指定参数`-a`会保留已CLOSED的TCP连接信息 -- 指定`-e`参数会记录SEQ错误或Checksum错误的TCP包 +- 指定`-e`参数会记录SEQ错误或Checksum错误的TCP包并输出到标准输出以及data/err.log中。 - 默认情况下,监控以下连接信息与包信息: - TCP连接pid - TCP连接\[源地址:端口,目的地址:端口\] @@ -115,12 +157,23 @@ Watch tcp/ip in network subsystem - 已使用的发送缓冲区 - 平滑往返时间 - 连接已建立时长 - - 连接总重传次数 + - 连接总重传次数。 +- 指定 `-i` 参数监控HTTP信息。从携带HTTP请求头的TCP包中将HTTP请求头提取并输出,记录HTTP的状态码等信息。 +- 指定 `-u` 参数监控UDP数据包信息。统计UDP协议的数据包流量、以us为单位的时延等信息。 +- 指定 `-n` 参数监控ipv4网络层的Netfilter延迟。监测ipv4网络层数据包经过Netfilter各个HOOK点的处理时延。 +- 指定 `-k` 参数监测系统中的各类丢包并分析丢包原因,详细定位协议丢包的指令地址。 +- 指定 `-S` 参数监控TCP的连接状态以及状态转换的时延。 +- 指定 `-I` 参数监控ICMP协议数据包收发过程中的时延。 +- 指定 `-T` 参数将捕获到的丢包事件虚拟地址转换成函数名+偏移量形式。 +- 指定 `-L` 参数监测网络协议栈数据包经过各层的时延,采用指数加权移动法对异常的时延数据进行监控并发出警告信息。 +- 指定 `-D` 参数监测DNS协议包信息。截取UDP包,对DNS协议包进行解析,获取其基本指标,包含事务ID、标志字段、问题部分计数、应答记录计数、域名等相关信息。 +- 指定 `-M` 参数监测Mysql信息。实现用户态下mysql监控,获取其sql语句及sql执行耗时,单位μs。 + ### 3.1 监控连接信息 `netwatcher`会将保存在内存中的连接相关信息实时地在`data/connects.log`中更新。默认情况下,为节省资源消耗,`netwatcher`会实时删除已CLOSED的TCP连接相关信息,并只会保存每个TCP连接的基本信息。 -``` +```c // data/connects.log -connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103.235.46.40:80",is_server="0",backlog="-",maxbacklog="-",cwnd="-",ssthresh="-",sndbuf="-",wmem_queued="-",rx_bytes="-",tx_bytes="-",srtt="-",duration="-",total_retrans="-",fast_retrans="-",timeout_retrans="-"} 0 +connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103.235.46.40:80",is_server="0",backlog="-",maxbacklog="-",cwnd="-",ssthresh="-",sndbuf="-",wmem_queued="-",rx_bytes="-",tx_bytes="-",srtt="-",duration="-",total_retrans="-",fast_retrans="-",timeout_retrans="-"} ``` #### 3.1.1 保留所有连接信息 对于想要保留所有连接信息的用户,需要指定`-a`参数。 @@ -128,58 +181,189 @@ connection{pid="44793",sock="0xffff9d1ecb3ba300",src="10.0.2.15:46348",dst="103. sudo ./netwatcher -a ``` #### 3.1.2 监控额外连接信息 -`netwatcher`默认只监控基本连接信息,额外连接信息输出为`-`;对于想要监控额外连接信息的用户,需要指定`-x`参数。 -``` -sudo ./netwatcher -x +`netwatcher`指定`-x`参数输出额外信息 ,额外信息实时更新于data/connects.log日志中。记录接收窗口大小rwnd、拥塞窗口大小cwnd、慢启动阈值ssthresh、发送缓冲区大小sndbuf、已使用的发送缓冲区wmem_queued、已接收字节数rx_bytes、已确认字节数tx_bytes、平滑往返时间srtt、连接建立时延duration等关键信息。 + +```c +sudo ./netwatcher -x +connection{pid="45395",sock="0xffff9780c13f7500",src="192.168.60.136:42938",dst="171.214.23.48:443",is_server="0",backlog="0",maxbacklog="0",rwnd="63986",cwnd="10",ssthresh="2147483647",sndbuf="87040",wmem_queued="0",rx_bytes="603",tx_bytes="1.563K",srtt="68166",duration="63879",total_retrans="0",fast_retrans="-",timeout_retrans="-"} ``` #### 3.1.3 监控重传信息 -`netwatcher`监控的额外连接信息并不提供对超时重传次数与快速重传次数的细化;对于想要获取此信息的用户,需要指定`-r`参数。 -``` +`netwatcher`监控超时重传次数、快速重传次数和连接总重传次数,指定`-r`参数。 + +```c sudo ./netwatcher -r +connection{pid="45395",sock="0xffff9780c13f6c00",src="192.168.60.136:60210",dst="124.237.208.55:443",is_server="0",backlog="0",maxbacklog="0",rwnd="63784",cwnd="10",ssthresh="2147483647",sndbuf="87040",wmem_queued="0",rx_bytes="568",tx_bytes="8.489K",srtt="65211",duration="319696",total_retrans="0",fast_retrans="-",timeout_retrans="2"} ``` ### 3.2 监控包信息 `netwatcher`会将各个TCP包的相关信息实时地输出在标准输出以及`data/packets.log`中。默认情况下,为节省资源消耗,`netwatcher`只会监控各个包的基本信息并输出。 -``` + +```c sudo ./netwatcher -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP +SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP 0xffff9d1ecb3ba300 629372796 279168002 - - - 0 - 0xffff9d1ecb3ba300 279168002 629372873 - - - 1 - ``` -``` +```c // data/packets.log -packet{sock="0xffff9d1ecb3ba300",seq="629372796",ack="279168002",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="0"} 0 -packet{sock="0xffff9d1ecb3ba300",seq="279168002",ack="629372873",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="1"} 0 +packet{sock="0xffff9d1ecb3ba300",seq="629372796",ack="279168002",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="0"} +packet{sock="0xffff9d1ecb3ba300",seq="279168002",ack="629372873",mac_time="-",ip_time="-",tcp_time="-",http_info="-",rx="1"} +``` +#### 3.2.1监控数据包在各层的处理时延 +`netwatcher`监控各个数据包在各层的处理时间,指定`-t`参数,`netwatcher`会输出以`us`为单位的各层处理时间,支持ipv4、ipv6协议。 +```c +sudo ./netwatcher -t +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff939118a9ad00 192.168.60.136 36236 1.1.1.1 80 2 5 11 0 - +0xffff939118a9ad00 1.1.1.1 80 192.168.60.136 36236 6 10 111 1 - +0xffff93911049b600 192.168.60.136 36244 1.1.1.1 80 6 18 38 0 - +0xffff93911049b600 1.1.1.1 80 192.168.60.136 36244 14 17 263 1 - +0xffff93911049ad00 192.168.60.136 36246 1.1.1.1 80 2 4 16 0 - ``` -#### 3.2.1 各层处理时间 -`netwatcher`提供监控各个数据包在各层的处理时间的支持。为了获得这部分信息,用户需要指定`-t`参数,`netwatcher`会输出以`us`为单位的各层处理时间。 + +指定参数`-u`,查看UDP数据包处理时延并记录于`data/udp.log`中,单位为微妙。 + +```c +sudo ./netwatcher -u +Saddr Daddr Sprot Dprot udp_time/μs RX/direction len/byte +192.168.60.136 192.168.60.2 53643 53 2 0 39 +192.168.60.136 192.168.60.2 34272 53 3 0 42 +192.168.60.2 192.168.60.136 53 53643 12 1 230 +192.168.60.136 192.168.60.2 34442 53 1 0 40 +192.168.60.2 192.168.60.136 53 59996 2 1 190 ``` -sudo ./netwatcher -t -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP -0xffff9d1ec43fd780 2018346083 420544002 1 3 9 0 - -0xffff9d1ec43fd780 420544002 2018346160 67 12 494 1 - -0xffff9d1ec43fd780 420545414 2018346160 40 5 258 1 - + +指定参数`-I`,查看ICMP数据包处理时延,单位为微妙。 + +```c +sudo ./netwatcher -I +Saddr Daddr icmp_time/μs RX/direction +192.168.60.136 192.168.60.136 11 1 +192.168.60.136 192.168.60.136 5 0 +192.168.60.136 192.168.60.136 80 1 +``` + +指定参数`-n`,查看数据包在Netfilter框架(包括PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT、POST_ROUTING链中处理的时间),单位为微妙,其网络数据包路径为: + +- 发往本地:**NF_INET_PRE_ROUTING**-->**NF_INET_LOCAL_IN** +- 转发:**NF_INET_PRE_ROUTING**-->**NF_INET_FORWARD**-->**NF_INET_POST_ROUTING** +- 本地发出:**NF_INET_LOCAL_OUT**-->**NF_INET_POST_ROUTING** + +```c +sudo ./netwatcher -n +Saddr Daddr Sprot Dprot PreRT/μs L_IN/μs FW/μs PostRT/μs L_OUT/μs RX/direction +127.0.0.53 127.0.0.1 53 60590 3 2 0 0 0 1 +127.0.0.1 127.0.0.1 55858 40327 0 0 0 2 4 0 +127.0.0.1 127.0.0.1 55858 40327 1 1 0 0 0 1 +127.0.0.1 127.0.0.1 40327 55858 0 0 0 1 5 0 ``` #### 3.2.2 监控错误数据包 + `netwatcher`提供对TCP错误的监控支持,用户只需指定`-e`参数,`netwatcher`会记录SEQ错误或Checksum错误的TCP包并输出到标准输出以及`data/err.log`中。 -``` -sudo ./netwatcher -e + +```c +sudo ./netwatcher -e +packet{sock="0xffff13235ac8ac8e",seq="1318124482",ack="2468218244",reason="Invalid SEQ"} ``` #### 3.2.3 HTTP1/1.1 -`netwatcher`提供对HTTP1/1.1信息的监控,在指定`-i`参数后,`netwatcher`会从携带HTTP请求头的TCP包中将HTTP请求头提取并输出。 -``` +`netwatcher`提供对HTTP1/1.1信息的监控,在指定`-i`参数后,`netwatcher`会从携带HTTP请求头的TCP包中将HTTP请求头提取并输出,提取信息包括包含请求资源方式、状态码、状态文本等。 + +```c sudo ./netwatcher -i -SOCK SEQ ACK MAC_TIME IP_TIME TCP_TIME RX HTTP -0xffff9d1ecb3b9180 3705894662 522176002 - - - 0 GET / HTTP/1.1 -0xffff9d1ecb3b9180 522176002 3705894739 - - - 1 HTTP/1.1 200 OK +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff93911049d100 192.168.60.136 44152 1.1.1.1 80 0 0 0 0 0 - +0xffff93911049d100 1.1.1.1 80 192.168.60.136 44152 0 0 0 1 1 HTTP/1.1 301 Moved Permanently +0xffff9391601a6c00 192.168.60.136 44154 1.1.1.1 80 0 0 0 0 0 - +0xffff9391601a6c00 1.1.1.1 80 192.168.60.136 44154 0 0 0 1 1 HTTP/1.1 301 Moved Permanently ``` -### 3.3 与Prometheus连接进行可视化 -可以注意到,`data`中的所有文件都满足Prometheus要求的时序数据库格式。`netwatcher`使用`visual.py`在端口41420暴露`metrics`API为Prometheus提供可视化支持,当Prometheus请求此API时,会获得当前时刻下三个log文件的所有内容。由于三个log文件被eBPF程序实时更新,因此满足时序性。 +#### 3.2.3 过滤指定目的端口、源端口 + +```c +sudo ./netwatcher -d 80 或者 sudo ./netwatcher -s 80 +``` + +#### 3.2.4 TCP协议数据包连接状态 + +指定参数`-S`,对TCP数据包连接状态的监控。netwatcher会跟踪TCP连接状态的转换,其中可以对各个状态所持续的时间进行监测。 + +```C +sudo ./netwatcher -S +Saddr Daddr Sport Dport oldstate newstate time/μs +192.168.60.136 1.1.1.1 0 80 CLOSE SYN_SENT 0 +192.168.60.136 1.1.1.1 41312 80 SYN_SENT ESTABLISHED 181270 +192.168.60.136 1.1.1.1 41312 80 ESTABLISHED FIN_WAIT1 183729 +``` + +#### 3.2.5 捕捉丢包及原因 + +指定参数`-k`,捕捉丢包信息并获取其导致丢包的函数地址、丢包原因。 + +```c +sudo ./netwatcher -k +Time Saddr Daddr Sprot Dprot prot addr reason +13:44:03 1.1.1.1 192.168.60.136 80 49668 ipv4 ffffffff9c914464 SKB_DROP_REASON_NOT_SPECIFIED +13:44:03 1.1.1.1 192.168.60.136 80 49680 ipv4 ffffffff9c914464 SKB_DROP_REASON_NOT_SPECIFIED +``` + +指定参数`-k -T`,可以将虚拟地址转换成函数名+偏移的形式,在此处可以捕捉发生丢包的内核函数名称。 + +```C +sudo ./netwatcher -k -T +Time Saddr Daddr Sprot Dprot prot addr reason +13:44:22 1.1.1.1 192.168.60.136 80 37078 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:22 1.1.1.1 192.168.60.136 80 37092 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:24 1.1.1.1 192.168.60.136 80 37104 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED +13:44:24 1.1.1.1 192.168.60.136 80 37118 ipv4 tcp_v4_rcv+0x84 SKB_DROP_REASON_NOT_SPECIFIED ``` + +#### 3.2.7 异常时延监控 + +对获取到的各层时延加上-L参数进行监控,捕获监测到的异常数据并给予警告信息。 + +```C +sudo ./netwatcher -t -L +SOCK Saddr Sport Daddr Dport MAC_TIME/μs IP_TIME/μs TRAN_TIME/μs RX/direction HTTP +0xffff939118a9c800 192.168.60.136 53788 1.1.1.1 80 2 4 20 0 - +0xffff939118a9c800 1.1.1.1 80 192.168.60.136 53788 14 22 345 1 - +0xffff9391601a7500 192.168.60.136 53790 1.1.1.1 80 3 11 31 0 - +0xffff9391601a7500 1.1.1.1 80 192.168.60.136 53790 22 37 170 1 - +0xffff9391107dec00 113.137.56.223 443 192.168.60.136 34104 11 10 1442 1 abnormal data +``` + +#### 3.2.8 DNS协议包监控 + +选择`udp_rcv`和`udp_send_skb`挂载捕获DNS收包和发包相关信息,从UDP头部开始分析并定位DNS数据部分获取其信息,头部信息存储在`query.header`,数据部分读取存储于`data`,可以获取到DNS协议包的事务ID、标志字段、问题部分计数、应答记录计数、授权记录计数、附加记录计数、域名等信息。 + +```C +sudo ./netwatcher -D +Saddr Daddr Id Flags Qd An Ns Ar Qr RX/direction +192.168.60.2 192.168.60.136 0x7894 0x8180 1 2 0 0 baidu.com 0 +127.0.0.53 0.0.0.0 0xc247 0x8180 1 2 0 1 baidu.com 1 +127.0.0.1 127.0.0.53 0x7637 0x120 1 0 0 1 contile.services.mozilla.com 1 +192.168.60.136 192.168.60.2 0x2c35 0x100 1 0 0 0 contile.services.mozilla.com 1 +``` + +#### 3.2.9 Mysql监控 + +利用uprobe和uretprobe挂载mysql-server层的命令分发处理函数`dispatch_command`,探测该函数获取进程pid、进程名comm、sql语句、sql执行耗时(μs)。 + +```C +sudo ./netwatcher -M +Pid Comm Size Sql duration/μs +1121 connection 32 select @@version_comment limit 1 295 +1121 connection 17 SELECT DATABASE() 277 +1121 connection 14 show databases 1361 +1121 connection 11 show tables 1080 +``` + +### 3.3 与Prometheus连接进行可视化 + +`data`目录下的所有文件都满足Prometheus要求的时序数据库格式。`netwatcher`使用`visual.py`在端口41420暴露`metrics`API为Prometheus提供可视化支持,当Prometheus请求此API时,会获得当前时刻所有log文件的全部内容。由于log文件被eBPF程序实时更新,因此满足时序性。 +```c python visual.py ``` ## 四、代码实现细节 -- 见`doc/implement.md` +- 详细实现细节见`doc/implement.md` diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h index 8ea57deb1..b7e775a8a 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h @@ -28,43 +28,42 @@ #include #include -struct ktime_info { // us time stamp info发送数据包 +struct ktime_info { // us time stamp info发送数据包 u64 qdisc_time; // tx包离开mac层时间戳 u64 mac_time; // tx、rx包到达mac层时间戳 u64 ip_time; // tx、rx包到达ip层时间戳 // u64 tcp_time; // tx、rx包到达tcp层时间戳 - u64 tran_time; // tx、rx包到达传输层时间戳 - u64 app_time; // rx包离开tcp层时间戳 - void *sk; // 此包所属 socket套接字 + u64 tran_time; // tx、rx包到达传输层时间戳 + u64 app_time; // rx包离开tcp层时间戳 + void *sk; // 此包所属 socket套接字 u8 data[MAX_HTTP_HEADER]; // 用户层数据 }; struct packet_tuple { unsigned __int128 saddr_v6; // ipv6 源地址 unsigned __int128 daddr_v6; // ipv6 目的地址 - u32 saddr; // 源地址 - u32 daddr; // 目的地址 - u16 sport; // 源端口号 - u16 dport; // 目的端口号 - u32 seq; // seq报文序号 - u32 ack; // ack确认号 - u32 tran_flag; // 1:tcp 2:udp + u32 saddr; // 源地址 + u32 daddr; // 目的地址 + u16 sport; // 源端口号 + u16 dport; // 目的端口号 + u32 seq; // seq报文序号 + u32 ack; // ack确认号 + u32 tran_flag; // 1:tcp 2:udp u32 len; }; struct tcpstate { - u32 saddr; - u32 daddr; + u32 saddr; + u32 daddr; u16 sport; - u16 dport; + u16 dport; u16 family; - int oldstate; - int newstate; + int oldstate; + int newstate; u64 time; }; - -enum -{ +#define MAX_SLOTS 27 +enum { e_ip_rcv = 0, e_ip_local_deliver, e_ip_local_deliver_finish, @@ -74,17 +73,81 @@ enum e_ip_finish_output, e_ip_forward, nf_max -}nf_hook; +} nf_hook; -struct filtertime { +struct filtertime { struct packet_tuple init; struct packet_tuple done; u64 time[nf_max]; }; -struct ip_packet -{ - unsigned int saddr; // 源地址 - unsigned int daddr; // 目的地址 + +struct ip_packet { + unsigned int saddr; // 源地址 + unsigned int daddr; // 目的地址 +}; + +struct dns_header { + u16 id; // 事务ID + u16 flags; // 标志字段 + u16 qdcount; // 问题部分计数 + u16 ancount; // 应答记录计数 + u16 nscount; // 授权记录计数 + u16 arcount; // 附加记录计数 +}; + +struct dns_query { + struct dns_header header; // DNS头部 + char data[64]; // 可变长度数据(域名+类型+类) +}; + +struct dns { + u32 saddr; + u32 daddr; +}; + +struct query_info { + char msql[256]; + u32 size; + u64 start_time; +}; + +struct hist { + u64 slots[MAX_SLOTS]; + u64 latency; + u64 cnt; +}; + +struct trace_event_raw_tcp_send_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skbaddr; + const void *skaddr; + int state; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; +}; + +struct trace_event_raw_tcp_receive_reset { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + const void *skaddr; + __u16 sport; + __u16 dport; + __u16 family; + __u8 saddr[4]; + __u8 daddr[4]; + __u8 saddr_v6[16]; + __u8 daddr_v6[16]; + __u64 sock_cookie; }; // 操作BPF映射的一个辅助函数 static __always_inline void * //__always_inline强制内联 @@ -122,6 +185,11 @@ struct { __uint(max_entries, 256 * 1024); } rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rtt_rb SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); @@ -132,6 +200,16 @@ struct { __uint(max_entries, 256 * 1024); } netfilter_rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} mysql_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} redis_rb SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); @@ -147,6 +225,21 @@ struct { __uint(max_entries, 256 * 1024); } tcp_rb SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} dns_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} trace_rb SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} events SEC(".maps"); + // 存储每个tcp连接所对应的conn_t struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); @@ -183,29 +276,98 @@ struct { __uint(max_entries, MAX_CONN *MAX_PACKET); __type(key, int); __type(value, struct packet_tuple); -} kfree SEC(".maps"); +} kfree SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_CONN * MAX_PACKET); + __uint(max_entries, MAX_CONN *MAX_PACKET); __type(key, struct ip_packet); - __type(value,unsigned long long); + __type(value, unsigned long long); } icmp_time SEC(".maps"); struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 256*1024); - __type(key, struct sock *); - __type(value, __u64); + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct sock *); + __type(value, __u64); } tcp_state SEC(".maps"); +// sql 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, __u64); +} mysql_time SEC(".maps"); + +// redis 耗时 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, struct redis_query); +} redis_time SEC(".maps"); + +// sql请求数 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, __u64); +} sql_count SEC(".maps"); + +// dns计数根据每个saddr、daddr +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_request_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct dns); + __type(value, __u64); +} dns_response_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, __u32); + __type(value, struct query_info); +} queries SEC(".maps"); + +// 定义一个哈希映射,用于存储直方图数据 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, struct ip_packet); + __type(value, struct hist); +} hists SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, u32); + __type(value, u64); + __uint(max_entries, 1024); +} counters SEC(".maps"); const volatile int filter_dport = 0; const volatile int filter_sport = 0; const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, - layer_time = 0, http_info = 0, retrans_info = 0, udp_info =0,net_filter = 0,kfree_info = 0,icmp_info = 0 ,tcp_info = 0; + layer_time = 0, http_info = 0, retrans_info = 0, + udp_info = 0, net_filter = 0, drop_reason = 0, icmp_info = 0, + tcp_info = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, rtt_info = 0, rst_info = 0; /* help macro */ +#define FILTER \ + if (filter_dport && filter_dport != pkt_tuple.dport) \ + return 0; \ + if (filter_sport && filter_sport != pkt_tuple.sport) \ + return 0; + // 连接的目标端口是否匹配于filter_dport的值 #define FILTER_DPORT \ if (filter_dport) { \ @@ -275,7 +437,6 @@ const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, #define CONN_INFO_TRANSFER tinfo->sk = conn->sock; // 将conn->sock赋给tinfo->sk - #define PACKET_INIT_WITH_COMMON_INFO \ struct pack_t *packet; \ packet = bpf_ringbuf_reserve(&rb, sizeof(*packet), 0); \ @@ -287,37 +448,51 @@ const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, packet->ack = pkt_tuple.ack; \ packet->seq = pkt_tuple.seq; +#define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) +#define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) + +#define INIT_PACKET_TCP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = TCP} + +#define INIT_PACKET_UDP_TUPLE(sk, pkt) \ + struct packet_tuple pkt = { \ + .saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr), \ + .daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr), \ + .sport = BPF_CORE_READ(sk, __sk_common.skc_num), \ + .dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)), \ + .tran_flag = UDP} /* help macro end */ /* help functions */ // 将struct sock类型的指针转化为struct tcp_sock类型的指针 -static __always_inline -struct tcp_sock *tcp_sk(const struct sock *sk) { +static __always_inline struct tcp_sock *tcp_sk(const struct sock *sk) { return (struct tcp_sock *)sk; } // 将struct sk_buff类型的指针转化为struct udphdr类型的指针 -static __always_inline -struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { +static __always_inline struct udphdr *skb_to_udphdr(const struct sk_buff *skb) { return (struct udphdr *)(( BPF_CORE_READ(skb, head) + // 报文头部偏移 BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 } // 将struct sk_buff类型的指针转化为struct tcphdr类型的指针 -static __always_inline -struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { +static __always_inline struct tcphdr *skb_to_tcphdr(const struct sk_buff *skb) { return (struct tcphdr *)(( BPF_CORE_READ(skb, head) + // 报文头部偏移 BPF_CORE_READ(skb, transport_header))); // 传输层部分偏移 } // 将struct sk_buff类型的指针转化为struct iphdr类型的指针 -static __always_inline -struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { +static __always_inline struct iphdr *skb_to_iphdr(const struct sk_buff *skb) { return (struct iphdr *)(BPF_CORE_READ(skb, head) + BPF_CORE_READ(skb, network_header)); } // 将struct sk_buff类型的指针转化为struct ipv6hdr类型的指针 -static __always_inline -struct ipv6hdr *skb_to_ipv6hdr(const struct sk_buff *skb) { +static __always_inline struct ipv6hdr * +skb_to_ipv6hdr(const struct sk_buff *skb) { return (struct ipv6hdr *)(BPF_CORE_READ(skb, head) + BPF_CORE_READ(skb, network_header)); } @@ -328,9 +503,9 @@ static void get_ip_pkt_tuple(struct ip_packet *ipk, struct iphdr *ip) { } // 初始化packet_tuple结构指针pkt_tuple -static __always_inline -void get_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct tcphdr *tcp) { +static __always_inline void get_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct tcphdr *tcp) { pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); u16 sport = BPF_CORE_READ(tcp, source); @@ -351,9 +526,9 @@ void get_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, pkt_tuple->len = 0; } // 初始化packet_tuple结构指针pkt_tuple -static __always_inline -void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, - struct udphdr *udp) { +static __always_inline void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, + struct iphdr *ip, + struct udphdr *udp) { pkt_tuple->saddr = BPF_CORE_READ(ip, saddr); pkt_tuple->daddr = BPF_CORE_READ(ip, daddr); u16 sport = BPF_CORE_READ(udp, source); @@ -366,9 +541,9 @@ void get_udp_pkt_tuple(struct packet_tuple *pkt_tuple, struct iphdr *ip, pkt_tuple->tran_flag = UDP; // udp包 } -static __always_inline -void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, - struct ipv6hdr *ip6h, struct tcphdr *tcp) { +static __always_inline void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, + struct ipv6hdr *ip6h, + struct tcphdr *tcp) { bpf_probe_read_kernel(&pkt_tuple->saddr_v6, sizeof(pkt_tuple->saddr_v6), &ip6h->saddr.in6_u.u6_addr32); bpf_probe_read_kernel(&pkt_tuple->daddr_v6, sizeof(pkt_tuple->daddr_v6), @@ -384,8 +559,78 @@ void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, pkt_tuple->tran_flag = 1; // tcp包 } -/* help functions end */ +int getstack(void *ctx) { + int pid = bpf_get_current_pid_tgid() >> 32; + int cpu_id = bpf_get_smp_processor_id(); + struct stacktrace_event *event; + int cp; + + event = bpf_ringbuf_reserve(&trace_rb, sizeof(*event), 0); + if (!event) + return 1; + + event->pid = pid; + event->cpu_id = cpu_id; + + if (bpf_get_current_comm(event->comm, sizeof(event->comm))) + event->comm[0] = 0; + event->kstack_sz = + bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); + bpf_ringbuf_submit(event, 0); + return 0; +} +#if KERNEL_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) >= \ + KERNEL_VERSION(6, 3, 1) +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.__iov, iov_base) +#else +#define GET_USER_DATA(msg) BPF_CORE_READ(msg, msg_iter.iov, iov_base) +#endif + +/* +例子: log2(16384) =14 +16384 2进制表示 1000000000000000 +初始值: v=16384 r=0 +1、16384 > 65535 不成立,r=0; v右移动0位 +2、16384 > 255 成立,shift = 8,v右移动8位100000000,r=0|8=8 +3、256 > 15 成立,shift = 4,v右移4位10000,r=8|4=12 +4、16 > 3 成立,shift = 2,右移2位100,r=12|2=14 +5、v=4,右移1位10,r|=2>>1=1 r=14|1=14 +*/ + +static __always_inline u64 log2(u32 v) { + u32 shift, r; + //检测v是否大于0xFFFF(65535),如果是,则将r设置为16 + r = (v > 0xFFFF) << 4; + v >>= r; //右移 + shift = (v > 0xFF) << 3; + v >>= shift; + r |= shift; + shift = (v > 0xF) << 2; + v >>= shift; + r |= shift; + shift = (v > 0x3) << 1; + v >>= shift; + r |= shift; + //右移v一位并将结果累加到r中 + r |= (v >> 1); + return r; +} +/* +例子:log2l(4294967296)=32 +4294967296 2进制表示 100000000000000000000000000000000 +1、v右移32位 1 +2、log2(1)=0 计算得0+32=32 +*/ +static __always_inline u64 log2l(u64 v) { + u32 hi = v >> 32; //取v的高32位 + // 如果高32位非0,计算高32位的对数并加32 + if (hi) + return log2(hi) + 32; + else + return log2(v); +} +/* help functions end */ -#endif \ No newline at end of file +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/data/netfilter.log b/eBPF_Supermarket/Network_Subsystem/net_watcher/data/netfilter.log new file mode 100644 index 000000000..e69de29bb diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png index 831a6373c..459a2e15a 100644 Binary files a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png and b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/image/process.png differ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md index 24785b5f0..b740c065b 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/doc/implement.md @@ -153,3 +153,4 @@ printf("[X] invalid checksum: sock = %p\n", pack_info->sock); fprintf(file, "error{sock=\"%p\",seq=\"%u\",ack=\"%u\",reason=\"%s\"} 0\n", pack_info->sock, pack_info->seq, pack_info->ack, reason); ``` +fprintf(file, "connection{pid=\"%d\",sock=\"%p\",src=\"%s\",dst=\"%s\",is_server=\"%d\"", \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h index 1eb219c06..8c7883c17 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/drop.bpf.h @@ -13,12 +13,13 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf 丢包 #include "common.bpf.h" static __always_inline int __tp_kfree(struct trace_event_raw_kfree_skb *ctx) { - if(!kfree_info) + if(!drop_reason) return 0; struct sk_buff *skb=ctx->skbaddr; if (skb == NULL) // 判断是否为空 @@ -41,5 +42,7 @@ int __tp_kfree(struct trace_event_raw_kfree_skb *ctx) message->location = (long)ctx->location; message->drop_reason = ctx->reason; bpf_ringbuf_submit(message,0); + if(stack_info) + getstack(ctx); return 0; -} \ No newline at end of file +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h index 68ff87eaa..3c172c63f 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/dropreason.h @@ -1,3 +1,20 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: blown.away@qq.com +// netwatcher libbpf 丢包原因 + #ifndef __DROPREASON_H #define __DROPREASON_H const char *SKB_Drop_Reason_Strings[] = { diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h index 96c8a37ed..8ba117c49 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/icmp.bpf.h @@ -13,6 +13,7 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf icmp #include "common.bpf.h" @@ -26,7 +27,6 @@ int __icmp_time(struct sk_buff *skb) get_ip_pkt_tuple(&ipk, ip); unsigned long long time= bpf_ktime_get_ns() / 1000; bpf_map_update_elem(&icmp_time, &ipk, &time, BPF_ANY); - return 0; } @@ -46,7 +46,6 @@ int __rcvend_icmp_time(struct sk_buff *skb) unsigned long long new_time= bpf_ktime_get_ns() / 1000; unsigned long long time=new_time-*pre_time; - //bpf_printk("%d %d %d",ip->saddr,ip->daddr,time); struct icmptime *message; message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); if(!message){ @@ -76,7 +75,6 @@ int __reply_icmp_time(struct sk_buff *skb) return 0; unsigned long long new_time= bpf_ktime_get_ns() / 1000; unsigned long long time=new_time-*pre_time; - //bpf_printk("%d %d %d",ip->saddr,ip->daddr,time); struct icmptime *message; message = bpf_ringbuf_reserve(&icmp_rb, sizeof(*message), 0); if(!message){ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h new file mode 100644 index 000000000..13e802887 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h @@ -0,0 +1,78 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: blown.away@qq.com +// mysql + +#include "common.bpf.h" +#include "mysql_helper.bpf.h" +static __always_inline int __handle_mysql_start(struct pt_regs *ctx) { + // dispatch_command(THD *thd, const COM_DATA *com_data, enum + enum enum_server_command command = PT_REGS_PARM3(ctx); + union COM_DATA *com_data = (union COM_DATA *)PT_REGS_PARM2(ctx); + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + void *thd = (void *)PT_REGS_PARM1(ctx); + struct query_info info; + u32 size = 0; + char *sql; + + if (command != COM_QUERY) { + return 0; + } + + bpf_probe_read(&info.size, sizeof(info.size), &com_data->com_query.length); + bpf_probe_read_str(&sql, sizeof(sql), &com_data->com_query.query); + bpf_probe_read_str(&info.msql, sizeof(info.msql), sql); + // bpf_printk("sql1==%s size1==%lu", info.msql,info.size); + info.start_time = bpf_ktime_get_ns() / 1000; + + bpf_map_update_elem(&queries, &tid, &info, BPF_ANY); + return 0; +} + +static __always_inline int __handle_mysql_end(struct pt_regs *ctx) { + char comm[16]; + pid_t pid = bpf_get_current_pid_tgid() >> 32; + pid_t tid = bpf_get_current_pid_tgid(); + struct query_info *info = bpf_map_lookup_elem(&queries, &tid); + if (!info) { + return 0; + } + + struct mysql_query *message = + bpf_ringbuf_reserve(&mysql_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + u64 *count_ptr, count = 1; + count_ptr = bpf_map_lookup_elem(&sql_count, &tid); + if (count_ptr) { + count = *count_ptr + 1; + } + + message->count = count; + bpf_map_update_elem(&sql_count, &tid, &count, BPF_ANY); + message->duratime = bpf_ktime_get_ns() / 1000 - info->start_time; + message->pid = pid; + message->tid = tid; + bpf_get_current_comm(&message->comm, sizeof(comm)); + message->size = info->size; + bpf_probe_read_str(&message->msql, sizeof(message->msql), info->msql); + // bpf_printk("C==%d D==%lu S==%lu SQL==%s",count, + // message->duratime,message->size,message->msql); + + bpf_ringbuf_submit(message, 0); + return 0; +} diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h new file mode 100644 index 000000000..222764e1c --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql_helper.bpf.h @@ -0,0 +1,171 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: blown.away@qq.com +// +// netwatcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __MYSQL_HELPER_BPF_H +#define __MYSQL_HELPER_BPF_H + +#include "netwatcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +enum enum_server_command { + COM_SLEEP, + COM_QUIT, + COM_INIT_DB, + COM_QUERY, + COM_FIELD_LIST, + COM_CREATE_DB, + COM_DROP_DB, + COM_REFRESH, + COM_SHUTDOWN, + COM_STATISTICS, + COM_PROCESS_INFO, + COM_CONNECT, + COM_PROCESS_KILL, + COM_DEBUG, + COM_PING, + COM_TIME, + COM_DELAYED_INSERT, + COM_CHANGE_USER, + COM_BINLOG_DUMP, + COM_TABLE_DUMP, + COM_CONNECT_OUT, + COM_REGISTER_SLAVE, + COM_STMT_PREPARE, + COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, + COM_STMT_CLOSE, + COM_STMT_RESET, + COM_SET_OPTION, + COM_STMT_FETCH, + COM_DAEMON, + COM_BINLOG_DUMP_GTID, + COM_RESET_CONNECTION, + /* don't forget to update const char *command_name[] in sql_parse.cc */ + /* Must be last */ + COM_END +}; + +typedef struct st_com_init_db_data { + const char *db_name; + unsigned long length; +} COM_INIT_DB_DATA; + +#define MYSQL_SHUTDOWN_KILLABLE_CONNECT (unsigned char)(1 << 0) +#define MYSQL_SHUTDOWN_KILLABLE_TRANS (unsigned char)(1 << 1) +#define MYSQL_SHUTDOWN_KILLABLE_LOCK_TABLE (unsigned char)(1 << 2) +#define MYSQL_SHUTDOWN_KILLABLE_UPDATE (unsigned char)(1 << 3) + +#define LOCK_MODE_MASK 0xFUL +#define LOCK_TYPE_MASK 0xF0UL + +enum mysql_enum_shutdown_level { + SHUTDOWN_DEFAULT = 0, + SHUTDOWN_WAIT_CONNECTIONS = MYSQL_SHUTDOWN_KILLABLE_CONNECT, + SHUTDOWN_WAIT_TRANSACTIONS = MYSQL_SHUTDOWN_KILLABLE_TRANS, + SHUTDOWN_WAIT_UPDATES = MYSQL_SHUTDOWN_KILLABLE_UPDATE, + SHUTDOWN_WAIT_ALL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1), + SHUTDOWN_WAIT_CRITICAL_BUFFERS = (MYSQL_SHUTDOWN_KILLABLE_UPDATE << 1) + 1, + KILL_QUERY = 254, + KILL_CONNECTION = 255 +}; + +typedef struct st_com_refresh_data { + unsigned char options; +} COM_REFRESH_DATA; + +typedef struct st_com_shutdown_data { + enum mysql_enum_shutdown_level level; +} COM_SHUTDOWN_DATA; + +typedef struct st_com_kill_data { + unsigned long id; +} COM_KILL_DATA; + +typedef struct st_com_set_option_data { + unsigned int opt_command; +} COM_SET_OPTION_DATA; + +typedef struct st_com_stmt_execute_data { + unsigned long stmt_id; + unsigned long flags; + unsigned char *params; + unsigned long params_length; +} COM_STMT_EXECUTE_DATA; + +typedef struct st_com_stmt_fetch_data { + unsigned long stmt_id; + unsigned long num_rows; +} COM_STMT_FETCH_DATA; + +typedef struct st_com_stmt_send_long_data_data { + unsigned long stmt_id; + unsigned int param_number; + unsigned char *longdata; + unsigned long length; +} COM_STMT_SEND_LONG_DATA_DATA; + +typedef struct st_com_stmt_prepare_data { + const char *query; + unsigned int length; +} COM_STMT_PREPARE_DATA; + +typedef struct st_stmt_close_data { + unsigned int stmt_id; +} COM_STMT_CLOSE_DATA; + +typedef struct st_com_stmt_reset_data { + unsigned int stmt_id; +} COM_STMT_RESET_DATA; + +typedef struct st_com_query_data { + const char *query; + unsigned int length; +} COM_QUERY_DATA; + +typedef struct st_com_field_list_data { + unsigned char *table_name; + unsigned int table_name_length; + const unsigned char *query; + unsigned int query_length; +} COM_FIELD_LIST_DATA; + +union COM_DATA { + COM_INIT_DB_DATA com_init_db; + COM_REFRESH_DATA com_refresh; + COM_SHUTDOWN_DATA com_shutdown; + COM_KILL_DATA com_kill; + COM_SET_OPTION_DATA com_set_option; + COM_STMT_EXECUTE_DATA com_stmt_execute; + COM_STMT_FETCH_DATA com_stmt_fetch; + COM_STMT_SEND_LONG_DATA_DATA com_stmt_send_long_data; + COM_STMT_PREPARE_DATA com_stmt_prepare; + COM_STMT_CLOSE_DATA com_stmt_close; + COM_STMT_RESET_DATA com_stmt_reset; + COM_QUERY_DATA com_query; + COM_FIELD_LIST_DATA com_field_list; +}; + +/* help functions end */ + +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h index ad0c8cd21..5e92a025c 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netfilter.bpf.h @@ -13,14 +13,16 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf netfilter #include "common.bpf.h" static __always_inline int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int rx) { + int time =0; struct netfilter *message; - + FILTER message = bpf_ringbuf_reserve(&netfilter_rb, sizeof(*message), 0); if(!message){ return 0; @@ -34,6 +36,7 @@ int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int message->pre_routing_time = 0; message->local_out_time = 0; message->post_routing_time = 0; + message->forward_time=0; message->rx = rx; //收/发/转发方向 if(rx == 1){ @@ -44,20 +47,31 @@ int submit_nf_time(struct packet_tuple pkt_tuple, struct filtertime *tinfo, int message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - tinfo->time[e_ip_local_deliver]; message->pre_routing_time = tinfo->time[e_ip_local_deliver] - - tinfo->time[e_ip_rcv]; + tinfo->time[e_ip_rcv]; if((int)message->local_input_time < 0 || (int)message->pre_routing_time < 0){ bpf_ringbuf_discard(message, 0); return 0; } } }else{ - if(tinfo->time[e_ip_forward] && tinfo->time[e_ip_output]) + if(tinfo->time[e_ip_local_deliver_finish] && + tinfo->time[e_ip_local_deliver] && + tinfo->time[e_ip_rcv] && + tinfo->time[e_ip_forward] && + tinfo->time[e_ip_output]) { - message->forward_time = tinfo->time[e_ip_output] - tinfo->time[e_ip_forward]; - if((int)message->forward_time < 0){ + message->local_input_time = tinfo->time[e_ip_local_deliver_finish] - + tinfo->time[e_ip_local_deliver]; + message->pre_routing_time = tinfo->time[e_ip_local_deliver] - + tinfo->time[e_ip_rcv]; + + u64 forward_time = tinfo->time[e_ip_output] - tinfo->time[e_ip_forward]; + + if((int)forward_time < 0){ bpf_ringbuf_discard(message, 0); return 0; } + message->forward_time = forward_time; message->rx = 2; } if(tinfo->time[e_ip_output] && @@ -102,9 +116,7 @@ int store_nf_time(struct sk_buff *skb, int hook) return 0; } } - tinfo->time[hook] = bpf_ktime_get_ns() / 1000; - if(hook == e_ip_local_deliver_finish){ submit_nf_time(tinfo->init, tinfo, 1); bpf_map_delete_elem(&netfilter_time, &skb); diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c index a41f72e13..46834bfc4 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c @@ -22,23 +22,26 @@ #include "icmp.bpf.h" - #include "tcp.bpf.h" #include "packet.bpf.h" #include "udp.bpf.h" +#include "mysql.bpf.h" + +#include "redis.bpf.h" + #include "drop.bpf.h" -//accecpt an TCP connection +// accecpt an TCP connection SEC("kretprobe/inet_csk_accept") int BPF_KRETPROBE(inet_csk_accept_exit, // 接受tcp连接 struct sock *sk) { // this func return a newsk return __inet_csk_accept(sk); } -//connect an TCP connection +// connect an TCP connection SEC("kprobe/tcp_v4_connect") // 进入tcp_v4_connect int BPF_KPROBE(tcp_v4_connect, const struct sock *sk) { return __tcp_v4_connect(sk); @@ -59,10 +62,10 @@ int BPF_KRETPROBE(tcp_v6_connect_exit, int ret) { return __tcp_v6_connect_exit(ret); } -// erase CLOSED TCP connection +// erase CLOSED TCP connection SEC("kprobe/tcp_set_state") int BPF_KPROBE(tcp_set_state, struct sock *sk, int state) { - return __tcp_set_state(sk,state); + return __tcp_set_state(sk, state); } /*! @@ -103,9 +106,7 @@ int BPF_KPROBE(eth_type_trans, struct sk_buff *skb) { /** in only ipv4 */ SEC("kprobe/ip_rcv_core") // 跟踪记录ipv4数据包在内核中的处理时间 -int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { - return __ip_rcv_core(skb); -} +int BPF_KPROBE(ip_rcv_core, struct sk_buff *skb) { return __ip_rcv_core(skb); } /** in only ipv6 */ SEC("kprobe/ip6_rcv_core") int BPF_KPROBE(ip6_rcv_core, struct sk_buff *skb) { @@ -114,24 +115,20 @@ int BPF_KPROBE(ip6_rcv_core, struct sk_buff *skb) { /**in only ipv4 */ // 接收数据包 SEC("kprobe/tcp_v4_rcv") // 记录数据包在tcpv4层时间戳 -int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { - return __tcp_v4_rcv(skb); -} +int BPF_KPROBE(tcp_v4_rcv, struct sk_buff *skb) { return __tcp_v4_rcv(skb); } /** in only ipv6 */ SEC("kprobe/tcp_v6_rcv") // 接收tcpv6数据包 -int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { - return __tcp_v6_rcv(skb); -} +int BPF_KPROBE(tcp_v6_rcv, struct sk_buff *skb) { return __tcp_v6_rcv(skb); } // v4 & v6 do_rcv to get sk and other info SEC("kprobe/tcp_v4_do_rcv") int BPF_KPROBE(tcp_v4_do_rcv, struct sock *sk, struct sk_buff *skb) { - return __tcp_v4_do_rcv(sk,skb); + return __tcp_v4_do_rcv(sk, skb); } SEC("kprobe/tcp_v6_do_rcv") // tcp层包时间 int BPF_KPROBE(tcp_v6_do_rcv, struct sock *sk, struct sk_buff *skb) { - return __tcp_v6_do_rcv(sk,skb); + return __tcp_v6_do_rcv(sk, skb); } /** in ipv4 && ipv6 */ @@ -140,23 +137,21 @@ int BPF_KPROBE(skb_copy_datagram_iter, struct sk_buff *skb) { return __skb_copy_datagram_iter(skb); } - // receive error packet /* TCP invalid seq error */ // 根据传入的数据包提取关键信息(如IP和TCP头部信息),并将这些信息与其他元数据(如套接字信息和错误标识)一同存储到BPF // ring buffer中 SEC("kprobe/tcp_validate_incoming") // 验证传入数据包的序列号 int BPF_KPROBE(tcp_validate_incoming, struct sock *sk, struct sk_buff *skb) { - return __tcp_validate_incoming(sk,skb); + return __tcp_validate_incoming(sk, skb); } // 跟踪网络数据包检测tcp检验和错误 /* TCP invalid checksum error*/ SEC("kretprobe/__skb_checksum_complete") int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { - return skb_checksum_complete(ret); + return skb_checksum_complete(ret); } - /**** send path ****/ /*! * \brief: 获取数据包进入TCP层时刻的时间戳, 发送tcp层起始点 @@ -164,7 +159,7 @@ int BPF_KRETPROBE(__skb_checksum_complete_exit, int ret) { */ SEC("kprobe/tcp_sendmsg") // 跟踪tcp发送包信息 int BPF_KPROBE(tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) { - return __tcp_sendmsg(sk,msg,size); + return __tcp_sendmsg(sk, msg, size); } /*! @@ -174,7 +169,7 @@ tcp)获取ip段的数据 out only ipv4 */ SEC("kprobe/ip_queue_xmit") int BPF_KPROBE(ip_queue_xmit, struct sock *sk, struct sk_buff *skb) { - return __ip_queue_xmit(sk,skb); + return __ip_queue_xmit(sk, skb); }; /*! @@ -184,7 +179,7 @@ tcp)获取ip段的数据 out only ipv6 */ SEC("kprobe/inet6_csk_xmit") int BPF_KPROBE(inet6_csk_xmit, struct sock *sk, struct sk_buff *skb) { - return __inet6_csk_xmit(sk,skb); + return __inet6_csk_xmit(sk, skb); }; /*! @@ -205,8 +200,7 @@ int BPF_KPROBE(dev_hard_start_xmit, struct sk_buff *skb) { return __dev_hard_start_xmit(skb); }; - -//retrans +// retrans /* 在进入快速恢复阶段时,不管是基于Reno或者SACK的快速恢复, * 还是RACK触发的快速恢复,都将使用函数tcp_enter_recovery进入 * TCP_CA_Recovery拥塞阶段。 @@ -222,92 +216,126 @@ int BPF_KPROBE(tcp_enter_recovery, struct sock *sk) { * 在报文的重传定时器到期时,在tcp_retransmit_timer函数中,进入TCP_CA_Loss拥塞状态。 */ SEC("kprobe/tcp_enter_loss") -int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { - return __tcp_enter_loss(sk); -} +int BPF_KPROBE(tcp_enter_loss, struct sock *sk) { return __tcp_enter_loss(sk); } /* udp */ SEC("kprobe/udp_rcv") int BPF_KPROBE(udp_rcv, struct sk_buff *skb) { - return __udp_rcv(skb); + if (udp_info) + return __udp_rcv(skb); + else if (dns_info) + return __dns_rcv(skb); + else + return 0; } SEC("kprobe/__udp_enqueue_schedule_skb") -int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk,struct sk_buff *skb) { - return udp_enqueue_schedule_skb(sk,skb); +int BPF_KPROBE(__udp_enqueue_schedule_skb, struct sock *sk, + struct sk_buff *skb) { + return udp_enqueue_schedule_skb(sk, skb); } SEC("kprobe/udp_send_skb") int BPF_KPROBE(udp_send_skb, struct sk_buff *skb) { - return __udp_send_skb(skb); + if (udp_info) + return __udp_send_skb(skb); + else if (dns_info) + return __dns_send(skb); + else + return 0; } SEC("kprobe/ip_send_skb") -int BPF_KPROBE(ip_send_skb,struct net *net, struct sk_buff *skb) { +int BPF_KPROBE(ip_send_skb, struct net *net, struct sk_buff *skb) { return __ip_send_skb(skb); } -//netfilter +// netfilter SEC("kprobe/ip_rcv") -int BPF_KPROBE(ip_rcv, struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, - struct net_device *orig_dev) { +int BPF_KPROBE(ip_rcv, struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) { return store_nf_time(skb, e_ip_rcv); } SEC("kprobe/ip_local_deliver") -int BPF_KPROBE(ip_local_deliver,struct sk_buff *skb) { +int BPF_KPROBE(ip_local_deliver, struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_deliver); } SEC("kprobe/ip_local_deliver_finish") -int BPF_KPROBE(ip_local_deliver_finish,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_local_deliver_finish, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_deliver_finish); } SEC("kprobe/ip_local_out") -int BPF_KPROBE(ip_local_out, struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_local_out, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_local_out); } SEC("kprobe/ip_output") -int BPF_KPROBE(ip_output,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(ip_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_output); } SEC("kprobe/__ip_finish_output") -int BPF_KPROBE(__ip_finish_output,struct net *net, struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(__ip_finish_output, struct net *net, struct sock *sk, + struct sk_buff *skb) { return store_nf_time(skb, e_ip_finish_output); } SEC("kprobe/ip_forward") -int BPF_KPROBE(ip_forward,struct sk_buff *skb) { +int BPF_KPROBE(ip_forward, struct sk_buff *skb) { return store_nf_time(skb, e_ip_forward); } -//drop +// drop SEC("tp/skb/kfree_skb") -int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { - return __tp_kfree(ctx); -} +int tp_kfree(struct trace_event_raw_kfree_skb *ctx) { return __tp_kfree(ctx); } SEC("kprobe/icmp_rcv") -int BPF_KPROBE(icmp_rcv,struct sk_buff *skb) { - return __icmp_time(skb); -} +int BPF_KPROBE(icmp_rcv, struct sk_buff *skb) { return __icmp_time(skb); } SEC("kprobe/__sock_queue_rcv_skb") -int BPF_KPROBE(__sock_queue_rcv_skb,struct sock *sk, struct sk_buff *skb) { +int BPF_KPROBE(__sock_queue_rcv_skb, struct sock *sk, struct sk_buff *skb) { return __rcvend_icmp_time(skb); } SEC("kprobe/icmp_reply") -int BPF_KPROBE(icmp_reply,struct icmp_bxm *icmp_param, struct sk_buff *skb) { +int BPF_KPROBE(icmp_reply, struct icmp_bxm *icmp_param, struct sk_buff *skb) { return __reply_icmp_time(skb); } -//tcpstate -SEC("tracepoint/sock/inet_sock_set_state") -int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) -{ +// mysql +SEC("uprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__start) { return __handle_mysql_start(ctx); } +SEC("uretprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") +int BPF_KPROBE(query__end) { return __handle_mysql_end(ctx); } + +SEC("uprobe/processCommand") +int BPF_KPROBE(query__start_redis_process) { return __handle_redis_start(ctx); } +SEC("uretprobe/call") +int BPF_KPROBE(query__end_redis) { return __handle_redis_end(ctx); } +// rtt +SEC("kprobe/tcp_rcv_established") +int BPF_KPROBE(tcp_rcv_established, struct sock *sk, struct sk_buff *skb) { + return __tcp_rcv_established(sk, skb); +} + +// tcpstate +SEC("tracepoint/sock/inet_sock_set_state") +int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { return __handle_set_state(ctx); } +// RST +SEC("tracepoint/tcp/tcp_send_reset") +int handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + return __handle_send_reset(ctx); +} + +SEC("tracepoint/tcp/tcp_receive_reset") +int handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + return __handle_receive_reset(ctx); +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c index c5fca5b48..263459c7c 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c @@ -17,6 +17,7 @@ // netwatcher libbpf 用户态代码 #include "netwatcher.h" +#include "dropreason.h" #include "netwatcher.skel.h" #include #include @@ -28,8 +29,9 @@ #include #include #include +#include #include -#include "dropreason.h" +#include static volatile bool exiting = false; @@ -40,9 +42,12 @@ static char udp_file_path[1024]; static int sport = 0, dport = 0; // for filter static int all_conn = 0, err_packet = 0, extra_conn_info = 0, layer_time = 0, - http_info = 0, retrans_info = 0, udp_info = 0,net_filter = 0,kfree_info = 0,addr_to_func=0 ,icmp_info = 0 , tcp_info = 0; // flag + http_info = 0, retrans_info = 0, udp_info = 0, net_filter = 0, + drop_reason = 0, addr_to_func = 0, icmp_info = 0, tcp_info = 0, + time_load = 0, dns_info = 0, stack_info = 0, mysql_info = 0, + redis_info = 0, count_info = 0, rtt_info = 0, rst_info = 0; // flag -static const char* tcp_states[] = { +static const char *tcp_states[] = { [1] = "ESTABLISHED", [2] = "SYN_SENT", [3] = "SYN_RECV", [4] = "FIN_WAIT1", [5] = "FIN_WAIT2", [6] = "TIME_WAIT", [7] = "CLOSE", [8] = "CLOSE_WAIT", [9] = "LAST_ACK", @@ -51,7 +56,6 @@ static const char* tcp_states[] = { }; static const char argp_program_doc[] = "Watch tcp/ip in network subsystem \n"; - static const struct argp_option opts[] = { {"all", 'a', 0, 0, "set to trace CLOSED connection"}, {"err", 'e', 0, 0, "set to trace TCP error packets"}, @@ -62,11 +66,25 @@ static const struct argp_option opts[] = { {"sport", 's', "SPORT", 0, "trace this source port only"}, {"dport", 'd', "DPORT", 0, "trace this destination port only"}, {"udp", 'u', 0, 0, "trace the udp message"}, - {"net_filter",'n',0,0,"trace ipv4 packget filter "}, - {"kfree_info",'k',0,0,"trace kfree "}, - {"addr_to_func",'T',0,0,"translation addr to func and offset"}, + {"net_filter", 'n', 0, 0, "trace ipv4 packget filter "}, + {"drop_reason", 'k', 0, 0, "trace kfree "}, + {"addr_to_func", 'F', 0, 0, "translation addr to func and offset"}, {"icmptime", 'I', 0, 0, "set to trace layer time of icmp"}, {"tcpstate", 'S', 0, 0, "set to trace tcpstate"}, + {"timeload", 'L', 0, 0, "analysis time load"}, + {"dns", 'D', 0, 0, + "set to trace dns information info include Id 事务ID、Flags 标志字段、Qd " + "问题部分计数、An 应答记录计数、Ns 授权记录计数、Ar 附加记录计数、Qr " + "域名、rx 收发包 、Qc请求数、Sc响应数"}, + {"stack", 'A', 0, 0, "set to trace of stack "}, + {"mysql", 'M', 0, 0, + "set to trace mysql information info include Pid 进程id、Comm " + "进程名、Size sql语句字节大小、Sql 语句"}, + {"redis", 'R', 0, 0}, + {"count", 'C', "NUMBER", 0, + "specify the time to count the number of requests"}, + {"rtt", 'T', 0, 0, "set to trace rtt"}, + {"rst_counters", 'U', 0, 0, "set to trace rst"}, {}}; static error_t parse_arg(int key, char *arg, struct argp_state *state) { @@ -103,9 +121,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { net_filter = 1; break; case 'k': - kfree_info = 1; + drop_reason = 1; break; - case 'T': + case 'F': addr_to_func = 1; break; case 'I': @@ -114,6 +132,30 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'S': tcp_info = 1; break; + case 'L': + time_load = 1; + break; + case 'D': + dns_info = 1; + break; + case 'A': + stack_info = 1; + break; + case 'M': + mysql_info = 1; + break; + case 'R': + redis_info = 1; + break; + case 'T': + rtt_info = 1; + break; + case 'U': + rst_info = 1; + break; + case 'C': + count_info = strtoul(arg, &end, 10); + break; default: return ARGP_ERR_UNKNOWN; } @@ -126,16 +168,171 @@ static const struct argp argp = { .doc = argp_program_doc, }; -struct SymbolEntry{ +struct SymbolEntry { unsigned long addr; char name[30]; }; + +enum MonitorMode { + MODE_UDP, + MODE_NET_FILTER, + MODE_DROP_REASON, + MODE_ICMP, + MODE_TCP, + MODE_DNS, + MODE_MYSQL, + MODE_REDIS, + MODE_RTT, + MODE_RST, + MODE_DEFAULT +}; + +enum MonitorMode get_monitor_mode() { + if (udp_info) { + return MODE_UDP; + } else if (net_filter) { + return MODE_NET_FILTER; + } else if (drop_reason) { + return MODE_DROP_REASON; + } else if (icmp_info) { + return MODE_ICMP; + } else if (tcp_info) { + return MODE_TCP; + } else if (dns_info) { + return MODE_DNS; + } else if (mysql_info) { + return MODE_MYSQL; + } else if (redis_info) { + return MODE_REDIS; + } else if (rtt_info) { + return MODE_RTT; + } else if (rst_info) { + return MODE_RST; + } else { + return MODE_DEFAULT; + } +} +#define LOGO_STRING \ + " " \ + " __ __ __ " \ + " \n" \ + " /\\ \\__ /\\ \\__ /\\ \\ " \ + " \n" \ + " ___ __\\ \\ _\\ __ __ __ __ \\ \\ _\\ ___\\ \\ \\___ " \ + " __ _ __ \n" \ + "/ _ \\ / __ \\ \\ \\/ /\\ \\/\\ \\/\\ \\ / __ \\ \\ \\ \\/ / ___\\ " \ + "\\ _ \\ / __ \\/\\ __\\ \n" \ + "/\\ \\/\\ \\/\\ __/\\ \\ \\_\\ \\ \\_/ \\_/ \\/\\ \\_\\ \\_\\ \\ " \ + "\\_/\\ \\__/\\ \\ \\ \\ \\/\\ __/\\ \\ \\/ \n" \ + "\\ \\_\\ \\_\\ \\____\\ \\__\\ \\_______ / /\\ \\__/\\ \\_\\ \\__\\ " \ + "\\____/\\ \\_\\ \\_\\ \\____ \\ \\_\\ \n" \ + " \\/_/\\/_/\\/____/ \\/__/ \\/__//__ / \\/_/ \\/_/\\/__/\\/____/ " \ + "\\/_/\\/_/\\/____/ \\/_/ \n\n" + +// 通过lolcat命令彩色处理 +void print_logo() { + char *logo = LOGO_STRING; + int i = 0; + FILE *lolcat_pipe = popen("/usr/games/lolcat", "w"); + if (lolcat_pipe == NULL) { + printf("Error: Unable to execute lolcat command.\n"); + return; + } + // 像lolcat管道逐个字符写入字符串 + while (logo[i] != '\0') { + fputc(logo[i], lolcat_pipe); + fflush(lolcat_pipe); // 刷新管道,确保字符被立即发送给lolcat + usleep(150); + i++; + } + + pclose(lolcat_pipe); +} +static char binary_path[64] = ""; +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, -1, binary_path, 0, &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do { \ + if (!skel->links.prog_name) { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) struct SymbolEntry symbols[300000]; int num_symbols = 0; -struct SymbolEntry findfunc(unsigned long int addr) -{ - int low = 0, high = num_symbols - 1; - int result = -1; +// 定义快表 +#define CACHEMAXSIZE 5 +struct SymbolEntry cache[CACHEMAXSIZE]; +int cache_size = 0; +// LRU算法查找函数 +struct SymbolEntry find_in_cache(unsigned long int addr) { + // 查找地址是否在快表中 + for (int i = 0; i < cache_size; i++) { + if (cache[i].addr == addr) { + // 更新访问时间 + struct SymbolEntry temp = cache[i]; + // 将访问的元素移动到快表的最前面,即最近使用的位置 + for (int j = i; j > 0; j--) { + cache[j] = cache[j - 1]; + } + cache[0] = temp; + return temp; + } + } + // 如果地址不在快表中,则返回空 + struct SymbolEntry empty_entry; + empty_entry.addr = 0; + return empty_entry; +} + +// 将新的符号条目加入快表 +void add_to_cache(struct SymbolEntry entry) { + // 如果快表已满,则移除最久未使用的条目 + if (cache_size == CACHEMAXSIZE) { + for (int i = cache_size - 1; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + } else { + // 否则,直接加入快表 + for (int i = cache_size; i > 0; i--) { + cache[i] = cache[i - 1]; + } + cache[0] = entry; + cache_size++; + } +} + +struct SymbolEntry findfunc(unsigned long int addr) { + // 先在快表中查找 + struct SymbolEntry entry = find_in_cache(addr); + if (entry.addr != 0) { + return entry; + } + unsigned long long low = 0, high = num_symbols - 1; + unsigned long long result = -1; while (low <= high) { int mid = low + (high - low) / 2; @@ -146,11 +343,10 @@ struct SymbolEntry findfunc(unsigned long int addr) high = mid - 1; } } - + add_to_cache(symbols[result]); return symbols[result]; }; -void readallsym() -{ +void readallsym() { FILE *file = fopen("/proc/kallsyms", "r"); if (!file) { perror("Error opening file"); @@ -161,7 +357,7 @@ void readallsym() unsigned long addr; char type, name[30]; int ret = sscanf(line, "%lx %c %s", &addr, &type, name); - if (ret == 3) { + if (ret == 3) { symbols[num_symbols].addr = addr; strncpy(symbols[num_symbols].name, name, 30); num_symbols++; @@ -170,6 +366,377 @@ void readallsym() fclose(file); } +/* + 指数加权移动平均算法(EWMA) + 1.使用指数加权移动平均算法(EWMA)来计算每层的指数加权移动平均值, + 公式EWMA_new = alpha * new_value + (1 - alpha) * old_ewma ,alpha + 指数加权系数,表示新数据点的权重,new_value 当前时延,old_ewma + 旧的指数加权移动平均值 + 2.根据当前时延和指数加权移动平均值*预先设定的粒度阈值(GRANULARITY)对比,来判断时延是否异常 + 3.可以快速适应数据的变化,并能够有效地检测异常时延 + +*/ +struct LayerDelayInfo { + float delay; // 时延数据 + int layer_index; // 层索引 +}; +#define GRANULARITY 3 +#define ALPHA 0.2 // 衰减因子 +#define MAXTIME 10000 +#define SLOW_QUERY_THRESHOLD 10000 // +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_RESET "\x1b[0m" + +// 全局变量用于存储每层的移动平均值 +float ewma_values[NUM_LAYERS] = {0}; +int count[NUM_LAYERS] = {0}; + +// 指数加权移动平均算法 +float calculate_ewma(float new_value, float old_ewma) { + return ALPHA * new_value + (1 - ALPHA) * old_ewma; +} + +// 收集时延数据并检测异常 +int process_delay(float layer_delay, int layer_index) { + + if (layer_delay == 0) + return 0; + count[layer_index]++; + if (ewma_values[layer_index] == 0) { + ewma_values[layer_index] = layer_delay; + return 0; + } + // 计算阈值,指数加权移动平均值乘以粒度因子 + ewma_values[layer_index] = + calculate_ewma(layer_delay, ewma_values[layer_index]); + float threshold = ewma_values[layer_index] * GRANULARITY; + if (count[layer_index] > 30) { + // 判断当前时延是否超过阈值 + // printf("%d %d:%f %f + // ",layer_index,count[layer_index]++,threshold,layer_delay); + if (layer_delay > threshold) { // 异常 + return 1; + } else { + return 0; + } + } + return 0; +} + +static void set_rodata_flags(struct netwatcher_bpf *skel) { + skel->rodata->filter_dport = dport; + skel->rodata->filter_sport = sport; + skel->rodata->all_conn = all_conn; + skel->rodata->err_packet = err_packet; + skel->rodata->extra_conn_info = extra_conn_info; + skel->rodata->layer_time = layer_time; + skel->rodata->http_info = http_info; + skel->rodata->retrans_info = retrans_info; + skel->rodata->udp_info = udp_info; + skel->rodata->net_filter = net_filter; + skel->rodata->drop_reason = drop_reason; + skel->rodata->tcp_info = tcp_info; + skel->rodata->icmp_info = icmp_info; + skel->rodata->dns_info = dns_info; + skel->rodata->stack_info = stack_info; + skel->rodata->mysql_info = mysql_info; + skel->rodata->redis_info = redis_info; + skel->rodata->rtt_info = rtt_info; + skel->rodata->rst_info = rst_info; +} +static void set_disable_load(struct netwatcher_bpf *skel) { + + bpf_program__set_autoload(skel->progs.inet_csk_accept_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_connect_exit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_set_state, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.eth_type_trans, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip6_rcv_core, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v4_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_v6_do_rcv, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.skb_copy_datagram_iter, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_validate_incoming, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.__skb_checksum_complete_exit, + err_packet ? true : false); + bpf_program__set_autoload(skel->progs.tcp_sendmsg, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.ip_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.inet6_csk_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.__dev_queue_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.dev_hard_start_xmit, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.tcp_enter_recovery, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_enter_loss, + retrans_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_rcv, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.__udp_enqueue_schedule_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.udp_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_send_skb, + udp_info || dns_info ? true : false); + bpf_program__set_autoload(skel->progs.ip_rcv, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_deliver_finish, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_local_out, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_output, net_filter ? true : false); + bpf_program__set_autoload(skel->progs.__ip_finish_output, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.ip_forward, + net_filter ? true : false); + bpf_program__set_autoload(skel->progs.tp_kfree, drop_reason ? true : false); + bpf_program__set_autoload(skel->progs.icmp_rcv, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.__sock_queue_rcv_skb, + icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.icmp_reply, icmp_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_set_state, + tcp_info ? true : false); + bpf_program__set_autoload(skel->progs.query__start, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.query__end, + mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.query__end_redis, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.query__start_redis_process, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.tcp_rcv_established, + (all_conn || err_packet || extra_conn_info || + retrans_info || layer_time || http_info || + rtt_info) + ? true + : false); + bpf_program__set_autoload(skel->progs.handle_send_reset, + rst_info ? true : false); + bpf_program__set_autoload(skel->progs.handle_receive_reset, + rst_info ? true : false); +} + +static void print_header(enum MonitorMode mode) { + switch (mode) { + case MODE_UDP: + printf("===============================================================" + "UDP " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", + "Sprot", "Dprot", "udp_time/μs", "RX/direction", "len/byte"); + break; + case MODE_NET_FILTER: + printf("===============================================================" + "===NETFILTER " + "INFORMATION====================================================" + "=======\n"); + printf("%-20s %-20s %-12s %-12s %-8s %-8s %-7s %-8s %-8s %-8s\n", + "Saddr", "Daddr", "Sprot", "Dprot", "PreRT/μs", "L_IN/μs", + "FW/μs", "PostRT/μs", "L_OUT/μs", "RX/direction"); + break; + case MODE_DROP_REASON: + printf("===============================================================" + "DROP " + "INFORMATION====================================================" + "====\n"); + printf("%-13s %-17s %-17s %-10s %-10s %-9s %-33s %-30s\n", "Time", + "Saddr", "Daddr", "Sprot", "Dprot", "prot", "addr", "reason"); + break; + case MODE_ICMP: + printf("=================================================ICMP " + "INFORMATION==============================================\n"); + printf("%-20s %-20s %-20s %-20s\n", "Saddr", "Daddr", "icmp_time/μs", + "RX/direction"); + break; + case MODE_TCP: + printf("===============================================================" + "TCP STATE " + "INFORMATION====================================================" + "====\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Saddr", "Daddr", + "Sport", "Dport", "oldstate", "newstate", "time/μs"); + break; + case MODE_DNS: + printf("===============================================================" + "====================DNS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-12s %-12s %-5s %-5s %-5s %-5s %-47s %-10s %-10s " + "%-10s \n", + "Saddr", "Daddr", "Id", "Flags", "Qd", "An", "Ns", "Ar", "Qr", + "Qc", "Sc", "RX/direction"); + break; + case MODE_MYSQL: + printf("===============================================================" + "====================MYSQL " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-40s %-20s %-20s \n", "Pid", "Tid", + "Comm", "Size", "Sql", "Duration/μs", "Request"); + break; + case MODE_REDIS: + printf("===============================================================" + "====================REDIS " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", "Size", + "Redis", "duration/μs"); + break; + case MODE_RTT: + printf("===============================================================" + "====================RTT " + "INFORMATION====================================================" + "============================\n"); + break; + case MODE_RST: + printf("===============================================================" + "====================RST " + "INFORMATION====================================================" + "============================\n"); + printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "Pid", "Comm", + "Saddr", "Daddr", "Sport", "Dport", "Time"); + break; + case MODE_DEFAULT: + printf("===============================================================" + "=INFORMATION===================================================" + "======================\n"); + printf("%-22s %-20s %-8s %-20s %-8s %-15s %-15s %-15s %-15s %-15s \n", + "SOCK", "Saddr", "Sport", "Daddr", "Dport", "MAC_TIME/μs", + "IP_TIME/μs", "TRAN_TIME/μs", "RX/direction", "HTTP"); + break; + } +} + +static void open_log_files() { + FILE *connect_file = fopen(connects_file_path, "w+"); + if (connect_file == NULL) { + fprintf(stderr, "Failed to open connect.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(connect_file); + + FILE *err_file = fopen(err_file_path, "w+"); + if (err_file == NULL) { + fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(err_file); + + FILE *packet_file = fopen(packets_file_path, "w+"); + if (packet_file == NULL) { + fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(packet_file); + + FILE *udp_file = fopen(udp_file_path, "w+"); + if (udp_file == NULL) { + fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); + exit(EXIT_FAILURE); + } + fclose(udp_file); +} + static void sig_handler(int signo) { exiting = true; } static void bytes_to_str(char *str, unsigned long long num) { @@ -212,7 +779,9 @@ static int print_conns(struct netwatcher_bpf *skel) { char s_ip_port_str[INET6_ADDRSTRLEN + 6]; char d_ip_port_str[INET6_ADDRSTRLEN + 6]; - + if ((d.saddr & 0x0000FFFF) == 0x0000007F || + (d.daddr & 0x0000FFFF) == 0x0000007F) + return 0; if (d.family == AF_INET) { sprintf(s_ip_port_str, "%s:%d", inet_ntop(AF_INET, &d.saddr, s_str, sizeof(s_str)), @@ -274,9 +843,29 @@ static int print_conns(struct netwatcher_bpf *skel) { } static int print_packet(void *ctx, void *packet_info, size_t size) { - if (udp_info || net_filter || kfree_info || icmp_info || tcp_info) + if (udp_info || net_filter || drop_reason || icmp_info || tcp_info || + dns_info || mysql_info || redis_info || rtt_info) return 0; const struct pack_t *pack_info = packet_info; + if (pack_info->mac_time > MAXTIME || pack_info->ip_time > MAXTIME || + pack_info->tran_time > MAXTIME) { + return 0; + } + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + if ((daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + if (dport) + if (pack_info->dport != dport) + return 0; + + if (sport) + if (pack_info->sport != sport) + return 0; + if (pack_info->err) { FILE *file = fopen(err_file_path, "a"); char reason[20]; @@ -314,38 +903,64 @@ static int print_packet(void *ctx, void *packet_info, size_t size) { sprintf(http_data, "-"); } if (layer_time) { - printf("%-22p %-10u %-10u %-10llu %-10llu %-10llu %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, - pack_info->mac_time, pack_info->ip_time, + printf("%-22p %-20s %-8d %-20s %-8d %-14llu %-14llu %-14llu %-15d " + "%-16s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, pack_info->rx, http_data); fprintf( file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%llu\",ip_time=\"%llu\",tran_time=\"%llu\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, pack_info->mac_time, pack_info->ip_time, pack_info->tran_time, http_data, pack_info->rx); } else { - printf("%-22p %-10u %-10u %-10d %-10d %-10d %-5d %s\n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, - pack_info->rx, http_data); + printf("%-22p %-20s %-8d %-20s %-8d %-10d %-10d %-10d %-5d %-10s", + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, 0, 0, 0, pack_info->rx, http_data); fprintf(file, - "packet{sock=\"%p\",seq=\"%u\",ack=\"%u\"," + "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\"," + "dport=\"%d\",seq=\"%u\",ack=\"%u\"," "mac_time=\"%d\",ip_time=\"%d\",tran_time=\"%d\",http_" "info=\"%s\",rx=\"%d\"} \n", - pack_info->sock, pack_info->seq, pack_info->ack, 0, 0, 0, + pack_info->sock, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + pack_info->sport, + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->dport, pack_info->seq, pack_info->ack, 0, 0, 0, http_data, pack_info->rx); } fclose(file); } + if (time_load) { + int mac = process_delay(pack_info->mac_time, 0); + int ip = process_delay(pack_info->ip_time, 1); + int tran = process_delay(pack_info->tran_time, 2); + if (mac || ip || tran) { + printf("%-15s", "abnormal data"); + } + } + printf("\n"); return 0; } static int print_udp(void *ctx, void *packet_info, size_t size) { if (!udp_info) return 0; - FILE *file = fopen(udp_file_path, "a+");//追加 - if (file == NULL) { + FILE *file = fopen(udp_file_path, "a+"); // 追加 + if (file == NULL) { fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); return 0; } @@ -354,112 +969,387 @@ static int print_udp(void *ctx, void *packet_info, size_t size) { const struct udp_message *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d\n", + if (pack_info->tran_time > MAXTIME || (daddr & 0x0000FFFF) == 0x0000007F || + (saddr & 0x0000FFFF) == 0x0000007F) + return 0; + printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - fprintf( - file, + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); + fprintf(file, "packet{saddr=\"%s\",daddr=\"%s\",sport=\"%u\"," "dport=\"%u\",udp_time=\"%llu\",rx=\"%d\",len=\"%d\"} \n", inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, - pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len); - + pack_info->dport, pack_info->tran_time, pack_info->rx, + pack_info->len); fclose(file); + if (time_load) { + int flag = process_delay(pack_info->tran_time, 3); + if (flag) + printf("%-15s", "abnormal data"); + } + printf("\n"); + return 0; } static int print_netfilter(void *ctx, void *packet_info, size_t size) { - if(!net_filter) + if (!net_filter) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct netfilter *pack_info = packet_info; + if (pack_info->local_input_time > MAXTIME || + pack_info->forward_time > MAXTIME || + pack_info->local_out_time > MAXTIME || + pack_info->post_routing_time > MAXTIME || + pack_info->pre_routing_time > MAXTIME) + return 0; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-20d %-20d %-20lld %-20lld %-20lld %-20lld %-20lld %-20d\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->sport,pack_info->dport, - pack_info->pre_routing_time, - pack_info->local_input_time, - pack_info->forward_time, - pack_info->post_routing_time, - pack_info->local_out_time, - pack_info->rx); - + // if ((daddr & 0x0000FFFF) == 0x0000007F || + // (saddr & 0x0000FFFF) == 0x0000007F) + // return 0; + printf("%-20s %-20s %-12d %-12d %-8lld %-8lld% -8lld %-8lld %-8lld %-8d", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, pack_info->pre_routing_time, + pack_info->local_input_time, pack_info->forward_time, + pack_info->post_routing_time, pack_info->local_out_time, + pack_info->rx); + // 定义一个数组用于存储需要检测的时延数据和对应的层索引 + struct LayerDelayInfo layer_delay_infos[] = { + {pack_info->pre_routing_time, 4}, + {pack_info->local_input_time, 5}, + {pack_info->forward_time, 6}, + {pack_info->post_routing_time, 7}, + {pack_info->local_out_time, 8}}; + if (time_load) { + // 循环遍历数组 + for (int i = 0; i < 5; i++) { + // 数组的总字节数除以第一个元素的字节数得到元素的个数 + float delay = layer_delay_infos[i].delay; + int layer_net = layer_delay_infos[i].layer_index; + int flag = process_delay(delay, layer_net); + if (flag) + printf("%-15s", "abnormal data"); + } + } + printf("\n"); + return 0; } - static int print_tcpstate(void *ctx, void *packet_info, size_t size) { - if(!tcp_info) + if (!tcp_info) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct tcp_state *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; printf("%-20s %-20s %-20d %-20d %-20s %-20s %-20lld\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->sport,pack_info->dport,tcp_states[pack_info->oldstate],tcp_states[pack_info->newstate],pack_info->time); + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, tcp_states[pack_info->oldstate], + tcp_states[pack_info->newstate], pack_info->time); return 0; } static int print_kfree(void *ctx, void *packet_info, size_t size) { - if(!kfree_info) + if (!drop_reason) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct reasonissue *pack_info = packet_info; unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - if(saddr == 0 && daddr ==0 ) - { + if (saddr == 0 && daddr == 0) { return 0; } char prot[6]; - if(pack_info->protocol==2048) - { + if (pack_info->protocol == 2048) { strcpy(prot, "ipv4"); - } - else if(pack_info->protocol==34525) - { + } else if (pack_info->protocol == 34525) { strcpy(prot, "ipv6"); - } - else { + } else { // 其他协议 strcpy(prot, "other"); } - printf("%-20s %-20s %-10u %-10u %-10s", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport,pack_info->dport,prot); - if(!addr_to_func) - printf("%-20lx",pack_info->location); + time_t now = time(NULL); + struct tm *localTime = localtime(&now); + printf("%02d:%02d:%02d %-17s %-17s %-10u %-10u %-10s", + localTime->tm_hour, localTime->tm_min, localTime->tm_sec, + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport, + pack_info->dport, prot); + if (!addr_to_func) + printf("%-34lx", pack_info->location); else { - struct SymbolEntry data= findfunc(pack_info->location); - printf("%s+0x%-10lx",data.name,pack_info->location-data.addr); + struct SymbolEntry data = findfunc(pack_info->location); + char result[40]; + sprintf(result, "%s+0x%lx", data.name, pack_info->location - data.addr); + printf("%-34s", result); } printf("%s\n", SKB_Drop_Reason_Strings[pack_info->drop_reason]); return 0; } static int print_icmptime(void *ctx, void *packet_info, size_t size) { - if(!icmp_info) + if (!icmp_info) return 0; char d_str[INET_ADDRSTRLEN]; - char s_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; const struct icmptime *pack_info = packet_info; + if (pack_info->icmp_tran_time > MAXTIME) { + return 0; + } unsigned int saddr = pack_info->saddr; unsigned int daddr = pack_info->daddr; - printf("%-20s %-20s %-10lld %-10d\n", - inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), - inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), - pack_info->icmp_tran_time, - pack_info->flag); + printf("%-20s %-20s %-20lld %-20d", + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)), + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), + pack_info->icmp_tran_time, pack_info->flag); + if (time_load) { + int icmp_data = process_delay(pack_info->icmp_tran_time, 9); + if (icmp_data) { + printf("%-15s\n", "abnormal data"); + } + } + printf("\n"); + return 0; +} +#define MAX_EVENTS 1024 + +static __u64 rst_count = 0; +static struct reset_event_t event_store[MAX_EVENTS]; +static int event_count = 0; + +static int print_rst(void *ctx, void *packet_info, size_t size) { + struct reset_event_t *event = packet_info; + + // 将事件存储到全局存储中 + if (event_count < MAX_EVENTS) { + memcpy(&event_store[event_count], event, sizeof(struct reset_event_t)); + event_count++; + } + + rst_count++; + return 0; +} +void print_stored_events() { + char s_str[INET_ADDRSTRLEN]; + char d_str[INET_ADDRSTRLEN]; + + for (int i = 0; i < event_count; i++) { + struct reset_event_t *event = &event_store[i]; + unsigned int saddr = event->saddr; + unsigned int daddr = event->daddr; + + if (event->family == AF_INET) { + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + printf("%-20llu %-20s %-20s %-20s %-20u %-20u %-20llu\n", + (unsigned long long)event->pid, event->comm, s_str, d_str, + event->sport, event->dport, + (unsigned long long)event->timestamp); + } else if (event->family == AF_INET6) { + char saddr_v6[INET6_ADDRSTRLEN]; + char daddr_v6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &event->saddr_v6, saddr_v6, sizeof(saddr_v6)); + inet_ntop(AF_INET6, &event->daddr_v6, daddr_v6, sizeof(daddr_v6)); + printf("%-10llu %-16s %-16s %-16s %-8u %-8u %-20llu\n", + (unsigned long long)event->pid, event->comm, saddr_v6, + daddr_v6, event->sport, event->dport, + (unsigned long long)event->timestamp); + } + } +} +// 从DNS数据包中提取并打印域名 +static void print_domain_name(const unsigned char *data, char *output) { + const unsigned char *next = data; + int pos = 0, first = 1; + // 循环到尾部,标志0 + while (*next != 0) { + if (!first) { + output[pos++] = '.'; // 在每个段之前添加点号 + } else { + first = 0; // 第一个段后清除标志 + } + int len = *next++; // 下一个段长度 + + for (int i = 0; i < len; ++i) { + output[pos++] = *next++; + } + } + output[pos] = '\0'; // 确保字符串正确结束 +} + +static int print_dns(void *ctx, void *packet_info, size_t size) { + if (!packet_info) + return 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + const struct dns_information *pack_info = + (const struct dns_information *)packet_info; // 强制类型转换 + unsigned int saddr = pack_info->saddr; + unsigned int daddr = pack_info->daddr; + char domain_name[256]; // 用于存储输出的域名 + + inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)); + + print_domain_name((const unsigned char *)pack_info->data, domain_name); + if (pack_info->daddr == 0) { + return 0; + } + printf("%-20s %-20s %-#12x %-#12x %-5x %-5x %-5x %-5x %-47s %-10d %-10d " + "%-10d \n", + s_str, d_str, pack_info->id, pack_info->flags, pack_info->qdcount, + pack_info->ancount, pack_info->nscount, pack_info->arcount, + domain_name, pack_info->request_count, pack_info->response_count, + pack_info->rx); + return 0; +} + +static int print_mysql(void *ctx, void *packet_info, size_t size) { + if (!mysql_info) { + return 0; + } + + const mysql_query *pack_info = packet_info; + printf("%-20d %-20d %-20s %-20u %-41s", pack_info->pid, pack_info->tid, + pack_info->comm, pack_info->size, pack_info->msql); + // 当 duratime 大于 count_info 时,才打印 duratime + if (pack_info->duratime > count_info) { + printf("%-21llu", pack_info->duratime); + } else { + printf("%-21s", ""); + } + printf("%-20d\n", pack_info->count); + return 0; +} + +static int print_redis(void *ctx, void *packet_info, size_t size) { + const struct redis_query *pack_info = packet_info; + int i = 0; + char redis[64]; + for (i = 0; i < pack_info->argc; i++) { + strcat(redis, pack_info->redis[i]); + strcat(redis, " "); + } + printf("%-20d %-20s %-20d %-20s %-21llu\n", pack_info->pid, pack_info->comm, + pack_info->argc, redis, pack_info->duratime); + strcpy(redis, ""); + return 0; +} +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) { + return vfprintf(stderr, format, args); +} +static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) { + int i; + printf("-----------------------------------\n"); + for (i = 1; i < stack_sz; i++) { + if (addr_to_func) { + struct SymbolEntry data = findfunc(stack[i]); + char result[40]; + sprintf(result, "%s+0x%llx", data.name, stack[i] - data.addr); + printf("%-10d [<%016llx>]=%s\n", i, stack[i], result); + } else { + printf("%-10d [<%016llx>]\n", i, stack[i]); + } + } + printf("-----------------------------------\n"); +} +static int print_trace(void *_ctx, void *data, size_t size) { + struct stacktrace_event *event = data; + + if (event->kstack_sz <= 0 && event->ustack_sz <= 0) + return 1; + + printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, + event->cpu_id); + + if (event->kstack_sz > 0) { + printf("Kernel:\n"); + show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); + } else { + printf("No Kernel Stack\n"); + } + printf("\n"); + return 0; +} + +static int print_rtt(void *ctx, void *data, size_t size) { + if (!rtt_info) + return 0; + struct RTT *rtt_tuple = data; + unsigned long long total_latency = 0; + unsigned long long total_count = 0; + char d_str[INET_ADDRSTRLEN]; + char s_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &rtt_tuple->saddr, s_str, sizeof(s_str)); + inet_ntop(AF_INET, &rtt_tuple->daddr, d_str, sizeof(d_str)); + if ((rtt_tuple->saddr & 0x0000FFFF) == 0x0000007F || + (rtt_tuple->daddr & 0x0000FFFF) == 0x0000007F || + rtt_tuple->saddr == htonl(0xC0A83C01) || + rtt_tuple->daddr == htonl(0xC0A83C01)) { + return 0; // 如果匹配任一过滤条件,放弃处理这些数据包 + } + // 打印源地址和目的地址 + printf("Source Address: %s\n", s_str); + printf("Destination Address: %s\n", d_str); + // 更新总延迟和计数 + total_latency += rtt_tuple->latency; + total_count += rtt_tuple->cnt; + + // 打印总延迟和平均RTT + double average_rtt = + (total_count > 0) ? (double)total_latency / total_count : 0; + printf("Total Latency: %llu μs\n", total_latency); + printf("Average RTT: %.2f ms\n", average_rtt / 1000.0); + + // 计算和打印RTT分布图 + printf(" usecs : count distribution\n"); + int bucket_size = 1; + for (int i = 0; i < MAX_SLOTS; i++) { + int start_range = bucket_size == 1 ? 0 : bucket_size; + int end_range = bucket_size * 2 - 1; + printf("%8d -> %-8d : %-8llu |", start_range, end_range, + rtt_tuple->slots[i]); + int bar_length = + rtt_tuple->slots[i] / + 10; //计算该延迟范围内的计数对应的直方图条形长度,每个'*' + //表示 10 个计数 + for (int j = 0; j < bar_length; j++) { + printf("*"); + } + printf("\n"); + bucket_size *= 2; //以对数方式扩展 + } + + printf("===============================================================\n"); + + return 0; +} + +int attach_uprobe_mysql(struct netwatcher_bpf *skel) { + + ATTACH_UPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__start); + ATTACH_URETPROBE_CHECKED( + skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, + query__end); + return 0; +} +int attach_uprobe_redis(struct netwatcher_bpf *skel) { + ATTACH_UPROBE_CHECKED(skel, call, query__end_redis); + ATTACH_UPROBE_CHECKED(skel, processCommand, query__start_redis_process); return 0; } int main(int argc, char **argv) { @@ -474,13 +1364,19 @@ int main(int argc, char **argv) { strcat(connects_file_path, "data/connects.log"); strcat(err_file_path, "data/err.log"); strcat(packets_file_path, "data/packets.log"); - strcat(udp_file_path,"data/udp.log"); + strcat(udp_file_path, "data/udp.log"); struct ring_buffer *rb = NULL; struct ring_buffer *udp_rb = NULL; struct ring_buffer *netfilter_rb = NULL; struct ring_buffer *kfree_rb = NULL; struct ring_buffer *icmp_rb = NULL; struct ring_buffer *tcp_rb = NULL; + struct ring_buffer *dns_rb = NULL; + struct ring_buffer *trace_rb = NULL; + struct ring_buffer *mysql_rb = NULL; + struct ring_buffer *redis_rb = NULL; + struct ring_buffer *rtt_rb = NULL; + struct ring_buffer *events = NULL; struct netwatcher_bpf *skel; int err; /* Parse command line arguments */ @@ -490,6 +1386,7 @@ int main(int argc, char **argv) { return err; } + libbpf_set_print(libbpf_print_fn); /* Cleaner handling of Ctrl-C */ signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); @@ -500,23 +1397,11 @@ int main(int argc, char **argv) { fprintf(stderr, "Failed to open BPF skeleton\n"); return 1; } - /* Parameterize BPF code */ - skel->rodata->filter_dport = dport; - skel->rodata->filter_sport = sport; - skel->rodata->all_conn = all_conn; - skel->rodata->err_packet = err_packet; - skel->rodata->extra_conn_info = extra_conn_info; - skel->rodata->layer_time = layer_time; - skel->rodata->http_info = http_info; - skel->rodata->retrans_info = retrans_info; - skel->rodata->udp_info = udp_info; - skel->rodata->net_filter = net_filter; - skel->rodata->kfree_info = kfree_info; - skel->rodata->tcp_info = tcp_info; - skel->rodata->icmp_info = icmp_info; + set_rodata_flags(skel); + set_disable_load(skel); - if(addr_to_func) + if (addr_to_func) readallsym(); err = netwatcher_bpf__load(skel); @@ -526,70 +1411,110 @@ int main(int argc, char **argv) { } /* Attach tracepoint handler */ - err = netwatcher_bpf__attach(skel); - if (err) { - fprintf(stderr, "Failed to attach BPF skeleton\n"); - goto cleanup; - } - if (udp_info) { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr", "sprot", - "dprot", "udp_time","rx","len"); - } - else if(net_filter) - { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr","dprot", "sprot", - "PreRT","L_IN","FW","PostRT","L_OUT","rx"); - } - else if(kfree_info) - { - printf("%-20s %-20s %-10s %-10s %-9s %-24s %-25s\n", "saddr", "daddr","sprot", "dprot","prot","addr","reason"); - } - else if(icmp_info) - { - printf("%-20s %-20s %-10s %-10s\n", "saddr", "daddr","time","flag"); - } - else if(tcp_info) - { - printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "saddr", "daddr","sport","dport","oldstate","newstate","time"); - } - else{ - printf("%-22s %-10s %-10s %-10s %-10s %-10s %-5s %s\n", "SOCK", "SEQ", - "ACK", "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP"); + if (mysql_info) { + strcpy(binary_path, "/usr/sbin/mysqld"); + err = attach_uprobe_mysql(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else if (redis_info) { + strcpy(binary_path, "/usr/bin/redis-server"); + err = attach_uprobe_redis(skel); + if (err) { + fprintf(stderr, "failed to attach uprobes\n"); + + goto cleanup; + } + } else { + err = netwatcher_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } } - udp_rb =ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); + enum MonitorMode mode = get_monitor_mode(); + + // print_logo(); + + print_header(mode); + + udp_rb = + ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL); if (!udp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(udp)\n"); goto cleanup; } - netfilter_rb =ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), print_netfilter, NULL, NULL); + netfilter_rb = ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), + print_netfilter, NULL, NULL); if (!netfilter_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(netfilter)\n"); goto cleanup; } - kfree_rb =ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, NULL, NULL); + kfree_rb = ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, + NULL, NULL); if (!kfree_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(kfree)\n"); goto cleanup; } - icmp_rb =ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, NULL, NULL); + icmp_rb = ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, + NULL, NULL); if (!icmp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(icmp)\n"); goto cleanup; } - tcp_rb =ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, NULL, NULL); + tcp_rb = ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, + NULL, NULL); if (!tcp_rb) { err = -1; fprintf(stderr, "Failed to create ring buffer(tcp)\n"); goto cleanup; } - icmp_rb =ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, NULL, NULL); - if (!icmp_rb) { + dns_rb = + ring_buffer__new(bpf_map__fd(skel->maps.dns_rb), print_dns, NULL, NULL); + if (!dns_rb) { err = -1; - fprintf(stderr, "Failed to create ring buffer(icmp)\n"); + fprintf(stderr, "Failed to create ring buffer(dns)\n"); + goto cleanup; + } + trace_rb = ring_buffer__new(bpf_map__fd(skel->maps.trace_rb), print_trace, + NULL, NULL); + if (!trace_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + mysql_rb = ring_buffer__new(bpf_map__fd(skel->maps.mysql_rb), print_mysql, + NULL, NULL); + if (!mysql_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + redis_rb = ring_buffer__new(bpf_map__fd(skel->maps.redis_rb), print_redis, + NULL, NULL); + if (!redis_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(trace)\n"); + goto cleanup; + } + rtt_rb = + ring_buffer__new(bpf_map__fd(skel->maps.rtt_rb), print_rtt, NULL, NULL); + if (!rtt_rb) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(connect_rb)\n"); + goto cleanup; + } + events = + ring_buffer__new(bpf_map__fd(skel->maps.events), print_rst, NULL, NULL); + if (!events) { + err = -1; + fprintf(stderr, "Failed to create ring buffer(rst_rb)\n"); goto cleanup; } /* Set up ring buffer polling */ @@ -599,25 +1524,10 @@ int main(int argc, char **argv) { fprintf(stderr, "Failed to create ring buffer(packet)\n"); goto cleanup; } - FILE *err_file = fopen(err_file_path, "w+"); - if (err_file == NULL) { - fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(err_file); - FILE *packet_file = fopen(packets_file_path, "w+"); - if (packet_file == NULL) { - fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(packet_file); - FILE *udp_file = fopen(udp_file_path, "w+"); - if (udp_file == NULL) { - fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno)); - return 0; - } - fclose(udp_file); + open_log_files(); + struct timeval start, end; + gettimeofday(&start, NULL); /* Process events */ while (!exiting) { err = ring_buffer__poll(rb, 100 /* timeout, ms */); @@ -626,6 +1536,12 @@ int main(int argc, char **argv) { err = ring_buffer__poll(kfree_rb, 100 /* timeout, ms */); err = ring_buffer__poll(icmp_rb, 100 /* timeout, ms */); err = ring_buffer__poll(tcp_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(dns_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(trace_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(mysql_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(redis_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(rtt_rb, 100 /* timeout, ms */); + err = ring_buffer__poll(events, 100 /* timeout, ms */); print_conns(skel); sleep(1); /* Ctrl-C will cause -EINTR */ @@ -637,9 +1553,20 @@ int main(int argc, char **argv) { printf("Error polling perf buffer: %d\n", err); break; } + + gettimeofday(&end, NULL); + if ((end.tv_sec - start.tv_sec) >= 5) { + print_stored_events(); + printf("Total RSTs in the last 5 seconds: %llu\n\n", rst_count); + + // 重置计数器和事件存储 + rst_count = 0; + event_count = 0; + gettimeofday(&start, NULL); + } } cleanup: netwatcher_bpf__destroy(skel); return err < 0 ? -err : 0; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h index c0955a298..e7559671d 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h @@ -26,6 +26,7 @@ typedef unsigned long long u64; #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define MAX_SLOTS 27 #ifndef AF_INET #define AF_INET 2 @@ -42,11 +43,11 @@ typedef unsigned long long u64; #define UDP 2 struct conn_t { - void *sock; // 此tcp连接的 socket 地址 - int pid; // pid - u64 ptid; // 此tcp连接的 ptid(ebpf def) - char comm[MAX_COMM]; // 此tcp连接的 command - u16 family; // 10(AF_INET6):v6 or 2(AF_INET):v4 + void *sock; // 此tcp连接的 socket 地址 + int pid; // pid + u64 ptid; // 此tcp连接的 ptid(ebpf def) + char comm[MAX_COMM]; // 此tcp连接的 command + u16 family; // 10(AF_INET6):v6 or 2(AF_INET):v4 unsigned __int128 saddr_v6; unsigned __int128 daddr_v6; u32 saddr; @@ -55,10 +56,10 @@ struct conn_t { u16 dport; int is_server; // 1: 被动连接 0: 主动连接 - u32 tcp_backlog; // backlog - u32 max_tcp_backlog; // max_backlog - u64 bytes_acked; // 已确认的字节数 - u64 bytes_received; // 已接收的字节数 + u32 tcp_backlog; // backlog + u32 max_tcp_backlog; // max_backlog + u64 bytes_acked; // 已确认的字节数 + u64 bytes_received; // 已接收的字节数 u32 snd_cwnd; // 拥塞窗口大小 u32 rcv_wnd; // 接收窗口大小 @@ -69,25 +70,32 @@ struct conn_t { u32 fastRe; // 快速重传次数 u32 timeout; // 超时重传次数 - u32 srtt; // 平滑往返时间 + u32 srtt; // 平滑往返时间 u64 init_timestamp; // 建立连接时间戳 u64 duration; // 连接已建立时长 }; #define MAX_PACKET 1000 #define MAX_HTTP_HEADER 256 +#define NUM_LAYERS 5 struct pack_t { - int err; // no err(0) invalid seq(1) invalid checksum(2) + int err; // no err(0) invalid seq(1) invalid checksum(2) u64 mac_time; // mac layer 处理时间(us) u64 ip_time; // ip layer 处理时间(us) // u64 tcp_time; // tcp layer 处理时间(us) - u64 tran_time; // tcp layer 处理时间(us) - u32 seq; // the seq num of packet - u32 ack; // the ack num of packet + u64 tran_time; // tcp layer 处理时间(us) + u32 seq; // the seq num of packet + u32 ack; // the ack num of packet u8 data[MAX_HTTP_HEADER]; // 用户层数据 - const void *sock; // 此包tcp连接的 socket 指针 - int rx; // rx packet(1) or tx packet(0) + const void *sock; // 此包tcp连接的 socket 指针 + int rx; // rx packet(1) or tx packet(0) + u32 saddr; + u32 daddr; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u16 sport; + u16 dport; }; struct udp_message { @@ -96,11 +104,10 @@ struct udp_message { u16 sport; u16 dport; u64 tran_time; - int rx; + int rx; int len; }; -struct netfilter -{ +struct netfilter { u32 saddr; u32 daddr; u16 sport; @@ -112,31 +119,101 @@ struct netfilter u64 post_routing_time; u32 rx; }; -struct reasonissue -{ +struct reasonissue { u32 saddr; u32 daddr; u16 sport; u16 dport; long location; u16 protocol; - int drop_reason; + int drop_reason; }; -struct icmptime{ +struct icmptime { unsigned int saddr; unsigned int daddr; unsigned long long icmp_tran_time; - unsigned int flag; //0 send 1 rcv + unsigned int flag; // 0 send 1 rcv }; struct tcp_state { - u32 saddr; - u32 daddr; + u32 saddr; + u32 daddr; u16 sport; - u16 dport; - int oldstate; - int newstate; + u16 dport; + int oldstate; + int newstate; u64 time; }; +struct dns_information { + u32 saddr; + u32 daddr; + u16 id; + u16 flags; + u16 qdcount; + u16 ancount; + u16 nscount; + u16 arcount; + char data[64]; + int rx; + int response_count; + int request_count; +}; +#define MAX_STACK_DEPTH 128 +typedef u64 stack_trace_t[MAX_STACK_DEPTH]; +struct stacktrace_event { + u32 pid; + u32 cpu_id; + char comm[16]; + signed int kstack_sz; + signed int ustack_sz; + stack_trace_t kstack; + stack_trace_t ustack; +}; + +typedef struct mysql_query { + int pid; + int tid; + char comm[20]; + u32 size; + char msql[256]; + u64 duratime; + int count; +} mysql_query; + +struct redis_query { + int pid; + int tid; + char comm[20]; + u32 size; + char redis[4][8]; + u64 duratime; + int count; + u64 begin_time; + int argc; +}; + +struct RTT { + u32 saddr; + u32 daddr; + u64 slots[64]; + u64 latency; + u64 cnt; +}; + +struct reset_event_t { + int pid; + char comm[16]; + u16 family; + unsigned __int128 saddr_v6; + unsigned __int128 daddr_v6; + u32 saddr; + u32 daddr; + u16 sport; + u16 dport; + u8 direction; // 0 for send, 1 for receive + u64 count; + u64 timestamp; + u8 state; +}; #endif /* __NETWATCHER_H */ \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h index e3adf22a4..d515ef54b 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/packet.bpf.h @@ -276,6 +276,10 @@ int __skb_copy_datagram_iter(struct sk_buff *skb) // bpf_printk("rx enter app layer.\n"); PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; if (layer_time) { packet->mac_time = tinfo->ip_time - tinfo->mac_time; @@ -389,7 +393,7 @@ int __tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) // TX HTTP info if (http_info) { - u8 *user_data = BPF_CORE_READ(msg, msg_iter.iov, iov_base); + u8 *user_data = GET_USER_DATA(msg); tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init( ×tamps, &pkt_tuple, &zero); if (tinfo == NULL) { @@ -480,16 +484,13 @@ int dev_queue_xmit(struct sk_buff *skb) eth, h_proto); // 以太网头部协议字段该字段存储的是以太网帧所封装的上层协议类型 struct tcphdr *tcp = skb_to_tcphdr(skb); - struct packet_tuple pkt_tuple = {0}; +struct packet_tuple pkt_tuple = {0}; struct ktime_info *tinfo; if (protocol == __bpf_ntohs(ETH_P_IP)) { /** ipv4 */ struct iphdr *ip = skb_to_iphdr(skb); get_pkt_tuple(&pkt_tuple, ip, tcp); - // FILTER_DPORT - // FILTER_SPORT - if ((tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple)) == NULL) { return 0; } @@ -546,22 +547,24 @@ int __dev_hard_start_xmit(struct sk_buff *skb) return 0; } PACKET_INIT_WITH_COMMON_INFO + packet->saddr = pkt_tuple.saddr; + packet->daddr = pkt_tuple.daddr; + packet->sport = pkt_tuple.sport; + packet->dport = pkt_tuple.dport; // 记录各层的时间差值 if (layer_time) { packet->tran_time = tinfo->ip_time - tinfo->tran_time; packet->ip_time = tinfo->mac_time - tinfo->ip_time; packet->mac_time =tinfo->qdisc_time -tinfo->mac_time; // 队列纪律层,处于网络协议栈最底层,负责实际数据传输与接收 } - packet->rx = 0; // 发送一个数据包 // TX HTTP Info if (http_info) { bpf_probe_read_str(packet->data, sizeof(packet->data), tinfo->data); - bpf_printk("%s", packet->data); + // bpf_printk("%s", packet->data); } bpf_ringbuf_submit(packet, 0); return 0; } - diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h new file mode 100644 index 000000000..c3e319ce3 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h @@ -0,0 +1,68 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: blown.away@qq.com +// redis + +#include "common.bpf.h" +#include "redis_helper.bpf.h" +#define MAXEPOLL 5 +static __always_inline int __handle_redis_start(struct pt_regs *ctx) { + struct client *cli = (struct client *)PT_REGS_PARM1(ctx); + struct redis_query start={}; + void *ptr; + char name[100]=""; + int argv_len; + bpf_probe_read(&start.argc, sizeof(start.argc), &cli->argc); + robj **arg0; + robj *arg1; + bpf_probe_read(&arg0, sizeof(arg0), &cli->argv); + bpf_probe_read(&arg1, sizeof(arg1), &arg0[0]); + for(int i=0;iptr); + bpf_probe_read_str(&start.redis[i], sizeof(start.redis[i]), ptr); + } + pid_t pid = bpf_get_current_pid_tgid() >> 32; + u64 start_time = bpf_ktime_get_ns() / 1000; + start.begin_time=start_time; + bpf_map_update_elem(&redis_time, &pid, &start, BPF_ANY); + return 0; +} + +static __always_inline int __handle_redis_end(struct pt_regs *ctx) { + pid_t pid = bpf_get_current_pid_tgid() >> 32; + struct redis_query *start; + u64 end_time = bpf_ktime_get_ns() / 1000; + start = bpf_map_lookup_elem(&redis_time, &pid); + if (!start) { + return 0; + } + struct redis_query *message = bpf_ringbuf_reserve(&redis_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->pid = pid; + message->argc = start->argc; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + for(int i=0;iargc&&iredis[i], sizeof(message->redis[i]), start->redis[i]); + } + bpf_probe_read_str(&message->redis, sizeof(start->redis), start->redis); + message->duratime = end_time - start->begin_time; + bpf_ringbuf_submit(message, 0); + return 0; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h new file mode 100644 index 000000000..21f2031f7 --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis_helper.bpf.h @@ -0,0 +1,55 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: blown.away@qq.com +// +// netwatcher libbpf 内核<->用户 传递信息相关结构体 + +#ifndef __REDIS_HELPER_BPF_H +#define __REDIS_HELPER_BPF_H + +#include "netwatcher.h" +#include "vmlinux.h" +#include +#include +#include +#include +#include +#include + +#define LRU_BITS 24 +typedef struct redisObject { + unsigned type:4; + unsigned encoding:4; + unsigned lru:24; + int refcount; + void *ptr; +} robj; + +struct client { + u64 id; /* Client incremental unique ID. */ + u64 conn; + int resp; /* RESP protocol version. Can be 2 or 3. */ + u64 db; /* Pointer to currently SELECTed DB. */ + robj *name; /* As set by CLIENT SETNAME. */ + char* querybuf; /* Buffer we use to accumulate client queries. */ + unsigned long qb_pos; /* The position we have read in querybuf. */ + char* pending_querybuf; + unsigned long querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */ + int argc; /* Num of arguments of current command. */ + robj **argv; /* Arguments of current command. */ + unsigned long argv_len_sum; /* Size of argv array (may be more than argc) */ +}; + +#endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h index 0d0bb5f70..abcc05698 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/tcp.bpf.h @@ -13,12 +13,11 @@ // limitations under the License. // // author: blown.away@qq.com +// netwatcher libbpf tcp #include "common.bpf.h" -static __always_inline -int __inet_csk_accept(struct sock *sk) -{ +static __always_inline int __inet_csk_accept(struct sock *sk) { if (sk == NULL) { // newsk is null // bpf_printk("inet_accept_ret err: newsk is null\n"); return 0; @@ -28,11 +27,11 @@ int __inet_csk_accept(struct sock *sk) CONN_INIT // 初始化conn_t结构中基本信息 conn.is_server = 1; - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 - - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + FILTER_SPORT // 过滤源端口 + + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 // 更新/插入conns_info中的键值对 int err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); @@ -44,9 +43,7 @@ int __inet_csk_accept(struct sock *sk) return 0; } -static __always_inline -int __tcp_v4_connect(const struct sock *sk) -{ +static __always_inline int __tcp_v4_connect(const struct sock *sk) { u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid int err = bpf_map_update_elem(&sock_stores, &ptid, &sk, BPF_ANY); // 更新/插入sock_stores中的键值对 @@ -57,9 +54,7 @@ int __tcp_v4_connect(const struct sock *sk) return 0; } -static __always_inline -int __tcp_v4_connect_exit(int ret) -{ +static __always_inline int __tcp_v4_connect_exit(int ret) { u64 ptid = bpf_get_current_pid_tgid(); // 获取当前pid struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); // 获得sock_stores中ptid对应的*sk 用skp指向 @@ -74,13 +69,13 @@ int __tcp_v4_connect_exit(int ret) } struct sock *sk = *skp; CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 + conn.is_server = 0; // 主动连接 - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 + FILTER_SPORT // 过滤源端口 - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); // 更新conns_info中sk对应的conn @@ -90,9 +85,7 @@ int __tcp_v4_connect_exit(int ret) return 0; } -static __always_inline -int __tcp_v6_connect(const struct sock *sk) -{ +static __always_inline int __tcp_v6_connect(const struct sock *sk) { u64 pid = bpf_get_current_pid_tgid(); // 获取pid int err = bpf_map_update_elem(&sock_stores, &pid, &sk, BPF_ANY); // 更新sock_stores中对应pid对应的sk @@ -102,9 +95,7 @@ int __tcp_v6_connect(const struct sock *sk) return 0; } -static __always_inline -int __tcp_v6_connect_exit(int ret) -{ +static __always_inline int __tcp_v6_connect_exit(int ret) { u64 ptid = bpf_get_current_pid_tgid(); // 获取pid struct sock **skp = bpf_map_lookup_elem(&sock_stores, &ptid); // 获得sock_stores中ptid对应的*sk 用skp指向 @@ -118,15 +109,15 @@ int __tcp_v6_connect_exit(int ret) struct sock *sk = *skp; CONN_INIT // 初始化conn_t结构中基本信息 - conn.is_server = 0; // 主动连接 + conn.is_server = 0; // 主动连接 - FILTER_DPORT // 过滤目标端口 + FILTER_DPORT // 过滤目标端口 - FILTER_SPORT // 过滤源端口 + FILTER_SPORT // 过滤源端口 - CONN_ADD_ADDRESS // conn_t结构中增加地址信息 + CONN_ADD_ADDRESS // conn_t结构中增加地址信息 - long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); + long err = bpf_map_update_elem(&conns_info, &sk, &conn, BPF_ANY); // 更新conns_info中sk对应的conn if (err) { return 0; @@ -134,9 +125,7 @@ int __tcp_v6_connect_exit(int ret) // bpf_printk("tcp_v4_connect_exit update sk: %p.\n", sk); return 0; } -static __always_inline -int __tcp_set_state( struct sock *sk, int state) -{ +static __always_inline int __tcp_set_state(struct sock *sk, int state) { if (all_conn) { return 0; } @@ -151,9 +140,8 @@ int __tcp_set_state( struct sock *sk, int state) } // receive error packet -static __always_inline -int __tcp_validate_incoming(struct sock *sk, struct sk_buff *skb) -{ +static __always_inline int __tcp_validate_incoming(struct sock *sk, + struct sk_buff *skb) { if (!err_packet) { return 0; } @@ -209,9 +197,7 @@ int __tcp_validate_incoming(struct sock *sk, struct sk_buff *skb) bpf_ringbuf_submit(packet, 0); return 0; } -static __always_inline -int skb_checksum_complete(int ret) -{ +static __always_inline int skb_checksum_complete(int ret) { if (!err_packet) { return 0; } @@ -242,9 +228,7 @@ int skb_checksum_complete(int ret) return 0; } ////retrans packet -static __always_inline -int __tcp_enter_recovery(struct sock *sk) -{ +static __always_inline int __tcp_enter_recovery(struct sock *sk) { if (!retrans_info) { return 0; } @@ -258,9 +242,7 @@ int __tcp_enter_recovery(struct sock *sk) return 0; } -static __always_inline -int __tcp_enter_loss(struct sock *sk) -{ +static __always_inline int __tcp_enter_loss(struct sock *sk) { if (!retrans_info) { return 0; } @@ -271,9 +253,8 @@ int __tcp_enter_loss(struct sock *sk) conn->timeout += 1; return 0; } -static __always_inline -int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) -{ +static __always_inline int +__handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) { if (ctx->protocol != IPPROTO_TCP) return 0; @@ -281,31 +262,30 @@ int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) __u64 *before_time, new_time, time; before_time = bpf_map_lookup_elem(&tcp_state, &sk); - new_time= bpf_ktime_get_ns(); + new_time = bpf_ktime_get_ns(); if (!before_time) time = 0; else time = (new_time - *before_time) / 1000; - struct tcpstate tcpstate = {}; tcpstate.oldstate = ctx->oldstate; tcpstate.newstate = ctx->newstate; tcpstate.family = ctx->family; tcpstate.sport = ctx->sport; tcpstate.dport = ctx->dport; - bpf_probe_read_kernel(&tcpstate.saddr, sizeof(tcpstate.saddr), &sk->__sk_common.skc_rcv_saddr); - bpf_probe_read_kernel(&tcpstate.daddr, sizeof(tcpstate.daddr), &sk->__sk_common.skc_daddr); + bpf_probe_read_kernel(&tcpstate.saddr, sizeof(tcpstate.saddr), + &sk->__sk_common.skc_rcv_saddr); + bpf_probe_read_kernel(&tcpstate.daddr, sizeof(tcpstate.daddr), + &sk->__sk_common.skc_daddr); tcpstate.time = time; - //bpf_printk(" %d %d %d %d %d %d ",tcpstate.saddr,tcpstate.sport,tcpstate.daddr,tcpstate.dport, - // tcpstate.oldstate,tcpstate.newstate,tcpstate.time); if (ctx->newstate == TCP_CLOSE) bpf_map_delete_elem(&tcp_state, &sk); else bpf_map_update_elem(&tcp_state, &sk, &new_time, BPF_ANY); - struct tcp_state *message; + struct tcp_state *message; message = bpf_ringbuf_reserve(&tcp_rb, sizeof(*message), 0); - if(!message){ + if (!message) { return 0; } message->saddr = tcpstate.saddr; @@ -314,8 +294,151 @@ int __handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) message->dport = tcpstate.dport; message->oldstate = tcpstate.oldstate; message->newstate = tcpstate.newstate; - message->time = tcpstate.time; + message->time = tcpstate.time; + bpf_printk("Dport:%d time:%d", tcpstate.dport, tcpstate.time); bpf_ringbuf_submit(message, 0); return 0; +} + +static __always_inline int __tcp_rcv_established(struct sock *sk, + struct sk_buff *skb) { + const struct inet_sock *inet = (struct inet_sock *)(sk); + struct tcp_sock *ts; + struct hist *histp; + u64 slot; + u32 srtt; + struct iphdr *ip = skb_to_iphdr(skb); + struct tcphdr *tcp = skb_to_tcphdr(skb); + struct packet_tuple pkt_tuple = {0}; + get_pkt_tuple(&pkt_tuple, ip, tcp); + // INIT_PACKET_TCP_TUPLE(sk, pkt_tuple); + struct ip_packet key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) { + // 初始化值 + struct hist zero = {}; + bpf_map_update_elem(&hists, &key, &zero, BPF_ANY); + histp = bpf_map_lookup_elem(&hists, &key); + if (!histp) + return 0; // 如果仍然查找失败,则返回 + } + ts = (struct tcp_sock *)(sk); + + // 读取并处理SRTT(平滑往返时间) + srtt = BPF_CORE_READ(ts, srtt_us) >> 3; + // 计算对数值,根据得到的结果决定数据应该归入直方图的哪个槽位 + slot = log2l(srtt); + if (slot >= MAX_SLOTS) + slot = MAX_SLOTS - 1; // 确保槽位置不超过最大槽数 + + // 更新 + __sync_fetch_and_add(&histp->slots[slot], 1); + __sync_fetch_and_add(&histp->latency, srtt); + __sync_fetch_and_add(&histp->cnt, 1); + + struct RTT *message; + message = bpf_ringbuf_reserve(&rtt_rb, sizeof(*message), 0); + if (!message) { + return 0; + } + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + // bpf_printk("Saddr:%u Daddr:%u", pkt_tuple.saddr, pkt_tuple.daddr); + bpf_probe_read_kernel(message->slots, sizeof(message->slots), histp->slots); + message->latency = histp->latency; + message->cnt = histp->cnt; + // bpf_printk("Updating histogram: latency %llu, cnt %llu, slot %llu, + // slot_count %llu", histp->latency, histp->cnt, slot, histp->slots[slot]); + bpf_ringbuf_submit(message, 0); + return 0; +} + +static __always_inline int ret(void *ctx, u8 direction, u16 sport, + u16 dport) { + struct reset_event_t *message = + bpf_ringbuf_reserve(&events, sizeof(*message), 0); + if (!message) + return 0; + + message->pid = bpf_get_current_pid_tgid() >> 32; + bpf_get_current_comm(&message->comm, sizeof(message->comm)); + + struct sock *sk = (struct sock *)ctx; + message->family = BPF_CORE_READ(sk, __sk_common.skc_family); + message->timestamp = bpf_ktime_get_ns(); + if (message->family == AF_INET) { + if (direction == 0) { // Send + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + } else { // Receive + message->saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); + message->daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); + } + message->saddr_v6 = 0; + message->daddr_v6 = 0; + } else if (message->family == AF_INET6) { + if (direction == 0) { // Send + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + } else { // Receive + bpf_probe_read_kernel( + &message->saddr_v6, sizeof(message->saddr_v6), + &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); + bpf_probe_read_kernel( + &message->daddr_v6, sizeof(message->daddr_v6), + &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); + } + + message->saddr = 0; + message->daddr = 0; + } + + if (direction == 0) { // Send + message->sport = bpf_ntohs(sport); + message->dport = bpf_ntohs(dport); + } else { // Receive + message->sport = bpf_ntohs(dport); + message->dport = bpf_ntohs(sport); + } + message->direction = direction; + + // 增加 RST 计数 + u32 pid = message->pid; + u64 *count = bpf_map_lookup_elem(&counters, &pid); + if (count) { + *count += 1; + } else { + u64 initial_count = 1; + bpf_map_update_elem(&counters, &pid, &initial_count, BPF_ANY); + count = &initial_count; + } + message->count = *count; + + bpf_ringbuf_submit(message, 0); + + return 0; +} +static __always_inline int +__handle_send_reset(struct trace_event_raw_tcp_send_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Send reset: sport=%u, dport=%u\n", ctx->sport, ctx->dport); + return ret((void *)ctx->skaddr, 0, ctx->sport, ctx->dport); +} + +static __always_inline int +__handle_receive_reset(struct trace_event_raw_tcp_receive_reset *ctx) { + struct sock *sk = (struct sock *)ctx->skaddr; + if (!sk) + return 0; + // bpf_printk("Receive reset: sport=%u, dport=%u\n", ctx->sport, + // ctx->dport); + return ret((void *)ctx->skaddr, 1, ctx->sport, ctx->dport); } \ No newline at end of file diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h index f64cc7023..0b38ebb40 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/udp.bpf.h @@ -16,15 +16,14 @@ #include "common.bpf.h" -static __always_inline -int __udp_rcv(struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int __udp_rcv(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, &pkt_tuple, &zero); @@ -34,19 +33,15 @@ int __udp_rcv(struct sk_buff *skb) tinfo->tran_time = bpf_ktime_get_ns() / 1000; return 0; } -static __always_inline -int udp_enqueue_schedule_skb(struct sock *sk,struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int udp_enqueue_schedule_skb(struct sock *sk, + struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.dport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.sport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); - pkt_tuple.tran_flag = UDP ; + get_udp_pkt_tuple(&pkt_tuple, ip, udp); + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); if (tinfo == NULL) { @@ -59,71 +54,153 @@ int udp_enqueue_schedule_skb(struct sock *sk,struct sk_buff *skb) if (!message) { return 0; } - message->saddr = pkt_tuple.saddr; - message->daddr = pkt_tuple.daddr; - message->dport = pkt_tuple.sport; - message->sport = pkt_tuple.dport; + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->dport = pkt_tuple.dport; + message->sport = pkt_tuple.sport; message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->rx=1;//收包 - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); + message->rx = 1; // 收包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); bpf_ringbuf_submit(message, 0); return 0; } - -static __always_inline -int __udp_send_skb(struct sk_buff *skb) -{ - if (!udp_info||skb==NULL) +static __always_inline int __udp_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; - struct iphdr *ip = skb_to_iphdr(skb); - struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; - get_udp_pkt_tuple(&pkt_tuple, ip, udp); + struct sock *sk = BPF_CORE_READ(skb, sk); + u16 dport = BPF_CORE_READ(sk, __sk_common.skc_dport); + u16 sport = BPF_CORE_READ(sk, __sk_common.skc_num); + pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); // 源ip + pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); // 目的ip + pkt_tuple.sport = sport; // 源端口 + pkt_tuple.dport = __bpf_ntohs(dport); // 目的端口并进行字节序转换 + pkt_tuple.tran_flag = UDP; + FILTER struct ktime_info *tinfo, zero = {0}; + bpf_printk("udp_send_skb%d %d %d %d", pkt_tuple.saddr, pkt_tuple.daddr, + pkt_tuple.sport, pkt_tuple.dport); tinfo = (struct ktime_info *)bpf_map_lookup_or_try_init(×tamps, - &pkt_tuple, &zero); + &pkt_tuple, &zero); if (tinfo == NULL) { return 0; } tinfo->tran_time = bpf_ktime_get_ns() / 1000; - return 0; } -static __always_inline -int __ip_send_skb(struct sk_buff *skb) -{ - if (!udp_info||skb == NULL) +static __always_inline int __ip_send_skb(struct sk_buff *skb) { + if (!udp_info || skb == NULL) return 0; struct iphdr *ip = skb_to_iphdr(skb); struct udphdr *udp = skb_to_udphdr(skb); struct packet_tuple pkt_tuple = {0}; get_udp_pkt_tuple(&pkt_tuple, ip, udp); - // bpf_printk("%d %d",pkt_tuple.saddr,pkt_tuple.daddr); - struct sock *sk = BPF_CORE_READ(skb, sk); - pkt_tuple.saddr = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); - pkt_tuple.daddr = BPF_CORE_READ(sk, __sk_common.skc_daddr); - pkt_tuple.sport = BPF_CORE_READ(sk, __sk_common.skc_num); - pkt_tuple.dport = __bpf_ntohs(BPF_CORE_READ(sk, __sk_common.skc_dport)); - pkt_tuple.tran_flag = UDP; + FILTER struct ktime_info *tinfo, zero = {0}; tinfo = bpf_map_lookup_elem(×tamps, &pkt_tuple); if (tinfo == NULL) { return 0; } struct udp_message *message; - struct udp_message *udp_message =bpf_map_lookup_elem(×tamps,&pkt_tuple); + struct udp_message *udp_message = + bpf_map_lookup_elem(×tamps, &pkt_tuple); message = bpf_ringbuf_reserve(&udp_rb, sizeof(*message), 0); if (!message) { return 0; } + udp = skb_to_udphdr(skb); message->tran_time = bpf_ktime_get_ns() / 1000 - tinfo->tran_time; - message->saddr = pkt_tuple.saddr; - message->daddr = pkt_tuple.daddr; - message->sport = pkt_tuple.sport; - message->dport = pkt_tuple.dport; - message->rx=0;//发包 - message->len=__bpf_ntohs(BPF_CORE_READ(udp,len)); + message->saddr = pkt_tuple.saddr; + message->daddr = pkt_tuple.daddr; + message->sport = pkt_tuple.sport; + message->dport = pkt_tuple.dport; + message->rx = 0; // 发包 + message->len = __bpf_ntohs(BPF_CORE_READ(udp, len)); + bpf_ringbuf_submit(message, 0); + return 0; +} +static __always_inline int process_dns_packet(struct sk_buff *skb, int rx) { + if (skb == NULL) + return 0; + u16 QR_flags; + u64 *count_ptr, response_count = 0, request_count = 0; + struct sock *sk = BPF_CORE_READ(skb, sk); + INIT_PACKET_UDP_TUPLE(sk, pkt_tuple); + // 使用saddr、daddr作为key + struct dns key = {.saddr = pkt_tuple.saddr, .daddr = pkt_tuple.daddr}; + + if ((pkt_tuple.sport != 53) && (pkt_tuple.dport != 53)) + return 0; + + struct dns_information *message = + bpf_ringbuf_reserve(&dns_rb, sizeof(*message), 0); + if (!message) + return 0; + + // 计算dns数据开始偏移 传输层头部的位置加上 UDP 头部的大小 + u32 dns_offset = + BPF_CORE_READ(skb, transport_header) + sizeof(struct udphdr); + struct dns_query query; + // dns头部位置 + bpf_probe_read_kernel(&query.header, sizeof(query.header), + BPF_CORE_READ(skb, head) + dns_offset); + // dns数据部分 + bpf_probe_read_kernel(message->data, sizeof(message->data), + BPF_CORE_READ(skb, head) + dns_offset + + sizeof(struct dns_header)); + QR_flags = __bpf_ntohs(query.header.flags); + /* + 1000 0000 0000 0000 + &运算提取最高位QR, QR=1 Response QR=0 Request + */ + if (QR_flags & 0x8000) { // 响应 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr + 1; + } else { + response_count = 1; + } + bpf_map_update_elem(&dns_response_count, &key, &response_count, + BPF_ANY); + // 保留映射中的请求计数值 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr; + } + } else { // 请求 + count_ptr = bpf_map_lookup_elem(&dns_request_count, &key); + if (count_ptr) { + request_count = *count_ptr + 1; + } else { + request_count = 1; + } + bpf_map_update_elem(&dns_request_count, &key, &request_count, BPF_ANY); + // 保留映射中的响应计数值 + count_ptr = bpf_map_lookup_elem(&dns_response_count, &key); + if (count_ptr) { + response_count = *count_ptr; + } + } + message->saddr = rx ? pkt_tuple.saddr : pkt_tuple.daddr; + message->daddr = rx ? pkt_tuple.daddr : pkt_tuple.saddr; + message->id = __bpf_ntohs(query.header.id); + message->flags = __bpf_ntohs(query.header.flags); + message->qdcount = __bpf_ntohs(query.header.qdcount); + message->ancount = __bpf_ntohs(query.header.ancount); + message->nscount = __bpf_ntohs(query.header.nscount); + message->arcount = __bpf_ntohs(query.header.arcount); + message->request_count = request_count; + message->response_count = response_count; + message->rx = rx; + bpf_ringbuf_submit(message, 0); return 0; } +static __always_inline int __dns_rcv(struct sk_buff *skb) { + return process_dns_packet(skb, 0); // 0 收 +} + +static __always_inline int __dns_send(struct sk_buff *skb) { + return process_dns_packet(skb, 1); // 1 发 +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/Makefile b/eBPF_Supermarket/Stack_Analyser/Makefile index 132bb1c85..4c357c824 100644 --- a/eBPF_Supermarket/Stack_Analyser/Makefile +++ b/eBPF_Supermarket/Stack_Analyser/Makefile @@ -19,8 +19,11 @@ OUTPUT := .output BPF_SKEL := bpf_skel CLANG ?= clang -LIBBPF_SRC := $(abspath libbpf-bootstrap/libbpf/src) -BPFTOOL_SRC := $(abspath libbpf-bootstrap/bpftool/src) +CXX := clang +LIB := ../lib +LIBBPF_ROOT := $(abspath $(LIB)/libbpf) +LIBBPF_SRC := $(LIBBPF_ROOT)/src +BPFTOOL_SRC := $(abspath $(LIB)/bpftool/src) LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool @@ -31,16 +34,21 @@ ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/mips.*/mips/' \ | sed 's/riscv64/riscv/' \ | sed 's/loongarch64/loongarch/') -VMLINUX := libbpf-bootstrap/vmlinux/$(ARCH)/vmlinux.h + +VMLINUX := $(LIB)/vmlinux.h # Use our own libbpf API headers and Linux UAPI headers distributed with # libbpf to avoid dependency on system-wide headers, which could be missing or # outdated -INCLUDES := -I./include -I./$(OUTPUT) -I./$(BPF_SKEL) -I./libbpf-bootstrap/libbpf/include/uapi -I$(dir $(VMLINUX)) +INCLUDES := -I./include -I./$(OUTPUT) -I./$(BPF_SKEL) -I$(LIBBPF_ROOT)/include/uapi -I$(dir $(VMLINUX)) CFLAGS := -Og -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) BIN = $(patsubst src/%.cpp, %, ${wildcard src/*.cpp}) BPF = $(patsubst bpf/%.bpf.c, %, ${wildcard bpf/*.bpf.c}) +BPF_OBJ = $(patsubst %,$(OUTPUT)/%.bpf.o,$(BPF)) +BPF_SKEL_H = $(patsubst %,$(BPF_SKEL)/%.skel.h,$(BPF)) +BPF_WAPPER = $(patsubst %,$(OUTPUT)/%.o,$(BPF)) +BIN_OBJ = $(patsubst %,$(OUTPUT)/%.o,$(BIN)) TARGETS = stack_analyzer @@ -85,12 +93,22 @@ clean: $(call msg,CLEAN) $(Q)rm -rf $(OUTPUT) $(TARGETS) $(BPF_SKEL) +init: + $(call msg,INIT,$(LIB)) + $(Q)git submodule update --init --recursive ../lib/ + +$(LIBBPF_SRC) $(BPFTOOL_SRC): init + +$(VMLINUX): + $(call msg,BTFDUMP,$@) + $(Q)bpftool btf dump file /sys/kernel/btf/vmlinux format c > $@ + $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT) $(BPF_SKEL): $(call msg,MKDIR,$@) $(Q)mkdir -p $@ # Build libbpf -$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf +$(LIBBPF_OBJ): $(LIBBPF_SRC) $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf $(call msg,LIB,$@) $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ @@ -98,12 +116,12 @@ $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPU install # Build bpftool -$(BPFTOOL): | $(BPFTOOL_OUTPUT) +$(BPFTOOL): $(BPFTOOL_SRC) | $(BPFTOOL_OUTPUT) $(call msg,BPFTOOL,$@) $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap # Build BPF code -$(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/sa_ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) +$(BPF_OBJ): $(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) $(call msg,BPF,$@) $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ @@ -111,27 +129,25 @@ $(OUTPUT)/%.bpf.o: bpf/%.bpf.c include/sa_ebpf.h $(LIBBPF_OBJ) $(VMLINUX) | $(OU $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) # Generate BPF skeletons -$(BPF_SKEL)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(BPF_SKEL) +$(BPF_SKEL_H): $(BPF_SKEL)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) $(BPF_SKEL) $(call msg,GEN-SKEL,$@) $(Q)$(BPFTOOL) gen skeleton $< > $@ -$(patsubst %,include/bpf_wapper/%.h,$(BPF)): include/bpf_wapper/%.h: $(BPF_SKEL)/%.skel.h - -$(patsubst %,$(OUTPUT)/%.o,$(BPF)): $(OUTPUT)/%.o: src/bpf_wapper/%.cpp include/bpf_wapper/%.h $(OUTPUT)/eBPFStackCollector.o +$(BPF_WAPPER): $(OUTPUT)/%.o: src/bpf_wapper/%.cpp include/bpf_wapper/%.h $(BPF_SKEL)/%.skel.h $(OUTPUT)/eBPFStackCollector.o $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ # Build depending library -$(patsubst %,$(OUTPUT)/%.o,$(BIN)): $(OUTPUT)/%.o: src/%.cpp $(patsubst %,include/bpf_wapper/%.h,$(BPF)) +$(BIN_OBJ): $(OUTPUT)/%.o: src/%.cpp $(BPF_SKEL_H) $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ -$(OUTPUT)/eBPFStackCollector.o: src/bpf_wapper/eBPFStackCollector.cpp | $(LIBBPF_OBJ) +$(OUTPUT)/eBPFStackCollector.o: src/bpf_wapper/eBPFStackCollector.cpp include/bpf_wapper/eBPFStackCollector.h | $(LIBBPF_OBJ) $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ # Build application binary -$(TARGETS): $(OUTPUT)/eBPFStackCollector.o $(patsubst %,$(OUTPUT)/%.o,$(BIN)) $(patsubst %,$(OUTPUT)/%.o,$(BPF)) $(LIBBPF_OBJ) +$(TARGETS): $(OUTPUT)/eBPFStackCollector.o $(BIN_OBJ) $(BPF_WAPPER) $(LIBBPF_OBJ) $(call msg,BINARY,$@) $(Q)$(CXX) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@ diff --git a/eBPF_Supermarket/Stack_Analyser/README.md b/eBPF_Supermarket/Stack_Analyser/README.md index 6ba720da4..2df17eb1b 100644 --- a/eBPF_Supermarket/Stack_Analyser/README.md +++ b/eBPF_Supermarket/Stack_Analyser/README.md @@ -61,21 +61,34 @@ Stack_Analyzer是一个基于eBPF的按照指定时间间隔(默认为5s)来 ## 编译要求 -Ubuntu下需要安装一下依赖,其他发行版类似 +初始化并更新libbpf和bpftool的代码仓库: ```shell -$ git submodule update --init --recursive -$ apt install clang libelf1 libelf-dev zlib1g-dev +git submodule update --init --recursive ../lib/* ``` +需要安装一下依赖: -g++-10以上,clang-12以上 +Ubuntu下 + +```shell +sudo apt update +sudo apt install -y clang libelf1 libelf-dev zlib1g-dev bpftool +``` + +CentOS下 + +```shell +sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel bpftool +``` + +clang-12以上 # 使用方法 ## 工具编译 ```shell -$ make +$ make -j$(nproc) ``` ## 命令使用方法 @@ -87,20 +100,27 @@ $ ./stack_analyzer -h ## 输出格式 ```shell -Type:OnCPUTime Unit:nanoseconds Period:20408163 -time:20240309_06_23_32 +time:20240516_17_37_41 counts: -pid usid ksid count -23640 126625 88396 1 +pid usid ksid OnCPUTime/20408163nanoseconds +720 9226 49641 1 +3149 35172 -14 1 +91036 70751 78610 1 +91045 33012 -14 1 traces: sid trace -88396 entry_SYSCALL_64_after_hwframe;do_syscall_64;__x64_sys_clone;__do_sys_clone;kernel_clone;copy_process;dup_mm.constprop.0;dup_mmap;copy_page_range;copy_p4d_range;copy_pte_range;__pte_alloc;pte_alloc_one;alloc_pages;__alloc_pages;get_page_from_freelist;clear_page_orig; -126625 _Fork+0x7ff997200027; -groups: -pid tgid -23640 23640 -commands: -23640 node +9226 GlobalConfig_DownloadConfig+0xffffa9f3f4439631;__socket+0xb; +33012 0x40;0x7f2e8c34183c;0x7f2e8c344845; +35172 runtime.goexit.abi0+0x1;github.com/prometheus/node_exporter/collector.NodeCollector.Collect.gowrap1+0x30;github.com/prometheus/node_exporter/collector.NodeCollector.Collect.func1+0x37;github.com/prometheus/node_exporter/collector.execute+0x90;github.com/prometheus/node_exporter/collector.(*netClassCollector).Update+0x2a;github.com/prometheus/node_exporter/collector.(*netClassCollector).netClassSysfsUpdate+0x32;github.com/prometheus/node_exporter/collector.(*netClassCollector).getNetClassInfo+0xf9;github.com/prometheus/procfs/sysfs.FS.NetClassByIface+0x105;github.com/prometheus/procfs/sysfs.parseNetClassIface+0xc5;github.com/prometheus/procfs/sysfs.ParseNetClassAttribute+0x94;github.com/prometheus/procfs/internal/util.SysReadFile+0x4d;os.OpenFile+0x3e;os.openFileNolog+0x92;os.open+0x2b;syscall.openat+0x90;syscall.Syscall6+0x6a;runtime/internal/syscall.Syscall6+0xe; +49641 entry_SYSCALL_64_after_hwframe+0x67;do_syscall_64+0x56;x64_sys_call+0x1ec2;__x64_sys_socket+0x17;__sys_socket+0x5d;__sock_create+0x133;unix_create+0x43;unix_create1+0x67;sk_alloc+0x31;sk_prot_alloc+0x8b; +70751 0x7f19ac2e37e2; +78610 entry_SYSCALL_64_after_hwframe+0x67;do_syscall_64+0x56;x64_sys_call+0x1dba;__x64_sys_read+0x19;ksys_read+0x67;vfs_read+0x9c;seq_read+0xf2;seq_read_iter+0x121;proc_single_show+0x4f;proc_pid_status+0x40c;cpuset_task_status_allowed+0x3a;seq_printf+0x91;vsnprintf+0x1e3;pointer+0x2a5;bitmap_string.constprop.0+0xe7; +info: +pid NSpid comm tgid cgroup +720 0 vmtoolsd 720 open-vm-tools.service +3149 0 node_exporter 3046 docker-bb4adf6694d50bea52f9e57038ab2870cd070fac4c8fe241f3d34dd1791a9a32.scope +91036 0 ps 91036 session-4.scope +91045 0 sed 91045 session-4.scope OK ``` @@ -108,6 +128,24 @@ OK 请阅读[`exporter/README.md`](exporter/README.md)。 +## 将Pyroscope集成到Grafana + +1. 选择pyroscope数据源 + ![选择数据源](asset/image.png) +2. 设置pyroscope服务器地址并测试保存 + ![配置](asset/image-1.png) +3. 创建仪表盘并添加可视化面板,将数据源设为刚刚创建的pyroscope数据源,调整参数,其中,metrics请求可以获取调用栈对应指标总体走势,profile请求可以获取火焰图绘制数据,展示时间区间内的火焰图。 + ![metrics](asset/image-2.png) + ![profile](asset/image-3.png) +4. 总体效果展示 + ![stack analyzer dashboard](asset/image-4.png) + +## grafana dashboard 导出及导入 + +本仓库提供了上节效果展示所使用的dashboard,即`./grafana_stack_analyzer_dashboard.json` +其导出方法为:在仪表盘界面点击[分享]按钮,导出,导出为文件,即可。 +导入则需要在创建仪表盘菜单选择[导入仪表盘],通过仪表板 JSON 模型导入,选择需要的仪表盘文件,即可。 + # 目录描述 - include:各种定义。 @@ -117,4 +155,12 @@ OK - exporter:使用Golang开发的数据推送程序,将采集到的调用栈数据推送到Pyroscope服务器,获取更强的数据存储和可视化性能。 - main.cpp:负责参数解析、配置、调用栈数据收集器管理和子进程管理。 - libbpf-bootstrap: 项目依赖的libbpf及相关工具源代码,方便移植。 -- new_bpf.sh:初始化新的采集能力的脚本,详情请参考`框架使用说明.md`。 \ No newline at end of file +- new_bpf.sh:初始化新的采集能力的脚本,详情请参考`框架使用说明.md`。 + +# 问题描述 + +- [x] 符号解析库没有访问容器内可执行文件的能力 +- [ ] memleak挂载在uprobe上时无数据 +- [ ] memleak在定频时依旧会采集大量数据 +- [ ] golang语言调用栈解析效果差 +- [ ] readahead采集结果中存在指标值为0的项 \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-1.png b/eBPF_Supermarket/Stack_Analyser/asset/image-1.png new file mode 100644 index 000000000..877526a07 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-1.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-2.png b/eBPF_Supermarket/Stack_Analyser/asset/image-2.png new file mode 100644 index 000000000..bd6e27996 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-2.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-3.png b/eBPF_Supermarket/Stack_Analyser/asset/image-3.png new file mode 100644 index 000000000..26c23b9b6 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-3.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image-4.png b/eBPF_Supermarket/Stack_Analyser/asset/image-4.png new file mode 100644 index 000000000..9eb534213 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image-4.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/asset/image.png b/eBPF_Supermarket/Stack_Analyser/asset/image.png new file mode 100644 index 000000000..60d4a6679 Binary files /dev/null and b/eBPF_Supermarket/Stack_Analyser/asset/image.png differ diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c index 65a9a60a7..2469004fe 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c @@ -21,32 +21,31 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/io.h" #include "task.h" COMMON_MAPS(io_tuple); COMMON_VALS; -const volatile int target_pid = 0; const char LICENSE[] SEC("license") = "GPL"; static int do_stack(struct trace_event_raw_sys_enter *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); // 利用bpf_get_current_task()获得当前的进程tsk - RET_IF_KERN(curr); - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获得当前进程的pid - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - SAVE_TASK_INFO(pid, curr); - - // record time delta - psid apsid = GET_COUNT_KEY(pid, ctx); + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); io_tuple *d = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count表当中的apsid表项,即size u64 len = BPF_CORE_READ(ctx, args[2]); // 读取系统调用的第三个参数 - if (!d) { io_tuple tmp = {.count = 1, .size = len}; diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c index 89c444690..b48d433d0 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/llc_stat.h" #include "task.h" @@ -31,13 +31,17 @@ COMMON_VALS; static __always_inline int trace_event(__u64 sample_period, bool miss, struct bpf_perf_event_data *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获得当前进程的pid - if (!pid || pid == self_pid) - return 0; - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + // perf 中已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + u32 tgid = BPF_CORE_READ(curr, tgid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); llc_stat *infop = bpf_map_lookup_elem(&psid_count_map, &apsid); if (!infop) { diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c index 239a7edba..d94b29d97 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c @@ -21,41 +21,34 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" #include "bpf_wapper/memleak.h" COMMON_MAPS(union combined_alloc_info); COMMON_VALS; -const volatile __u64 sample_rate = 1; const volatile bool wa_missing_free = false; const volatile size_t page_size = 4096; const volatile bool trace_all = false; -BPF_HASH(pid_size_map, u32, u64); // 记录了对应进程使用malloc,calloc等函数申请内存的大小 -BPF_HASH(piddr_meminfo_map, piddr, mem_info); // 记录了每次申请的内存空间的起始地址等信息 -BPF_HASH(memptrs_map, u32, u64); +BPF_HASH(pid_size_map, u32, u64, MAX_ENTRIES); // 记录了对应进程使用malloc,calloc等函数申请内存的大小 +BPF_HASH(piddr_meminfo_map, piddr, mem_info, MAX_ENTRIES); // 记录了每次申请的内存空间的起始地址等信息 +BPF_HASH(memptrs_map, u32, u64, MAX_ENTRIES); const char LICENSE[] SEC("license") = "GPL"; static int gen_alloc_enter(size_t size) { CHECK_ACTIVE; - if (!size) - return 0; - if (sample_rate > 1) - { - if (bpf_ktime_get_ns() % sample_rate != 0) - return 0; - } - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - // update group - // group share memory + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + // attach 时已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + u32 tgid = BPF_CORE_READ(curr, tgid); - if (tgid == self_pid) - return 0; - SAVE_TASK_INFO(tgid, curr); + TRY_SAVE_INFO(curr, tgid, tgid, knode); if (trace_all) bpf_printk("alloc entered, size = %lu\n", size); // record size @@ -72,7 +65,7 @@ static int gen_alloc_exit2(void *ctx, u64 addr) if (!size) return 0; // record counts - psid apsid = GET_COUNT_KEY(tgid, ctx); + psid apsid = TRACE_AND_GET_COUNT_KEY(tgid, ctx); union combined_alloc_info *count = bpf_map_lookup_elem(&psid_count_map, &apsid); union combined_alloc_info cur = { .number_of_allocs = 1, @@ -100,7 +93,6 @@ static int gen_alloc_exit2(void *ctx, u64 addr) static int gen_alloc_exit(struct pt_regs *ctx) { - CHECK_ACTIVE; return gen_alloc_exit2(ctx, PT_REGS_RC(ctx)); } @@ -111,7 +103,7 @@ static int gen_free_enter(const void *addr) piddr a = {.addr = (u64)addr, .pid = tgid, ._pad = 0}; mem_info *info = bpf_map_lookup_elem(&piddr_meminfo_map, &a); if (!info) - return -1; + return 0; // get allocated size psid apsid = { @@ -492,7 +484,7 @@ int memleak__mm_page_free(struct trace_event_raw_mm_page_free *ctx) SEC("tracepoint/percpu/percpu_alloc_percpu") int memleak__percpu_alloc_percpu(struct trace_event_raw_percpu_alloc_percpu *ctx) { - gen_alloc_enter(ctx->bytes_alloc); + gen_alloc_enter(ctx->size); return gen_alloc_exit2(ctx, (u64)(ctx->ptr)); } diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c index 10445e9db..80877b067 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c @@ -21,50 +21,67 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" COMMON_MAPS(u32); COMMON_VALS; - -const volatile int target_pid = 0; -BPF_HASH(pid_offTs_map, u32, u64); // 记录进程运行的起始时间 +// 记录进程运行的起始时间 +BPF_HASH(pid_offTs_map, u32, u64, MAX_ENTRIES/10); const char LICENSE[] SEC("license") = "GPL"; -SEC("kprobe/finish_task_switch") // 动态挂载点finish_task_switch.isra.0 -int BPF_KPROBE(do_stack, struct task_struct *curr) +static int prev_part(struct task_struct *prev) { - CHECK_ACTIVE; - u32 pid = BPF_CORE_READ(curr, pid); // 利用帮助函数获取换出进程tsk的pid - RET_IF_KERN(curr); - if ((target_pid >= 0 && pid == target_pid) || (target_pid < 0 && pid && pid != self_pid)) - { - // record curr block time - u64 ts = bpf_ktime_get_ns(); // ts=当前的时间戳(ns) - bpf_map_update_elem(&pid_offTs_map, &pid, &ts, BPF_NOEXIST); // 如果start表中不存在pid对应的时间,则就创建pid-->ts - } + u64 ts = bpf_ktime_get_ns(); + CHECK_FREQ(ts); + CHECK_KTHREAD(prev); + u32 tgid = BPF_CORE_READ(prev, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(prev); + CHECK_CGID(knode); + u32 pid = BPF_CORE_READ(prev, pid); + bpf_map_update_elem(&pid_offTs_map, &pid, &ts, BPF_ANY); + return 0; +} - // calculate time delta, next ready to run - struct task_struct *next = (struct task_struct *)bpf_get_current_task(); // next指向换入进程结构体 - pid = BPF_CORE_READ(next, pid); // 利用帮助函数获取next指向的tsk的pid - u64 *tsp = bpf_map_lookup_elem(&pid_offTs_map, &pid); // tsp指向start表中的pid的值 +static int next_part(struct task_struct *next, void *ctx) +{ + // 利用帮助函数获取next指向的tsk的pid + u32 pid = BPF_CORE_READ(next, pid); + // tsp指向start表中的pid的值 + u64 *tsp = bpf_map_lookup_elem(&pid_offTs_map, &pid); if (!tsp) return 0; - bpf_map_delete_elem(&pid_offTs_map, &pid); // 存在tsp,则删除pid对应的值 - u32 delta = (bpf_ktime_get_ns() - *tsp) >> 20; // delta为当前时间戳 - 原先tsp指向start表中的pid的值.代表运行时间 + // delta为当前时间戳 - 原先tsp指向start表中的pid的值.代表运行时间 + u32 delta = (bpf_ktime_get_ns() - *tsp) >> 20; if (!delta) return 0; // record data - SAVE_TASK_INFO(pid, next); - psid apsid = GET_COUNT_KEY(pid, ctx); + struct kernfs_node *knode = GET_KNODE(next); + TRY_SAVE_INFO(next, pid, BPF_CORE_READ(next, tgid), knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); // record time delta - u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count中的apsid对应的值 + // count指向psid_count中的apsid对应的值 + u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); if (count) - (*count) += delta; // 如果count存在,则psid_count中的apsid对应的值+=时间戳 + // 如果count存在,则psid_count中的apsid对应的值+=时间戳 + (*count) += delta; else - bpf_map_update_elem(&psid_count_map, &apsid, &delta, BPF_NOEXIST); // 如果不存在,则将psid_count表中的apsid设置为delta + // 如果不存在,则将psid_count表中的apsid设置为delta + bpf_map_update_elem(&psid_count_map, &apsid, &delta, BPF_NOEXIST); + return 0; +} + +// 动态挂载点finish_task_switch.isra.0 +SEC("kprobe/finish_task_switch") +int BPF_KPROBE(do_stack, struct task_struct *prev) +{ + CHECK_ACTIVE; + prev_part(prev); + // calculate time delta, next ready to run + next_part(GET_CURR, ctx); return 0; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c index 7830850ad..c1b103f19 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" const char LICENSE[] SEC("license") = "GPL"; @@ -33,15 +33,15 @@ SEC("perf_event") // 挂载点为perf_event int do_stack(void *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (void *)bpf_get_current_task(); // curr指向当前进程的tsk - RET_IF_KERN(curr); // 忽略内核线程 - u32 pid = BPF_CORE_READ(curr, pid); // pid保存当前进程的pid,是cgroup pid 对应的level 0 pid - if (!pid || pid == self_pid) - return 0; - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); + struct task_struct *curr = GET_CURR; // curr指向当前进程的tsk + CHECK_KTHREAD(curr); + // perf 中已设置目标tgid,这里无需再次过滤tgid + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - // add cosunt + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, BPF_CORE_READ(curr, tgid), knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); u32 *count = bpf_map_lookup_elem(&psid_count_map, &apsid); // count指向psid_count对应的apsid的值 if (count) (*count)++; // count不为空,则psid_count对应的apsid的值+1 diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c index 65706ad64..623bb5ae0 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: GaoYixiang +// author: luiyanbing@foxmail.com // -// 内核态eBPF的通用的调用栈计数代码 +// probe功能的内核态bpf程序代码 #include "vmlinux.h" #include @@ -22,54 +22,122 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" +#include "bpf_wapper/probe.h" #include "task.h" -COMMON_MAPS(u32); +COMMON_MAPS(time_tuple); COMMON_VALS; -const volatile int target_pid = 0; +BPF_HASH(starts, u32, u64, MAX_ENTRIES/10); -const char LICENSE[] SEC("license") = "GPL"; - -static int handle_func(void *ctx) +static int entry(void *ctx) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); // 利用bpf_get_current_task()获得当前的进程tsk - RET_IF_KERN(curr); + u64 ts = bpf_ktime_get_ns(); + CHECK_FREQ(ts); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); - u32 pid = get_task_ns_pid(curr); // 利用帮助函数获得当前进程的pid - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + bpf_map_update_elem(&starts, &pid, &ts, BPF_ANY); + return 0; +} - SAVE_TASK_INFO(pid, curr); +static int exit(void *ctx) +{ + CHECK_ACTIVE; + u32 pid = bpf_get_current_pid_tgid() >> 32; + u64 *start = bpf_map_lookup_elem(&starts, &pid); + if (!start) + return 0; + u64 delta = TS - *start; - psid a_psid = GET_COUNT_KEY(pid, ctx); - u32 *cnt = bpf_map_lookup_elem(&psid_count_map, &a_psid); - if (!cnt) + psid a_psid = TRACE_AND_GET_COUNT_KEY(pid, ctx); + time_tuple *d = bpf_map_lookup_elem(&psid_count_map, &a_psid); + if (!d) { - u32 ONE = 1; - bpf_map_update_elem(&psid_count_map, &a_psid, &ONE, BPF_NOEXIST); + time_tuple tmp = {.lat = delta, .count = 1}; + bpf_map_update_elem(&psid_count_map, &a_psid, &tmp, BPF_NOEXIST); } else - (*cnt)++; - + { + d->lat += delta; + d->count++; + } return 0; } + SEC("kprobe/dummy_kprobe") -int BPF_KPROBE(handle) +int BPF_KPROBE(dummy_kprobe) { - handle_func(ctx); + entry(ctx); return 0; } + +SEC("kretprobe/dummy_kretprobe") +int BPF_KRETPROBE(dummy_kretprobe) +{ + exit(ctx); + return 0; +} + +SEC("fentry/dummy_fentry") +int BPF_PROG(dummy_fentry) +{ + entry(ctx); + return 0; +} + +SEC("fexit/dummy_fexit") +int BPF_PROG(dummy_fexit) +{ + exit(ctx); + return 0; +} + +static int static_tracing_handler(void *ctx) +{ + CHECK_ACTIVE; + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid a_psid = TRACE_AND_GET_COUNT_KEY(pid, ctx); + time_tuple *d = bpf_map_lookup_elem(&psid_count_map, &a_psid); + if (!d) + { + time_tuple tmp = {.lat = 0, .count = 1}; + bpf_map_update_elem(&psid_count_map, &a_psid, &tmp, BPF_NOEXIST); + } + else + d->count++; + return 0; +} + SEC("tp/sched/dummy_tp") -int handle_tp(void *ctx) +int tp_exit(void *ctx) { - handle_func(ctx); + static_tracing_handler(ctx); return 0; } + SEC("usdt") -int handle_usdt(void *ctx) +int usdt_exit(void *ctx) { - return handle_func(ctx); -} \ No newline at end of file + static_tracing_handler(ctx); + return 0; +} + +const char LICENSE[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c index 59c390076..69925f805 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" #include "bpf_wapper/readahead.h" @@ -31,24 +31,24 @@ COMMON_MAPS(ra_tuple); COMMON_VALS; -int target_pid = 0; -BPF_HASH(in_ra_map, u32, psid); -BPF_HASH(page_psid_map, struct page *, psid); +BPF_HASH(in_ra_map, u32, psid, MAX_ENTRIES/10); +BPF_HASH(page_psid_map, struct page *, psid, MAX_ENTRIES); SEC("fentry/page_cache_ra_unbounded") // fentry在内核函数page_cache_ra_unbounded进入时触发的挂载点 int BPF_PROG(page_cache_ra_unbounded) { CHECK_ACTIVE; - struct task_struct *curr = (struct task_struct *)bpf_get_current_task(); - RET_IF_KERN(curr); - u32 pid = get_task_ns_pid(curr); // 获取当前进程tgid,用户空间的pid即是tgid - - if ((target_pid >= 0 && pid != target_pid) || !pid || pid == self_pid) - return 0; - - SAVE_TASK_INFO(pid, curr); - psid apsid = GET_COUNT_KEY(pid, ctx); - + CHECK_FREQ(TS); + struct task_struct *curr = GET_CURR; + CHECK_KTHREAD(curr); + u32 tgid = BPF_CORE_READ(curr, tgid); + CHECK_TGID(tgid); + struct kernfs_node *knode = GET_KNODE(curr); + CHECK_CGID(knode); + + u32 pid = BPF_CORE_READ(curr, pid); + TRY_SAVE_INFO(curr, pid, tgid, knode); + psid apsid = TRACE_AND_GET_COUNT_KEY(pid, ctx); ra_tuple *d = bpf_map_lookup_elem(&psid_count_map, &apsid); // d指向psid_count表中的apsid对应的类型为tuple的值 if (!d) { @@ -63,11 +63,7 @@ SEC("fexit/alloc_pages") // fexit在内核函数alloc_pages退出时触发,挂 int BPF_PROG(filemap_alloc_folio_ret, gfp_t gfp, unsigned int order, u64 ret) { CHECK_ACTIVE; - u32 pid = bpf_get_current_pid_tgid() >> 32; // pid为当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; - + u32 pid = bpf_get_current_pid_tgid() >> 32; // pid为当前进程的pid struct psid *apsid = bpf_map_lookup_elem(&in_ra_map, &pid); // apsid指向了当前in_ra中pid的表项内容 if (!apsid) return 0; @@ -91,11 +87,7 @@ int BPF_PROG(page_cache_ra_unbounded_ret) // fexit在内核函数page_cache_ra_u { CHECK_ACTIVE; u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; - - bpf_map_delete_elem(&in_ra_map, &pid); // 删除了in_ra对应的pid的表项,即删除对应的栈计数信息 + bpf_map_delete_elem(&in_ra_map, &pid); // 删除了in_ra对应的pid的表项,即删除对应的栈计数信息 return 0; } @@ -104,9 +96,6 @@ int BPF_PROG(mark_page_accessed, u64 page) { CHECK_ACTIVE; u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取当前进程的pid - - if ((target_pid >= 0 && pid != target_pid) || !pid) - return 0; psid *apsid; apsid = bpf_map_lookup_elem(&page_psid_map, &page); // 查看page_psid对应的 地址page 对应类型为psid的值,并保存在apsid if (!apsid) diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c index 8f741746f..108412609 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/template.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/template.h" #include "task.h" diff --git a/eBPF_Supermarket/Stack_Analyser/exporter/README.md b/eBPF_Supermarket/Stack_Analyser/exporter/README.md index 911164863..90fdde8a7 100644 --- a/eBPF_Supermarket/Stack_Analyser/exporter/README.md +++ b/eBPF_Supermarket/Stack_Analyser/exporter/README.md @@ -1,6 +1,6 @@ # 功能描述 -适用于Pyroscope服务器的数据发送程序,程序通过监听标准输入来获取调用栈数据,输入格式与stack_analyzer工具输出格式一致,可通过管道配合stack_analyer使用,将stack_analyzer的数据发送到Pyroscope服务器获取更强的数据存储和可视化能力。 +适用于Pyroscope调用栈可视化服务器的数据发送程序,程序通过监听标准输入来获取调用栈数据,输入格式与stack_analyzer工具输出格式一致,可通过管道配合stack_analyer使用,将stack_analyzer的数据发送到Pyroscope服务器获取更强的数据存储和可视化能力。Pyroscope安装方法可参考[这里](https://grafana.com/docs/pyroscope/latest/get-started/)。 # 使用方法 diff --git a/eBPF_Supermarket/Stack_Analyser/exporter/main.go b/eBPF_Supermarket/Stack_Analyser/exporter/main.go index 66a074f54..f81dbb098 100644 --- a/eBPF_Supermarket/Stack_Analyser/exporter/main.go +++ b/eBPF_Supermarket/Stack_Analyser/exporter/main.go @@ -292,6 +292,7 @@ func CollectProfiles(cb CollectProfilesCallback) error { for i, s := range scales { target := sd.NewTarget("", k.pid, sd.DiscoveryTarget{ "__container_id__": info[k.pid].cid, + "service_name": "Stack_Analyzer", labels.MetricName: s.Type, }) cb(target, group_trace, v[i], s, true) diff --git "a/eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" b/eBPF_Supermarket/Stack_Analyser/frame-use-guide.md similarity index 87% rename from "eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" rename to eBPF_Supermarket/Stack_Analyser/frame-use-guide.md index 2f393cbd5..8d829a678 100644 --- "a/eBPF_Supermarket/Stack_Analyser/\346\241\206\346\236\266\344\275\277\347\224\250\346\226\271\346\263\225.md" +++ b/eBPF_Supermarket/Stack_Analyser/frame-use-guide.md @@ -32,18 +32,18 @@ new_ebpf.sh 1. 实现包装类初始化函数,若数据标度`scale`的值可以确定,请在此初始化 2. 若标度无法确定,则实现`setScale`,对标度及包装类参数进行设置 -3. 实现一系列虚函数:`count_value, load, attach, detach, unload` +3. 实现一系列虚函数:`count_values, load, attach, detach, unload` 4. 可实现一些辅助函数 ## src/bpf/.bpf.c -1. 通过修改`DeclareCommonMaps(__u32)`中的`__u32`设置计数变量类型 +1. 通过修改`COMMON_MAPS(__u32)`中的`__u32`设置计数变量类型 2. 可声明额外的map和全局变量在eBPF程序内部使用,但不会被框架输出。 3. 实现eBPF程序,请使用通用的`eBPF map`进行数据存储,使用通用的全局变量进行进程和数据过滤,否则无法正确输出数据 通用的map分别为: - 1. psid_count:键为psid类型,值为 1. 中设置的计数变量类型 - 2. stack_trace:键为uint32类型,标识唯一的栈嗲用路径,值为void*[]类型,存储栈上的调用地址 + 1. psid_count_map:键为psid类型,值为 1. 中设置的计数变量类型 + 2. sid_trace_map:键为uint32类型,标识唯一的栈嗲用路径,值为void*[]类型,存储栈上的调用地址 3. pid_tgid:键为uint32类型,表示pid,值为uint32类型,表示tgid 4. pid_comm:键为uint32类型,表示pid,值为comm类型,表示进程名 diff --git a/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json b/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json new file mode 100644 index 000000000..dfe7e0cab --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/grafana_stack_analyzer_dashboard.json @@ -0,0 +1,1362 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "panels": [], + "title": "cpu", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OnCPUTime:OnCPUTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "cpu metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 2, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OnCPUTime:OnCPUTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "cpu profile", + "type": "flamegraph" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 5, + "panels": [], + "title": "block", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OffCPUTime:OffCPUTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "block time metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 4, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "OffCPUTime:OffCPUTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "block time profile", + "type": "flamegraph" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 7, + "panels": [], + "title": "memory inuse", + "type": "row" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedSize:LeakedSize:bytes::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_space metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 19 + }, + "id": 9, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedSize:LeakedSize:bytes::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_space profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedCount:LeakedCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_alloc metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 12, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "LeakedCount:LeakedCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "memory-inuse_alloc profile", + "type": "flamegraph" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 13, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOSize:IOSize:bytes::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "IO size metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 20, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOSize:IOSize:bytes::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "io size profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOCount:IOCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "IO count metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 17, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "IOCount:IOCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "io count profile", + "type": "flamegraph" + } + ], + "title": "IO", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 36 + }, + "id": 18, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UnusedReadaheadPages:UnusedReadaheadPages:pages::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead unused metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 25, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UnusedReadaheadPages:UnusedReadaheadPages:pages::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead unused profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UsedReadaheadPages:UsedReadaheadPages:pages::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead used metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 22, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "UsedReadaheadPages:UsedReadaheadPages:pages::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "readahead used profile", + "type": "flamegraph" + } + ], + "title": "readahead", + "type": "row" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 23, + "panels": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openCount:vfs_openCount:counts::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open count metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 15, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openCount:vfs_openCount:counts::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open count profile", + "type": "flamegraph" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openTime:vfs_openTime:nanoseconds::", + "queryType": "metrics", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open time metrics", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 27, + "targets": [ + { + "datasource": { + "type": "grafana-pyroscope-datasource", + "uid": "ddlp0mnbltkw0c" + }, + "groupBy": [], + "labelSelector": "{}", + "profileTypeId": "vfs_openTime:vfs_openTime:nanoseconds::", + "queryType": "profile", + "refId": "A", + "spanSelector": [] + } + ], + "title": "vfs_open time profile", + "type": "flamegraph" + } + ], + "title": "vfs_open", + "type": "row" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "stack_analyzer_dashboard", + "uid": "fdlqh6mke9hq8e", + "version": 6, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h index 08bf2cb04..ff07187c6 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h @@ -23,7 +23,7 @@ #include #include #include -#include "sa_user.h" +#include "user.h" struct Scale { @@ -48,7 +48,7 @@ struct CountItem class StackCollector { protected: - int self_pid = -1; + int self_tgid = -1; struct bpf_object *obj = NULL; // 默认显示计数的变化情况,即每次输出数据后清除计数 @@ -58,8 +58,11 @@ class StackCollector public: Scale *scales; - int pid = -1; // 用于设置ebpf程序跟踪的pid - int err = 0; // 用于保存错误代码 + uint32_t top = 10; + uint32_t freq = 49; + uint64_t cgroup = 0; + uint32_t tgid = 0; + int err = 0; // 用于保存错误代码 bool ustack = false; // 是否跟踪用户栈 bool kstack = false; // 是否跟踪内核栈 @@ -76,23 +79,8 @@ class StackCollector StackCollector(); operator std::string(); - /// @brief 负责ebpf程序的加载、参数设置和打开操作 - /// @param 无 - /// @return 成功则返回0,否则返回负数 - virtual int load(void) = 0; - - /// @brief 将ebpf程序挂载到跟踪点上 - /// @param 无 - /// @return 成功则返回0,否则返回负数 - virtual int attach(void) = 0; - - /// @brief 断开ebpf的跟踪点和处理函数间的连接 - /// @param 无 - virtual void detach(void) = 0; - - /// @brief 卸载ebpf程序 - /// @param 无 - virtual void unload(void) = 0; + virtual int ready(void) = 0; + virtual void finish(void) = 0; /// @brief 激活eBPF程序 /// @param 无 @@ -106,23 +94,26 @@ class StackCollector /// @brief 加载、初始化参数并打开指定类型的ebpf程序 /// @param ... 一些ebpf程序全局变量初始化语句 /// @note 失败会使上层函数返回-1 -#define EBPF_LOAD_OPEN_INIT(...) \ - { \ - skel = skel->open(NULL); \ - CHECK_ERR(!skel, "Fail to open BPF skeleton"); \ - __VA_ARGS__; \ - skel->rodata->trace_user = ustack; \ - skel->rodata->trace_kernel = kstack; \ - skel->rodata->self_pid = self_pid; \ - err = skel->load(skel); \ - CHECK_ERR(err, "Fail to load BPF skeleton"); \ - obj = skel->obj; \ +#define EBPF_LOAD_OPEN_INIT(...) \ + { \ + skel = skel->open(NULL); \ + CHECK_ERR_RN1(!skel, "Fail to open BPF skeleton"); \ + __VA_ARGS__; \ + skel->rodata->trace_user = ustack; \ + skel->rodata->trace_kernel = kstack; \ + skel->rodata->self_tgid = self_tgid; \ + skel->rodata->target_tgid = tgid; \ + skel->rodata->target_cgroupid = cgroup; \ + skel->rodata->freq = freq; \ + err = skel->load(skel); \ + CHECK_ERR_RN1(err, "Fail to load BPF skeleton"); \ + obj = skel->obj; \ } -#define ATTACH_PROTO \ - { \ - err = skel->attach(skel); \ - CHECK_ERR(err, "Failed to attach BPF skeleton"); \ +#define ATTACH_PROTO \ + { \ + err = skel->attach(skel); \ + CHECK_ERR_RN1(err, "Failed to attach BPF skeleton"); \ } #define DETACH_PROTO \ diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h index 8cef7ece0..227b1bcf7 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/io.h @@ -40,10 +40,8 @@ class IOStackCollector : public StackCollector public: IOStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h index e801e4ea5..2a365255e 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/llc_stat.h @@ -51,10 +51,8 @@ class LlcStatStackCollector : public StackCollector public: LlcStatStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); void setScale(uint64_t period); diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h index 08afd79fb..db7a5f774 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/memleak.h @@ -60,7 +60,6 @@ class MemleakStackCollector : public StackCollector public: char *object = (char *)"libc.so.6"; bool percpu = false; - __u64 sample_rate = 1; bool wa_missing_free = false; protected: @@ -70,10 +69,8 @@ class MemleakStackCollector : public StackCollector public: MemleakStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); @@ -91,7 +88,7 @@ class MemleakStackCollector : public StackCollector skel->links.prog_name = \ bpf_program__attach_uprobe_opts( \ skel->progs.prog_name, \ - pid, \ + tgid, \ object, \ 0, \ &uprobe_opts); \ @@ -107,7 +104,7 @@ class MemleakStackCollector : public StackCollector do \ { \ __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ - CHECK_ERR(!skel->links.prog_name, "no program attached for " #prog_name "\n") \ + CHECK_ERR_RN1(!skel->links.prog_name, "no program attached for " #prog_name "\n") \ } while (false) #define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false) diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h index df76f5789..d5bcc0aac 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/off_cpu.h @@ -32,10 +32,8 @@ class OffCPUStackCollector : public StackCollector public: OffCPUStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h index cd79dfccc..26d11ed28 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/on_cpu.h @@ -31,7 +31,6 @@ class OnCPUStackCollector : public StackCollector int *pefds = NULL; int num_cpus = 0; struct bpf_link **links = NULL; - unsigned long long freq = 49; protected: virtual uint64_t *count_values(void *); @@ -39,10 +38,8 @@ class OnCPUStackCollector : public StackCollector public: void setScale(uint64_t freq); OnCPUStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h index 86e0b6182..39dd4a7bb 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/probe.h @@ -14,16 +14,29 @@ // // author: luiyanbing@foxmail.com // -// probe ebpf程序的包装类,声明接口和一些自定义方法 +// ebpf程序的包装类的模板,声明接口和一些自定义方法,以及辅助结构 -#include "bpf_wapper/eBPFStackCollector.h" +#ifndef _SA_PROBE_H__ +#define _SA_PROBE_H__ + +// ========== C code part ========== +#include +typedef struct +{ + __u64 lat; + __u64 count; +} time_tuple; +// ========== C code end ========== + +#ifdef __cplusplus +// ========== C++ code part ========== #include "probe.skel.h" +#include "bpf_wapper/eBPFStackCollector.h" class ProbeStackCollector : public StackCollector { private: - struct probe_bpf *skel = __null; - + DECL_SKEL(probe); public: std::string probe; @@ -33,10 +46,12 @@ class ProbeStackCollector : public StackCollector public: void setScale(std::string probe); ProbeStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; +// ========== C++ code end ========== +#endif + +#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h index d3cd94850..41e48947e 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/readahead.h @@ -40,10 +40,8 @@ class ReadaheadStackCollector : public StackCollector public: ReadaheadStackCollector(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h index f2b9aa53d..fe728eb4d 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h +++ b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/template.h @@ -38,10 +38,8 @@ class TemplateClass : public StackCollector public: TemplateClass(); - virtual int load(void); - virtual int attach(void); - virtual void detach(void); - virtual void unload(void); + virtual int ready(void); + virtual void finish(void); virtual void activate(bool tf); virtual const char *getName(void); }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/cgroup.h b/eBPF_Supermarket/Stack_Analyser/include/cgroup.h new file mode 100644 index 000000000..a9157a603 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/cgroup.h @@ -0,0 +1,9 @@ +#include +struct cgid_file_handle +{ + // struct file_handle handle; + unsigned int handle_bytes; + int handle_type; + uint64_t cgid; +}; +uint64_t get_cgroupid(const char *pathname); \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_common.h b/eBPF_Supermarket/Stack_Analyser/include/common.h similarity index 81% rename from eBPF_Supermarket/Stack_Analyser/include/sa_common.h rename to eBPF_Supermarket/Stack_Analyser/include/common.h index b5707d644..ad77f45e3 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_common.h +++ b/eBPF_Supermarket/Stack_Analyser/include/common.h @@ -27,23 +27,17 @@ #define CONTAINER_ID_LEN (128) /// @brief 栈计数的键,可以唯一标识一个用户内核栈 -typedef struct { +typedef struct +{ __u32 pid; __s32 ksid, usid; } psid; -typedef struct { +typedef struct +{ __u32 pid; __u32 tgid; - char cid[CONTAINER_ID_LEN]; char comm[COMM_LEN]; } task_info; -#define _COL_PREFIX "\033[" -#define _BLUE _COL_PREFIX "1;34m" -#define _GREEN _COL_PREFIX "1;32m" -#define _RED _COL_PREFIX "1;35m" -#define _ERED _COL_PREFIX "1;31m" -#define _RE _COL_PREFIX "0m" - #endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h b/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h deleted file mode 100644 index a6174a376..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/dt_elf.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Linux内核诊断工具--elf相关函数头文件 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#ifndef _PERF_ELF_H__ -#define _PERF_ELF_H__ - -#include -#include - -#include "dt_symbol.h" - -#define BUILD_ID_SIZE 40 -bool save_symbol_cache(std::set &ss, const char *path); -bool load_symbol_cache(std::set &ss, const char *path, const char *filename); - -bool get_symbol_from_elf(std::set &ss, const char *path); -bool search_symbol(const std::set &ss, symbol &sym); -int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size); -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h b/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h deleted file mode 100644 index 3f2c3ffe9..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/dt_symbol.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Linux内核诊断工具--用户态符号表解析 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#ifndef __PERF_SYMBOL_H__ -#define __PERF_SYMBOL_H__ - -#include -#include -#include - -//#include - -#define INVALID_ADDR ((size_t)(-1)) -enum { - NATIVE_TYPE = 0, - JIT_TYPE = 1, - UNKNOWN = 2, -}; - -struct elf_file { - unsigned char elf_read_error; - size_t eh_frame_hdr_offset; - size_t fde_count; - size_t table_data; - std::string filename; - int type; - - // TODO get builid from elf header or build hash for elf - elf_file(const std::string &name) : filename(name), type(NATIVE_TYPE) { - elf_read_error = 0; - eh_frame_hdr_offset = 0; - fde_count = 0; - table_data = 0; - } - - elf_file() :type(NATIVE_TYPE) {} - - // TODO get builid from elf header or build hash for elf - void reset(const std::string &name) { - filename = name; - elf_read_error = 0; - eh_frame_hdr_offset = 0; - fde_count = 0; - table_data = 0; - } - - bool operator< (const elf_file &rhs) const { - return filename < rhs.filename; - } -}; - -struct symbol { - size_t start; - size_t end; - size_t ip; - std::string name; - - symbol() :start(0), end(0), ip(0) {} - symbol(size_t pc) :start(0), end(0), ip(pc) {} - - void reset(size_t va) { start = end = 0; ip = va; } - bool operator< (const symbol &sym) const { - return sym.ip < start; - } - - bool operator> (const symbol &sym) const { - return sym.ip > end; - } -}; - -struct vma { - size_t start; - size_t end; - size_t offset; - size_t pc; - int type; - std::string name; - struct { - unsigned char elf_read_error; - size_t eh_frame_hdr_offset; - size_t fde_count; - size_t table_data; - }; - - size_t map(size_t pc) { - return pc - start + offset; - } - - void set_type(int t) { type = t; } - - vma(size_t s, size_t e, size_t o, const std::string &n) - :start(s), end(e), offset(o), pc(0), type(NATIVE_TYPE), name(n) {} - - vma() : start(0), end(0), offset(0), pc(0), type(NATIVE_TYPE) {} - - vma(size_t addr) : start(0), end(0), offset(0), pc(addr), type(NATIVE_TYPE) {} - - bool operator<(const vma &vm) { - return vm.start < vm.pc; - } - - vma &operator=(const vma &vm) { - if (this == &vm) { - return *this; - } - start = vm.start; - end = vm.end; - offset = vm.offset; - name = vm.name; - return *this; - } -}; - -static inline bool operator==(const vma &lhs, const vma &rhs) { - return lhs.start == rhs.start && lhs.end == rhs.end && lhs.name == rhs.name; -} - -class symbol_parser { -private: - typedef std::map proc_vma; - - std::map > file_symbols; - std::map > java_symbols; - std::set kernel_symbols; - std::map machine_vma; - std::set java_procs; - std::map > symbols_cache; -public: - bool load_kernel(); - std::set& get_java_procs() { return java_procs; } - - bool find_kernel_symbol(symbol &sym); - bool complete_kernel_symbol(symbol &sym); - - /// @brief 从elf file中查找sym中地址对应的符号名存入sym - /// @param sym 符号对象 - /// @param file 进程对应的elf file - /// @param pid 进程 - /// @param pid_ns 进程的命名空间? - /// @return 查找成功返回true,否则返回false - bool find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns); - bool find_java_symbol(symbol &sym, int pid, int pid_ns); - - bool get_symbol_info(int pid, symbol &sym, elf_file &file); - - bool find_vma(pid_t pid, vma &vm); - vma* find_vma(pid_t pid, size_t pc); - void clear_symbol_info(int); - bool add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name); - - bool find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol); - bool putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol); - - void dump(void); -private: - bool load_pid_maps(int pid); - /// @brief 对elf_file对应的符号表进行缓存 -/// @param pid 未使用 -/// @param file elf file -/// @return 缓存成功返回true,否则返回false - bool load_elf(pid_t pid, const elf_file& file); - bool load_perf_map(int pid, int pid_ns); -public: - int java_only; - int user_symbol; -}; - -extern symbol_parser g_symbol_parser; - -std::string demangleCppSym(std::string symbol); -void clearSpace(std::string &sym); - -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/ebpf.h b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h new file mode 100644 index 000000000..84ee017f6 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h @@ -0,0 +1,169 @@ +// Copyright 2024 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: luiyanbing@foxmail.com +// +// 用于eBPF程序的宏 + +#ifndef STACK_ANALYZER_EBPF +#define STACK_ANALYZER_EBPF + +#include "common.h" +#include + +#define PF_KTHREAD 0x00200000 + +/// @brief 创建一个指定名字的ebpf调用栈表 +/// @param 新栈表的名字 +#define BPF_STACK_TRACE(name) \ + struct \ + { \ + __uint(type, BPF_MAP_TYPE_STACK_TRACE); \ + __uint(key_size, sizeof(__u32)); \ + __uint(value_size, MAX_STACKS * sizeof(__u64)); \ + __uint(max_entries, MAX_ENTRIES); \ + } name SEC(".maps") + +/// @brief 创建一个指定名字和键值类型的ebpf散列表 +/// @param name 新散列表的名字 +/// @param type1 键的类型 +/// @param type2 值的类型 +#define BPF_HASH(name, _kt, _vt, _cap) \ + struct \ + { \ + __uint(type, BPF_MAP_TYPE_HASH); \ + __type(key, _kt); \ + __type(value, _vt); \ + __uint(max_entries, _cap); \ + } name SEC(".maps") + +/** + * 用于在eBPF代码中声明通用的maps,其中 + * psid_count_map 存储 键值对,记录了id(由pid、ksid和usid(内核、用户栈id))及相应的值 + * sid_trace_map 存储 键值对,记录了栈id(ksid或usid)及相应的栈 + * pid_tgid 存储 键值对,记录pid以及对应的tgid + * pid_comm 存储 键值对,记录pid以及对应的命令名 + * type:指定count值的类型 + */ +#define COMMON_MAPS(count_type) \ + BPF_HASH(psid_count_map, psid, count_type, MAX_ENTRIES); \ + BPF_STACK_TRACE(sid_trace_map); \ + BPF_HASH(tgid_cgroup_map, __u32, \ + char[CONTAINER_ID_LEN], MAX_ENTRIES / 100); \ + BPF_HASH(pid_info_map, u32, task_info, MAX_ENTRIES / 10); + +#define COMMON_VALS \ + const volatile bool trace_user = false; \ + const volatile bool trace_kernel = false; \ + const volatile __u64 target_cgroupid = 0; \ + const volatile __u32 target_tgid = 0; \ + const volatile __u32 self_tgid = 0; \ + const volatile __u32 freq = 0; \ + bool __active = false; \ + __u64 __last_n = 0; \ + __u64 __next_n = 0; + +#define CHECK_ACTIVE \ + if (!__active) \ + return 0; + +#define TS bpf_ktime_get_ns() + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) +#define CHECK_FREQ(_ts) +#else +/* + 内置函数: + bool __atomic_compare_exchange_n ( + type *ptr, type *expected, type desired, bool weak, + int success_memorder, int failure_memorder + ) + + * 内置函数__atomic_compare_exchange_n实现了原子性的比较和交换操作。 + * 该函数用于比较指针ptr所指向位置的内容与expected所指向位置的内容。 + * 如果相等,则进行读-修改-写操作,将desired数据写入ptr; + * 如果不相等,则进行读操作,将当前ptr的内容写入expected。 + * 参数weak表示使用弱类型compare_exchange(true) + * 或强类型compare_exchange(false), + * 其中弱类型可能会失败而强类型永远不会失败。 + * 在许多情况下,目标只提供了强变体并忽略了该参数。 + * 当存在疑虑时,请使用强变体。 + * + * 如果desired成功写入ptr,则返回true, + * 并根据success_memorder指定的内存顺序影响内存; + * 对可用的内存顺序没有限制。 + * + * 否则,返回false,并根据failure_memorder影响内存; + * 该内存顺序不能是__ATOMIC_RELEASE或__ATOMIC_ACQ_REL, + * 并且不能比success_memorder更严格。 + */ +#define CHECK_FREQ(_ts) \ + if (freq) \ + { \ + __next_n = (_ts * freq) >> 30; \ + if (__atomic_compare_exchange_n( \ + &__next_n, &__last_n, __next_n, true, \ + __ATOMIC_RELAXED, __ATOMIC_RELAXED)) \ + return 0; \ + } +#endif + +#define GET_CURR \ + (struct task_struct *)bpf_get_current_task() + +// 如果没有设置目标进程,则检查被采集进程是否为内核线程,是则退出采集 +#define CHECK_KTHREAD(_task) \ + if (!target_tgid && BPF_CORE_READ(_task, flags) & PF_KTHREAD) \ + return 0; + +#define CHECK_TGID(_tgid) \ + if ((!_tgid) || (_tgid == self_tgid) || (target_tgid > 0 && _tgid != target_tgid)) \ + return 0; + +#define GET_KNODE(_task) \ + BPF_CORE_READ(_task, cgroups, dfl_cgrp, kn) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) +#define CHECK_CGID(_knode) \ + if (target_cgroupid > 0 && BPF_CORE_READ(_knode, id.id) != target_cgroupid) \ + return 0; +#else +#define CHECK_CGID(_knode) \ + if (target_cgroupid > 0 && BPF_CORE_READ(_knode, id) != target_cgroupid) \ + return 0; +#endif + +#define TRY_SAVE_INFO(_task, _pid, _tgid, _knode) \ + if (!bpf_map_lookup_elem(&pid_info_map, &_pid)) \ + { \ + task_info info = {0}; \ + bpf_core_read_str(info.comm, COMM_LEN, &_task->comm); \ + info.tgid = _tgid; \ + bpf_map_update_elem(&pid_info_map, &_pid, &info, BPF_NOEXIST); \ + if (!bpf_map_lookup_elem(&tgid_cgroup_map, &(info.tgid))) \ + { \ + char cgroup_name[CONTAINER_ID_LEN] = {0}; \ + bpf_probe_read_kernel_str(cgroup_name, CONTAINER_ID_LEN, BPF_CORE_READ(_knode, name)); \ + bpf_map_update_elem(&tgid_cgroup_map, &(info.tgid), &cgroup_name, BPF_NOEXIST); \ + } \ + } + +#define TRACE_AND_GET_COUNT_KEY(_pid, _ctx) \ + { \ + .pid = _pid, \ + .usid = trace_user ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) : -1, \ + .ksid = trace_kernel ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP) : -1, \ + } + +#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h b/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h deleted file mode 100644 index 433068353..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 The LMP Authors. -// -// Licensed 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// 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. -// -// author: luiyanbing@foxmail.com -// -// 用于eBPF程序的宏 - -#ifndef STACK_ANALYZER_EBPF -#define STACK_ANALYZER_EBPF - -#include "sa_common.h" - -#define PF_KTHREAD 0x00200000 -#define RET_IF_KERN(task) \ - do \ - { \ - int flags = BPF_CORE_READ(task, flags); \ - if (flags & PF_KTHREAD) \ - return 0; \ - } while (false) - -/// @brief 创建一个指定名字的ebpf调用栈表 -/// @param 新栈表的名字 -#define BPF_STACK_TRACE(name) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_STACK_TRACE); \ - __uint(key_size, sizeof(__u32)); \ - __uint(value_size, MAX_STACKS * sizeof(__u64)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - -/// @brief 创建一个指定名字和键值类型的ebpf散列表 -/// @param name 新散列表的名字 -/// @param type1 键的类型 -/// @param type2 值的类型 -#define BPF_HASH(name, type1, type2) \ - struct \ - { \ - __uint(type, BPF_MAP_TYPE_HASH); \ - __uint(key_size, sizeof(type1)); \ - __uint(value_size, sizeof(type2)); \ - __uint(max_entries, MAX_ENTRIES); \ - } name SEC(".maps") - -/** - * 用于在eBPF代码中声明通用的maps,其中 - * psid_count_map 存储 键值对,记录了id(由pid、ksid和usid(内核、用户栈id))及相应的值 - * sid_trace_map 存储 键值对,记录了栈id(ksid或usid)及相应的栈 - * pid_tgid 存储 键值对,记录pid以及对应的tgid - * pid_comm 存储 键值对,记录pid以及对应的命令名 - * type:指定count值的类型 - */ -#define COMMON_MAPS(count_type) \ - BPF_HASH(psid_count_map, psid, count_type); \ - BPF_STACK_TRACE(sid_trace_map); \ - BPF_HASH(pid_info_map, u32, task_info); - -#define COMMON_VALS \ - const volatile bool trace_user = false; \ - const volatile bool trace_kernel = false; \ - const volatile int self_pid = 0; \ - bool __active = false; - -#define CHECK_ACTIVE if(!__active) return 0; - -#define SAVE_TASK_INFO(_pid, _task) \ - if (!bpf_map_lookup_elem(&pid_info_map, &_pid)) \ - { \ - task_info info; \ - info.pid = get_task_ns_pid(_task); \ - bpf_get_current_comm(info.comm, COMM_LEN); \ - info.tgid = get_task_ns_tgid(_task); \ - fill_container_id(_task, info.cid); \ - bpf_map_update_elem(&pid_info_map, &_pid, &info, BPF_NOEXIST); \ - } - -#define GET_COUNT_KEY(_pid, _ctx) \ - ((psid){ \ - .pid = _pid, \ - .usid = trace_user ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK) : -1, \ - .ksid = trace_kernel ? bpf_get_stackid(_ctx, &sid_trace_map, BPF_F_FAST_STACK_CMP) : -1, \ - }) - -#endif \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/include/sa_user.h b/eBPF_Supermarket/Stack_Analyser/include/sa_user.h deleted file mode 100644 index ae05013f2..000000000 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_user.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2024 The LMP Authors. -// -// Licensed 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 -// -// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE -// -// 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. -// -// author: luiyanbing@foxmail.com -// -// 用户态使用的宏 - -#ifndef STACK_ANALYZER_USER -#define STACK_ANALYZER_USER - -#include -#include -#include -#include -#include -#include - -#include "sa_common.h" - -/// @brief 检查错误,若错误成立则打印带原因的错误信息并使上层函数返回-1 -/// @param cond 被检查的条件表达式 -/// @param info 要打印的错误信息 -#define CHECK_ERR(cond, ...) \ - if (cond) \ - { \ - fprintf(stderr, _ERED __VA_ARGS__); \ - fprintf(stderr, " [%s]\n" _RE, strerror(errno)); \ - return -1; \ - } - -#include -/// @brief 检查错误,若错误成立则打印带原因的错误信息并退出 -/// @param cond 被检查的条件表达式 -/// @param info 要打印的错误信息 -#define CHECK_ERR_EXIT(cond, ...) \ - if (cond) \ - { \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " [%s]\n", strerror(errno)); \ - exit(EXIT_FAILURE); \ - } - -#define BANNER \ - "\033[38;2;214;165;4m \033[39m\033[38;2;219;159;5m \033[39m\033[38;2;223;152;7m \033[39m\033[38;2;227;146;9m_\033[39m\033[38;2;231;140;12m_\033[39m\033[38;2;234;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;121;21m_\033[39m\033[38;2;243;114;25m \033[39m\033[38;2;246;108;28m_\033[39m\033[38;2;248;102;33m_\033[39m\033[38;2;250;96;37m \033[39m\033[38;2;251;90;41m \033[39m\033[38;2;253;84;46m \033[39m\033[38;2;254;78;51m \033[39m\033[38;2;254;72;56m \033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;61;67m \033[39m\033[38;2;254;55;73m \033[39m\033[38;2;253;50;79m \033[39m\033[38;2;252;45;85m \033[39m\033[38;2;251;41;91m \033[39m\033[38;2;250;36;97m \033[39m\033[38;2;248;32;103m \033[39m\033[38;2;245;28;109m_\033[39m\033[38;2;243;24;116m_\033[39m\033[38;2;240;20;122m \033[39m\033[38;2;237;17;128m \033[39m\033[38;2;234;14;135m \033[39m\033[38;2;230;11;141m_\033[39m\033[38;2;226;9;147m_\033[39m\033[38;2;222;7;153m_\033[39m\033[38;2;218;5;160m \033[39m\033[38;2;213;4;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;204;1;178m \033[39m\033[38;2;198;1;183m \033[39m\033[38;2;193;1;189m \033[39m\033[38;2;187;1;194m \033[39m\033[38;2;182;1;200m \033[39m\033[38;2;176;2;205m \033[39m\033[38;2;170;3;210m \033[39m\033[38;2;164;4;215m \033[39m\033[38;2;158;6;219m \033[39m\033[38;2;152;7;223m \033[39m\033[38;2;145;10;227m \033[39m\033[38;2;139;12;231m \033[39m\033[38;2;133;15;235m \033[39m\033[38;2;126;18;238m_\033[39m\033[38;2;120;21;241m_\033[39m\033[38;2;114;25;244m \033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;95;37;250m \033[39m\033[38;2;89;42;252m \033[39m\033[38;2;83;47;253m \033[39m\033[38;2;77;52;254m \033[39m\033[38;2;71;57;254m \033[39m\033[38;2;66;62;254m \033[39m\033[38;2;60;68;254m \033[39m\033[38;2;55;74;254m \033[39m\033[38;2;50;79;253m \033[39m\033[38;2;45;85;252m \033[39m\033[38;2;40;91;251m \033[39m\033[38;2;36;97;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;110;245m \033[39m\033[38;2;24;116;243m \033[39m\033[38;2;20;123;240m \033[39m\033[38;2;17;129;237m \033[39m\033[38;2;14;135;233m \033[39m\033[38;2;11;142;230m\033[39m\n" \ - "\033[38;2;215;164;4m \033[39m\033[38;2;219;158;5m \033[39m\033[38;2;223;152;7m/\033[39m\033[38;2;227;145;10m \033[39m\033[38;2;231;139;12m_\033[39m\033[38;2;235;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;120;21m/\033[39m\033[38;2;244;114;25m/\033[39m\033[38;2;246;108;29m \033[39m\033[38;2;248;101;33m/\033[39m\033[38;2;250;95;37m_\033[39m\033[38;2;252;89;42m_\033[39m\033[38;2;253;83;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m_\033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;60;68m_\033[39m\033[38;2;254;55;73m_\033[39m\033[38;2;253;50;79m_\033[39m\033[38;2;252;45;85m_\033[39m\033[38;2;251;40;91m_\033[39m\033[38;2;249;36;97m_\033[39m\033[38;2;247;31;104m/\033[39m\033[38;2;245;27;110m \033[39m\033[38;2;243;24;116m/\033[39m\033[38;2;240;20;122m_\033[39m\033[38;2;237;17;129m_\033[39m\033[38;2;233;14;135m/\033[39m\033[38;2;230;11;142m \033[39m\033[38;2;226;9;148m \033[39m\033[38;2;222;7;154m \033[39m\033[38;2;217;5;160m|\033[39m\033[38;2;213;3;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;203;1;178m_\033[39m\033[38;2;198;1;184m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;187;1;195m_\033[39m\033[38;2;181;1;200m \033[39m\033[38;2;175;2;205m \033[39m\033[38;2;169;3;210m_\033[39m\033[38;2;163;4;215m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;151;8;224m_\033[39m\033[38;2;145;10;228m \033[39m\033[38;2;138;12;232m_\033[39m\033[38;2;132;15;235m/\033[39m\033[38;2;126;18;238m \033[39m\033[38;2;119;22;241m/\033[39m\033[38;2;113;25;244m_\033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;94;38;250m_\033[39m\033[38;2;88;42;252m_\033[39m\033[38;2;82;47;253m_\033[39m\033[38;2;76;52;254m_\033[39m\033[38;2;71;57;254m_\033[39m\033[38;2;65;63;254m_\033[39m\033[38;2;60;68;254m \033[39m\033[38;2;54;74;254m \033[39m\033[38;2;49;80;253m_\033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;40;92;251m_\033[39m\033[38;2;35;98;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;111;245m_\033[39m\033[38;2;23;117;242m_\033[39m\033[38;2;20;123;240m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;14;136;233m_\033[39m\033[38;2;11;142;229m\033[39m\n" \ - "\033[38;2;215;163;4m \033[39m\033[38;2;219;157;6m \033[39m\033[38;2;224;151;8m\\\033[39m\033[38;2;228;145;10m_\033[39m\033[38;2;232;139;12m_\033[39m\033[38;2;235;132;15m \033[39m\033[38;2;238;126;18m\\\033[39m\033[38;2;241;120;22m/\033[39m\033[38;2;244;113;25m \033[39m\033[38;2;246;107;29m_\033[39m\033[38;2;248;101;33m_\033[39m\033[38;2;250;95;38m/\033[39m\033[38;2;252;88;42m \033[39m\033[38;2;253;82;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m \033[39m\033[38;2;254;65;63m`\033[39m\033[38;2;254;60;68m/\033[39m\033[38;2;254;54;74m \033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;40;92m_\033[39m\033[38;2;249;35;98m/\033[39m\033[38;2;247;31;104m \033[39m\033[38;2;245;27;110m/\033[39m\033[38;2;242;23;117m/\033[39m\033[38;2;240;20;123m_\033[39m\033[38;2;236;17;129m/\033[39m\033[38;2;233;14;136m \033[39m\033[38;2;229;11;142m/\033[39m\033[38;2;226;9;148m|\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;167m \033[39m\033[38;2;208;2;173m/\033[39m\033[38;2;202;1;179m \033[39m\033[38;2;197;1;185m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;181;1;201m\\\033[39m\033[38;2;175;2;206m/\033[39m\033[38;2;169;3;211m \033[39m\033[38;2;163;4;216m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;150;8;224m \033[39m\033[38;2;144;10;228m`\033[39m\033[38;2;138;13;232m/\033[39m\033[38;2;132;16;235m \033[39m\033[38;2;125;19;239m/\033[39m\033[38;2;119;22;242m \033[39m\033[38;2;112;26;244m/\033[39m\033[38;2;106;30;247m \033[39m\033[38;2;100;34;249m/\033[39m\033[38;2;94;38;250m \033[39m\033[38;2;88;43;252m/\033[39m\033[38;2;82;48;253m_\033[39m\033[38;2;76;53;254m \033[39m\033[38;2;70;58;254m \033[39m\033[38;2;65;63;254m/\033[39m\033[38;2;59;69;254m \033[39m\033[38;2;54;75;254m/\033[39m\033[38;2;49;81;253m \033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;39;93;251m \033[39m\033[38;2;35;99;249m\\\033[39m\033[38;2;31;105;247m/\033[39m\033[38;2;27;111;245m \033[39m\033[38;2;23;118;242m_\033[39m\033[38;2;19;124;239m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;13;137;233m/\033[39m\033[38;2;11;143;229m\033[39m\n" \ - "\033[38;2;215;163;4m \033[39m\033[38;2;220;157;6m_\033[39m\033[38;2;224;151;8m_\033[39m\033[38;2;228;144;10m_\033[39m\033[38;2;232;138;13m/\033[39m\033[38;2;235;132;16m \033[39m\033[38;2;239;125;19m/\033[39m\033[38;2;242;119;22m \033[39m\033[38;2;244;113;26m/\033[39m\033[38;2;247;106;30m_\033[39m\033[38;2;249;100;34m/\033[39m\033[38;2;250;94;38m \033[39m\033[38;2;252;88;43m/\033[39m\033[38;2;253;82;48m_\033[39m\033[38;2;254;76;53m/\033[39m\033[38;2;254;70;58m \033[39m\033[38;2;254;65;63m/\033[39m\033[38;2;254;59;69m \033[39m\033[38;2;254;54;75m/\033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;39;92m/\033[39m\033[38;2;249;35;99m \033[39m\033[38;2;247;31;105m,\033[39m\033[38;2;245;27;111m<\033[39m\033[38;2;242;23;117m \033[39m\033[38;2;239;19;124m/\033[39m\033[38;2;236;16;130m \033[39m\033[38;2;233;13;136m_\033[39m\033[38;2;229;11;143m_\033[39m\033[38;2;225;8;149m_\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;168m/\033[39m\033[38;2;207;2;173m \033[39m\033[38;2;202;1;179m/\033[39m\033[38;2;197;1;185m \033[39m\033[38;2;191;1;191m/\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;180;1;201m/\033[39m\033[38;2;174;2;206m \033[39m\033[38;2;168;3;211m/\033[39m\033[38;2;162;4;216m_\033[39m\033[38;2;156;6;220m/\033[39m\033[38;2;150;8;225m \033[39m\033[38;2;144;10;229m/\033[39m\033[38;2;137;13;232m \033[39m\033[38;2;131;16;236m/\033[39m\033[38;2;125;19;239m \033[39m\033[38;2;118;22;242m/\033[39m\033[38;2;112;26;244m_\033[39m\033[38;2;106;30;247m/\033[39m\033[38;2;99;34;249m \033[39m\033[38;2;93;39;251m/\033[39m\033[38;2;87;43;252m \033[39m\033[38;2;81;48;253m/\033[39m\033[38;2;75;53;254m \033[39m\033[38;2;70;59;254m/\033[39m\033[38;2;64;64;254m_\033[39m\033[38;2;59;70;254m/\033[39m\033[38;2;53;75;254m \033[39m\033[38;2;48;81;253m \033[39m\033[38;2;43;87;252m_\033[39m\033[38;2;39;93;251m_\033[39m\033[38;2;34;99;249m/\033[39m\033[38;2;30;106;247m \033[39m\033[38;2;26;112;244m/\033[39m\033[38;2;22;118;242m \033[39m\033[38;2;19;124;239m \033[39m\033[38;2;16;131;236m \033[39m\033[38;2;13;137;232m \033[39m\033[38;2;10;143;229m\033[39m\n" \ - "\033[38;2;216;162;4m/\033[39m\033[38;2;220;156;6m_\033[39m\033[38;2;225;150;8m_\033[39m\033[38;2;229;144;10m_\033[39m\033[38;2;232;137;13m_\033[39m\033[38;2;236;131;16m/\033[39m\033[38;2;239;125;19m\\\033[39m\033[38;2;242;118;22m_\033[39m\033[38;2;244;112;26m_\033[39m\033[38;2;247;106;30m/\033[39m\033[38;2;249;99;34m\\\033[39m\033[38;2;251;93;39m_\033[39m\033[38;2;252;87;43m_\033[39m\033[38;2;253;81;48m,\033[39m\033[38;2;254;75;53m_\033[39m\033[38;2;254;70;58m/\033[39m\033[38;2;254;64;64m\\\033[39m\033[38;2;254;59;69m_\033[39m\033[38;2;254;53;75m_\033[39m\033[38;2;253;48;81m_\033[39m\033[38;2;252;43;87m/\033[39m\033[38;2;251;39;93m_\033[39m\033[38;2;249;34;99m/\033[39m\033[38;2;247;30;105m|\033[39m\033[38;2;245;26;112m_\033[39m\033[38;2;242;23;118m/\033[39m\033[38;2;239;19;124m_\033[39m\033[38;2;236;16;131m/\033[39m\033[38;2;232;13;137m \033[39m\033[38;2;229;10;143m \033[39m\033[38;2;225;8;150m|\033[39m\033[38;2;220;6;156m_\033[39m\033[38;2;216;4;162m/\033[39m\033[38;2;211;3;168m_\033[39m\033[38;2;207;2;174m/\033[39m\033[38;2;201;1;180m \033[39m\033[38;2;196;1;186m/\033[39m\033[38;2;191;1;191m_\033[39m\033[38;2;185;1;197m/\033[39m\033[38;2;179;1;202m\\\033[39m\033[38;2;174;2;207m_\033[39m\033[38;2;168;3;212m_\033[39m\033[38;2;162;5;216m,\033[39m\033[38;2;155;6;221m_\033[39m\033[38;2;149;8;225m/\033[39m\033[38;2;143;11;229m_\033[39m\033[38;2;137;13;233m/\033[39m\033[38;2;130;16;236m\\\033[39m\033[38;2;124;19;239m_\033[39m\033[38;2;118;23;242m_\033[39m\033[38;2;111;27;245m,\033[39m\033[38;2;105;31;247m \033[39m\033[38;2;99;35;249m/\033[39m\033[38;2;93;39;251m \033[39m\033[38;2;87;44;252m/\033[39m\033[38;2;81;49;253m_\033[39m\033[38;2;75;54;254m_\033[39m\033[38;2;69;59;254m_\033[39m\033[38;2;63;65;254m/\033[39m\033[38;2;58;70;254m\\\033[39m\033[38;2;53;76;254m_\033[39m\033[38;2;48;82;253m_\033[39m\033[38;2;43;88;252m_\033[39m\033[38;2;38;94;250m/\033[39m\033[38;2;34;100;249m_\033[39m\033[38;2;30;106;247m/\033[39m\033[38;2;26;112;244m \033[39m\033[38;2;22;119;242m \033[39m\033[38;2;19;125;239m \033[39m\033[38;2;16;131;235m \033[39m\033[38;2;13;138;232m \033[39m\033[38;2;10;144;228m\033[39m\n" \ - "\033[38;2;216;162;4m \033[39m\033[38;2;221;155;6m \033[39m\033[38;2;225;149;8m \033[39m\033[38;2;229;143;11m \033[39m\033[38;2;233;137;13m \033[39m\033[38;2;236;130;16m \033[39m\033[38;2;239;124;19m \033[39m\033[38;2;242;118;23m \033[39m\033[38;2;245;111;27m \033[39m\033[38;2;247;105;30m \033[39m\033[38;2;249;99;35m \033[39m\033[38;2;251;93;39m \033[39m\033[38;2;252;87;44m \033[39m\033[38;2;253;81;49m \033[39m\033[38;2;254;75;54m \033[39m\033[38;2;254;69;59m \033[39m\033[38;2;254;64;64m \033[39m\033[38;2;254;58;70m \033[39m\033[38;2;254;53;76m \033[39m\033[38;2;253;48;82m \033[39m\033[38;2;252;43;88m \033[39m\033[38;2;250;38;94m \033[39m\033[38;2;249;34;100m \033[39m\033[38;2;247;30;106m \033[39m\033[38;2;244;26;112m \033[39m\033[38;2;242;22;119m \033[39m\033[38;2;239;19;125m \033[39m\033[38;2;235;16;131m \033[39m\033[38;2;232;13;138m \033[39m\033[38;2;228;10;144m \033[39m\033[38;2;224;8;150m \033[39m\033[38;2;220;6;157m \033[39m\033[38;2;216;4;163m \033[39m\033[38;2;211;3;169m \033[39m\033[38;2;206;2;175m \033[39m\033[38;2;201;1;181m \033[39m\033[38;2;196;1;186m \033[39m\033[38;2;190;1;192m \033[39m\033[38;2;185;1;197m \033[39m\033[38;2;179;1;202m \033[39m\033[38;2;173;2;207m \033[39m\033[38;2;167;3;212m \033[39m\033[38;2;161;5;217m \033[39m\033[38;2;155;6;221m \033[39m\033[38;2;149;9;225m \033[39m\033[38;2;142;11;229m \033[39m\033[38;2;136;14;233m/\033[39m\033[38;2;130;16;236m_\033[39m\033[38;2;123;20;240m_\033[39m\033[38;2;117;23;242m_\033[39m\033[38;2;111;27;245m_\033[39m\033[38;2;104;31;247m/\033[39m\033[38;2;98;35;249m \033[39m\033[38;2;92;40;251m \033[39m\033[38;2;86;44;252m \033[39m\033[38;2;80;49;253m \033[39m\033[38;2;74;54;254m \033[39m\033[38;2;68;60;254m \033[39m\033[38;2;63;65;254m \033[39m\033[38;2;58;71;254m \033[39m\033[38;2;52;76;254m \033[39m\033[38;2;47;82;253m \033[39m\033[38;2;42;88;252m \033[39m\033[38;2;38;94;250m \033[39m\033[38;2;33;101;248m \033[39m\033[38;2;29;107;246m \033[39m\033[38;2;25;113;244m \033[39m\033[38;2;22;119;241m \033[39m\033[38;2;18;126;238m \033[39m\033[38;2;15;132;235m \033[39m\033[38;2;12;138;232m \033[39m\033[38;2;10;145;228m\033[39m" - -#define COLLECTOR_INFO(_name) _BLUE "\b\b\b\bCollector for " _name " trace" _RE - -#endif diff --git a/eBPF_Supermarket/Stack_Analyser/include/task.h b/eBPF_Supermarket/Stack_Analyser/include/task.h index 79f532543..db2e9b388 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/task.h +++ b/eBPF_Supermarket/Stack_Analyser/include/task.h @@ -71,13 +71,12 @@ statfunc u32 get_task_ns_ppid(struct task_struct *task) return get_task_pid_vnr(real_parent); } -static void fill_container_id(struct task_struct *task, char *container_id) +static void fill_container_id(struct kernfs_node *knode, char *container_id) { - struct kernfs_node *knode = BPF_CORE_READ(task, cgroups, subsys[0], cgroup, kn); if (BPF_CORE_READ(knode, parent) != NULL) { char *aus; - bpf_probe_read(&aus, sizeof(void *), &knode->name); + bpf_probe_read(&aus, sizeof(void *), &(knode->name)); bpf_probe_read_str(container_id, CONTAINER_ID_LEN, aus); } } diff --git a/eBPF_Supermarket/Stack_Analyser/include/trace.h b/eBPF_Supermarket/Stack_Analyser/include/trace.h new file mode 100644 index 000000000..777248f94 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/trace.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __TRACE_HELPERS_H +#define __TRACE_HELPERS_H + +#include + +#define NSEC_PER_SEC 1000000000ULL + +struct ksym +{ + const char *name; + unsigned long addr; +}; + +struct ksyms; + +struct ksyms *ksyms__load(void); +void ksyms__free(struct ksyms *ksyms); +const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, + unsigned long addr); +const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, + const char *name); + +struct sym +{ + const char *name; + unsigned long start; + unsigned long size; + unsigned long offset; +}; + +struct syms; + +struct syms *syms__load_pid(int tgid); +struct syms *syms__load_file(const char *fname, int tgid); +void syms__free(struct syms *syms); +struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); +const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, + char **dso_name, unsigned long *dso_offset); + +struct syms_cache; + +struct syms_cache *syms_cache__new(int nr); +struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); +void syms_cache__free(struct syms_cache *syms_cache); + +struct partition +{ + char *name; + unsigned int dev; +}; + +struct partitions; + +struct partitions *partitions__load(void); +void partitions__free(struct partitions *partitions); +const struct partition * +partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); +const struct partition * +partitions__get_by_name(const struct partitions *partitions, const char *name); + +void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); +void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, + unsigned int step, const char *val_type); + +unsigned long long get_ktime_ns(void); + +bool is_kernel_module(const char *name); + +/* + * When attempting to use kprobe/kretprobe, please check out new fentry/fexit + * probes, as they provide better performance and usability. But in some + * situations we have to fallback to kprobe/kretprobe probes. This helper + * is used to detect fentry/fexit support for the specified kernel function. + * + * 1. A gap between kernel versions, kernel BTF is exposed + * starting from 5.4 kernel. but fentry/fexit is actually + * supported starting from 5.5. + * 2. Whether kernel supports module BTF or not + * + * *name* is the name of a kernel function to be attached to, which can be + * from vmlinux or a kernel module. + * *mod* is a hint that indicates the *name* may reside in module BTF, + * if NULL, it means *name* belongs to vmlinux. + */ +bool fentry_can_attach(const char *name, const char *mod); + +/* + * The name of a kernel function to be attached to may be changed between + * kernel releases. This helper is used to confirm whether the target kernel + * uses a certain function name before attaching. + * + * It is achieved by scaning + * /sys/kernel/debug/tracing/available_filter_functions + * If this file does not exist, it fallbacks to parse /proc/kallsyms, + * which is slower. + */ +bool kprobe_exists(const char *name); +bool tracepoint_exists(const char *category, const char *event); + +bool vmlinux_btf_exists(void); +bool module_btf_exists(const char *mod); + +bool probe_tp_btf(const char *name); +bool probe_ringbuf(); +const struct ksym *ksyms__find_symbol(const struct ksyms *ksyms, + const char *name); + +extern struct ksyms *ksyms; +extern struct syms_cache *syms_cache; +extern struct syms *syms; + +#endif /* __TRACE_HELPERS_H */ diff --git a/eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h b/eBPF_Supermarket/Stack_Analyser/include/uprobe.h similarity index 100% rename from eBPF_Supermarket/Stack_Analyser/include/uprobe_helpers.h rename to eBPF_Supermarket/Stack_Analyser/include/uprobe.h diff --git a/eBPF_Supermarket/Stack_Analyser/include/user.h b/eBPF_Supermarket/Stack_Analyser/include/user.h new file mode 100644 index 000000000..b46c53a76 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/include/user.h @@ -0,0 +1,90 @@ +// Copyright 2024 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: luiyanbing@foxmail.com +// +// 用户态使用的宏 + +#ifndef STACK_ANALYZER_USER +#define STACK_ANALYZER_USER + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define _COL_PREFIX "\033[" +#define _BLUE _COL_PREFIX "1;34m" +#define _GREEN _COL_PREFIX "1;32m" +#define _RED _COL_PREFIX "1;35m" +#define _ERED _COL_PREFIX "1;31m" +#define _RE _COL_PREFIX "0m" + +#define PUT_ERR_REASON \ + fprintf(stderr, _ERED " [%s]\n" _RE, strerror(errno)); + +#define HINT_ERR(...) \ + { \ + fprintf(stderr, _ERED __VA_ARGS__); \ + PUT_ERR_REASON; \ + } + +#define DEAL_ERR(action, ...) \ + { \ + HINT_ERR(__VA_ARGS__); \ + action; \ + } + +/// @brief 检查错误,若错误成立则打印带原因的错误信息,并执行action +/// @param action cond成立执行的语句 +/// @param cond 被检查的条件表达式 +/// @param info 要打印的错误信息 +#define CHECK_ERR(action, cond, ...) \ + if (cond) \ + DEAL_ERR(action, __VA_ARGS__) + +/// @brief 检查错误,若错误成立则打印带原因的错误信息并使上层函数返回-1 +/// @param cond 被检查的条件表达式 +/// @param info 要打印的错误信息 +#define CHECK_ERR_RN1(cond, ...) \ + CHECK_ERR(return -1, cond, __VA_ARGS__) + +#define BANNER \ + "\033[38;2;214;165;4m \033[39m\033[38;2;219;159;5m \033[39m\033[38;2;223;152;7m \033[39m\033[38;2;227;146;9m_\033[39m\033[38;2;231;140;12m_\033[39m\033[38;2;234;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;121;21m_\033[39m\033[38;2;243;114;25m \033[39m\033[38;2;246;108;28m_\033[39m\033[38;2;248;102;33m_\033[39m\033[38;2;250;96;37m \033[39m\033[38;2;251;90;41m \033[39m\033[38;2;253;84;46m \033[39m\033[38;2;254;78;51m \033[39m\033[38;2;254;72;56m \033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;61;67m \033[39m\033[38;2;254;55;73m \033[39m\033[38;2;253;50;79m \033[39m\033[38;2;252;45;85m \033[39m\033[38;2;251;41;91m \033[39m\033[38;2;250;36;97m \033[39m\033[38;2;248;32;103m \033[39m\033[38;2;245;28;109m_\033[39m\033[38;2;243;24;116m_\033[39m\033[38;2;240;20;122m \033[39m\033[38;2;237;17;128m \033[39m\033[38;2;234;14;135m \033[39m\033[38;2;230;11;141m_\033[39m\033[38;2;226;9;147m_\033[39m\033[38;2;222;7;153m_\033[39m\033[38;2;218;5;160m \033[39m\033[38;2;213;4;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;204;1;178m \033[39m\033[38;2;198;1;183m \033[39m\033[38;2;193;1;189m \033[39m\033[38;2;187;1;194m \033[39m\033[38;2;182;1;200m \033[39m\033[38;2;176;2;205m \033[39m\033[38;2;170;3;210m \033[39m\033[38;2;164;4;215m \033[39m\033[38;2;158;6;219m \033[39m\033[38;2;152;7;223m \033[39m\033[38;2;145;10;227m \033[39m\033[38;2;139;12;231m \033[39m\033[38;2;133;15;235m \033[39m\033[38;2;126;18;238m_\033[39m\033[38;2;120;21;241m_\033[39m\033[38;2;114;25;244m \033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;95;37;250m \033[39m\033[38;2;89;42;252m \033[39m\033[38;2;83;47;253m \033[39m\033[38;2;77;52;254m \033[39m\033[38;2;71;57;254m \033[39m\033[38;2;66;62;254m \033[39m\033[38;2;60;68;254m \033[39m\033[38;2;55;74;254m \033[39m\033[38;2;50;79;253m \033[39m\033[38;2;45;85;252m \033[39m\033[38;2;40;91;251m \033[39m\033[38;2;36;97;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;110;245m \033[39m\033[38;2;24;116;243m \033[39m\033[38;2;20;123;240m \033[39m\033[38;2;17;129;237m \033[39m\033[38;2;14;135;233m \033[39m\033[38;2;11;142;230m\033[39m\n" \ + "\033[38;2;215;164;4m \033[39m\033[38;2;219;158;5m \033[39m\033[38;2;223;152;7m/\033[39m\033[38;2;227;145;10m \033[39m\033[38;2;231;139;12m_\033[39m\033[38;2;235;133;15m_\033[39m\033[38;2;238;127;18m_\033[39m\033[38;2;241;120;21m/\033[39m\033[38;2;244;114;25m/\033[39m\033[38;2;246;108;29m \033[39m\033[38;2;248;101;33m/\033[39m\033[38;2;250;95;37m_\033[39m\033[38;2;252;89;42m_\033[39m\033[38;2;253;83;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m_\033[39m\033[38;2;254;66;62m \033[39m\033[38;2;254;60;68m_\033[39m\033[38;2;254;55;73m_\033[39m\033[38;2;253;50;79m_\033[39m\033[38;2;252;45;85m_\033[39m\033[38;2;251;40;91m_\033[39m\033[38;2;249;36;97m_\033[39m\033[38;2;247;31;104m/\033[39m\033[38;2;245;27;110m \033[39m\033[38;2;243;24;116m/\033[39m\033[38;2;240;20;122m_\033[39m\033[38;2;237;17;129m_\033[39m\033[38;2;233;14;135m/\033[39m\033[38;2;230;11;142m \033[39m\033[38;2;226;9;148m \033[39m\033[38;2;222;7;154m \033[39m\033[38;2;217;5;160m|\033[39m\033[38;2;213;3;166m \033[39m\033[38;2;208;2;172m \033[39m\033[38;2;203;1;178m_\033[39m\033[38;2;198;1;184m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;187;1;195m_\033[39m\033[38;2;181;1;200m \033[39m\033[38;2;175;2;205m \033[39m\033[38;2;169;3;210m_\033[39m\033[38;2;163;4;215m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;151;8;224m_\033[39m\033[38;2;145;10;228m \033[39m\033[38;2;138;12;232m_\033[39m\033[38;2;132;15;235m/\033[39m\033[38;2;126;18;238m \033[39m\033[38;2;119;22;241m/\033[39m\033[38;2;113;25;244m_\033[39m\033[38;2;107;29;246m \033[39m\033[38;2;101;33;248m \033[39m\033[38;2;94;38;250m_\033[39m\033[38;2;88;42;252m_\033[39m\033[38;2;82;47;253m_\033[39m\033[38;2;76;52;254m_\033[39m\033[38;2;71;57;254m_\033[39m\033[38;2;65;63;254m_\033[39m\033[38;2;60;68;254m \033[39m\033[38;2;54;74;254m \033[39m\033[38;2;49;80;253m_\033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;40;92;251m_\033[39m\033[38;2;35;98;249m \033[39m\033[38;2;31;104;247m \033[39m\033[38;2;27;111;245m_\033[39m\033[38;2;23;117;242m_\033[39m\033[38;2;20;123;240m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;14;136;233m_\033[39m\033[38;2;11;142;229m\033[39m\n" \ + "\033[38;2;215;163;4m \033[39m\033[38;2;219;157;6m \033[39m\033[38;2;224;151;8m\\\033[39m\033[38;2;228;145;10m_\033[39m\033[38;2;232;139;12m_\033[39m\033[38;2;235;132;15m \033[39m\033[38;2;238;126;18m\\\033[39m\033[38;2;241;120;22m/\033[39m\033[38;2;244;113;25m \033[39m\033[38;2;246;107;29m_\033[39m\033[38;2;248;101;33m_\033[39m\033[38;2;250;95;38m/\033[39m\033[38;2;252;88;42m \033[39m\033[38;2;253;82;47m_\033[39m\033[38;2;254;77;52m_\033[39m\033[38;2;254;71;57m \033[39m\033[38;2;254;65;63m`\033[39m\033[38;2;254;60;68m/\033[39m\033[38;2;254;54;74m \033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;40;92m_\033[39m\033[38;2;249;35;98m/\033[39m\033[38;2;247;31;104m \033[39m\033[38;2;245;27;110m/\033[39m\033[38;2;242;23;117m/\033[39m\033[38;2;240;20;123m_\033[39m\033[38;2;236;17;129m/\033[39m\033[38;2;233;14;136m \033[39m\033[38;2;229;11;142m/\033[39m\033[38;2;226;9;148m|\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;167m \033[39m\033[38;2;208;2;173m/\033[39m\033[38;2;202;1;179m \033[39m\033[38;2;197;1;185m_\033[39m\033[38;2;192;1;190m_\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;181;1;201m\\\033[39m\033[38;2;175;2;206m/\033[39m\033[38;2;169;3;211m \033[39m\033[38;2;163;4;216m_\033[39m\033[38;2;157;6;220m_\033[39m\033[38;2;150;8;224m \033[39m\033[38;2;144;10;228m`\033[39m\033[38;2;138;13;232m/\033[39m\033[38;2;132;16;235m \033[39m\033[38;2;125;19;239m/\033[39m\033[38;2;119;22;242m \033[39m\033[38;2;112;26;244m/\033[39m\033[38;2;106;30;247m \033[39m\033[38;2;100;34;249m/\033[39m\033[38;2;94;38;250m \033[39m\033[38;2;88;43;252m/\033[39m\033[38;2;82;48;253m_\033[39m\033[38;2;76;53;254m \033[39m\033[38;2;70;58;254m \033[39m\033[38;2;65;63;254m/\033[39m\033[38;2;59;69;254m \033[39m\033[38;2;54;75;254m/\033[39m\033[38;2;49;81;253m \033[39m\033[38;2;44;86;252m_\033[39m\033[38;2;39;93;251m \033[39m\033[38;2;35;99;249m\\\033[39m\033[38;2;31;105;247m/\033[39m\033[38;2;27;111;245m \033[39m\033[38;2;23;118;242m_\033[39m\033[38;2;19;124;239m_\033[39m\033[38;2;16;130;236m_\033[39m\033[38;2;13;137;233m/\033[39m\033[38;2;11;143;229m\033[39m\n" \ + "\033[38;2;215;163;4m \033[39m\033[38;2;220;157;6m_\033[39m\033[38;2;224;151;8m_\033[39m\033[38;2;228;144;10m_\033[39m\033[38;2;232;138;13m/\033[39m\033[38;2;235;132;16m \033[39m\033[38;2;239;125;19m/\033[39m\033[38;2;242;119;22m \033[39m\033[38;2;244;113;26m/\033[39m\033[38;2;247;106;30m_\033[39m\033[38;2;249;100;34m/\033[39m\033[38;2;250;94;38m \033[39m\033[38;2;252;88;43m/\033[39m\033[38;2;253;82;48m_\033[39m\033[38;2;254;76;53m/\033[39m\033[38;2;254;70;58m \033[39m\033[38;2;254;65;63m/\033[39m\033[38;2;254;59;69m \033[39m\033[38;2;254;54;75m/\033[39m\033[38;2;253;49;80m_\033[39m\033[38;2;252;44;86m_\033[39m\033[38;2;251;39;92m/\033[39m\033[38;2;249;35;99m \033[39m\033[38;2;247;31;105m,\033[39m\033[38;2;245;27;111m<\033[39m\033[38;2;242;23;117m \033[39m\033[38;2;239;19;124m/\033[39m\033[38;2;236;16;130m \033[39m\033[38;2;233;13;136m_\033[39m\033[38;2;229;11;143m_\033[39m\033[38;2;225;8;149m_\033[39m\033[38;2;221;6;155m \033[39m\033[38;2;217;5;161m|\033[39m\033[38;2;212;3;168m/\033[39m\033[38;2;207;2;173m \033[39m\033[38;2;202;1;179m/\033[39m\033[38;2;197;1;185m \033[39m\033[38;2;191;1;191m/\033[39m\033[38;2;186;1;196m \033[39m\033[38;2;180;1;201m/\033[39m\033[38;2;174;2;206m \033[39m\033[38;2;168;3;211m/\033[39m\033[38;2;162;4;216m_\033[39m\033[38;2;156;6;220m/\033[39m\033[38;2;150;8;225m \033[39m\033[38;2;144;10;229m/\033[39m\033[38;2;137;13;232m \033[39m\033[38;2;131;16;236m/\033[39m\033[38;2;125;19;239m \033[39m\033[38;2;118;22;242m/\033[39m\033[38;2;112;26;244m_\033[39m\033[38;2;106;30;247m/\033[39m\033[38;2;99;34;249m \033[39m\033[38;2;93;39;251m/\033[39m\033[38;2;87;43;252m \033[39m\033[38;2;81;48;253m/\033[39m\033[38;2;75;53;254m \033[39m\033[38;2;70;59;254m/\033[39m\033[38;2;64;64;254m_\033[39m\033[38;2;59;70;254m/\033[39m\033[38;2;53;75;254m \033[39m\033[38;2;48;81;253m \033[39m\033[38;2;43;87;252m_\033[39m\033[38;2;39;93;251m_\033[39m\033[38;2;34;99;249m/\033[39m\033[38;2;30;106;247m \033[39m\033[38;2;26;112;244m/\033[39m\033[38;2;22;118;242m \033[39m\033[38;2;19;124;239m \033[39m\033[38;2;16;131;236m \033[39m\033[38;2;13;137;232m \033[39m\033[38;2;10;143;229m\033[39m\n" \ + "\033[38;2;216;162;4m/\033[39m\033[38;2;220;156;6m_\033[39m\033[38;2;225;150;8m_\033[39m\033[38;2;229;144;10m_\033[39m\033[38;2;232;137;13m_\033[39m\033[38;2;236;131;16m/\033[39m\033[38;2;239;125;19m\\\033[39m\033[38;2;242;118;22m_\033[39m\033[38;2;244;112;26m_\033[39m\033[38;2;247;106;30m/\033[39m\033[38;2;249;99;34m\\\033[39m\033[38;2;251;93;39m_\033[39m\033[38;2;252;87;43m_\033[39m\033[38;2;253;81;48m,\033[39m\033[38;2;254;75;53m_\033[39m\033[38;2;254;70;58m/\033[39m\033[38;2;254;64;64m\\\033[39m\033[38;2;254;59;69m_\033[39m\033[38;2;254;53;75m_\033[39m\033[38;2;253;48;81m_\033[39m\033[38;2;252;43;87m/\033[39m\033[38;2;251;39;93m_\033[39m\033[38;2;249;34;99m/\033[39m\033[38;2;247;30;105m|\033[39m\033[38;2;245;26;112m_\033[39m\033[38;2;242;23;118m/\033[39m\033[38;2;239;19;124m_\033[39m\033[38;2;236;16;131m/\033[39m\033[38;2;232;13;137m \033[39m\033[38;2;229;10;143m \033[39m\033[38;2;225;8;150m|\033[39m\033[38;2;220;6;156m_\033[39m\033[38;2;216;4;162m/\033[39m\033[38;2;211;3;168m_\033[39m\033[38;2;207;2;174m/\033[39m\033[38;2;201;1;180m \033[39m\033[38;2;196;1;186m/\033[39m\033[38;2;191;1;191m_\033[39m\033[38;2;185;1;197m/\033[39m\033[38;2;179;1;202m\\\033[39m\033[38;2;174;2;207m_\033[39m\033[38;2;168;3;212m_\033[39m\033[38;2;162;5;216m,\033[39m\033[38;2;155;6;221m_\033[39m\033[38;2;149;8;225m/\033[39m\033[38;2;143;11;229m_\033[39m\033[38;2;137;13;233m/\033[39m\033[38;2;130;16;236m\\\033[39m\033[38;2;124;19;239m_\033[39m\033[38;2;118;23;242m_\033[39m\033[38;2;111;27;245m,\033[39m\033[38;2;105;31;247m \033[39m\033[38;2;99;35;249m/\033[39m\033[38;2;93;39;251m \033[39m\033[38;2;87;44;252m/\033[39m\033[38;2;81;49;253m_\033[39m\033[38;2;75;54;254m_\033[39m\033[38;2;69;59;254m_\033[39m\033[38;2;63;65;254m/\033[39m\033[38;2;58;70;254m\\\033[39m\033[38;2;53;76;254m_\033[39m\033[38;2;48;82;253m_\033[39m\033[38;2;43;88;252m_\033[39m\033[38;2;38;94;250m/\033[39m\033[38;2;34;100;249m_\033[39m\033[38;2;30;106;247m/\033[39m\033[38;2;26;112;244m \033[39m\033[38;2;22;119;242m \033[39m\033[38;2;19;125;239m \033[39m\033[38;2;16;131;235m \033[39m\033[38;2;13;138;232m \033[39m\033[38;2;10;144;228m\033[39m\n" \ + "\033[38;2;216;162;4m \033[39m\033[38;2;221;155;6m \033[39m\033[38;2;225;149;8m \033[39m\033[38;2;229;143;11m \033[39m\033[38;2;233;137;13m \033[39m\033[38;2;236;130;16m \033[39m\033[38;2;239;124;19m \033[39m\033[38;2;242;118;23m \033[39m\033[38;2;245;111;27m \033[39m\033[38;2;247;105;30m \033[39m\033[38;2;249;99;35m \033[39m\033[38;2;251;93;39m \033[39m\033[38;2;252;87;44m \033[39m\033[38;2;253;81;49m \033[39m\033[38;2;254;75;54m \033[39m\033[38;2;254;69;59m \033[39m\033[38;2;254;64;64m \033[39m\033[38;2;254;58;70m \033[39m\033[38;2;254;53;76m \033[39m\033[38;2;253;48;82m \033[39m\033[38;2;252;43;88m \033[39m\033[38;2;250;38;94m \033[39m\033[38;2;249;34;100m \033[39m\033[38;2;247;30;106m \033[39m\033[38;2;244;26;112m \033[39m\033[38;2;242;22;119m \033[39m\033[38;2;239;19;125m \033[39m\033[38;2;235;16;131m \033[39m\033[38;2;232;13;138m \033[39m\033[38;2;228;10;144m \033[39m\033[38;2;224;8;150m \033[39m\033[38;2;220;6;157m \033[39m\033[38;2;216;4;163m \033[39m\033[38;2;211;3;169m \033[39m\033[38;2;206;2;175m \033[39m\033[38;2;201;1;181m \033[39m\033[38;2;196;1;186m \033[39m\033[38;2;190;1;192m \033[39m\033[38;2;185;1;197m \033[39m\033[38;2;179;1;202m \033[39m\033[38;2;173;2;207m \033[39m\033[38;2;167;3;212m \033[39m\033[38;2;161;5;217m \033[39m\033[38;2;155;6;221m \033[39m\033[38;2;149;9;225m \033[39m\033[38;2;142;11;229m \033[39m\033[38;2;136;14;233m/\033[39m\033[38;2;130;16;236m_\033[39m\033[38;2;123;20;240m_\033[39m\033[38;2;117;23;242m_\033[39m\033[38;2;111;27;245m_\033[39m\033[38;2;104;31;247m/\033[39m\033[38;2;98;35;249m \033[39m\033[38;2;92;40;251m \033[39m\033[38;2;86;44;252m \033[39m\033[38;2;80;49;253m \033[39m\033[38;2;74;54;254m \033[39m\033[38;2;68;60;254m \033[39m\033[38;2;63;65;254m \033[39m\033[38;2;58;71;254m \033[39m\033[38;2;52;76;254m \033[39m\033[38;2;47;82;253m \033[39m\033[38;2;42;88;252m \033[39m\033[38;2;38;94;250m \033[39m\033[38;2;33;101;248m \033[39m\033[38;2;29;107;246m \033[39m\033[38;2;25;113;244m \033[39m\033[38;2;22;119;241m \033[39m\033[38;2;18;126;238m \033[39m\033[38;2;15;132;235m \033[39m\033[38;2;12;138;232m \033[39m\033[38;2;10;145;228m\033[39m" + +#define COLLECTOR_INFO(_name) _BLUE "\b\b\b\bCollector for " _name " trace" _RE + +inline void clearSpace(char *sym) +{ + for (char *p = sym; *p; p++) + { + if (*p != ' ') + { + *sym = *p; + sym++; + } + } + *sym = '\0'; +} + +#endif diff --git a/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap b/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap deleted file mode 160000 index b0c8234df..000000000 --- a/eBPF_Supermarket/Stack_Analyser/libbpf-bootstrap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0c8234dfb8f31eb12c99b26bb2bec96eb76aff3 diff --git a/eBPF_Supermarket/Stack_Analyser/new_bpf.sh b/eBPF_Supermarket/Stack_Analyser/new_bpf.sh index 8ed45cdb9..8aa1507a2 100755 --- a/eBPF_Supermarket/Stack_Analyser/new_bpf.sh +++ b/eBPF_Supermarket/Stack_Analyser/new_bpf.sh @@ -27,6 +27,6 @@ sed -i 's/template/'$origin_name'/g' bpf/$origin_name.bpf.c sed -i '/#include "bpf_wapper\/on_cpu.h"/a#include "bpf_wapper\/'$origin_name'.h"' src/main.cpp -sed -i '/auto MainOption = _GREEN "Some overall options" _RE %/iauto '$name'Option = clipp::option("'$origin_name'").call([]{ StackCollectorList.push_back(new '$class_name'()); }) % COLLECTOR_INFO("'$origin_name'") & (TraceOption);' src/main.cpp +sed -i '/auto MainOption = _GREEN "Some overall options" _RE %/iauto '$name'Option = clipp::option("'$origin_name'").call([]{ StackCollectorList.push_back(new '$class_name'()); }) % COLLECTOR_INFO("'$origin_name'");' src/main.cpp sed -i '/MainOption,/i'$name'Option,' src/main.cpp \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp index f69a509f1..2f99dcc35 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp @@ -17,13 +17,15 @@ // 包装用于采集调用栈数据的eBPF程序,规定一些抽象接口和通用变量 #include "bpf_wapper/eBPFStackCollector.h" -#include "sa_user.h" -#include "dt_symbol.h" +#include "user.h" +#include "trace.h" #include #include #include #include +#include +#include std::string getLocalDateTime(void) { @@ -44,7 +46,7 @@ bool operator<(const CountItem a, const CountItem b) StackCollector::StackCollector() { - self_pid = getpid(); + self_tgid = getpid(); }; std::vector *StackCollector::sortedCountList(void) @@ -53,25 +55,44 @@ std::vector *StackCollector::sortedCountList(void) auto val_size = bpf_map__value_size(psid_count_map); auto value_fd = bpf_object__find_map_fd_by_name(obj, "psid_count_map"); + auto D = new std::vector(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) + for (psid prev_key = {0}, curr_key = {0};; prev_key = curr_key) + { + if (bpf_map_get_next_key(value_fd, &prev_key, &curr_key)) + { + if (errno != ENOENT) + perror("map get next key error"); + break; // no more keys, done + } + if (showDelta) + bpf_map_delete_elem(value_fd, &prev_key); + char val[val_size]; + memset(val, 0, val_size); + if (bpf_map_lookup_elem(value_fd, &curr_key, &val)) + { + if (errno != ENOENT) + { + perror("map lookup error"); + break; + } + continue; + } + CountItem d(curr_key, count_values(val)); + D->insert(std::lower_bound(D->begin(), D->end(), d), d); + } +#else auto keys = new psid[MAX_ENTRIES]; auto vals = new char[MAX_ENTRIES * val_size]; uint32_t count = MAX_ENTRIES; psid next_key; int err; if (showDelta) - { err = bpf_map_lookup_and_delete_batch(value_fd, NULL, &next_key, keys, vals, &count, NULL); - } else - { err = bpf_map_lookup_batch(value_fd, NULL, &next_key, keys, vals, &count, NULL); - } if (err == EFAULT) - { return NULL; - } - - auto D = new std::vector(); for (uint32_t i = 0; i < count; i++) { CountItem d(keys[i], count_values(vals + val_size * i)); @@ -79,6 +100,7 @@ std::vector *StackCollector::sortedCountList(void) } delete[] keys; delete[] vals; +#endif return D; }; @@ -87,12 +109,21 @@ StackCollector::operator std::string() std::ostringstream oss; oss << _RED "time:" << getLocalDateTime() << _RE "\n"; std::map> traces; + std::map infos; oss << _BLUE "counts:" _RE "\n"; { auto D = sortedCountList(); if (!D) return oss.str(); + if ((*D).size() > top) + { + auto end = (*D).end(); + auto begin = end - top; + for (auto i = (*D).begin(); i < begin; i++) + delete i->v; + (*D).assign(begin, end); + } oss << _GREEN "pid\tusid\tksid"; for (int i = 0; i < scale_num; i++) oss << '\t' << scales[i].Type << "/" << scales[i].Period << scales[i].Unit; @@ -112,67 +143,55 @@ StackCollector::operator std::string() auto trace_fd = bpf_object__find_map_fd_by_name(obj, "sid_trace_map"); if (id.usid > 0 && traces.find(id.usid) == traces.end()) { - std::vector sym_trace; - bpf_map_lookup_elem(trace_fd, &id.usid, trace); - for (p = trace + MAX_STACKS - 1; !*p; p--) - ; - for (; p >= trace; p--) + syms = syms_cache__get_syms(syms_cache, id.pid); + if (!syms) + fprintf(stderr, "failed to get syms\n"); + else { - uint64_t &addr = *p; - symbol sym; - sym.reset(addr); - elf_file file; - if (g_symbol_parser.find_symbol_in_cache(id.pid, addr, sym.name)) + bpf_map_lookup_elem(trace_fd, &id.usid, trace); + for (p = trace + MAX_STACKS - 1; !*p; p--) ; - else if (g_symbol_parser.get_symbol_info(id.pid, sym, file) && g_symbol_parser.find_elf_symbol(sym, file, id.pid, id.pid)) + std::vector sym_trace(p - trace + 1); + for (int i = 0; p >= trace; p--) { - if (sym.name[0] == '_' && sym.name[1] == 'Z') - // 代表是C++符号,则调用demangle解析 + struct sym *sym = syms__map_addr(syms, *p); + if (sym) { - sym.name = demangleCppSym(sym.name); + if (sym->name[0] == '_' && sym->name[1] == 'Z') + { + char *demangled = abi::__cxa_demangle(sym->name, NULL, NULL, NULL); + if (demangled) + { + clearSpace(demangled); + sym->name = demangled; + } + } + sym_trace[i++] = std::string(sym->name) + "+" + std::to_string(sym->offset); } - std::stringstream ss(""); - ss << "+0x" << std::hex << (addr - sym.start); - sym.name += ss.str(); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); - } - else - { - std::stringstream ss(""); - ss << "0x" << std::hex << addr; - sym.name = ss.str(); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); + else + sym_trace[i++] = "[unknown]"; } - clearSpace(sym.name); - sym_trace.push_back(sym.name); + traces[id.usid] = sym_trace; } - traces[id.usid] = sym_trace; } if (id.ksid > 0 && traces.find(id.ksid) == traces.end()) { - std::vector sym_trace; bpf_map_lookup_elem(trace_fd, &id.ksid, trace); for (p = trace + MAX_STACKS - 1; !*p; p--) ; - for (; p >= trace; p--) + std::vector sym_trace(p - trace + 1); + for (int i = 0; p >= trace; p--) { - uint64_t &addr = *p; - symbol sym; - sym.reset(addr); - if (g_symbol_parser.find_kernel_symbol(sym)) - ; - else - { - std::stringstream ss(""); - ss << "0x" << std::hex << addr; - sym.name = ss.str(); - g_symbol_parser.putin_symbol_cache(pid, addr, sym.name); - } - clearSpace(sym.name); - sym_trace.push_back(sym.name); + const struct ksym *ksym = ksyms__map_addr(ksyms, *p); + sym_trace[i++] = ksym ? std::string(ksym->name) + "+" + std::to_string(*p - ksym->addr) + : "[unknown]"; } traces[id.ksid] = sym_trace; } + auto info_fd = bpf_object__find_map_fd_by_name(obj, "pid_info_map"); + task_info info; + bpf_map_lookup_elem(info_fd, &id.pid, &info); + infos[id.pid] = info; } delete D; } @@ -191,37 +210,20 @@ StackCollector::operator std::string() oss << _BLUE "info:" _RE "\n"; { - auto info_fd = bpf_object__find_map_fd_by_name(obj, "pid_info_map"); - if (info_fd < 0) + oss << _GREEN "pid\tNSpid\tcomm\ttgid\tcgroup\t" _RE "\n"; + for (auto i : infos) { - return oss.str(); + auto cgroup_fd = bpf_object__find_map_fd_by_name(obj, "tgid_cgroup_map"); + char group[CONTAINER_ID_LEN]; + bpf_map_lookup_elem(cgroup_fd, &(i.second.tgid), &group); + oss << i.first << '\t' + << i.second.pid << '\t' + << i.second.comm << '\t' + << i.second.tgid << '\t' + << group << '\n'; } - auto keys = new uint32_t[MAX_ENTRIES]; - auto vals = new task_info[MAX_ENTRIES]; - uint32_t count = MAX_ENTRIES; - uint32_t next_key; - { - int err; - if (showDelta) - err = bpf_map_lookup_and_delete_batch(info_fd, NULL, &next_key, - keys, vals, &count, NULL); - else - err = bpf_map_lookup_batch(info_fd, NULL, &next_key, - keys, vals, &count, NULL); - if (err == EFAULT) - return oss.str(); - } - oss << _GREEN "pid\tNSpid\tcomm\ttgid\tcgroup" _RE "\n"; - for (uint32_t i = 0; i < count; i++) - oss << keys[i] << '\t' - << vals[i].pid << '\t' - << vals[i].comm << '\t' - << vals[i].tgid << '\t' - << vals[i].cid << '\n'; - delete[] keys; - delete[] vals; } oss << _BLUE "OK" _RE "\n"; return oss.str(); -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp index e12e8f474..910daaca6 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/io.cpp @@ -36,25 +36,16 @@ IOStackCollector::IOStackCollector() }; }; -int IOStackCollector::load(void) -{ - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); - return 0; -} - -int IOStackCollector::attach(void) +int IOStackCollector::ready(void) { + EBPF_LOAD_OPEN_INIT(); ATTACH_PROTO; return 0; } -void IOStackCollector::detach(void) +void IOStackCollector::finish(void) { DETACH_PROTO; -} - -void IOStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -63,6 +54,7 @@ void IOStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *IOStackCollector::getName(void) { +const char *IOStackCollector::getName(void) +{ return "IOStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp index a6bdc3986..575b14450 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/llc_stat.cpp @@ -37,25 +37,19 @@ uint64_t *LlcStatStackCollector::count_values(void *data) }; }; -int LlcStatStackCollector::load(void) +int LlcStatStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - return 0; -}; - -int LlcStatStackCollector::attach(void) -{ - const char *online_cpus_file = "/sys/devices/system/cpu/online"; bool *online_mask; int num_online_cpus; - err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); - CHECK_ERR(err, "Fail to get online CPU numbers"); + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online_mask, &num_online_cpus); + CHECK_ERR_RN1(err, "Fail to get online CPU numbers"); num_cpus = libbpf_num_possible_cpus(); - CHECK_ERR(num_cpus <= 0, "Fail to get the number of processors"); + CHECK_ERR_RN1(num_cpus <= 0, "Fail to get the number of processors"); struct perf_event_attr attr = { - .type = PERF_COUNT_HW_CPU_CYCLES, + .type = PERF_TYPE_HARDWARE, .size = sizeof(attr), .sample_period = scales->Period, .inherit = 1, @@ -78,25 +72,25 @@ int LlcStatStackCollector::attach(void) } /* Set up performance monitoring on a CPU/Core */ attr.config = PERF_COUNT_HW_CACHE_MISSES; - int pefd = syscall(SYS_perf_event_open, &attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + int pefd = syscall(SYS_perf_event_open, &attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); mpefds[cpu] = pefd; /* Attach a BPF program on a CPU */ mlinks[cpu] = bpf_program__attach_perf_event(skel->progs.on_cache_miss, pefd); - CHECK_ERR(!mlinks[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!mlinks[cpu], "Fail to attach bpf program"); attr.config = PERF_COUNT_HW_CACHE_REFERENCES; - pefd = syscall(SYS_perf_event_open, &attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + pefd = syscall(SYS_perf_event_open, &attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); rpefds[cpu] = pefd; /* Attach a BPF program on a CPU */ rlinks[cpu] = bpf_program__attach_perf_event(skel->progs.on_cache_ref, pefd); - CHECK_ERR(!rlinks[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!rlinks[cpu], "Fail to attach bpf program"); } - return 0; -}; + return 0; +} -void LlcStatStackCollector::detach(void) +void LlcStatStackCollector::finish(void) { for (int cpu = 0; cpu < num_cpus; cpu++) { @@ -111,12 +105,8 @@ void LlcStatStackCollector::detach(void) free(rpefds); mlinks = rlinks = NULL; mpefds = rpefds = NULL; -}; - -void LlcStatStackCollector::unload(void) -{ UNLOAD_PROTO; -}; +} void LlcStatStackCollector::activate(bool tf) { @@ -141,6 +131,7 @@ LlcStatStackCollector::LlcStatStackCollector() }; }; -void LlcStatStackCollector::setScale(uint64_t p) { +void LlcStatStackCollector::setScale(uint64_t p) +{ scales[0].Period = scales[1].Period = p; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp index e0edb6cda..d8224de6d 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp @@ -17,7 +17,7 @@ // mem ebpf程序的包装类,实现接口和一些自定义方法 #include "bpf_wapper/memleak.h" -#include "trace_helpers.h" +#include "trace.h" #include uint64_t *MemleakStackCollector::count_values(void *d) @@ -111,7 +111,7 @@ int MemleakStackCollector::attach_uprobes(struct memleak_bpf *skel) return 0; } -int MemleakStackCollector::load(void) +int MemleakStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT( if (kstack) { @@ -119,39 +119,29 @@ int MemleakStackCollector::load(void) disable_kernel_node_tracepoints(skel); if (!percpu) disable_kernel_percpu_tracepoints(skel); - } else { - disable_kernel_tracepoints(skel); - }; - skel->rodata->sample_rate = sample_rate; + } else disable_kernel_tracepoints(skel); skel->rodata->wa_missing_free = wa_missing_free; skel->rodata->page_size = sysconf(_SC_PAGE_SIZE);); - return 0; -}; - -int MemleakStackCollector::attach(void) -{ if (!kstack) - CHECK_ERR(attach_uprobes(skel), "failed to attach uprobes"); + CHECK_ERR_RN1(attach_uprobes(skel), "failed to attach uprobes"); err = skel->attach(skel); - CHECK_ERR(err, "Failed to attach BPF skeleton"); + CHECK_ERR_RN1(err, "Failed to attach BPF skeleton"); return 0; -}; +} -void MemleakStackCollector::detach(void) +void MemleakStackCollector::finish(void) { DETACH_PROTO; -}; - -void MemleakStackCollector::unload(void) -{ UNLOAD_PROTO; } + void MemleakStackCollector::activate(bool tf) { ACTIVE_SET(tf); } -const char *MemleakStackCollector::getName(void) { +const char *MemleakStackCollector::getName(void) +{ return "MemleakStackCollector"; -} \ No newline at end of file +} diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp index 068375f34..d1cd1d9ba 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/off_cpu.cpp @@ -17,7 +17,7 @@ // off cpu ebpf程序的包装类,实现接口和一些自定义方法 #include "bpf_wapper/off_cpu.h" -#include "dt_symbol.h" +#include "trace.h" OffCPUStackCollector::OffCPUStackCollector() { @@ -34,31 +34,20 @@ uint64_t *OffCPUStackCollector::count_values(void *data) }; }; -int OffCPUStackCollector::load(void) +int OffCPUStackCollector::ready(void) { - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); - return 0; -} - -int OffCPUStackCollector::attach(void) -{ - symbol sym; - sym.name = "finish_task_switch"; - if (!g_symbol_parser.complete_kernel_symbol(sym)) - { + EBPF_LOAD_OPEN_INIT(); + const char *name = "finish_task_switch"; + const struct ksym *ksym = ksyms__find_symbol(ksyms, name); + if (!ksym) return -1; - } - skel->links.do_stack = bpf_program__attach_kprobe(skel->progs.do_stack, false, sym.name.c_str()); + skel->links.do_stack = bpf_program__attach_kprobe(skel->progs.do_stack, false, ksym->name); return 0; } -void OffCPUStackCollector::detach(void) +void OffCPUStackCollector::finish(void) { DETACH_PROTO; -} - -void OffCPUStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -67,6 +56,7 @@ void OffCPUStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *OffCPUStackCollector::getName(void) { +const char *OffCPUStackCollector::getName(void) +{ return "OffCPUStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp index 8e4e1cf4a..3bf90f748 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/on_cpu.cpp @@ -59,26 +59,19 @@ uint64_t *OnCPUStackCollector::count_values(void *data) }; }; -int OnCPUStackCollector::load(void) +int OnCPUStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - - return 0; -}; - -int OnCPUStackCollector::attach(void) -{ - const char *online_cpus_file = "/sys/devices/system/cpu/online"; bool *online_mask; int num_online_cpus; - err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); - CHECK_ERR(err, "Fail to get online CPU numbers"); + err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online_mask, &num_online_cpus); + CHECK_ERR_RN1(err, "Fail to get online CPU numbers"); num_cpus = libbpf_num_possible_cpus(); - CHECK_ERR(num_cpus <= 0, "Fail to get the number of processors"); + CHECK_ERR_RN1(num_cpus <= 0, "Fail to get the number of processors"); struct perf_event_attr attr = { - .type = PERF_COUNT_HW_CPU_CYCLES, + .type = PERF_TYPE_HARDWARE, .size = sizeof(attr), .config = PERF_COUNT_HW_CPU_CYCLES, .sample_freq = freq, @@ -99,51 +92,48 @@ int OnCPUStackCollector::attach(void) continue; } /* Set up performance monitoring on a CPU/Core */ - int pefd = perf_event_open(&attr, pid, cpu, -1, 0); - CHECK_ERR(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + int pefd = perf_event_open(&attr, tgid ? tgid : -1, cpu, -1, 0); + if (pefd < 0) + { + if (attr.type != PERF_TYPE_SOFTWARE) + { + HINT_ERR("Hardware perf events not exist, try to attach to software event"); + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_CPU_CLOCK; + pefd = perf_event_open(&attr, tgid ? tgid : -1, cpu, -1, 0); + CHECK_ERR_RN1(pefd < 0, "Fail to set up performance monitor on a CPU/Core"); + } + else + DEAL_ERR(return -1, "Fail to set up performance monitor on a CPU/Core"); + } pefds[cpu] = pefd; /* Attach a BPF program on a CPU */ links[cpu] = bpf_program__attach_perf_event(skel->progs.do_stack, pefd); // 与内核bpf程序联系 - CHECK_ERR(!links[cpu], "Fail to attach bpf program"); + CHECK_ERR_RN1(!links[cpu], "Fail to attach bpf program"); } return 0; } -void OnCPUStackCollector::detach(void) +void OnCPUStackCollector::finish(void) { - if (links) - { - for (int cpu = 0; cpu < num_cpus; cpu++) - { - bpf_link__destroy(links[cpu]); - } - free(links); - links = NULL; - } - if (pefds) + for (int i = 0; i < num_cpus; i++) { - for (int i = 0; i < num_cpus; i++) - { - if (pefds[i] >= 0) - { - close(pefds[i]); - } - } - free(pefds); - pefds = NULL; + bpf_link__destroy(links[i]); + close(pefds[i]); } -}; - -void OnCPUStackCollector::unload(void) -{ + free(links); + free(pefds); + links = NULL; + pefds = NULL; UNLOAD_PROTO; -}; +} void OnCPUStackCollector::activate(bool tf) { ACTIVE_SET(tf); } -const char *OnCPUStackCollector::getName(void) { +const char *OnCPUStackCollector::getName(void) +{ return "OnCPUStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp index 3c82035a2..6b720c6b8 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp @@ -12,29 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// author: GaoYixiang +// author: luiyanbing@foxmail.com // -// probe ebpf 程序的包装类,实现接口和一些自定义方法 +// ebpf程序包装类的模板,实现接口和一些自定义方法 #include "bpf_wapper/probe.h" -#include "uprobe_helpers.h" +#include "trace.h" +#include "uprobe.h" -uint64_t *ProbeStackCollector::count_values(void *data) -{ - return new uint64_t[scale_num]{ - *(uint32_t *)data, - }; -} - -ProbeStackCollector::ProbeStackCollector() -{ - scale_num = 1; - scales = new Scale[scale_num]{ - {"", 1, "counts"}, - }; -}; - -void splitString(std::string symbol, const char split, std::vector &res) +void splitStr(const std::string &symbol, const char split, std::vector &res) { if (symbol == "") return; @@ -49,7 +35,25 @@ void splitString(std::string symbol, const char split, std::vector } } -static int get_path(char *path,int pid) +static bool try_fentry(struct probe_bpf *skel, const char *func) +{ + if (!fentry_can_attach(func, NULL) || + bpf_program__set_attach_target(skel->progs.dummy_fentry, 0, func) || + bpf_program__set_attach_target(skel->progs.dummy_fexit, 0, func)) + { + bpf_program__set_autoload(skel->progs.dummy_fentry, false); + bpf_program__set_autoload(skel->progs.dummy_fexit, false); + return false; + } + else + { + bpf_program__set_autoload(skel->progs.dummy_kprobe, false); + bpf_program__set_autoload(skel->progs.dummy_kretprobe, false); + return true; + } +}; + +static int get_binpath(char *path, int pid) { char mode[16], line[128], buf[64]; size_t seg_start, seg_end, seg_off; @@ -62,11 +66,13 @@ static int get_path(char *path,int pid) return -1; while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n", - &seg_start, &seg_end, mode, &seg_off, line) == 5) { + &seg_start, &seg_end, mode, &seg_off, line) == 5) + { i = 0; while (isblank(line[i])) i++; - if (strstr(line + i, "libc.so.6")) { + if (strstr(line + i, "libc.so.6")) + { break; } } @@ -74,105 +80,162 @@ static int get_path(char *path,int pid) strcpy(path, line + i); fclose(f); return 0; -} -void ProbeStackCollector::setScale(std::string probe) +}; + +static int attach_kprobes(struct probe_bpf *skel, const std::string &func) { - this->probe = probe; - scales->Type = probe + "Counts"; + skel->links.dummy_kprobe = + bpf_program__attach_kprobe(skel->progs.dummy_kprobe, false, func.c_str()); + CHECK_ERR_RN1(!skel->links.dummy_kprobe, "Fail to attach kprobe"); + skel->links.dummy_kretprobe = + bpf_program__attach_kprobe(skel->progs.dummy_kretprobe, true, func.c_str()); + CHECK_ERR_RN1(!skel->links.dummy_kretprobe, "Fail to attach ketprobe"); + return 0; }; -int ProbeStackCollector::load(void) +static int attach_fentry(struct probe_bpf *skel) { - EBPF_LOAD_OPEN_INIT(skel->rodata->target_pid = pid;); + skel->links.dummy_fentry = + bpf_program__attach(skel->progs.dummy_fentry); + CHECK_ERR_RN1(!skel->links.dummy_fentry, "Fail to attach fentry"); + skel->links.dummy_fexit = + bpf_program__attach(skel->progs.dummy_fexit); + CHECK_ERR_RN1(!skel->links.dummy_fexit, "Fail to attach fexit"); return 0; }; -int ProbeStackCollector::attach(void) +static int attach_uprobes(struct probe_bpf *skel, const std::string &probe, int pid) { - std::vector strList; - splitString(probe, ':', strList); + char *binary, *function; + char bin_path[128]; + std::string func = probe; + off_t func_off; + + binary = strdup(func.c_str()); + function = strchr(binary, ':'); // 查找:首次出现的位置 + *function = '\0'; + function++; + + if (resolve_binary_path(binary, pid, bin_path, sizeof(bin_path))) + free(binary); + + func_off = get_elf_func_offset(bin_path, function); + if (func_off < 0) + free(binary); + skel->links.dummy_kprobe = + bpf_program__attach_uprobe(skel->progs.dummy_kprobe, false, pid, + bin_path, func_off); + CHECK_ERR_RN1(!skel->links.dummy_kprobe, "Fail to attach uprobe"); + skel->links.dummy_kretprobe = + bpf_program__attach_uprobe(skel->progs.dummy_kretprobe, true, pid, + bin_path, func_off); + CHECK_ERR_RN1(!skel->links.dummy_kretprobe, "Fail to attach uprobe"); + return 0; +}; - if (strList.size() == 1 || (strList.size() == 3 && strList[0] == "p" && strList[1] == "")) - { - // probe a kernel function - std::string func = probe; - if (strList.size() == 3 && strList[0] == "p" && strList[1] == "") - func = strList[2]; - skel->links.handle = - bpf_program__attach_kprobe(skel->progs.handle, false, func.c_str()); - CHECK_ERR(!skel->links.handle, "Fail to attach kprobe"); - return 0; - } - else if (strList.size() == 3 && strList[0] == "t") - { - // probe a kernel tracepoint - skel->links.handle_tp = - bpf_program__attach_tracepoint(skel->progs.handle_tp, strList[1].c_str(), strList[2].c_str()); - CHECK_ERR(!skel->links.handle_tp, "Fail to attach tracepoint"); - return 0; - } - else if (strList.size() == 2 || (strList.size() == 3 && strList[0] == "p" && strList[1] != "")) - { - // probe a user-space function in the library 'lib' - char *binary, *function; - char bin_path[128]; - std::string func = probe; - off_t func_off; - if (strList.size() == 3) - func = strList[1] + ":" + strList[2]; - - binary = strdup(func.c_str()); - function = strchr(binary, ':'); // 查找:首次出现的位置 - *function = '\0'; - function++; - - if (resolve_binary_path(binary, pid, bin_path, sizeof(bin_path))) - free(binary); - - func_off = get_elf_func_offset(bin_path, function); - if (func_off < 0) - free(binary); - skel->links.handle = - bpf_program__attach_uprobe(skel->progs.handle, false, pid, - bin_path, func_off); - CHECK_ERR(!skel->links.handle, "Fail to attach uprobe"); - return 0; - } - else if (strList.size() == 3 && strList[0] == "u") - { - // probe a USDT tracepoint - char bin_path[128]; - std::string func = probe; - int err = get_path(bin_path,pid); - CHECK_ERR(err, "Fail to get lib path"); - skel->links.handle_usdt = - bpf_program__attach_usdt(skel->progs.handle_usdt,pid,bin_path,"libc",strList[2].c_str(),NULL); - CHECK_ERR(!skel->links.handle_usdt, "Fail to attach usdt"); - return 0; - } - else - { - printf("Type must be 'p', 't', or 'u' or too any args"); - } +static int attach_tp(struct probe_bpf *skel, const std::string &tp_class, const std::string &func) +{ + skel->links.tp_exit = + bpf_program__attach_tracepoint(skel->progs.tp_exit, tp_class.c_str(), func.c_str()); + CHECK_ERR_RN1(!skel->links.tp_exit, "Fail to attach tracepoint"); return 0; }; -void ProbeStackCollector::detach(void) +static int attach_usdt(struct probe_bpf *skel, const std::string &func, int pid) { - DETACH_PROTO; + char bin_path[128]; + int err = get_binpath(bin_path, pid); + CHECK_ERR_RN1(err, "Fail to get lib path"); + skel->links.usdt_exit = + bpf_program__attach_usdt(skel->progs.usdt_exit, pid, bin_path, "libc", func.c_str(), NULL); + CHECK_ERR_RN1(!skel->links.usdt_exit, "Fail to attach usdt"); + return 0; +}; + +// ========== implement virtual func ========== + +uint64_t *ProbeStackCollector::count_values(void *data) +{ + time_tuple *p = (time_tuple *)data; + return new uint64_t[scale_num]{ + p->lat, + p->count, + }; +}; + +void ProbeStackCollector::setScale(std::string probe) +{ + this->probe = probe; + for (int i = 0; i < scale_num; i++) + scales[i].Type = probe + scales[i].Type; }; -void ProbeStackCollector::unload(void) +int ProbeStackCollector::ready(void) { + bool can_ftrace = true; + std::vector strList; + splitStr(probe, ':', strList); + EBPF_LOAD_OPEN_INIT( + if ((strList.size() == 3 && strList[0] == "p" && strList[1] == "") || + strList.size() == 1) + can_ftrace = try_fentry(skel, (strList.size() == 1 + ? probe + : strList[2]) + .c_str()); + else { + bpf_program__set_autoload(skel->progs.dummy_fentry, false); + bpf_program__set_autoload(skel->progs.dummy_fexit, false); + }); + + if ((strList.size() == 3 && strList[0] == "p" && strList[1] == "") || + strList.size() == 1) + if (can_ftrace) + err = attach_fentry(skel); + else + err = attach_kprobes(skel, (strList.size() == 1 + ? probe + : strList[2])); + else if (strList.size() == 3 && strList[0] == "t") + err = attach_tp(skel, strList[1], strList[2]); + else if (strList.size() == 2 || + (strList.size() == 3 && strList[0] == "p" && strList[1] != "")) + err = attach_uprobes(skel, + strList.size() == 3 + ? strList[1] + ":" + strList[2] + : probe, + tgid); + else if (strList.size() == 3 && strList[0] == "u") + err = attach_usdt(skel, strList[2], tgid); + else + err = 1; + CHECK_ERR_RN1(err, "Fail to attach"); + return 0; +}; + +void ProbeStackCollector::finish(void) +{ + DETACH_PROTO; UNLOAD_PROTO; }; void ProbeStackCollector::activate(bool tf) { ACTIVE_SET(tf); -} +}; -const char *ProbeStackCollector::getName(void) { +const char *ProbeStackCollector::getName(void) +{ return "ProbeStackCollector"; -} \ No newline at end of file +}; + +// ========== other implementations ========== + +ProbeStackCollector::ProbeStackCollector() +{ + scale_num = 2; + scales = new Scale[scale_num]{ + {"Time", 1, "nanoseconds"}, + {"Count", 1, "counts"}, + }; +}; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp index 3cc1ae2e5..536bd923b 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/readahead.cpp @@ -37,25 +37,16 @@ ReadaheadStackCollector::ReadaheadStackCollector() }; }; -int ReadaheadStackCollector::load(void) +int ReadaheadStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - return 0; -} - -int ReadaheadStackCollector::attach(void) -{ ATTACH_PROTO; return 0; } -void ReadaheadStackCollector::detach(void) +void ReadaheadStackCollector::finish(void) { DETACH_PROTO; -} - -void ReadaheadStackCollector::unload(void) -{ UNLOAD_PROTO; } @@ -64,6 +55,7 @@ void ReadaheadStackCollector::activate(bool tf) ACTIVE_SET(tf); } -const char *ReadaheadStackCollector::getName(void) { +const char *ReadaheadStackCollector::getName(void) +{ return "ReadaheadStackCollector"; } \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp index ac3e42725..3228bc7e6 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/template.cpp @@ -27,26 +27,20 @@ uint64_t *TemplateClass::count_values(void *data) }; }; -int TemplateClass::load(void) +int TemplateClass::ready(void) { return 0; -}; - -int TemplateClass::attach(void) -{ - return 0; -}; - -void TemplateClass::detach(void){}; +} -void TemplateClass::unload(void){}; +void TemplateClass::finish(void) {}; void TemplateClass::activate(bool tf) { ACTIVE_SET(tf); } -const char *TemplateClass::getName(void) { +const char *TemplateClass::getName(void) +{ return "TemplateClass"; } diff --git a/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp b/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp new file mode 100644 index 000000000..fb6b4ad79 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/src/cgroup.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include "cgroup.h" + +uint64_t get_cgroupid(const char *pathname) +{ + struct statfs fs; + int err; + struct cgid_file_handle *h; + int mount_id; + uint64_t ret; + + err = statfs(pathname, &fs); + if (err != 0) + { + fprintf(stderr, "statfs on %s failed: %s\n", pathname, strerror(errno)); + exit(1); + } + + if ((fs.f_type != (typeof(fs.f_type))CGROUP2_SUPER_MAGIC)) + { + fprintf(stderr, "File %s is not on a cgroup2 mount.\n", pathname); + exit(1); + } + + h = (cgid_file_handle *)malloc(sizeof(struct cgid_file_handle)); + if (!h) + { + fprintf(stderr, "Cannot allocate memory.\n"); + exit(1); + } + + h->handle_bytes = 8; + err = name_to_handle_at(AT_FDCWD, pathname, (struct file_handle *)h, &mount_id, 0); + if (err != 0) + { + fprintf(stderr, "name_to_handle_at failed: %s\n", strerror(errno)); + exit(1); + } + + if (h->handle_bytes != 8) + { + fprintf(stderr, "Unexpected handle size: %d. \n", h->handle_bytes); + exit(1); + } + + ret = h->cgid; + free(h); + + return ret; +} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp b/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp deleted file mode 100644 index d040e8936..000000000 --- a/eBPF_Supermarket/Stack_Analyser/src/dt_elf.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Linux内核诊断工具--elf相关公共函数 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ -#include "dt_elf.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NOTE_ALIGN(n) (((n) + 3) & -4U) - -struct sym_section_ctx -{ - Elf_Data *syms; - Elf_Data *symstrs; - Elf_Data *rel_data; - int is_reloc; - int is_plt; - int sym_count; - int plt_rel_type; - unsigned long plt_offset; - unsigned long plt_entsize; -}; - -struct symbol_sections_ctx -{ - sym_section_ctx symtab; - sym_section_ctx symtab_in_dynsym; - sym_section_ctx dynsymtab; -}; - -struct section_info -{ - Elf_Scn *sec; - GElf_Shdr *hdr; -}; - -struct plt_ctx -{ - section_info dynsym; - section_info plt_rel; - section_info plt; -}; - -__attribute__((unused)) static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name, - size_t *idx) -{ - Elf_Scn *sec = NULL; - size_t cnt = 1; - - /* Elf is corrupted/truncated, avoid calling elf_strptr. */ - if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) - return NULL; - - while ((sec = elf_nextscn(elf, sec)) != NULL) - { - char *str; - - gelf_getshdr(sec, shp); - str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); - - if (!strcmp(name, str)) - { - if (idx) - *idx = cnt; - - break; - } - - ++cnt; - } - - return sec; -} - -__attribute__((unused)) static int elf_read_build_id(Elf *elf, char *bf, size_t size) -{ - int err = -1; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - Elf_Data *data; - Elf_Scn *sec; - Elf_Kind ek; - char *ptr; - - if (size < BUILD_ID_SIZE) - goto out; - - ek = elf_kind(elf); - - if (ek != ELF_K_ELF) - goto out; - - if (gelf_getehdr(elf, &ehdr) == NULL) - { - fprintf(stderr, "%s: cannot get elf header.\n", __func__); - goto out; - } - - /* - * Check following sections for notes: - * '.note.gnu.build-id' - * '.notes' - * '.note' (VDSO specific) - */ - do - { - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note.gnu.build-id", NULL); - - if (sec) - break; - - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".notes", NULL); - - if (sec) - break; - - sec = elf_section_by_name(elf, &ehdr, &shdr, - ".note", NULL); - - if (sec) - break; - - return err; - - } while (0); - - data = elf_getdata(sec, NULL); - - if (data == NULL) - goto out; - - ptr = (char *)data->d_buf; - - while ((intptr_t)ptr < (intptr_t)((char *)data->d_buf + data->d_size)) - { - GElf_Nhdr *nhdr = (GElf_Nhdr *)ptr; - size_t namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); - const char *name; - - ptr += sizeof(*nhdr); - name = (const char *)ptr; - ptr += namesz; - - if (nhdr->n_type == NT_GNU_BUILD_ID && - nhdr->n_namesz == sizeof("GNU")) - { - if (memcmp(name, "GNU", sizeof("GNU")) == 0) - { - size_t sz = size < descsz ? size : descsz; - memcpy(bf, ptr, sz); - memset(bf + sz, 0, size - sz); - err = descsz; - break; - } - } - - ptr += descsz; - } - -out: - return err; -} - -extern int calc_sha1_1M(const char *filename, unsigned char *buf); - -int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size) -{ - int fd, err = -1; - struct stat sb; - - if (size < BUILD_ID_SIZE) - goto out; - - fd = open(filename, O_RDONLY); - - if (fd < 0) - goto out; - - if (fstat(fd, &sb) == 0) - { - snprintf(bf, size, "%s[%lu]", filename, sb.st_size); - err = 0; - } - - close(fd); -out: - return err; -} - -static int is_function(const GElf_Sym *sym) -{ - return GELF_ST_TYPE(sym->st_info) == STT_FUNC && - sym->st_name != 0 && - sym->st_shndx != SHN_UNDEF; -} - -static int get_symbols_in_section(sym_section_ctx *sym, Elf *elf, Elf_Scn *sec, GElf_Shdr *shdr, int is_reloc) -{ - sym->syms = elf_getdata(sec, NULL); - if (!sym->syms) - { - return -1; - } - - Elf_Scn *symstrs_sec = elf_getscn(elf, shdr->sh_link); - if (!sec) - { - return -1; - } - - sym->symstrs = elf_getdata(symstrs_sec, NULL); - if (!sym->symstrs) - { - return -1; - } - - sym->sym_count = shdr->sh_size / shdr->sh_entsize; - sym->is_plt = 0; - sym->is_reloc = is_reloc; - - return 0; -} - -static int get_plt_symbols_in_section(sym_section_ctx *sym, Elf *elf, plt_ctx *plt) -{ - sym->syms = elf_getdata(plt->dynsym.sec, NULL); - if (!sym->syms) - { - return -1; - } - - sym->rel_data = elf_getdata(plt->plt_rel.sec, NULL); - if (!sym->rel_data) - { - return -1; - } - - Elf_Scn *symstrs_sec = elf_getscn(elf, plt->dynsym.hdr->sh_link); - if (!symstrs_sec) - { - return -1; - } - - sym->symstrs = elf_getdata(symstrs_sec, NULL); - if (!sym->symstrs) - { - return -1; - } - - sym->is_plt = 1; - sym->plt_entsize = plt->plt.hdr->sh_type; - sym->plt_offset = plt->plt.hdr->sh_offset; - sym->sym_count = plt->plt_rel.hdr->sh_size / plt->plt_rel.hdr->sh_entsize; - sym->plt_rel_type = plt->plt_rel.hdr->sh_type; - - return 0; -} - -static void __get_plt_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - symbol s; - GElf_Sym sym; - int symidx; - int index = 0; - const char *sym_name = NULL; - - s.end = 0; - s.start = 0; - - if (!si->dynsymtab.syms) - { - return; - } - - while (index < si->dynsymtab.sym_count) - { - if (si->dynsymtab.plt_rel_type == SHT_RELA) - { - GElf_Rela pos_mem, *pos; - pos = gelf_getrela(si->dynsymtab.rel_data, index, &pos_mem); - symidx = GELF_R_SYM(pos->r_info); - } - else if (si->dynsymtab.plt_rel_type == SHT_REL) - { - GElf_Rel pos_mem, *pos; - pos = gelf_getrel(si->dynsymtab.rel_data, index, &pos_mem); - symidx = GELF_R_SYM(pos->r_info); - } - else - { - return; - } - index++; - si->dynsymtab.plt_offset += si->dynsymtab.plt_entsize; - gelf_getsym(si->dynsymtab.syms, symidx, &sym); - - sym_name = (const char *)si->dynsymtab.symstrs->d_buf + sym.st_name; - s.start = si->dynsymtab.plt_offset; - s.end = s.start + si->dynsymtab.plt_entsize; - s.ip = s.start; - s.name = sym_name; - ss.insert(s); - } -} - -static void __get_symbol_without_plt(std::set &ss, sym_section_ctx *tab, Elf *elf) -{ - GElf_Sym sym; - int index = 0; - const char *sym_name; - symbol s; - s.end = 0; - s.start = 0; - - while (index < tab->sym_count) - { - gelf_getsym(tab->syms, index, &sym); - index++; - if (sym.st_shndx == SHN_ABS) - { - continue; - } - if (!is_function(&sym)) - { - continue; - } - sym_name = (const char *)tab->symstrs->d_buf + sym.st_name; - if (tab->is_reloc) - { - Elf_Scn *sec = elf_getscn(elf, sym.st_shndx); - if (!sec) - { - continue; - } - GElf_Shdr shdr; - gelf_getshdr(sec, &shdr); - sym.st_value -= shdr.sh_addr - shdr.sh_offset; - } - s.start = sym.st_value & 0xffffffff; - s.end = s.start + sym.st_size; - s.ip = s.start; - s.name = sym_name; - ss.insert(s); - } -} - -static void __get_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - symbol s; - s.end = 0; - s.start = 0; - - if (!si->symtab.syms && !si->dynsymtab.syms) - { - return; - } - - sym_section_ctx *tab = &si->symtab; - __get_symbol_without_plt(ss, tab, elf); - tab = &si->symtab_in_dynsym; - __get_symbol_without_plt(ss, tab, elf); -} - -static void get_all_symbols(std::set &ss, symbol_sections_ctx *si, Elf *elf) -{ - __get_symbol(ss, si, elf); - __get_plt_symbol(ss, si, elf); -} - -bool search_symbol(const std::set &ss, symbol &sym) -{ - std::set::const_iterator it = ss.find(sym); - - if (it != ss.end()) - { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - - return true; - } - - return false; -} - -bool get_symbol_from_elf(std::set &ss, const char *path) -{ - // static int first_init = 0; - - // if (!first_init) { - // first_init = true; - // init_global_env(); - // } - - int is_reloc = 0; - elf_version(EV_CURRENT); - int fd = open(path, O_RDONLY); - - Elf *elf = elf_begin(fd, ELF_C_READ, NULL); - if (elf == NULL) - { - close(fd); - return false; - } - - Elf_Kind ek = elf_kind(elf); - if (ek != ELF_K_ELF) - { - elf_end(elf); - close(fd); - return false; - } - GElf_Ehdr hdr; - if (gelf_getehdr(elf, &hdr) == NULL) - { - elf_end(elf); - close(fd); - return false; - } - - if (hdr.e_type == ET_EXEC) - { - is_reloc = 1; - } - - if (!elf_rawdata(elf_getscn(elf, hdr.e_shstrndx), NULL)) - { - elf_end(elf); - close(fd); - return false; - } - - GElf_Shdr shdr; - GElf_Shdr symtab_shdr; - GElf_Shdr dynsym_shdr; - GElf_Shdr plt_shdr; - GElf_Shdr plt_rel_shdr; - memset(&shdr, 0, sizeof(shdr)); - memset(&symtab_shdr, 0, sizeof(symtab_shdr)); - memset(&dynsym_shdr, 0, sizeof(dynsym_shdr)); - memset(&plt_shdr, 0, sizeof(plt_shdr)); - memset(&plt_rel_shdr, 0, sizeof(plt_rel_shdr)); - - Elf_Scn *sec = NULL; - Elf_Scn *dynsym_sec = NULL; - Elf_Scn *symtab_sec = NULL; - Elf_Scn *plt_sec = NULL; - Elf_Scn *plt_rel_sec = NULL; - - while ((sec = elf_nextscn(elf, sec)) != NULL) - { - char *str; - gelf_getshdr(sec, &shdr); - str = elf_strptr(elf, hdr.e_shstrndx, shdr.sh_name); - - if (str && strcmp(".symtab", str) == 0) - { - symtab_sec = sec; - memcpy(&symtab_shdr, &shdr, sizeof(dynsym_shdr)); - } - if (str && strcmp(".dynsym", str) == 0) - { - dynsym_sec = sec; - memcpy(&dynsym_shdr, &shdr, sizeof(dynsym_shdr)); - } - if (str && strcmp(".rela.plt", str) == 0) - { - plt_rel_sec = sec; - memcpy(&plt_rel_shdr, &shdr, sizeof(plt_rel_shdr)); - } - if (str && strcmp(".plt", str) == 0) - { - plt_sec = sec; - memcpy(&plt_shdr, &shdr, sizeof(plt_shdr)); - } - if (str && strcmp(".gnu.prelink_undo", str) == 0) - { - is_reloc = 1; - } - } - - plt_ctx plt; - plt.dynsym.hdr = &dynsym_shdr; - plt.dynsym.sec = dynsym_sec; - plt.plt.hdr = &plt_shdr; - plt.plt.sec = plt_sec; - plt.plt_rel.hdr = &plt_rel_shdr; - plt.plt_rel.sec = plt_rel_sec; - - symbol_sections_ctx si; - memset(&si, 0, sizeof(si)); - if (symtab_sec) - { - get_symbols_in_section(&si.symtab, elf, symtab_sec, &symtab_shdr, is_reloc); - } - if (dynsym_sec) - { - get_symbols_in_section(&si.symtab_in_dynsym, elf, dynsym_sec, &dynsym_shdr, is_reloc); - } - if (dynsym_sec && plt_sec) - { - get_plt_symbols_in_section(&si.dynsymtab, elf, &plt); - } - - get_all_symbols(ss, &si, elf); - elf_end(elf); - close(fd); - return true; -} - -struct symbol_cache_item -{ - int start; - int size; - char name[0]; -}; - -bool save_symbol_cache(std::set &ss, const char *path) -{ - char buf[2048]; - int len = 0; - bool status = true; - - int fd = open(path, O_RDONLY); - if (fd < 0) - { - status = false; - return status; - } - int ret; - ret = read(fd, &len, 4); - if (ret <= 0) - { - close(fd); - status = false; - return status; - } - ret = read(fd, buf, len); - if (ret <= 0) - { - close(fd); - status = false; - return status; - } - - while (1) - { - struct symbol_cache_item *sym; - symbol s; - ret = read(fd, &len, 4); - if (ret <= 0) - { - status = false; - break; - } - ret = read(fd, buf, len); - if (ret < len) - { - status = false; - break; - } - sym = (struct symbol_cache_item *)buf; - s.start = sym->start; - s.end = sym->start + sym->size; - s.ip = sym->start; - s.name = sym->name; - ss.insert(s); - } - close(fd); - return status; -} - -bool load_symbol_cache(std::set &ss, const char *path, const char *filename) -{ - int fd = open(path, O_RDWR | O_EXCL); - if (fd < 0) - { - return false; - } - int len = strlen(filename); - int ret = write(fd, &len, 4); - if (ret < 0) - { - close(fd); - return false; - } - ret = write(fd, filename, len); - if (ret < 0) - { - close(fd); - return false; - } - - std::set::iterator it; - int v; - for (it = ss.begin(); it != ss.end(); ++it) - { - v = it->start; - ret = write(fd, &v, 4); - v = it->end - it->start; - ret = write(fd, &v, 4); - ret = write(fd, it->name.c_str(), it->name.length()); - } - return true; -} diff --git a/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp b/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp deleted file mode 100644 index 92feb0d7b..000000000 --- a/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Linux内核诊断工具--用户态符号表解析 - * - * Copyright (C) 2020 Alibaba Ltd. - * - * License terms: GNU General Public License (GPL) version 3 - * - */ - -#include -#include -#include -#include - -#include "dt_symbol.h" -#include "dt_elf.h" - -void restore_global_env(); -int attach_ns_env(int pid); - -symbol_parser g_symbol_parser; -const bool debug_mode = false; - -bool symbol_parser::add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name) -{ - std::map::iterator it; - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - proc_vma proc; - machine_vma.insert(make_pair(pid, proc)); - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return false; - } - } - - vma vm(start, end, offset, name); - it->second.insert(std::make_pair(vm.start, std::move(vm))); - - return true; -} - -bool symbol_parser::load_pid_maps(int pid) -{ - std::map::iterator it; - it = machine_vma.find(pid); - if (it != machine_vma.end()) { - return true; - } - - proc_vma proc; - char fn[256]; - sprintf(fn, "/proc/%d/maps", pid); - FILE *fp = fopen(fn, "r"); - if (!fp) { - return false; - } - - char buf[4096]; - char exename[4096]; - size_t start, end, offset; - while (fgets(buf, sizeof(buf), fp) != NULL) { - start = end = offset = 0; - exename[0] = '\0'; - sscanf(buf, "%lx-%lx %*s %lx %*x:%*x %*u %s %*s\n", &start, &end, &offset, exename); - if (exename[0] == '\0') { - strcpy(exename, "[anon]"); - } - vma vm(start, end, offset, exename); - proc.insert(std::make_pair(vm.start, std::move(vm))); - } - - fclose(fp); - - machine_vma.insert(std::make_pair(pid, std::move(proc))); - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return false; - } - - return true; -} - -bool symbol_parser::load_perf_map(int pid, int pid_ns) -{ -#if 0 - if (pid != pid_ns) { - if (attach_ns_env(pid) < 0) { - return false; - } - } -#endif - char perfmapfile[64]; - snprintf(perfmapfile, sizeof(perfmapfile), "/tmp/perf-%d.map", pid); - FILE *fp = fopen(perfmapfile, "r"); - if (fp == NULL) { - if (debug_mode) { - printf("cannot read perf map %d\n", pid); - } - return false; - } - char line[256]; - char *buf; - long start; - int size; - char name[256]; - std::set syms; - symbol sym; - while ((buf = fgets(line, sizeof(line), fp)) != NULL) { - sscanf(buf, "%lx %x %s\n", &start, &size, name); - sym.start = start; - sym.end = sym.start + size; - sym.ip = sym.start; - sym.name = name; - syms.insert(sym); - } - java_symbols.insert(make_pair(pid, std::move(syms))); -#if 0 - if (pid != pid_ns) { - restore_global_env(); - } -#endif - return true; -} - -bool symbol_parser::find_java_symbol(symbol &sym, int pid, int pid_ns) -{ - std::set ss; - std::map >::iterator it; - //bool load_now = false; - it = java_symbols.find(pid); - if (it == java_symbols.end()) { - if (!load_perf_map(pid, pid_ns)) { - return false; - } - //load_now = true; - it = java_symbols.find(pid); - return search_symbol(it->second, sym); - } else { - return search_symbol(it->second, sym); - } - return true; - - //bool ret = search_symbol(syms, sym); -#if 0 - if (!ret && !load_now) { - java_symbols.erase(pid); - if (!load_perf_map(pid)) { - return false; - } - syms = java_symbols.find(pid)->second; - return search_symbol(syms, sym); - } -#endif - //return ret; -} - -static bool load_kernel_symbol_list(std::vector &sym_list) -{ - FILE *fp = fopen("/proc/kallsyms", "r"); - if (!fp) { - return -1; - } - - char buf[256]; - char type; - int len; - while (fgets(buf, sizeof(buf), fp) != NULL) { - sscanf(buf, "%*p %c %*s\n", &type); - if ((type | 0x20) != 't') { - continue; - } - len = strlen(buf); - if (buf[len-1] == '\n') { - buf[len-1] = '\0'; - } - sym_list.push_back(buf); - } - fclose(fp); - - std::sort(sym_list.begin(), sym_list.end()); - return true; -} - -bool is_space(int ch) { - return std::isspace(ch); -} - -static inline void rtrim(std::string &s) -{ - s.erase(std::find_if(s.rbegin(), s.rend(), is_space).base(), s.end()); -} - -static bool get_next_kernel_symbol( - std::set &syms, - std::vector &sym_list, - std::vector::iterator cursor) -{ - if (cursor == sym_list.end()) { - return false; - } - symbol sym; - size_t start, end; - sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&start); - sym.name = cursor->c_str() + 19; - // rtrim(sym.name); -// #if 0 - // if (sym.name[sym.name.size()-1] == ' ') { - // // sym.name[sym.name.size()-1] = '\0'; - // sym.name.pop_back(); - // } -// #endif - cursor++; - if (cursor != sym_list.end()) { - sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&end); - } - else { - end = INVALID_ADDR; - } - sym.start = start; - sym.end = end; - sym.ip = start; - - syms.insert(sym); - return true; -} - -bool symbol_parser::load_kernel() -{ - if (kernel_symbols.size() != 0) { - return true; - } - - std::vector sym_list; - if (!load_kernel_symbol_list(sym_list)) { - exit(0); - return false; - } - - std::vector::iterator cursor = sym_list.begin(); - while (get_next_kernel_symbol(kernel_symbols, sym_list, cursor)) { - cursor++; - } - return true; -} - -bool symbol_parser::load_elf(pid_t pid, const elf_file &file) -{ - std::map >::iterator it; - it = file_symbols.find(file); - std::set tmp; - std::set &syms = tmp; - if (it != file_symbols.end()) { - return true; - } - if (get_symbol_from_elf(syms, file.filename.c_str())) { - file_symbols.insert(make_pair(file, std::move(syms))); - return true; - } - return false; -} - -bool symbol_parser::find_kernel_symbol(symbol &sym) -{ - load_kernel(); - sym.end = sym.start = 0; - std::set::iterator it = kernel_symbols.find(sym); - if (it != kernel_symbols.end()) { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - return true; - } - return false; -} - -bool symbol_parser::complete_kernel_symbol(symbol &sym) -{ - load_kernel(); - sym.end = sym.start = 0; - for (auto it = kernel_symbols.begin(); it != kernel_symbols.end(); ++it) { - auto size = sym.name.size(), tsize = it->name.size(); - if(size > tsize || it->name.substr(tsize-5, 5) == ".cold") { - continue; - } - if(it->name.substr(0, size) == sym.name) { - sym.end = it->end; - sym.start = it->start; - sym.name = it->name; - return true; - } - } - return false; -} - -bool symbol_parser::find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol) -{ - std::map >::const_iterator it_pid = - symbols_cache.find(tgid); - - if (it_pid != symbols_cache.end()) { - std::map map = symbols_cache[tgid]; - std::map::const_iterator it_symbol = - map.find(addr); - - if (it_symbol != map.end()) { - symbol = map[addr]; - - return true; - } - } - - return false; -} - -bool symbol_parser::putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol) -{ - std::map >::const_iterator it_pid = - symbols_cache.find(tgid); - - if (it_pid == symbols_cache.end()) { - std::map map; - symbols_cache.insert(std::make_pair(tgid, map)); - } - - std::map &map = symbols_cache[tgid]; - std::map::const_iterator it_symbol = - map.find(addr); - - if (it_symbol == map.end()) { - map[addr] = symbol; - return true; - } - - return false; -} - -bool symbol_parser::get_symbol_info(int pid, symbol &sym, elf_file &file) -{ - std::map::iterator proc_vma_info; - - if (java_only) { - file.type = UNKNOWN; - return true; - } - - proc_vma_info = machine_vma.find(pid); - if (proc_vma_info == machine_vma.end()) { - if (!load_pid_maps(pid)) { - if (debug_mode) { - printf("load pid maps failed\n"); - } - return false; - } - } - - vma area(sym.ip); - if (!find_vma(pid, area)) { - if (debug_mode) { - printf("find vma failed\n"); - } - return false; - } - if (area.name == "[anon]") { - file.type = JIT_TYPE; - } - - file.reset(area.name); - if (file.type != JIT_TYPE) { - sym.reset(area.map(sym.ip)); - } - - return true; -} - -bool symbol_parser::find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns) -{ - if (java_only) { - return find_java_symbol(sym, pid, pid_ns); - } - - if (file.type == JIT_TYPE) { - return find_java_symbol(sym, pid, pid_ns); - } - - std::map >::iterator it; - it = file_symbols.find(file); - std::set ss; - if (it == file_symbols.end()) { - if (!load_elf(pid, file)) { - return false; - } - it = file_symbols.find(file); - } - return search_symbol(it->second, sym); -} - -vma* symbol_parser::find_vma(pid_t pid, size_t pc) -{ - std::map::iterator it; - - it = machine_vma.find(pid); - if (it == machine_vma.end()) { - return NULL; - } - - proc_vma::iterator vma_iter = it->second.upper_bound(pc); - if (vma_iter == it->second.end() || vma_iter->second.end < pc) { - return NULL; - } - - if (vma_iter != it->second.begin()) { - --vma_iter; - } - - return &vma_iter->second; -} - -bool symbol_parser::find_vma(pid_t pid, vma &vm) -{ - std::map::iterator proc_vma_map; - - proc_vma_map = machine_vma.find(pid); - if (proc_vma_map == machine_vma.end()) { - return false; - } - - proc_vma::const_iterator vma_iter = proc_vma_map->second.upper_bound(vm.pc); - if (vma_iter == proc_vma_map->second.end()) { - return false; - } - if (vma_iter->second.end < vm.pc) { - return false; - } - - if (vma_iter != proc_vma_map->second.begin()) { - --vma_iter; - } - - vm.start = vma_iter->second.start; - vm.end = vma_iter->second.end; - vm.name = vma_iter->second.name; - vm.offset = vma_iter->second.offset; - - return true; -} - -class pid_cmdline { - private: - std::map cmdlines; - public: - void clear(void); - std::string & get_pid_cmdline(int pid); -}; - -void pid_cmdline::clear(void) -{ - cmdlines.clear(); -} - -void clear_symbol_info(class pid_cmdline &pid_cmdline, std::set &procs, int dist) -{ - pid_cmdline.clear(); - procs.clear(); - g_symbol_parser.clear_symbol_info(dist); -} - -void symbol_parser::clear_symbol_info(int dist) -{ - machine_vma.clear(); - java_symbols.clear(); - if (dist) { - kernel_symbols.clear(); - file_symbols.clear(); - } -} - -void symbol_parser::dump(void) -{ - int count1, count2, count3; - - if (!debug_mode) - return; - - { - count1 = 0; - count2 = 0; - count3 = 0; - std::map >::iterator iter = file_symbols.begin(); - for(; iter != file_symbols.end(); ++iter) { - std::set& map = iter->second; - const elf_file& file = iter->first; - - count1++; - printf("xby-debug, file_symbols: %s, %lu\n", - file.filename.c_str(), - map.size()); - - count2 += map.size(); - std::set::iterator it = map.begin(); - for(; it != map.end(); ++it) { - count3 += it->name.length(); - } - } - printf("xby-debug, file_symbols: %d, %d, %d\n", count1, count2, count3); - printf("xby-debug, sizeof(symbol): %ld\n", sizeof(symbol)); - } - - { - count1 = 0; - count2 = 0; - std::map >::iterator iter = java_symbols.begin(); - for(; iter != java_symbols.end(); ++iter) { - count1++; - std::set& map = iter->second; - count2 += map.size(); - } - printf("xby-debug, java_symbols: %d, %d\n", count1, count2); - } - - { - printf("xby-debug, kernel_symbols: %lu\n", kernel_symbols.size()); - } - - { - count1 = 0; - count2 = 0; - std::map::iterator iter = machine_vma.begin(); - for(; iter != machine_vma.end(); ++iter) { - count1++; - proc_vma map = iter->second; - count2 += map.size(); - } - printf("xby-debug, machine_vma: %d, %d\n", count1, count2); - } - - { - count1 = 0; - count2 = 0; - std::map >::iterator iter = symbols_cache.begin(); - for(; iter != symbols_cache.end(); ++iter) { - count1++; - std::map& map = iter->second; - count2 += map.size(); - } - printf("xby-debug, symbols_cache: %d, %d\n", count1, count2); - } -} - -std::string demangleCppSym(std::string symbol) -{ - size_t size = 0; - int status = 0; - char *demangled = abi::__cxa_demangle(symbol.c_str(), NULL, &size, &status); - - if (status == 0 && demangled != NULL) - { - std::string FuncName(demangled); - free(demangled); - return FuncName; - } - else - { - // 解码失败,返回原始符号 - return symbol; - } -} - -void clearSpace(std::string &sym) -{ - for (auto i = sym.begin(); i != sym.end();) - { - if (isblank(*i)) - { - sym.erase(i); - } - else - { - i++; - } - } -} \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/main.cpp b/eBPF_Supermarket/Stack_Analyser/src/main.cpp index 842363627..435d380b9 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/main.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/main.cpp @@ -29,145 +29,127 @@ #include "bpf_wapper/io.h" #include "bpf_wapper/readahead.h" #include "bpf_wapper/probe.h" -#include "sa_user.h" +#include "user.h" #include "clipp.h" +#include "cgroup.h" +#include "trace.h" -uint64_t stop_time = -1; bool timeout = false; -uint64_t IntTmp; -std::string StrTmp; -clipp::man_page *man_page; +std::vector StackCollectorList; +void end_handle(void); namespace MainConfig { uint64_t run_time = -1; // 运行时间 unsigned delay = 5; // 设置输出间隔 std::string command = ""; - int32_t target_pid = -1; + uint32_t target_tgid = 0; + uint64_t target_cgroup = 0; std::string trigger = ""; // 触发器 std::string trig_event = ""; // 触发事件 -} - -std::vector StackCollectorList; - -void end_handle(void) -{ - signal(SIGINT, SIG_IGN); - for (auto Item : StackCollectorList) - { - Item->activate(false); - if (!timeout) - { - std::cout << std::string(*Item) << std::endl; - } - Item->detach(); - Item->unload(); - } - if (MainConfig::command.length()) - { - kill(MainConfig::target_pid, SIGTERM); - } + uint32_t top = 10; + uint32_t freq = 49; + bool trace_user = false; + bool trace_kernel = false; } int main(int argc, char *argv[]) { - man_page = new clipp::man_page(); + uint64_t stop_time = -1; + clipp::man_page man_page; clipp::group cli; { - auto TraceOption = (clipp::option("-u") - .call([] - { StackCollectorList.back()->ustack = true; }) % - "Sample user stacks", - clipp::option("-k") - .call([] - { StackCollectorList.back()->kstack = true; }) % - "Sample kernel stacks\n"); - + uint64_t IntTmp; + std::string StrTmp; auto OnCpuOption = (clipp::option("on_cpu") .call([] { StackCollectorList.push_back(new OnCPUStackCollector()); }) % - COLLECTOR_INFO("on-cpu")) & - ((clipp::option("-f") & - clipp::value("freq", IntTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->setScale(IntTmp); })) % - "Set sampling frequency; default is 49", - TraceOption); + COLLECTOR_INFO("on-cpu")); auto OffCpuOption = clipp::option("off_cpu") - .call([] - { StackCollectorList.push_back(new OffCPUStackCollector()); }) % - COLLECTOR_INFO("off-cpu") & - (TraceOption); + .call([] + { StackCollectorList.push_back(new OffCPUStackCollector()); }) % + COLLECTOR_INFO("off-cpu"); auto MemleakOption = (clipp::option("memleak") .call([] { StackCollectorList.push_back(new MemleakStackCollector()); }) % COLLECTOR_INFO("memleak")) & - ((clipp::option("-i") & - clipp::value("interval", IntTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->sample_rate = IntTmp; })) % - "Set the sampling interval; default is 1", - clipp::option("-w") - .call([] - { static_cast(StackCollectorList.back()) - ->wa_missing_free = true; }) % - "Free when missing in kernel to alleviate misjudgments", - TraceOption); + (clipp::option("-W") + .call([] + { static_cast(StackCollectorList.back()) + ->wa_missing_free = true; }) % + "Free when missing in kernel to alleviate misjudgments"); auto IOOption = clipp::option("io") - .call([] - { StackCollectorList.push_back(new IOStackCollector()); }) % - COLLECTOR_INFO("io") & - (TraceOption); + .call([] + { StackCollectorList.push_back(new IOStackCollector()); }) % + COLLECTOR_INFO("io"); auto ReadaheadOption = clipp::option("readahead") - .call([] - { StackCollectorList.push_back(new ReadaheadStackCollector()); }) % - COLLECTOR_INFO("readahead") & - (TraceOption); - - auto ProbeOption = clipp::option("probe") .call([] - { StackCollectorList.push_back(new ProbeStackCollector()); }) % - COLLECTOR_INFO("probe") & - (clipp::value("probe", StrTmp) - .call([] - { static_cast(StackCollectorList.back()) - ->setScale(StrTmp); }) % - "Set the probe string; specific use is: \n probe func && probe p::func -- probe a kernel function; \n lib:func && p:lib:func -- probe a user-space function in the library 'lib';\n probe t:cat:event -- probe a kernel tracepoint; \n probe u:lib:probe -- probe a USDT tracepoint" & - TraceOption); + { StackCollectorList.push_back(new ReadaheadStackCollector()); }) % + COLLECTOR_INFO("readahead"); auto LlcStatOption = clipp::option("llc_stat").call([] { StackCollectorList.push_back(new LlcStatStackCollector()); }) % COLLECTOR_INFO("llc_stat") & - ((clipp::option("-i") & + ((clipp::option("-P") & clipp::value("period", IntTmp) - .call([] + .call([IntTmp] { static_cast(StackCollectorList.back()) ->setScale(IntTmp); })) % - "Set sampling period; default is 100", - TraceOption); + "Set sampling period; default is 100"); + + auto ProbeOption = clipp::option("probe") + .call([] + { StackCollectorList.push_back(new ProbeStackCollector()); }) % + COLLECTOR_INFO("probe") & + (clipp::value("probe", StrTmp) + .call([&StrTmp] + { static_cast(StackCollectorList.back()) + ->setScale(StrTmp); }) % + "Set the probe string; specific use is:\n" + " | p:: -- probe a kernel function;\n" + ": | p:: -- probe a user-space function in the library 'lib';\n" + "t:: -- probe a kernel tracepoint;\n" + "u:: -- probe a USDT tracepoint"); auto MainOption = _GREEN "Some overall options" _RE % (( + ((clipp::option("-g") & + clipp::value("cgroup path", StrTmp) + .call([&StrTmp] + { MainConfig::target_cgroup = get_cgroupid(StrTmp.c_str()); printf("Trace cgroup %ld\n", MainConfig::target_cgroup); })) % + "Set the cgroup of the process to be tracked; default is -1, which keeps track of all cgroups") | ((clipp::option("-p") & - clipp::value("pid", MainConfig::target_pid)) % + clipp::value("pid", MainConfig::target_tgid)) % "Set the pid of the process to be tracked; default is -1, which keeps track of all processes") | ((clipp::option("-c") & clipp::value("command", MainConfig::command)) % "Set the command to be run and sampled; defaults is none")), - (clipp::option("-d") & + (clipp::option("-o") & + clipp::value("top", MainConfig::top)) % + "Set the top number; default is 10", + (clipp::option("-f") & + clipp::value("freq", MainConfig::freq)) % + "Set sampling frequency, 0 for close; default is 49", + (clipp::option("-i") & clipp::value("interval", MainConfig::delay)) % "Set the output delay time (seconds); default is 5", - (clipp::option("-t") & + (clipp::option("-d") & clipp::value("duration", MainConfig::run_time) - .call([] + .call([&stop_time] { stop_time = time(NULL) + MainConfig::run_time; })) % "Set the total sampling time; default is __INT_MAX__", + (clipp::option("-u") + .call([] + { MainConfig::trace_user = true; }) % + "Sample user stacks", + clipp::option("-k") + .call([] + { MainConfig::trace_kernel = true; }) % + "Sample kernel stacks"), (clipp::option("-T") & ((clipp::required("cpu").set(MainConfig::trigger) | clipp::required("memory").set(MainConfig::trigger) | @@ -183,8 +165,8 @@ int main(int argc, char *argv[]) { std::cout << "verion 2.0\n\n"; }) % "Show version"), (clipp::option("-h", "--help") - .call([] - { std::cout << *man_page << std::endl; exit(0); }) % + .call([&man_page] + { std::cout << man_page << std::endl; exit(0); }) % "Show man page")); cli = (OnCpuOption, @@ -192,8 +174,8 @@ int main(int argc, char *argv[]) MemleakOption, IOOption, ReadaheadOption, - ProbeOption, LlcStatOption, + clipp::repeatable(ProbeOption), MainOption, Info); } @@ -202,13 +184,13 @@ int main(int argc, char *argv[]) .first_column(3) .doc_column(25) .last_column(128); - *man_page = clipp::make_man_page(cli, argv[0], fmt) - .prepend_section("DESCRIPTION", _RED "Count the function call stack associated with some metric.\n" _RE BANNER) - .append_section("LICENSE", _RED "Apache Licence 2.0" _RE); + man_page = clipp::make_man_page(cli, argv[0], fmt) + .prepend_section("DESCRIPTION", _RED "Count the function call stack associated with some metric.\n" _RE BANNER) + .append_section("LICENSE", _RED "Apache Licence 2.0" _RE); } if (!clipp::parse(argc, argv, cli)) { - std::cerr << *man_page << std::endl; + std::cerr << man_page << std::endl; return -1; } if (StackCollectorList.size() == 0) @@ -221,46 +203,63 @@ int main(int argc, char *argv[]) uint64_t eventbuff = 1; int child_exec_event_fd = eventfd(0, EFD_CLOEXEC); - CHECK_ERR(child_exec_event_fd < 0, "failed to create event fd"); + CHECK_ERR_RN1(child_exec_event_fd < 0, "failed to create event fd"); if (MainConfig::command.length()) { - MainConfig::target_pid = fork(); - switch (MainConfig::target_pid) + MainConfig::target_tgid = fork(); + switch (MainConfig::target_tgid) { - case -1: + case (uint32_t)-1: { - CHECK_ERR(true, "Command create failed."); + CHECK_ERR_RN1(true, "Command create failed."); } case 0: { const auto bytes = read(child_exec_event_fd, &eventbuff, sizeof(eventbuff)); - CHECK_ERR(bytes < 0, "Failed to read from fd %ld", bytes) - else CHECK_ERR(bytes != sizeof(eventbuff), "Read unexpected size %ld", bytes); + CHECK_ERR_RN1(bytes < 0, "Failed to read from fd %ld", bytes) + else CHECK_ERR_RN1(bytes != sizeof(eventbuff), "Read unexpected size %ld", bytes); printf("child exec %s\n", MainConfig::command.c_str()); - CHECK_ERR_EXIT(execl("/bin/bash", "bash", "-c", MainConfig::command.c_str(), NULL), "failed to execute child command"); + CHECK_ERR(exit(-1), execl("/bin/bash", "bash", "-c", MainConfig::command.c_str(), NULL), "failed to execute child command"); break; } default: { - printf("Create child %d\n", MainConfig::target_pid); + printf("Create child %d\n", MainConfig::target_tgid); break; } } } + ksyms = ksyms__load(); + if (!ksyms) + { + fprintf(stderr, "failed to load kallsyms\n"); + exit(1); + } + syms_cache = syms_cache__new(0); + if (!syms_cache) + { + fprintf(stderr, "failed to create syms_cache\n"); + exit(1); + } + for (auto Item = StackCollectorList.begin(); Item != StackCollectorList.end();) { fprintf(stderr, _RED "Attach collecotor%d %s.\n" _RE, (int)(Item - StackCollectorList.begin()) + 1, (*Item)->getName()); - (*Item)->pid = MainConfig::target_pid; - if ((*Item)->load() || (*Item)->attach()) + (*Item)->tgid = MainConfig::target_tgid; + (*Item)->cgroup = MainConfig::target_cgroup; + (*Item)->top = MainConfig::top; + (*Item)->freq = MainConfig::freq; + (*Item)->kstack = MainConfig::trace_kernel; + (*Item)->ustack = MainConfig::trace_user; + if ((*Item)->ready()) goto err; Item++; continue; err: - fprintf(stderr, _ERED "Collector %s err.\n" _RE, (*Item)->scales->Type.c_str()); - (*Item)->detach(); - (*Item)->unload(); + fprintf(stderr, _ERED "Collector %s err.\n" _RE, (*Item)->getName()); + (*Item)->finish(); Item = StackCollectorList.erase(Item); } @@ -287,21 +286,21 @@ int main(int argc, char *argv[]) auto trig = MainConfig::trig_event.c_str(); fds.fd = open(path, O_RDWR | O_NONBLOCK); - CHECK_ERR(fds.fd < 0, "%s open error", path); + CHECK_ERR_RN1(fds.fd < 0, "%s open error", path); fds.events = POLLPRI; - CHECK_ERR(write(fds.fd, trig, strlen(trig) + 1) < 0, "%s write error", path); + CHECK_ERR_RN1(write(fds.fd, trig, strlen(trig) + 1) < 0, "%s write error", path); fprintf(stderr, _RED "Waiting for events...\n" _RE); } fprintf(stderr, _RED "Running for %lus or Hit Ctrl-C to end.\n" _RE, MainConfig::run_time); - for (; (uint64_t)time(NULL) < stop_time && (MainConfig::target_pid < 0 || !kill(MainConfig::target_pid, 0));) + for (; (uint64_t)time(NULL) < stop_time && (MainConfig::target_tgid < 0 || !kill(MainConfig::target_tgid, 0));) { if (fds.fd >= 0) { while (true) { int n = poll(&fds, 1, -1); - CHECK_ERR(n < 0, "Poll error"); - CHECK_ERR(fds.revents & POLLERR, "Got POLLERR, event source is gone"); + CHECK_ERR_RN1(n < 0, "Poll error"); + CHECK_ERR_RN1(fds.revents & POLLERR, "Got POLLERR, event source is gone"); if (fds.revents & POLLPRI) { fprintf(stderr, _RED "Event triggered!\n" _RE); @@ -319,4 +318,22 @@ int main(int argc, char *argv[]) } timeout = true; return 0; -} \ No newline at end of file +}; + +void end_handle(void) +{ + signal(SIGINT, SIG_IGN); + for (auto Item : StackCollectorList) + { + Item->activate(false); + if (!timeout) + { + std::cout << std::string(*Item) << std::endl; + } + Item->finish(); + } + if (MainConfig::command.length()) + { + kill(MainConfig::target_tgid, SIGTERM); + } +}; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp b/eBPF_Supermarket/Stack_Analyser/src/trace.cpp similarity index 82% rename from eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp rename to eBPF_Supermarket/Stack_Analyser/src/trace.cpp index 6097ec6e7..a8dc45044 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/trace_helpers.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/trace.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "trace_helpers.h" -#include "uprobe_helpers.h" +#include "trace.h" +#include "uprobe.h" #define min(x, y) ({ \ typeof(x) _min1 = (x); \ @@ -29,14 +29,15 @@ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) -#define DISK_NAME_LEN 32 +#define DISK_NAME_LEN 32 -#define MINORBITS 20 -#define MINORMASK ((1U << MINORBITS) - 1) +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) -#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) -struct ksyms { +struct ksyms +{ struct ksym *syms; int syms_sz; int syms_cap; @@ -51,7 +52,8 @@ static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned lon struct ksym *ksym; void *tmp; - if (ksyms->strs_sz + name_len > ksyms->strs_cap) { + if (ksyms->strs_sz + name_len > ksyms->strs_cap) + { new_cap = ksyms->strs_cap * 4 / 3; if (new_cap < ksyms->strs_sz + name_len) new_cap = ksyms->strs_sz + name_len; @@ -63,7 +65,8 @@ static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned lon ksyms->strs = (char *)tmp; ksyms->strs_cap = new_cap; } - if (ksyms->syms_sz + 1 > ksyms->syms_cap) { + if (ksyms->syms_sz + 1 > ksyms->syms_cap) + { new_cap = ksyms->syms_cap * 4 / 3; if (new_cap < 1024) new_cap = 1024; @@ -111,9 +114,10 @@ struct ksyms *ksyms__load(void) if (!ksyms) goto err_out; - while (true) { + while (true) + { ret = fscanf(f, "%lx %c %s%*[^\n]\n", - &sym_addr, &sym_type, sym_name); + &sym_addr, &sym_type, sym_name); if (ret == EOF && feof(f)) break; if (ret != 3) @@ -148,13 +152,14 @@ void ksyms__free(struct ksyms *ksyms) } const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, - unsigned long addr) + unsigned long addr) { int start = 0, end = ksyms->syms_sz - 1, mid; unsigned long sym_addr; /* find largest sym_addr <= addr using binary search */ - while (start < end) { + while (start < end) + { mid = start + (end - start + 1) / 2; sym_addr = ksyms->syms[mid].addr; @@ -170,11 +175,12 @@ const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, } const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, - const char *name) + const char *name) { int i; - for (i = 0; i < ksyms->syms_sz; i++) { + for (i = 0; i < ksyms->syms_sz; i++) + { if (strcmp(ksyms->syms[i].name, name) == 0) return &ksyms->syms[i]; } @@ -182,13 +188,15 @@ const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, return NULL; } -struct load_range { +struct load_range +{ uint64_t start; uint64_t end; uint64_t file_off; }; -enum elf_type { +enum elf_type +{ EXEC, DYN, PERF_MAP, @@ -196,7 +204,8 @@ enum elf_type { UNKNOWN, }; -struct dso { +struct dso +{ char *name; struct load_range *ranges; int range_sz; @@ -218,7 +227,8 @@ struct dso { struct btf *btf; }; -struct map { +struct map +{ uint64_t start_addr; uint64_t end_addr; uint64_t file_off; @@ -227,7 +237,8 @@ struct map { uint64_t inode; }; -struct syms { +struct syms +{ struct dso *dsos; int dso_sz; }; @@ -238,14 +249,15 @@ static bool is_file_backed(const char *mapname) (!strncmp(mapname, prefix, sizeof(prefix) - 1)) return mapname[0] && !( - STARTS_WITH(mapname, "//anon") || - STARTS_WITH(mapname, "/dev/zero") || - STARTS_WITH(mapname, "/anon_hugepage") || - STARTS_WITH(mapname, "[stack") || - STARTS_WITH(mapname, "/SYSV") || - STARTS_WITH(mapname, "[heap]") || - STARTS_WITH(mapname, "[uprobes]") || - STARTS_WITH(mapname, "[vsyscall]")); + STARTS_WITH(mapname, "//anon") || + STARTS_WITH(mapname, "/dev/zero") || + STARTS_WITH(mapname, "/anon_hugepage") || + STARTS_WITH(mapname, "[stack") || + STARTS_WITH(mapname, "/SYSV") || + STARTS_WITH(mapname, "[heap]") || + STARTS_WITH(mapname, "[uprobes]") || + STARTS_WITH(mapname, "[vsyscall]") || + STARTS_WITH(mapname, "[vdso]")); } static bool is_perf_map(const char *path) @@ -285,7 +297,7 @@ static int get_elf_type(const char *path) } static int get_elf_text_scn_info(const char *path, uint64_t *addr, - uint64_t *offset) + uint64_t *offset) { Elf_Scn *section = NULL; int fd = -1, err = -1; @@ -302,12 +314,14 @@ static int get_elf_text_scn_info(const char *path, uint64_t *addr, goto err_out; err = -1; - while ((section = elf_nextscn(e, section)) != 0) { + while ((section = elf_nextscn(e, section)) != 0) + { if (!gelf_getshdr(section, &header)) continue; name = elf_strptr(e, stridx, header.sh_name); - if (name && !strcmp(name, ".text")) { + if (name && !strcmp(name, ".text")) + { *addr = (uint64_t)header.sh_addr; *offset = (uint64_t)header.sh_offset; err = 0; @@ -326,16 +340,19 @@ static int syms__add_dso(struct syms *syms, struct map *map, const char *name) int i, type; void *tmp; - for (i = 0; i < syms->dso_sz; i++) { - if (!strcmp(syms->dsos[i].name, name)) { + for (i = 0; i < syms->dso_sz; i++) + { + if (!strcmp(syms->dsos[i].name, name)) + { dso = &syms->dsos[i]; break; } } - if (!dso) { + if (!dso) + { tmp = realloc(syms->dsos, (syms->dso_sz + 1) * - sizeof(*syms->dsos)); + sizeof(*syms->dsos)); if (!tmp) return -1; syms->dsos = (struct dso *)tmp; @@ -354,41 +371,55 @@ static int syms__add_dso(struct syms *syms, struct map *map, const char *name) dso->ranges[dso->range_sz].file_off = map->file_off; dso->range_sz++; type = get_elf_type(name); - if (type == ET_EXEC) { + if (type == ET_EXEC) + { dso->type = EXEC; - } else if (type == ET_DYN) { + } + else if (type == ET_DYN) + { dso->type = DYN; if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0) return -1; - } else if (is_perf_map(name)) { + } + else if (is_perf_map(name)) + { dso->type = PERF_MAP; - } else if (is_vdso(name)) { + } + else if (is_vdso(name)) + { dso->type = VDSO; - } else { + } + else + { dso->type = UNKNOWN; } return 0; } static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr, - uint64_t *offset) + uint64_t *offset) { struct load_range *range; struct dso *dso; int i, j; - for (i = 0; i < syms->dso_sz; i++) { + for (i = 0; i < syms->dso_sz; i++) + { dso = &syms->dsos[i]; - for (j = 0; j < dso->range_sz; j++) { + for (j = 0; j < dso->range_sz; j++) + { range = &dso->ranges[j]; if (addr <= range->start || addr >= range->end) continue; - if (dso->type == DYN || dso->type == VDSO) { + if (dso->type == DYN || dso->type == VDSO) + { /* Offset within the mmap */ *offset = addr - range->start + range->file_off; /* Offset within the ELF for dyn symbol lookup */ *offset += dso->sh_addr - dso->sh_offset; - } else { + } + else + { *offset = addr; } @@ -405,7 +436,7 @@ static int dso__load_sym_table_from_perf_map(struct dso *dso) } static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, - uint64_t size) + uint64_t size) { struct sym *sym; size_t new_cap; @@ -416,7 +447,8 @@ static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, if (off < 0) return off; - if (dso->syms_sz + 1 > dso->syms_cap) { + if (dso->syms_sz + 1 > dso->syms_cap) + { new_cap = dso->syms_cap * 4 / 3; if (new_cap < 1024) new_cap = 1024; @@ -447,17 +479,19 @@ static int sym_cmp(const void *p1, const void *p2) } static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section, - size_t stridx, size_t symsize) + size_t stridx, size_t symsize) { Elf_Data *data = NULL; - while ((data = elf_getdata(section, data)) != 0) { + while ((data = elf_getdata(section, data)) != 0) + { size_t i, symcount = data->d_size / symsize; if (data->d_size % symsize) return -1; - for (i = 0; i < symcount; ++i) { + for (i = 0; i < symcount; ++i) + { const char *name; GElf_Sym sym; @@ -503,18 +537,19 @@ static int dso__load_sym_table_from_elf(struct dso *dso, int fd) if (!e) return -1; - while ((section = elf_nextscn(e, section)) != 0) { + while ((section = elf_nextscn(e, section)) != 0) + { GElf_Shdr header; if (!gelf_getshdr(section, &header)) continue; if (header.sh_type != SHT_SYMTAB && - header.sh_type != SHT_DYNSYM) + header.sh_type != SHT_DYNSYM) continue; if (dso__add_syms(dso, e, section, header.sh_link, - header.sh_entsize)) + header.sh_entsize)) goto err_out; } @@ -522,7 +557,7 @@ static int dso__load_sym_table_from_elf(struct dso *dso, int fd) for (i = 0; i < dso->syms_sz; i++) dso->syms[i].name = btf__name_by_offset(dso->btf, - (unsigned long)dso->syms[i].name); + (unsigned long)dso->syms[i].name); qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp); @@ -552,10 +587,11 @@ static int create_tmp_vdso_image(struct dso *dso) if (!f) return -1; - while (true) { + while (true) + { ret = fscanf(f, "%llx-%llx %*s %*x %*x:%*x %*u%[^\n]", - (long long*)&start_addr, (long long*)&end_addr, - buf); + (long long *)&start_addr, (long long *)&end_addr, + buf); if (ret == EOF && feof(f)) break; if (ret != 3) @@ -577,20 +613,22 @@ static int create_tmp_vdso_image(struct dso *dso) memcpy(image, (void *)start_addr, sz); snprintf(tmpfile, sizeof(tmpfile), - "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid); + "/tmp/libbpf_%ld_vdso_image_XXXXXX", pid); fd = mkostemp(tmpfile, O_CLOEXEC); - if (fd < 0) { + if (fd < 0) + { fprintf(stderr, "failed to create temp file: %s\n", - strerror(errno)); + strerror(errno)); goto err_out; } /* Unlink the file to avoid leaking */ if (unlink(tmpfile) == -1) fprintf(stderr, "failed to unlink %s: %s\n", tmpfile, - strerror(errno)); - if (write(fd, image, sz) == -1) { + strerror(errno)); + if (write(fd, image, sz) == -1) + { fprintf(stderr, "failed to write to vDSO image: %s\n", - strerror(errno)); + strerror(errno)); close(fd); fd = -1; goto err_out; @@ -636,7 +674,8 @@ static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) end = dso->syms_sz - 1; /* find largest sym_addr <= addr using binary search */ - while (start < end) { + while (start < end) + { mid = start + (end - start + 1) / 2; sym_addr = dso->syms[mid].start; @@ -647,16 +686,17 @@ static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) } if (start == end && dso->syms[start].start <= offset && - offset < dso->syms[start].start + dso->syms[start].size) { + offset < dso->syms[start].start + dso->syms[start].size) + { (dso->syms[start]).offset = offset - dso->syms[start].start; return &dso->syms[start]; } return NULL; } -struct syms *syms__load_file(const char *fname) +struct syms *syms__load_file(const char *fname, pid_t tgid) { - char buf[PATH_MAX], perm[5]; + char buf[PATH_MAX], perm[5], path[PATH_MAX]; struct syms *syms; struct map map; char *name; @@ -671,17 +711,18 @@ struct syms *syms__load_file(const char *fname) if (!syms) goto err_out; - while (true) { + while (true) + { ret = fscanf(f, "%llx-%llx %4s %llx %llx:%llx %llu%[^\n]", - (long long*)&map.start_addr, - (long long*)&map.end_addr, perm, - (long long*)&map.file_off, - (long long*)&map.dev_major, - (long long*)&map.dev_minor, - (long long*)&map.inode, buf); + (long long *)&map.start_addr, + (long long *)&map.end_addr, perm, + (long long *)&map.file_off, + (long long *)&map.dev_major, + (long long *)&map.dev_minor, + (long long *)&map.inode, buf); if (ret == EOF && feof(f)) break; - if (ret != 8) /* perf-.map */ + if (ret != 8) /* perf-.map */ goto err_out; if (perm[2] != 'x') @@ -693,7 +734,8 @@ struct syms *syms__load_file(const char *fname) if (!is_file_backed(name)) continue; - if (syms__add_dso(syms, &map, name)) + sprintf(path, "/proc/%d/root/%s", tgid, name); + if (syms__add_dso(syms, &map, path)) goto err_out; } @@ -711,7 +753,7 @@ struct syms *syms__load_pid(pid_t tgid) char fname[128]; snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid); - return syms__load_file(fname); + return syms__load_file(fname, tgid); } void syms__free(struct syms *syms) @@ -727,7 +769,7 @@ void syms__free(struct syms *syms) free(syms); } -const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) +struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) { struct dso *dso; uint64_t offset; @@ -739,7 +781,7 @@ const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) } const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, - char **dso_name, unsigned long *dso_offset) + char **dso_name, unsigned long *dso_offset) { struct dso *dso; uint64_t offset; @@ -754,8 +796,10 @@ const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr return dso__find_sym(dso, offset); } -struct syms_cache { - struct { +struct syms_cache +{ + struct + { struct syms *syms; int tgid; } *data; @@ -792,13 +836,14 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) void *tmp; int i; - for (i = 0; i < syms_cache->nr; i++) { + for (i = 0; i < syms_cache->nr; i++) + { if (syms_cache->data[i].tgid == tgid) return syms_cache->data[i].syms; } tmp = realloc(syms_cache->data, (syms_cache->nr + 1) * - sizeof(*syms_cache->data)); + sizeof(*syms_cache->data)); if (!tmp) return NULL; syms_cache->data = (typeof(syms_cache->data))tmp; @@ -807,19 +852,20 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) return syms_cache->data[syms_cache->nr++].syms; } -struct partitions { +struct partitions +{ struct partition *items; int sz; }; static int partitions__add_partition(struct partitions *partitions, - const char *name, unsigned int dev) + const char *name, unsigned int dev) { struct partition *partition; void *tmp; tmp = realloc(partitions->items, (partitions->sz + 1) * - sizeof(*partitions->items)); + sizeof(*partitions->items)); if (!tmp) return -1; partitions->items = (struct partition *)tmp; @@ -848,15 +894,16 @@ struct partitions *partitions__load(void) if (!partitions) goto err_out; - while (fgets(buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) + { /* skip heading */ if (buf[0] != ' ' || buf[0] == '\n') continue; if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop, - part_name) != 4) + part_name) != 4) goto err_out; if (partitions__add_partition(partitions, part_name, - MKDEV(devmaj, devmin))) + MKDEV(devmaj, devmin))) goto err_out; } @@ -887,7 +934,8 @@ partitions__get_by_dev(const struct partitions *partitions, unsigned int dev) { int i; - for (i = 0; i < partitions->sz; i++) { + for (i = 0; i < partitions->sz; i++) + { if (partitions->items[i].dev == dev) return &partitions->items[i]; } @@ -900,7 +948,8 @@ partitions__get_by_name(const struct partitions *partitions, const char *name) { int i; - for (i = 0; i < partitions->sz; i++) { + for (i = 0; i < partitions->sz; i++) + { if (strcmp(partitions->items[i].name, name) == 0) return &partitions->items[i]; } @@ -932,7 +981,8 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) unsigned long long low, high; int stars, width, i; - for (i = 0; i < vals_size; i++) { + for (i = 0; i < vals_size; i++) + { val = vals[i]; if (val > 0) idx_max = i; @@ -944,14 +994,15 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) return; printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "", - idx_max <= 32 ? 19 : 29, val_type); + idx_max <= 32 ? 19 : 29, val_type); if (idx_max <= 32) stars = stars_max; else stars = stars_max / 2; - for (i = 0; i <= idx_max; i++) { + for (i = 0; i <= idx_max; i++) + { low = (1ULL << (i + 1)) >> 1; high = (1ULL << (i + 1)) - 1; if (low == high) @@ -965,14 +1016,16 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) } void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, - unsigned int step, const char *val_type) + unsigned int step, const char *val_type) { int i, stars_max = 40, idx_min = -1, idx_max = -1; unsigned int val, val_max = 0; - for (i = 0; i < vals_size; i++) { + for (i = 0; i < vals_size; i++) + { val = vals[i]; - if (val > 0) { + if (val > 0) + { idx_max = i; if (idx_min < 0) idx_min = i; @@ -985,7 +1038,8 @@ void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, return; printf(" %-13s : count distribution\n", val_type); - for (i = idx_min; i <= idx_max; i++) { + for (i = idx_min; i <= idx_max; i++) + { val = vals[i]; if (!val) continue; @@ -1013,10 +1067,12 @@ bool is_kernel_module(const char *name) if (!f) return false; - while (fgets(buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) + { if (sscanf(buf, "%s %*s\n", buf) != 1) break; - if (!strcmp(buf, name)) { + if (!strcmp(buf, name)) + { found = true; break; } @@ -1031,18 +1087,17 @@ static bool fentry_try_attach(int id) int prog_fd, attach_fd; char error[4096]; struct bpf_insn insns[] = { - { .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 }, - { .code = BPF_JMP | BPF_EXIT }, + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, }; LIBBPF_OPTS(bpf_prog_load_opts, opts, - .expected_attach_type = BPF_TRACE_FENTRY, - .attach_btf_id = (__u32)id, - .log_size = sizeof(error), - .log_buf = error, - ); + .expected_attach_type = BPF_TRACE_FENTRY, + .attach_btf_id = (__u32)id, + .log_size = sizeof(error), + .log_buf = error, ); prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", "GPL", insns, - sizeof(insns) / sizeof(struct bpf_insn), &opts); + sizeof(insns) / sizeof(struct bpf_insn), &opts); if (prog_fd < 0) return false; @@ -1066,7 +1121,8 @@ bool fentry_can_attach(const char *name, const char *mod) btf = vmlinux_btf; - if (mod) { + if (mod) + { module_btf = btf__load_module_btf(mod, vmlinux_btf); err = libbpf_get_error(module_btf); if (!err) @@ -1100,8 +1156,7 @@ static const char *tracefs_path(void) static const char *tracefs_available_filter_functions(void) { - return use_debugfs() ? DEBUGFS"/available_filter_functions" : - TRACEFS"/available_filter_functions"; + return use_debugfs() ? DEBUGFS "/available_filter_functions" : TRACEFS "/available_filter_functions"; } bool kprobe_exists(const char *name) @@ -1115,15 +1170,18 @@ bool kprobe_exists(const char *name) if (!f) goto avail_filter; - while (true) { + while (true) + { ret = fscanf(f, "%s %s%*[^\n]\n", addr_range, sym_name); if (ret == EOF && feof(f)) break; - if (ret != 2) { + if (ret != 2) + { fprintf(stderr, "failed to read symbol from kprobe blacklist\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return false; } @@ -1135,15 +1193,18 @@ bool kprobe_exists(const char *name) if (!f) goto slow_path; - while (true) { + while (true) + { ret = fscanf(f, "%s%*[^\n]\n", sym_name); if (ret == EOF && feof(f)) break; - if (ret != 1) { + if (ret != 1) + { fprintf(stderr, "failed to read symbol from available_filter_functions\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return true; } @@ -1157,15 +1218,18 @@ bool kprobe_exists(const char *name) if (!f) return false; - while (true) { + while (true) + { ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name); if (ret == EOF && feof(f)) break; - if (ret != 1) { + if (ret != 1) + { fprintf(stderr, "failed to read symbol from kallsyms\n"); break; } - if (!strcmp(name, sym_name)) { + if (!strcmp(name, sym_name)) + { fclose(f); return true; } @@ -1203,7 +1267,8 @@ bool module_btf_exists(const char *mod) { char sysfs_mod[80]; - if (mod) { + if (mod) + { snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod); if (!access(sysfs_mod, R_OK)) return true; @@ -1215,8 +1280,8 @@ bool probe_tp_btf(const char *name) { LIBBPF_OPTS(bpf_prog_load_opts, opts, .expected_attach_type = BPF_TRACE_RAW_TP); struct bpf_insn insns[] = { - { .code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0 }, - { .code = BPF_JMP | BPF_EXIT }, + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, }; int fd, insn_cnt = sizeof(insns) / sizeof(struct bpf_insn); @@ -1238,3 +1303,21 @@ bool probe_ringbuf() close(map_fd); return true; } + +const struct ksym *ksyms__find_symbol(const struct ksyms *ksyms, + const char *name) +{ + for (int i = 0; i < ksyms->syms_sz; i++) + { + int j; + for (j = 0; name[j] && name[j] == ksyms->syms[i].name[j]; j++) + ; + if (!name[j]) + return &ksyms->syms[i]; + } + return NULL; +} + +struct ksyms *ksyms; +struct syms_cache *syms_cache; +struct syms *syms; \ No newline at end of file diff --git a/eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cpp b/eBPF_Supermarket/Stack_Analyser/src/uprobe.cpp similarity index 100% rename from eBPF_Supermarket/Stack_Analyser/src/uprobe_helpers.cpp rename to eBPF_Supermarket/Stack_Analyser/src/uprobe.cpp diff --git a/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c b/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c new file mode 100644 index 000000000..182a29555 --- /dev/null +++ b/eBPF_Supermarket/Stack_Analyser/testdir/uprobe_malloc.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +int main() { + // 打印当前进程号 + printf("Process ID: %d\n", getpid()); + + // 申请内存 + char *buffer = (char *)malloc(BUFFER_SIZE * sizeof(char)); + if (buffer == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + return 1; + } + + // 文件描述符 + int fd = open("data.txt", O_CREAT | O_WRONLY, 0644); + if (fd == -1) { + fprintf(stderr, "Failed to open file\n"); + free(buffer); + return 1; + } + + // 写数据 + printf("Writing data. Process ID: %d\n", getpid()); + write(fd, "Hello, World!\n", 14); + + // 关闭文件描述符 + close(fd); + + // 打开文件以读取数据 + fd = open("data.txt", O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Failed to open file for reading\n"); + free(buffer); + return 1; + } + + // 读取数据 + printf("Reading data. Process ID: %d\n", getpid()); + ssize_t bytesRead = read(fd, buffer, BUFFER_SIZE); + if (bytesRead == -1) { + fprintf(stderr, "Failed to read data from file\n"); + free(buffer); + close(fd); + return 1; + } + + // 输出读取的数据 + printf("Data read: %s\n", buffer); + + // 关闭文件描述符 + close(fd); + + // 持续打印进程号 + while(1) { + printf("Still running. Process ID: %d\n", getpid()); + sleep(1); // 暂停一秒钟 + } + + // 释放内存 + free(buffer); + printf("Memory freed. Process ID: %d\n", getpid()); + + return 0; +} diff --git a/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c b/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c index 6c9729b8d..6714faf65 100644 --- a/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c +++ b/eBPF_Supermarket/Stack_Analyser/testdir/usdt_pthread.c @@ -1,45 +1,33 @@ +#define _GNU_SOURCE #include #include #include #include +#include // 线程函数 -void *thread_function(void *arg) { - while (1) { +void *thread_function(void *arg) +{ + while (1) + { // 打印当前线程的pid - printf("Thread PID: %d\n", getpid()); + printf("Thread ID: %d\n", gettid()); // 等待一秒 sleep(1); } - pthread_exit(NULL); -} +}; -// 创建线程的函数 -void create_thread() { - pthread_t thread; - int rc; +int main() +{ - // 创建线程 - rc = pthread_create(&thread, NULL, thread_function, NULL); - if (rc) { - fprintf(stderr, "Error: pthread_create() failed with code %d\n", rc); + pthread_t thread; + if (pthread_create(&thread, NULL, thread_function, NULL)) + { + fprintf(stderr, "Error: pthread_create() failed\n"); exit(EXIT_FAILURE); } -} - -int main() { - // 打印主进程的pid - printf("Main process PID: %d\n", getpid()); - - // 调用函数创建线程 - create_thread(); - // 主进程死循环打印pid - while (1) { - printf("Main process PID: %d\n", getpid()); - // 等待一秒 - sleep(1); - } + thread_function(NULL); return 0; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/Makefile b/eBPF_Supermarket/kvm_watcher/Makefile index 30f6a4b89..cc5a05466 100644 --- a/eBPF_Supermarket/kvm_watcher/Makefile +++ b/eBPF_Supermarket/kvm_watcher/Makefile @@ -1,4 +1,3 @@ -.SILENT: ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ | sed 's/arm.*/arm/' \ | sed 's/aarch64/arm64/' \ @@ -18,19 +17,12 @@ BPF_CFLAGS=-g -O2 -target bpf LIBS=-lbpf -lelf -lz -lzstd # 头文件目录 -INCLUDE_DIRS=-I/usr/include/x86_64-linux-gnu -I. +INCLUDE_DIRS=-I/usr/include/x86_64-linux-gnu -I. -I./include -I./include/bpf -I./include/helpers -# qemu 命令行参数变量化 -QEMU_CMD=sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=cirros-0.5.2-x86_64-disk.img,format=qcow2 -boot c -nographic - -CIRROS_IMG_URL=https://gitee.com/nan-shuaibo/cirros/releases/download/0.5.2/cirros-0.5.2-x86_64-disk.img -CIRROS_IMG_FILE=cirros-0.5.2-x86_64-disk.img - -# 定义检查虚拟化支持的命令 -CHECK_VIRT_SUPPORT = [ $$(grep -Eoc '(vmx|svm)' /proc/cpuinfo) -eq 0 ] - -# 定义检查 qemu-system-x86_64 进程是否存在的命令 -CHECK_QEMU_RUNNING = [ "$$(pgrep -f qemu-system-x86_64)" ] +# 帮助函数 +HELPERS_OBJ_DIR := src/helpers +HELPERS_FILES := $(wildcard $(HELPERS_OBJ_DIR)/*.c) +HELPERS_OBJ_FILES := $(patsubst $(HELPERS_OBJ_DIR)/%.c,$(HELPERS_OBJ_DIR)/%.o,$(HELPERS_FILES)) # 默认目标 .PHONY: default @@ -39,27 +31,53 @@ default: bpf # 安装必要的依赖 .PHONY: deps deps: - sudo apt-get update + sudo apt-get update && \ sudo apt-get install -y clang libelf1 libelf-dev zlib1g-dev libbpf-dev \ - linux-tools-$$(uname -r) linux-cloud-tools-$$(uname -r) - sudo apt-get install -y lolcat qemu-kvm wget + linux-tools-$$(uname -r) linux-cloud-tools-$$(uname -r) \ + libpcap-dev gcc-multilib build-essential lolcat qemu-kvm wget + # 生成 vmlinux.h .PHONY: vmlinux vmlinux: bpftool btf dump file /sys/kernel/btf/kvm format c > ./include/vmlinux.h +# 编译helpers目录下的所有.c文件 +$(HELPERS_OBJ_DIR)/%.o: $(HELPERS_OBJ_DIR)/%.c + clang $(CFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + +# 编译BPF程序 +$(APP).bpf.o: $(APP).bpf.c vmlinux + clang $(BPF_CFLAGS) -D__TARGET_ARCH_$(ARCH) $(INCLUDE_DIRS) -c $< -o $@ + +# 生成BPF骨架文件 +$(APP).skel.h: $(APP).bpf.o + bpftool gen skeleton $< > $@ + +# 编译用户空间应用程序 +${APP}.o: ${APP}.c + clang $(CFLAGS) $(INCLUDE_DIRS) -c $< -o $@ + +# 链接用户空间应用程序与库 +$(notdir $(APP)): ${APP}.o $(HELPERS_OBJ_FILES) + clang -Wall $(CFLAGS) ${APP}.o $(HELPERS_OBJ_FILES) $(LIBS) -o $@ + @echo "BPF program compiled successfully." + # bpf 目标 .PHONY: bpf -bpf: vmlinux - # 编译BPF程序 - clang $(BPF_CFLAGS) -D__TARGET_ARCH_$(ARCH) $(INCLUDE_DIRS) -c $(APP).bpf.c -o $(APP).bpf.o - # 生成BPF骨架文件 - bpftool gen skeleton ${APP}.bpf.o > $(APP).skel.h - # 编译用户空间应用程序 - clang $(CFLAGS) $(INCLUDE_DIRS) -c $(APP).c -o ${APP}.o - # 将用户空间应用程序与库链接 - clang -Wall $(CFLAGS) ${APP}.o $(LIBS) -o $(notdir $(APP)) - echo "BPF program compiled successfully." +bpf: $(APP).skel.h $(APP).bpf.o ${APP}.o $(HELPERS_OBJ_FILES) $(notdir $(APP)) + + +# qemu 命令行参数变量化 +QEMU_CMD=sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2048 -smp 4 -drive file=cirros-0.5.2-x86_64-disk.img,format=qcow2 -boot c -nographic + +CIRROS_IMG_URL=https://gitee.com/nan-shuaibo/cirros/releases/download/0.5.2/cirros-0.5.2-x86_64-disk.img +CIRROS_IMG_FILE=cirros-0.5.2-x86_64-disk.img + +# 定义检查虚拟化支持的命令 +CHECK_VIRT_SUPPORT = [ $$(grep -Eoc '(vmx|svm)' /proc/cpuinfo) -eq 0 ] + +# 定义检查 qemu-system-x86_64 进程是否存在的命令 +CHECK_QEMU_RUNNING = [ "$$(pgrep -f qemu-system-x86_64)" ] .PHONY: test test: bpf @@ -95,7 +113,7 @@ test: bpf clean: - cd src && rm -f *.o *.skel.h *.bpf.o + rm -f src/*.o src/*.skel.h src/helpers/*.o sudo rm -rf $(notdir $(APP)) include/vmlinux.h temp diff --git a/eBPF_Supermarket/kvm_watcher/README.md b/eBPF_Supermarket/kvm_watcher/README.md index 3c83aa161..c35a4647d 100755 --- a/eBPF_Supermarket/kvm_watcher/README.md +++ b/eBPF_Supermarket/kvm_watcher/README.md @@ -10,6 +10,8 @@ `kvm_watcher`是一款基于eBPF的kvm虚拟机检测工具,其旨在使用户方便快捷在宿主机侧获取kvm虚拟机中的各种信息,报告所有正在运行的guest行为。 +![kvm watcher项目框图](https://gitee.com/nan-shuaibo/image/raw/master/202404251704350.png) + 目前,其实现的功能主要包括: - **[VM Exit 事件分析](./docs/kvm_exit.md)** @@ -116,26 +118,33 @@ Report bugs to . ## 四、代码结构 ``` -├── docs //功能模块说明文档 +├── docs //功能模块说明文档 │ ├── kvm_exit.md │ ├── kvm_hypercall.md │ ├── kvm_irq.md │ ├── kvm_mmu.md │ └── kvm_vcpu.md -├── include //内核态bpf程序 -│ ├── kvm_exits.h -│ ├── kvm_hypercall.h -│ ├── kvm_ioctl.h -│ ├── kvm_irq.h -│ ├── kvm_mmu.h -│ ├── kvm_vcpu.h -│ └── kvm_watcher.h //公共头文件 +├── include +│ ├── bpf //内核态bpf程序 +│ │ ├── kvm_exits.h +│ │ ├── kvm_hypercall.h +│ │ ├── kvm_ioctl.h +│ │ ├── kvm_irq.h +│ │ ├── kvm_mmu.h +│ │ └── kvm_vcpu.h +│ ├── common.h //内核态和用户态公共头文件 +│ └── helpers //用户态帮助函数 +│ ├── trace_helpers.h +│ └── uprobe_helpers.h ├── Makefile //编译脚本 ├── README.md -├── src -│ ├── kvm_watcher.bpf.c //内核态bpf程序入口 -│ └── kvm_watcher.c //用户态bpf程序 -└── temp //临时文件目录 +├── src +│ ├── helpers //用户态帮助函数 +│ │ ├── trace_helpers.c +│ │ └── uprobe_helpers.c +│ ├── kvm_watcher.bpf.c //内核态bpf程序入口 +│ └── kvm_watcher.c //用户态bpf程序 +└── temp //临时文件目录 ``` ## 五、测试 diff --git a/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md b/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md new file mode 100644 index 000000000..043259e7e --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/Visualization_conf.md @@ -0,0 +1,151 @@ +# KVM_WATCHER可视化指南 + +​ 结合2023年开源之夏项目《使用 Prometheus 和 Grafana 构建 Linux 内核监控平台(基于 eBPF 程序采集内核数据)》进行可视化。 + +### 一、可视化策略: + +**1.工具介绍:** + +**Prometheus** 是一个开源的系统监控和报警工具,最初由SoundCloud开发,现在是CNCF(Cloud Native Computing Foundation)的一部分。 + +**Grafana** 是一个开源的分析和可视化平台,广泛用于数据可视化监控系统中。它能够与多种数据源集成,包括Prometheus。 + +Prometheus和Grafana常常一起使用,以实现全面的监控和可视化解决方案: + +1. **数据收集**:Prometheus负责从各种服务和应用程序中收集时间序列数据,并将这些数据存储在其时间序列数据库中。 +2. **数据可视化**:Grafana通过Prometheus数据源连接到Prometheus,使用PromQL查询数据,并在仪表盘中进行可视化展示。 +3. **报警**:虽然Prometheus可以独立管理报警,但与Grafana结合使用时,可以在Grafana中直观地配置和管理报警规则,并利用Grafana的通知渠道。 + +​ 通过kvm_watcher工具将虚拟机的各项指标采集出来,并将其放入到Prometheus所支持的Metrics中,利用http服务将采集出的Metrics数据通过端口的形式暴露出来,并将其存储到Prometheus的数据库中。接下来再通过grafana工具对Prometheus数据库中的数据进行可视化渲染,最终可以输出直观的监测结果。 + +**2.可视化流程:** + +![](./images/visualization_platform.png) + +### 二、使用说明: + +环境要求: + +```shell +golang 1.19+ +Docker version 24.0.4+ +``` + +数据格式要求: + +数据的格式必须要满足第一行是各项指标的名称,之后的每一行是数据(且是数字类型),并都以空格分隔。 + +``` +pid tid total_time max_time min_time counts vcpuid +6898 6908 2.0879 0.2376 0.0737 12 0 +6970 6979 0.5860 0.2039 0.1088 4 0 +6970 6980 1.6281 0.2237 0.1169 9 1 +6898 6908 2.0206 0.2355 0.0632 13 0 +``` + +#### **1.安装Golang:** + +```shell +#通过APT包下载go语言 +sudo apt install golang +#查看go版本: +go version +``` + +#### **2.安装docker:** + +```shell +请大家在docker官网的说明文档中,自行安装符合自己linux发行版的docker,链接如下: +https://docs.docker.com/engine/install/ubuntu +#开启docker服务 +sudo service docker start +#若如下指令出现hello-world,则表明docker创建成功 +sudo docker run hello-world +``` + +#### **3.运行可视化工具:** + +在运行可视化工具之前,我们需要先配置需要被监控机器的地址: + +配置文件目录:lmp/eBPF_Visualization/eBPF_prometheus/prom_core/prometheus.yaml + +``` +#全局配置部分,应用于所有的 job 配置 +global: + # 设置全局的抓取间隔,即 Prometheus 将每隔 15 秒收集一次指标 + scrape_interval: 15s + +# 抓取配置部分,用于指定要抓取的目标和相应的设置 +scrape_configs: + # 定义一个 job,命名为 'bpf_collector',这个 job 用于配置 Prometheus 如何抓取数据 + - job_name: 'bpf_collector' + # 指定从目标获取指标的路径,即采集路径 + metrics_path: '/metrics' + # 配置静态目标的部分,其中的目标地址是静态的,不会动态变化 + static_configs: + - targets: ["192.168.79.128:8090"] +``` + +这里我们需要将 - targets: ["192.168.79.128:8090"]配置项的192.168.79.128改为需要被采集的主机IP或者域名。 + +**3.1编译工具** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录 + +#执行make指令,编译可视化的go语言工具 +make +``` + +**3.2开启prometheus和grafana容器服务:** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录,执行以下操作 +make start_service +``` + +**3.3开启ebpf程序** + +```shell +#进入lmp/eBPF_Visualization/eBPF_prometheus目录,执行以下操作 +#开启ebpf程序,并且向8090端口推送ebpf程序采集的数据,发送给prometheus服务端 +#这里以监测vcpu调度的数据来举例: +#目前-e、-o、-i、-f -m功能的数据格式已经适配了可视化工具,请读者使用以上四种功能的数据来进行可视化展示 +./data-visual collect lmp/eBPF_Supermarket/kvm_watcher/kvm_watcher -o -p [进程号] +``` + +#### **4.可视化工具** + +**4.1metrics:** + +在浏览器中输入http://192.168.79.128:8090/metrics(IP地址是自己需要采集数据的主机地址),就可以查看ebpf程序输出的metrics数据了。 + +![image-20240523144259149](./images/metrics.png) + +**4.2 prometheus** + +在浏览器中输入http://192.168.79.128:9090,就可以进入到prometheus管理界面,在Status/Targets选项中,就可以看到上面的metrics信息了。 + +![image-20240523144553617](./images/prometheus.png) + +**4.3Grafana** + +在浏览器中输入http://192.168.79.128:3000/,就可以进入到Grafana管理界面。 + +点击【Home-Connection-Add new connection】,选择Prometheus,建立与Prometheus服务器的连接: + +![image-20240523145817562](./images/prome-config.png) + +这里的Prometheus server URL为被监测主机的IP+9090端口。 + +进入可视化配置界面: + +![](./images/new-data-source.png) + +选择需要可视化的数据,并点击Run queries,最后点击右上角的Apply,即可生成可视化图像: + +![image-20240523150326003](./images/result.png) + +### 三、说明 + +​ Prometheus和Grafana提供了许多强大的查询和运算功能,大家可以在环境搭建好以后,自己去定制化自己想要的可视化图表。总结一下,在对kvm_watcher进行可视化的时候,必须要注意数据的输出格式,并且要注意涉及的工具版本。 \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json b/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json new file mode 100644 index 000000000..166cc6917 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/docs/grafana_kvm_watcher_dashboard.json @@ -0,0 +1,522 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 5, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"DELAY(us)\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "mmio_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"DELAY\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "irq_delay", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MAX_TIME\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"min_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"max_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"total_time\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "D", + "useBackend": false + } + ], + "title": "vcpu_load", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MAX_TIME\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"MIN_TIME\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "cdncx8i00775sa" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bpf_metrics{bpf_out_data=\"TOTAL_TIME\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "vm_exit", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "123123", + "uid": "edonx3cyzyyv4d", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png b/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png new file mode 100644 index 000000000..f319d8201 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/metrics.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png b/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png new file mode 100644 index 000000000..28b3704fa Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/new-data-source.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png b/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png new file mode 100644 index 000000000..11140788e Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/prome-config.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png b/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png new file mode 100644 index 000000000..a7cb7b997 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/prometheus.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/result.png b/eBPF_Supermarket/kvm_watcher/docs/images/result.png new file mode 100644 index 000000000..3ad019d48 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/result.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png b/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png new file mode 100644 index 000000000..039052268 Binary files /dev/null and b/eBPF_Supermarket/kvm_watcher/docs/images/visualization_platform.png differ diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md index bb84b25ec..65f954df8 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_exit.md @@ -2,6 +2,8 @@ 考虑到频繁的虚拟机退出事件可能会导致性能问题,kvm_watcher中的kvm_exit子功能通过显示详细的退出原因和在一台主机上运行的所有vm的每个虚拟机的vcpu上的退出计数及处理时延,可以捕获和分析vm exit事件,该工具旨在定位频繁退出的原因(如EPT_VIOLATION、EPT_MISCONFIG、PML_FULL等),在vm exit基础上,如果kvm这个时候因为某些原因,需要退出到用户态的hypervisor(比如qemu),kvm就要设置KVM_EXIT_XXX,此工具包含了这两部分exit reason。 +![kvm exit](https://gitee.com/nan-shuaibo/image/raw/master/202404251707665.png) + ## 原理介绍 ### VMX 操作模式 @@ -46,7 +48,7 @@ ## 示例输出 4391为主机上的虚拟机进程,4508、4509、4510...分别是虚拟机中的vcpu子进程,每隔两秒输出虚拟机中产生的exit事件及其处理延时等信息。 - +结果会以进程号(VM的唯一标识)以及线程号(VM中每个VCPU的唯一标识)的优先级依次从小到大的顺序输出。 ``` ubuntu@rd350x:~/nans/lmp/eBPF_Supermarket/kvm_watcher$ sudo ./kvm_watcher -e @@ -102,5 +104,4 @@ pid tid total_time max_time min_time counts re - **VM Exit 原因统计**:记录并展示触发 VM Exit 的具体原因,帮助用户理解 VM Exit 发生的上下文和背景。 - **VM Exit 延时分析**:统计每次 VM Exit 处理的最大、最小和总共延时,为性能分析提供量化数据。 - **VM Exit 次数计数**:计算每种类型的 VM Exit 发生的次数,帮助识别最频繁的性能瓶颈。 -- **PID、TID号**:其中PID为主机侧的虚拟机进程号,TID为虚拟机内部的vcpu**的进程号** - \ No newline at end of file +- **PID、TID号**:其中PID为主机侧的虚拟机进程号,TID为虚拟机内部的vcpu**的进程号** \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md index 9a7e2a801..5f30152f4 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_irq.md @@ -4,6 +4,8 @@ kvm watcher中的kvm irq子功能模块可以对kvm中的虚拟化中断事件的实时监控和分析能力,可以捕获和记录各种中断事件,支持监控传统的PIC中断、高级的IOAPIC中断以及基于消息的MSI中断,覆盖了KVM虚拟化环境中的主要中断类型。对于每个捕获的中断事件,记录详细信息,包括中断类型、中断注入延时、引脚号、触发方式、目标LAPIC的ID、向量号以及是否被屏蔽等关键数据。 +![kvm_irq](https://gitee.com/nan-shuaibo/image/raw/master/202404251710847.png) + ## 原理介绍 x86平台主要使用的中断类型有pic、apic及msi中断,在多核系统下的apic结构图如下所示,每个cpu有一个lapic,外部中断通过ioapic转发到lapic,如果是msi中断,则绕过了io apic直接发给lapic。 @@ -14,48 +16,48 @@ KVM_CREATE_IRQCHIP的ioctl用于在虚拟机初始化阶段创建中断请求芯 ``` int kvm_set_routing_entry(struct kvm *kvm, - struct kvm_kernel_irq_routing_entry *e, - const struct kvm_irq_routing_entry *ue) + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) { - switch (ue->type) { - case KVM_IRQ_ROUTING_IRQCHIP: //中断路由芯片 - if (irqchip_split(kvm)) - return -EINVAL; - e->irqchip.pin = ue->u.irqchip.pin;//设置中断芯片引脚 - switch (ue->u.irqchip.irqchip) { - case KVM_IRQCHIP_PIC_SLAVE: - e->irqchip.pin += PIC_NUM_PINS / 2; //从片引脚 - fallthrough; - case KVM_IRQCHIP_PIC_MASTER: - if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) - return -EINVAL; - //// 设置处理 PIC 中断的回调函数 - e->set = kvm_set_pic_irq; - break; - case KVM_IRQCHIP_IOAPIC: - if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) - return -EINVAL; - // 设置处理 IOPIC 中断的回调函数 - e->set = kvm_set_ioapic_irq; - break; - default: - return -EINVAL; - } - e->irqchip.irqchip = ue->u.irqchip.irqchip; - break; - case KVM_IRQ_ROUTING_MSI: - // 设置处理 MSI 中断的回调函数 - e->set = kvm_set_msi; - e->msi.address_lo = ue->u.msi.address_lo; - e->msi.address_hi = ue->u.msi.address_hi; - e->msi.data = ue->u.msi.data; - - if (kvm_msi_route_invalid(kvm, e)) - return -EINVAL; - break; + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: //中断路由芯片 + if (irqchip_split(kvm)) + return -EINVAL; + e->irqchip.pin = ue->u.irqchip.pin;//设置中断芯片引脚 + switch (ue->u.irqchip.irqchip) { + case KVM_IRQCHIP_PIC_SLAVE: + e->irqchip.pin += PIC_NUM_PINS / 2; //从片引脚 + fallthrough; + case KVM_IRQCHIP_PIC_MASTER: + if (ue->u.irqchip.pin >= PIC_NUM_PINS / 2) + return -EINVAL; + //// 设置处理 PIC 中断的回调函数 + e->set = kvm_set_pic_irq; + break; + case KVM_IRQCHIP_IOAPIC: + if (ue->u.irqchip.pin >= KVM_IOAPIC_NUM_PINS) + return -EINVAL; + // 设置处理 IOPIC 中断的回调函数 + e->set = kvm_set_ioapic_irq; + break; + default: + return -EINVAL; + } + e->irqchip.irqchip = ue->u.irqchip.irqchip; + break; + case KVM_IRQ_ROUTING_MSI: + // 设置处理 MSI 中断的回调函数 + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + + if (kvm_msi_route_invalid(kvm, e)) + return -EINVAL; + break; ..... - return 0; + return 0; } ``` @@ -75,22 +77,22 @@ KVM_CREATE_IRQCHIP用于虚拟机向VMM的虚拟apic发送中断请求,再有V * > 0 中断成功送达的 CPU 数量 */ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, - bool line_status) + bool line_status) { - struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS]; - .... + struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS]; + .... - while (i--) { - int r; - r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level, - line_status); - if (r < 0) - continue; + while (i--) { + int r; + r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level, + line_status); + if (r < 0) + continue; - ret = r + ((ret < 0) ? 0 : ret); - } + ret = r + ((ret < 0) ? 0 : ret); + } - return ret; + return ret; } ``` @@ -101,7 +103,7 @@ KVM_CREATE_IRQCHIP用于虚拟机向VMM的虚拟apic发送中断请求,再有V 其中ioapic的回调函数kvm_set_ioapic_irq依次调用kvm_ioapic_set_irq、ioapic_set_irq最后调用ioapic_service函数,ioapic_service主要是找到中断的重映射表,然后查找中断的目的地信息并转发到对应vcpu的lapic去处理。然后会调用kvm_irq_delivery_to_apic负责将中断分发给lapic。 > 中断虚拟化详细介绍可以参考:[kvm中断虚拟化 ](https://blog.csdn.net/zgy666/article/details/105456569) -> [内核虚拟化:虚拟中断注入](https://blog.csdn.net/weixin_46324627/article/details/136661252?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136661252%22%2C%22source%22%3A%22weixin_46324627%22%7D) +> [内核虚拟化:虚拟中断注入](https://blog.csdn.net/weixin_46324627/article/details/136661252?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136661252%22%2C%22source%22%3A%22weixin_46324627%22%7D) ## 挂载点 diff --git a/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md b/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md index e3733231b..59eec0e20 100644 --- a/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md +++ b/eBPF_Supermarket/kvm_watcher/docs/kvm_vcpu.md @@ -2,12 +2,14 @@ ## 概述 -kvm watcher中的kvm vcpu子功能模块是设计用于监控和分析虚拟化环境中虚拟 CPU (VCPU) 活动的工具,它通过精确记录 VCPU 的唤醒/挂起事件、halt poll 时间的变化,以及 KVM 虚拟机在热迁移过程中产生的脏页信息,为优化虚拟机性能、提高系统响应速度、提供数据支持。 +kvm watcher中的kvm vcpu子功能模块是设计用于监控和分析虚拟化环境中虚拟 CPU (VCPU) 活动的工具,它通过精确记录 VCPU 的唤醒/挂起事件、halt poll 时间的变化,虚拟cpu进调度和出调度的时间记录,以及 KVM 虚拟机在热迁移过程中产生的脏页信息,为优化虚拟机性能、提高系统响应速度、提供数据支持。 ## 原理介绍 ### wakeup、暂停轮询 +![kvm vcpu](https://gitee.com/nan-shuaibo/image/raw/master/202404251709557.png) + KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以通过在vCPU 放弃运行并让出后,在主机中进行一段时间的轮询来降低虚拟机的延迟。简而言之,当vCPU 放弃运行(即执行 cede 操作)或在 PowerPC 中,当一个虚拟核心(vcore)的所有vCPU 都放弃运行时,主机内核会在将 CPU 让给调度程序之前,通过轮询等待唤醒条件。 轮询在某些情况下提供了延迟优势,尤其是在虚拟机可以非常快速地再次运行的情况下。这至少可以通过减少通过调度程序的开销来节省一些时间,通常在几微秒的数量级上,尽管性能优势取决于工作负载。在轮询间隔内,如果没有唤醒源到达,或者运行队列上有其他可运行的任务,则会调用调度程序。因此,在具有非常短唤醒周期的工作负载中,halt轮询特别有用,因为最小化了halt轮询的时间,同时可以避免调用调度程序的时间花费。 @@ -16,8 +18,13 @@ KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以 ### dirty page +![dirty page](https://gitee.com/nan-shuaibo/image/raw/master/202404251709559.png) + 在虚拟化环境中,脏页指的是自上次同步以来已经被修改的内存页。特别是在虚拟机热迁移过程中,源虚拟机上的内存页在复制到目标虚拟机的同时仍然处于活动状态,任何在此过程中对这些页的修改都会导致脏页的产生。监控这些脏页对于优化热迁移过程至关重要,因为过多的脏页生成可能会导致迁移延迟,甚至影响到虚拟机的运行性能。此监控功能特别适用于虚拟机热迁移的场景,其中脏页的精确监控和管理可以显著优化迁移过程。 +### load_vcpu + +加载虚拟 CPU(vCPU)是虚拟化环境中的一个重要步骤,它涉及为虚拟机创建和配置虚拟 CPU 实例,并将其加载到虚拟化平台中的运行环境中。加载 vCPU 将为客户机提供计算资源,允许其执行操作系统和应用程序代码。每个 vCPU 实例代表了一个独立的处理器核心,可以并行执行客户机指令。虚拟化环境中的多个 vCPU 可以并发执行客户机代码,从而支持虚拟机中的多任务和并发执行。加载多个 vCPU 允许虚拟机同时运行多个线程或进程。通过合理地分配物理资源并加载 vCPU,KVM可以优化虚拟机的性能和资源利用率。 ## 挂载点 ### wakeup @@ -39,6 +46,12 @@ KVM 暂停轮询系统是 KVM 内的一项功能,其在某些情况下可以 | :----- | :---------------------- | | kprobe | mark_page_dirty_in_slot | +### load_vcpu + +| 类型 | 名称 | +| :----- | :----------------------| +| kprobe | vmx_vcpu_load | +| kprobe | vmx_vcpu_put | ## 示例输出 ### wakeup @@ -124,7 +137,22 @@ PID GFN REL_GFN SLOT_ID COUNTS 630632 f0122 122 3 61 .... ``` +### load_vcpu +``` +sudo ./kvm_watcher -o +TIME:15:23:05 +pid tid total_time max_time min_time counts vcpuid pcpuid +------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ +49214 49224 1.2344 0.3670 0.0346 9 1 2 +49214 49225 0.5978 0.2171 0.1820 3 2 0 +49062 49072 2.4008 0.2656 0.0806 14 0 4 +49214 49223 4.5310 0.2794 0.0217 66 0 7 +54504 54514 6.9243 0.8872 0.0235 34 1 10 +49145 49166 2.8076 1.1653 0.0689 10 1 9 +54504 54513 3.8860 0.3099 0.0210 30 0 0 +49145 49165 1.4737 0.4106 0.0690 8 0 12 +``` ## 参数介绍 ### dirty page @@ -136,4 +164,15 @@ PID GFN REL_GFN SLOT_ID COUNTS - **REL_GFN**: 相对于 GFN 的偏移量。 - **NPAGES**: 内存槽的页面数量。 - **USERSPACE_ADDR**: 触发脏页的用户空间地址。 -- **SLOT_ID**: 内存插槽标识,指示哪个内存区域包含了脏页。 \ No newline at end of file +- **SLOT_ID**: 内存插槽标识,指示哪个内存区域包含了脏页。 + +### load_vcpu + +- **TIME(ms)**: 事件发生的时间,以毫秒为单位。 +- **PID/TID**: 进程或线程的标识符。 +- **total_time**: 2秒内调度vcpu的总时间。 +- **max_time**: 2秒内调度vcpu的最大时间。 +- **min_time**: 2秒内调度vcpu的最小时间。 +- **counts**: 2秒内调度vcpu的次数。 +- **vcpuid**: 虚拟CPU号。 +- **pcpuid**: vCPU对应绑定的物理CPU号。 \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/include/bpf/container.h b/eBPF_Supermarket/kvm_watcher/include/bpf/container.h new file mode 100644 index 000000000..5271054ef --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/container.h @@ -0,0 +1,136 @@ +// Copyright 2023 The LMP Authors. +// +// Licensed 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 +// +// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE +// +// 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. +// +// author: yys2020haha@163.com +// +// Kernel space BPF program used for counting container sys_entry/sys_exit info. + +#ifndef __CONTAINER_H +#define __CONTAINER_H + +#include "common.h" +#include "vmlinux.h" +#include +#include +#include +#define MAX_NODENAME_LEN 64 +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +}time_info SEC(".maps"); + +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value, u64); +}id SEC(".maps"); + +struct { + __uint(type,BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, pid_t); + __type(value,struct container_id); +}container_id_map SEC(".maps"); + + +static int trace_container_sys_entry(struct trace_event_raw_sys_enter *args){ + u64 st = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + u64 syscall_id = (u64)args->id; + bpf_map_update_elem(&time_info,&pid,&st,BPF_ANY); + bpf_map_update_elem(&id,&pid,&syscall_id,BPF_ANY); + return 0; +} +static int trace_container_sys_exit(struct trace_event_raw_sys_exit *args,void *rb,struct common_event *e){ + u64 exit_time = bpf_ktime_get_ns(); + pid_t pid = bpf_get_current_pid_tgid(); + u64 delay,start_time,syscallid; + u64 *st = bpf_map_lookup_elem(&time_info,&pid); + if( st !=0){ + start_time = *st; + delay = (exit_time - start_time)/1000; + bpf_map_delete_elem(&time_info, &pid); + }else{ + return 0; + } + u64 *sc_id = bpf_map_lookup_elem(&id,&pid); + if( sc_id != 0){ + syscallid = *sc_id; + bpf_map_delete_elem(&id, &pid); + }else{ + return 0; + } + const void *contain_id = bpf_map_lookup_elem(&container_id_map,&pid); + if(contain_id != NULL){ + bpf_printk("hostname=%s\n",contain_id); + }else{ + return 0; + } + RESERVE_RINGBUF_ENTRY(rb, e); + e->syscall_data.delay = delay; + bpf_get_current_comm(&e->syscall_data.comm, sizeof(e->syscall_data.comm)); + e->syscall_data.pid = pid; + bpf_probe_read_kernel_str(&(e->syscall_data.container_id),sizeof(e->syscall_data.container_id),contain_id); + e->syscall_data.syscall_id = syscallid; + bpf_ringbuf_submit(e, 0); + return 0; +} + +struct data_t { + char nodename[MAX_NODENAME_LEN]; +}; +static bool is_container_task(const volatile char hostname[MAX_NODENAME_LEN]){ + struct task_struct *task; + struct nsproxy *ns; + struct uts_namespace *uts; + struct data_t data = {}; + // 获取当前任务的 task_struct + task = (struct task_struct *)bpf_get_current_task(); + + // 获取 nsproxy + bpf_probe_read_kernel(&ns, sizeof(ns), &task->nsproxy); + if (!ns) { + return false; + } + + // 获取 uts_namespace + bpf_probe_read_kernel(&uts, sizeof(uts), &ns->uts_ns); + if (!uts) { + return false; + } + // 读取主机名 + bpf_probe_read_kernel_str(&data.nodename, sizeof(data.nodename), uts->name.nodename); + // 打印主机名 + bool is_equal = true; + for(int i = 0;i #include @@ -135,7 +135,7 @@ static int trace_kvm_entry() { return 0; } -static int trace_kvm_userspace_entry(struct kvm_vcpu *vcpu) { +static int trace_kvm_vcpu_ioctl() { pid_t tid = (u32)bpf_get_current_pid_tgid(); u64 ts = bpf_ktime_get_ns(); bpf_map_update_elem(&userspace_exit_times, &tid, &ts, BPF_ANY); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h similarity index 97% rename from eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h index ab9b26a52..723200827 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_hypercall.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_hypercall.h @@ -19,7 +19,7 @@ #ifndef __KVM_HYPERCALL_H #define __KVM_HYPERCALL_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include @@ -53,7 +53,7 @@ struct { __type(value, u32); } hc_count SEC(".maps"); -static int entry_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, +static int trace_emulate_hypercall(struct kvm_vcpu *vcpu, void *rb, struct common_event *e) { u32 pid = bpf_get_current_pid_tgid() >> 32; u64 nr, a0, a1, a2, a3; diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h similarity index 84% rename from eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h index b3cda58b1..6b619e437 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_ioctl.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_ioctl.h @@ -19,24 +19,11 @@ #ifndef __KVM_IOCTL_H #define __KVM_IOCTL_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" -#include #include #include #include -#include - -#define KVMIO 0xAE -#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */ -#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) -#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) -#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) -#define KVM_SET_USER_MEMORY_REGION \ - _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) -#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) -#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) -#define KVM_RUN _IO(KVMIO, 0x80) static int trace_kvm_ioctl(struct trace_event_raw_sys_enter *args) { int fd = (int)args->args[0]; diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h similarity index 99% rename from eBPF_Supermarket/kvm_watcher/include/kvm_irq.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h index fa1fda75c..2325f2467 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_irq.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_irq.h @@ -18,7 +18,7 @@ #ifndef __KVM_IRQ_H #define __KVM_IRQ_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h similarity index 97% rename from eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h index ceea6d10d..45d5c1c52 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_mmu.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_mmu.h @@ -19,7 +19,7 @@ #ifndef __KVM_MMU_H #define __KVM_MMU_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include @@ -60,16 +60,18 @@ static int trace_tdp_page_fault(struct kvm_vcpu *vcpu, struct common_event *e) { u64 addr; bpf_probe_read_kernel(&addr, sizeof(u64), &fault->addr); - u64 *ts; - ts = bpf_map_lookup_elem(&pf_delay, &addr); - if (!ts) { - return 0; - } u32 *count; u32 new_count = 1; u32 error_code; u64 hva, pfn; bpf_probe_read_kernel(&error_code, sizeof(u32), &fault->error_code); + u64 *ts; + ts = bpf_map_lookup_elem(&pf_delay, &addr); + if (!ts) { + int a = *ts; + bpf_printk("trace_tdp_page_fault:ts = %d", a); + return 0; + } bpf_probe_read_kernel(&hva, sizeof(u64), &fault->hva); bpf_probe_read_kernel(&pfn, sizeof(u64), &fault->pfn); short memslot_id = BPF_CORE_READ(fault, slot, id); diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h similarity index 99% rename from eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h rename to eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h index abf6f3f5a..65ef18a54 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_vcpu.h +++ b/eBPF_Supermarket/kvm_watcher/include/bpf/kvm_vcpu.h @@ -19,7 +19,7 @@ #ifndef __KVM_VCPU_H #define __KVM_VCPU_H -#include "kvm_watcher.h" +#include "common.h" #include "vmlinux.h" #include #include diff --git a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h b/eBPF_Supermarket/kvm_watcher/include/common.h similarity index 70% rename from eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h rename to eBPF_Supermarket/kvm_watcher/include/common.h index 6d5e34e53..930821f2b 100644 --- a/eBPF_Supermarket/kvm_watcher/include/kvm_watcher.h +++ b/eBPF_Supermarket/kvm_watcher/include/common.h @@ -19,6 +19,49 @@ #ifndef __KVM_WATCHER_H #define __KVM_WATCHER_H +#define SET_KP_OR_FENTRY_LOAD(function_name, module_name) \ + do { \ + if (fentry_can_attach(#function_name, #module_name)) { \ + bpf_program__set_autoload(skel->progs.fentry_##function_name, \ + true); \ + } else { \ + bpf_program__set_autoload(skel->progs.kp_##function_name, true); \ + } \ + } while (0) + +static const char binary_path[] = "/bin/qemu-system-x86_64"; +#define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \ + do { \ + LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, .func_name = #sym_name, \ + .retprobe = is_retprobe); \ + skel->links.prog_name = bpf_program__attach_uprobe_opts( \ + skel->progs.prog_name, env.vm_pid, binary_path, 0, &uprobe_opts); \ + } while (false) + +#define __CHECK_PROGRAM(skel, prog_name) \ + do { \ + if (!skel->links.prog_name) { \ + perror("no program attached for " #prog_name); \ + return -errno; \ + } \ + } while (false) + +#define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \ + do { \ + __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \ + __CHECK_PROGRAM(skel, prog_name); \ + } while (false) + +#define ATTACH_UPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE(skel, sym_name, prog_name) \ + __ATTACH_UPROBE(skel, sym_name, prog_name, true) + +#define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false) +#define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) \ + __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true) + #define TASK_COMM_LEN 16 #define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0) @@ -51,11 +94,25 @@ #define PFERR_RSVD_MASK (1UL << 3) // mmio + // 定时器模式 #define APIC_LVT_TIMER_ONESHOT (0 << 17) // 单次触发 #define APIC_LVT_TIMER_PERIODIC (1 << 17) // 周期性触发模式 #define APIC_LVT_TIMER_TSCDEADLINE (2 << 17) // TSC 截止模式 +// IOCTL +#include +#define KVMIO 0xAE +#define KVM_CREATE_VM _IO(KVMIO, 0x01) +#define KVM_CREATE_VCPU _IO(KVMIO, 0x41) +#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) +#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) +#define KVM_SET_USER_MEMORY_REGION \ + _IOW(KVMIO, 0x46, struct kvm_userspace_memory_region) +#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation) +#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt) +#define KVM_RUN _IO(KVMIO, 0x80) + #define PRINT_USAGE_ERR() \ do { \ fprintf(stderr, "Please specify exactly one option from %s.\n", \ @@ -134,7 +191,9 @@ struct exit_value { __u32 count; __u32 pad; }; - +struct container_id{ + char container_id[20]; +}; struct dirty_page_info { __u64 gfn; __u64 rel_gfn; @@ -176,6 +235,7 @@ struct process { char comm[TASK_COMM_LEN]; }; + enum EventType { NONE_TYPE, VCPU_WAKEUP, @@ -188,6 +248,7 @@ enum EventType { IRQ_INJECT, HYPERCALL, IOCTL, + CONTAINER_SYSCALL, TIMER, } event_type; @@ -286,6 +347,14 @@ struct common_event { __u32 vcpu_id; // HYPERCALL 特有成员 } hypercall_data; + + struct{ + __u64 pid; + __u64 syscall_id; + __u64 delay; + char comm[20]; + char container_id[20]; + } syscall_data; }; }; diff --git a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h b/eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h similarity index 76% rename from eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h rename to eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h index 171bc4ee2..052e278ac 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h +++ b/eBPF_Supermarket/kvm_watcher/include/helpers/trace_helpers.h @@ -4,11 +4,11 @@ #include -#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_SEC 1000000000ULL struct ksym { - const char *name; - unsigned long addr; + const char *name; + unsigned long addr; }; struct ksyms; @@ -16,15 +16,15 @@ struct ksyms; struct ksyms *ksyms__load(void); void ksyms__free(struct ksyms *ksyms); const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, - unsigned long addr); + unsigned long addr); const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, - const char *name); + const char *name); struct sym { - const char *name; - unsigned long start; - unsigned long size; - unsigned long offset; + const char *name; + unsigned long start; + unsigned long size; + unsigned long offset; }; struct syms; @@ -33,8 +33,9 @@ struct syms *syms__load_pid(int tgid); struct syms *syms__load_file(const char *fname); void syms__free(struct syms *syms); const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); -const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, - char **dso_name, unsigned long *dso_offset); +const struct sym *syms__map_addr_dso(const struct syms *syms, + unsigned long addr, char **dso_name, + unsigned long *dso_offset); struct syms_cache; @@ -43,22 +44,22 @@ struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); void syms_cache__free(struct syms_cache *syms_cache); struct partition { - char *name; - unsigned int dev; + char *name; + unsigned int dev; }; struct partitions; struct partitions *partitions__load(void); void partitions__free(struct partitions *partitions); -const struct partition * -partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); -const struct partition * -partitions__get_by_name(const struct partitions *partitions, const char *name); +const struct partition *partitions__get_by_dev( + const struct partitions *partitions, unsigned int dev); +const struct partition *partitions__get_by_name( + const struct partitions *partitions, const char *name); void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, - unsigned int step, const char *val_type); + unsigned int step, const char *val_type); unsigned long long get_ktime_ns(void); diff --git a/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h b/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h new file mode 100644 index 000000000..d33f7c1cf --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/include/helpers/uprobe_helpers.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2021 Google LLC. */ +#ifndef __UPROBE_HELPERS_H +#define __UPROBE_HELPERS_H + +#include +#include +#include + +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz); +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz); +int resolve_binary_path(const char *binary, pid_t pid, char *path, + size_t path_sz); +off_t get_elf_func_offset(const char *path, const char *func); +Elf *open_elf(const char *path, int *fd_close); +Elf *open_elf_by_fd(int fd); +void close_elf(Elf *e, int fd_close); + +#endif /* __UPROBE_HELPERS_H */ diff --git a/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c b/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c new file mode 100644 index 000000000..72b78524b --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/helpers/trace_helpers.c @@ -0,0 +1,1182 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +// Copyright (c) 2020 Wenbo Zhang +// +// Based on ksyms improvements from Andrii Nakryiko, add more helpers. +// 28-Feb-2020 Wenbo Zhang Created this. +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trace_helpers.h" +#include "uprobe_helpers.h" + +#define min(x, y) \ + ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void)(&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; \ + }) + +#define DISK_NAME_LEN 32 + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define MKDEV(ma, mi) (((ma) << MINORBITS) | (mi)) + +struct ksyms { + struct ksym *syms; + int syms_sz; + int syms_cap; + char *strs; + int strs_sz; + int strs_cap; +}; + +static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, + unsigned long addr) { + size_t new_cap, name_len = strlen(name) + 1; + struct ksym *ksym; + void *tmp; + + if (ksyms->strs_sz + name_len > ksyms->strs_cap) { + new_cap = ksyms->strs_cap * 4 / 3; + if (new_cap < ksyms->strs_sz + name_len) + new_cap = ksyms->strs_sz + name_len; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(ksyms->strs, new_cap); + if (!tmp) + return -1; + ksyms->strs = tmp; + ksyms->strs_cap = new_cap; + } + if (ksyms->syms_sz + 1 > ksyms->syms_cap) { + new_cap = ksyms->syms_cap * 4 / 3; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap); + if (!tmp) + return -1; + ksyms->syms = tmp; + ksyms->syms_cap = new_cap; + } + + ksym = &ksyms->syms[ksyms->syms_sz]; + /* while constructing, re-use pointer as just a plain offset */ + ksym->name = (void *)(unsigned long)ksyms->strs_sz; + ksym->addr = addr; + + memcpy(ksyms->strs + ksyms->strs_sz, name, name_len); + ksyms->strs_sz += name_len; + ksyms->syms_sz++; + + return 0; +} + +static int ksym_cmp(const void *p1, const void *p2) { + const struct ksym *s1 = p1, *s2 = p2; + + if (s1->addr == s2->addr) + return strcmp(s1->name, s2->name); + return s1->addr < s2->addr ? -1 : 1; +} + +struct ksyms *ksyms__load(void) { + char sym_type, sym_name[256]; + struct ksyms *ksyms; + unsigned long sym_addr; + int i, ret; + FILE *f; + + f = fopen("/proc/kallsyms", "r"); + if (!f) + return NULL; + + ksyms = calloc(1, sizeof(*ksyms)); + if (!ksyms) + goto err_out; + + while (true) { + ret = fscanf(f, "%lx %c %s%*[^\n]\n", &sym_addr, &sym_type, sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 3) + goto err_out; + if (ksyms__add_symbol(ksyms, sym_name, sym_addr)) + goto err_out; + } + + /* now when strings are finalized, adjust pointers properly */ + for (i = 0; i < ksyms->syms_sz; i++) + ksyms->syms[i].name += (unsigned long)ksyms->strs; + + qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp); + + fclose(f); + return ksyms; + +err_out: + ksyms__free(ksyms); + fclose(f); + return NULL; +} + +void ksyms__free(struct ksyms *ksyms) { + if (!ksyms) + return; + + free(ksyms->syms); + free(ksyms->strs); + free(ksyms); +} + +const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, + unsigned long addr) { + int start = 0, end = ksyms->syms_sz - 1, mid; + unsigned long sym_addr; + + /* find largest sym_addr <= addr using binary search */ + while (start < end) { + mid = start + (end - start + 1) / 2; + sym_addr = ksyms->syms[mid].addr; + + if (sym_addr <= addr) + start = mid; + else + end = mid - 1; + } + + if (start == end && ksyms->syms[start].addr <= addr) + return &ksyms->syms[start]; + return NULL; +} + +const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, + const char *name) { + int i; + + for (i = 0; i < ksyms->syms_sz; i++) { + if (strcmp(ksyms->syms[i].name, name) == 0) + return &ksyms->syms[i]; + } + + return NULL; +} + +struct load_range { + uint64_t start; + uint64_t end; + uint64_t file_off; +}; + +enum elf_type { + EXEC, + DYN, + PERF_MAP, + VDSO, + UNKNOWN, +}; + +struct dso { + char *name; + struct load_range *ranges; + int range_sz; + /* Dyn's first text section virtual addr at execution */ + uint64_t sh_addr; + /* Dyn's first text section file offset */ + uint64_t sh_offset; + enum elf_type type; + + struct sym *syms; + int syms_sz; + int syms_cap; + + /* + * libbpf's struct btf is actually a pretty efficient + * "set of strings" data structure, so we create an + * empty one and use it to store symbol names. + */ + struct btf *btf; +}; + +struct map { + uint64_t start_addr; + uint64_t end_addr; + uint64_t file_off; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t inode; +}; + +struct syms { + struct dso *dsos; + int dso_sz; +}; + +static bool is_file_backed(const char *mapname) { +#define STARTS_WITH(mapname, prefix) \ + (!strncmp(mapname, prefix, sizeof(prefix) - 1)) + + return mapname[0] && + !(STARTS_WITH(mapname, "//anon") || + STARTS_WITH(mapname, "/dev/zero") || + STARTS_WITH(mapname, "/anon_hugepage") || + STARTS_WITH(mapname, "[stack") || STARTS_WITH(mapname, "/SYSV") || + STARTS_WITH(mapname, "[heap]") || + STARTS_WITH(mapname, "[uprobes]") || + STARTS_WITH(mapname, "[vsyscall]")); +} + +static bool is_perf_map(const char *path) { + return false; +} + +static bool is_vdso(const char *path) { + return !strcmp(path, "[vdso]"); +} + +static bool is_uprobes(const char *path) { + return !strcmp(path, "[uprobes]"); +} + +static int get_elf_type(const char *path) { + GElf_Ehdr hdr; + void *res; + Elf *e; + int fd; + + if (is_vdso(path)) + return -1; + if (is_uprobes(path)) + return -1; + e = open_elf(path, &fd); + if (!e) + return -1; + res = gelf_getehdr(e, &hdr); + close_elf(e, fd); + if (!res) + return -1; + return hdr.e_type; +} + +static int get_elf_text_scn_info(const char *path, uint64_t *addr, + uint64_t *offset) { + Elf_Scn *section = NULL; + int fd = -1, err = -1; + GElf_Shdr header; + size_t stridx; + Elf *e = NULL; + char *name; + + e = open_elf(path, &fd); + if (!e) + goto err_out; + err = elf_getshdrstrndx(e, &stridx); + if (err < 0) + goto err_out; + + err = -1; + while ((section = elf_nextscn(e, section)) != 0) { + if (!gelf_getshdr(section, &header)) + continue; + + name = elf_strptr(e, stridx, header.sh_name); + if (name && !strcmp(name, ".text")) { + *addr = (uint64_t)header.sh_addr; + *offset = (uint64_t)header.sh_offset; + err = 0; + break; + } + } + +err_out: + close_elf(e, fd); + return err; +} + +static int syms__add_dso(struct syms *syms, struct map *map, const char *name) { + struct dso *dso = NULL; + int i, type; + void *tmp; + + for (i = 0; i < syms->dso_sz; i++) { + if (!strcmp(syms->dsos[i].name, name)) { + dso = &syms->dsos[i]; + break; + } + } + + if (!dso) { + tmp = realloc(syms->dsos, (syms->dso_sz + 1) * sizeof(*syms->dsos)); + if (!tmp) + return -1; + syms->dsos = tmp; + dso = &syms->dsos[syms->dso_sz++]; + memset(dso, 0, sizeof(*dso)); + dso->name = strdup(name); + dso->btf = btf__new_empty(); + } + + tmp = realloc(dso->ranges, (dso->range_sz + 1) * sizeof(*dso->ranges)); + if (!tmp) + return -1; + dso->ranges = tmp; + dso->ranges[dso->range_sz].start = map->start_addr; + dso->ranges[dso->range_sz].end = map->end_addr; + dso->ranges[dso->range_sz].file_off = map->file_off; + dso->range_sz++; + type = get_elf_type(name); + if (type == ET_EXEC) { + dso->type = EXEC; + } else if (type == ET_DYN) { + dso->type = DYN; + if (get_elf_text_scn_info(name, &dso->sh_addr, &dso->sh_offset) < 0) + return -1; + } else if (is_perf_map(name)) { + dso->type = PERF_MAP; + } else if (is_vdso(name)) { + dso->type = VDSO; + } else { + dso->type = UNKNOWN; + } + return 0; +} + +static struct dso *syms__find_dso(const struct syms *syms, unsigned long addr, + uint64_t *offset) { + struct load_range *range; + struct dso *dso; + int i, j; + + for (i = 0; i < syms->dso_sz; i++) { + dso = &syms->dsos[i]; + for (j = 0; j < dso->range_sz; j++) { + range = &dso->ranges[j]; + if (addr <= range->start || addr >= range->end) + continue; + if (dso->type == DYN || dso->type == VDSO) { + /* Offset within the mmap */ + *offset = addr - range->start + range->file_off; + /* Offset within the ELF for dyn symbol lookup */ + *offset += dso->sh_addr - dso->sh_offset; + } else { + *offset = addr; + } + + return dso; + } + } + + return NULL; +} + +static int dso__load_sym_table_from_perf_map(struct dso *dso) { + return -1; +} + +static int dso__add_sym(struct dso *dso, const char *name, uint64_t start, + uint64_t size) { + struct sym *sym; + size_t new_cap; + void *tmp; + int off; + + off = btf__add_str(dso->btf, name); + if (off < 0) + return off; + + if (dso->syms_sz + 1 > dso->syms_cap) { + new_cap = dso->syms_cap * 4 / 3; + if (new_cap < 1024) + new_cap = 1024; + tmp = realloc(dso->syms, sizeof(*dso->syms) * new_cap); + if (!tmp) + return -1; + dso->syms = tmp; + dso->syms_cap = new_cap; + } + + sym = &dso->syms[dso->syms_sz++]; + /* while constructing, re-use pointer as just a plain offset */ + sym->name = (void *)(unsigned long)off; + sym->start = start; + sym->size = size; + sym->offset = 0; + + return 0; +} + +static int sym_cmp(const void *p1, const void *p2) { + const struct sym *s1 = p1, *s2 = p2; + + if (s1->start == s2->start) + return strcmp(s1->name, s2->name); + return s1->start < s2->start ? -1 : 1; +} + +static int dso__add_syms(struct dso *dso, Elf *e, Elf_Scn *section, + size_t stridx, size_t symsize) { + Elf_Data *data = NULL; + + while ((data = elf_getdata(section, data)) != 0) { + size_t i, symcount = data->d_size / symsize; + + if (data->d_size % symsize) + return -1; + + for (i = 0; i < symcount; ++i) { + const char *name; + GElf_Sym sym; + + if (!gelf_getsym(data, (int)i, &sym)) + continue; + if (!(name = elf_strptr(e, stridx, sym.st_name))) + continue; + if (name[0] == '\0') + continue; + + if (sym.st_value == 0) + continue; + + if (dso__add_sym(dso, name, sym.st_value, sym.st_size)) + goto err_out; + } + } + + return 0; + +err_out: + return -1; +} + +static void dso__free_fields(struct dso *dso) { + if (!dso) + return; + + free(dso->name); + free(dso->ranges); + free(dso->syms); + btf__free(dso->btf); +} + +static int dso__load_sym_table_from_elf(struct dso *dso, int fd) { + Elf_Scn *section = NULL; + Elf *e; + int i; + + e = fd > 0 ? open_elf_by_fd(fd) : open_elf(dso->name, &fd); + if (!e) + return -1; + + while ((section = elf_nextscn(e, section)) != 0) { + GElf_Shdr header; + + if (!gelf_getshdr(section, &header)) + continue; + + if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM) + continue; + + if (dso__add_syms(dso, e, section, header.sh_link, header.sh_entsize)) + goto err_out; + } + + /* now when strings are finalized, adjust pointers properly */ + for (i = 0; i < dso->syms_sz; i++) + dso->syms[i].name = + btf__name_by_offset(dso->btf, (unsigned long)dso->syms[i].name); + + qsort(dso->syms, dso->syms_sz, sizeof(*dso->syms), sym_cmp); + + close_elf(e, fd); + return 0; + +err_out: + dso__free_fields(dso); + close_elf(e, fd); + return -1; +} + +static int create_tmp_vdso_image(struct dso *dso) { + uint64_t start_addr, end_addr; + long pid = getpid(); + char buf[PATH_MAX]; + void *image = NULL; + char tmpfile[128]; + int ret, fd = -1; + uint64_t sz; + char *name; + FILE *f; + + snprintf(tmpfile, sizeof(tmpfile), "/proc/%ld/maps", pid); + f = fopen(tmpfile, "r"); + if (!f) + return -1; + + while (true) { + ret = fscanf(f, "%llx-%llx %*s %*x %*x:%*x %*u%[^\n]", + (long long *)&start_addr, (long long *)&end_addr, buf); + if (ret == EOF && feof(f)) + break; + if (ret != 3) + goto err_out; + + name = buf; + while (isspace(*name)) + name++; + if (!is_file_backed(name)) + continue; + if (is_vdso(name)) + break; + } + + sz = end_addr - start_addr; + image = malloc(sz); + if (!image) + goto err_out; + memcpy(image, (void *)start_addr, sz); + + snprintf(tmpfile, sizeof(tmpfile), "/tmp/libbpf_%ld_vdso_image_XXXXXX", + pid); + fd = mkostemp(tmpfile, O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "failed to create temp file: %s\n", strerror(errno)); + goto err_out; + } + /* Unlink the file to avoid leaking */ + if (unlink(tmpfile) == -1) + fprintf(stderr, "failed to unlink %s: %s\n", tmpfile, strerror(errno)); + if (write(fd, image, sz) == -1) { + fprintf(stderr, "failed to write to vDSO image: %s\n", strerror(errno)); + close(fd); + fd = -1; + goto err_out; + } + +err_out: + fclose(f); + free(image); + return fd; +} + +static int dso__load_sym_table_from_vdso_image(struct dso *dso) { + int fd = create_tmp_vdso_image(dso); + + if (fd < 0) + return -1; + return dso__load_sym_table_from_elf(dso, fd); +} + +static int dso__load_sym_table(struct dso *dso) { + if (dso->type == UNKNOWN) + return -1; + if (dso->type == PERF_MAP) + return dso__load_sym_table_from_perf_map(dso); + if (dso->type == EXEC || dso->type == DYN) + return dso__load_sym_table_from_elf(dso, 0); + if (dso->type == VDSO) + return dso__load_sym_table_from_vdso_image(dso); + return -1; +} + +static struct sym *dso__find_sym(struct dso *dso, uint64_t offset) { + unsigned long sym_addr; + int start, end, mid; + + if (!dso->syms && dso__load_sym_table(dso)) + return NULL; + + start = 0; + end = dso->syms_sz - 1; + + /* find largest sym_addr <= addr using binary search */ + while (start < end) { + mid = start + (end - start + 1) / 2; + sym_addr = dso->syms[mid].start; + + if (sym_addr <= offset) + start = mid; + else + end = mid - 1; + } + + if (start == end && dso->syms[start].start <= offset && + offset < dso->syms[start].start + dso->syms[start].size) { + (dso->syms[start]).offset = offset - dso->syms[start].start; + return &dso->syms[start]; + } + return NULL; +} + +struct syms *syms__load_file(const char *fname) { + char buf[PATH_MAX], perm[5]; + struct syms *syms; + struct map map; + char *name; + FILE *f; + int ret; + + f = fopen(fname, "r"); + if (!f) + return NULL; + + syms = calloc(1, sizeof(*syms)); + if (!syms) + goto err_out; + + while (true) { + ret = fscanf(f, "%llx-%llx %4s %llx %llx:%llx %llu%[^\n]", + (long long *)&map.start_addr, (long long *)&map.end_addr, + perm, (long long *)&map.file_off, + (long long *)&map.dev_major, (long long *)&map.dev_minor, + (long long *)&map.inode, buf); + if (ret == EOF && feof(f)) + break; + if (ret != 8) /* perf-.map */ + goto err_out; + + if (perm[2] != 'x') + continue; + + name = buf; + while (isspace(*name)) + name++; + if (!is_file_backed(name)) + continue; + + if (syms__add_dso(syms, &map, name)) + goto err_out; + } + + fclose(f); + return syms; + +err_out: + syms__free(syms); + fclose(f); + return NULL; +} + +struct syms *syms__load_pid(pid_t tgid) { + char fname[128]; + + snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid); + return syms__load_file(fname); +} + +void syms__free(struct syms *syms) { + int i; + + if (!syms) + return; + + for (i = 0; i < syms->dso_sz; i++) + dso__free_fields(&syms->dsos[i]); + free(syms->dsos); + free(syms); +} + +const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr) { + struct dso *dso; + uint64_t offset; + + dso = syms__find_dso(syms, addr, &offset); + if (!dso) + return NULL; + return dso__find_sym(dso, offset); +} + +const struct sym *syms__map_addr_dso(const struct syms *syms, + unsigned long addr, char **dso_name, + unsigned long *dso_offset) { + struct dso *dso; + uint64_t offset; + + dso = syms__find_dso(syms, addr, &offset); + if (!dso) + return NULL; + + *dso_name = dso->name; + *dso_offset = offset; + + return dso__find_sym(dso, offset); +} + +struct syms_cache { + struct { + struct syms *syms; + int tgid; + } *data; + int nr; +}; + +struct syms_cache *syms_cache__new(int nr) { + struct syms_cache *syms_cache; + + syms_cache = calloc(1, sizeof(*syms_cache)); + if (!syms_cache) + return NULL; + if (nr > 0) + syms_cache->data = calloc(nr, sizeof(*syms_cache->data)); + return syms_cache; +} + +void syms_cache__free(struct syms_cache *syms_cache) { + int i; + + if (!syms_cache) + return; + + for (i = 0; i < syms_cache->nr; i++) + syms__free(syms_cache->data[i].syms); + free(syms_cache->data); + free(syms_cache); +} + +struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid) { + void *tmp; + int i; + + for (i = 0; i < syms_cache->nr; i++) { + if (syms_cache->data[i].tgid == tgid) + return syms_cache->data[i].syms; + } + + tmp = realloc(syms_cache->data, + (syms_cache->nr + 1) * sizeof(*syms_cache->data)); + if (!tmp) + return NULL; + syms_cache->data = tmp; + syms_cache->data[syms_cache->nr].syms = syms__load_pid(tgid); + syms_cache->data[syms_cache->nr].tgid = tgid; + return syms_cache->data[syms_cache->nr++].syms; +} + +struct partitions { + struct partition *items; + int sz; +}; + +static int partitions__add_partition(struct partitions *partitions, + const char *name, unsigned int dev) { + struct partition *partition; + void *tmp; + + tmp = realloc(partitions->items, + (partitions->sz + 1) * sizeof(*partitions->items)); + if (!tmp) + return -1; + partitions->items = tmp; + partition = &partitions->items[partitions->sz]; + partition->name = strdup(name); + partition->dev = dev; + partitions->sz++; + + return 0; +} + +struct partitions *partitions__load(void) { + char part_name[DISK_NAME_LEN]; + unsigned int devmaj, devmin; + unsigned long long nop; + struct partitions *partitions; + char buf[64]; + FILE *f; + + f = fopen("/proc/partitions", "r"); + if (!f) + return NULL; + + partitions = calloc(1, sizeof(*partitions)); + if (!partitions) + goto err_out; + + while (fgets(buf, sizeof(buf), f) != NULL) { + /* skip heading */ + if (buf[0] != ' ' || buf[0] == '\n') + continue; + if (sscanf(buf, "%u %u %llu %s", &devmaj, &devmin, &nop, part_name) != + 4) + goto err_out; + if (partitions__add_partition(partitions, part_name, + MKDEV(devmaj, devmin))) + goto err_out; + } + + fclose(f); + return partitions; + +err_out: + partitions__free(partitions); + fclose(f); + return NULL; +} + +void partitions__free(struct partitions *partitions) { + int i; + + if (!partitions) + return; + + for (i = 0; i < partitions->sz; i++) + free(partitions->items[i].name); + free(partitions->items); + free(partitions); +} + +const struct partition *partitions__get_by_dev( + const struct partitions *partitions, unsigned int dev) { + int i; + + for (i = 0; i < partitions->sz; i++) { + if (partitions->items[i].dev == dev) + return &partitions->items[i]; + } + + return NULL; +} + +const struct partition *partitions__get_by_name( + const struct partitions *partitions, const char *name) { + int i; + + for (i = 0; i < partitions->sz; i++) { + if (strcmp(partitions->items[i].name, name) == 0) + return &partitions->items[i]; + } + + return NULL; +} + +static void print_stars(unsigned int val, unsigned int val_max, int width) { + int num_stars, num_spaces, i; + bool need_plus; + + num_stars = min(val, val_max) * width / val_max; + num_spaces = width - num_stars; + need_plus = val > val_max; + + for (i = 0; i < num_stars; i++) + printf("*"); + for (i = 0; i < num_spaces; i++) + printf(" "); + if (need_plus) + printf("+"); +} + +void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type) { + int stars_max = 40, idx_max = -1; + unsigned int val, val_max = 0; + unsigned long long low, high; + int stars, width, i; + + for (i = 0; i < vals_size; i++) { + val = vals[i]; + if (val > 0) + idx_max = i; + if (val > val_max) + val_max = val; + } + + if (idx_max < 0) + return; + + printf("%*s%-*s : count distribution\n", idx_max <= 32 ? 5 : 15, "", + idx_max <= 32 ? 19 : 29, val_type); + + if (idx_max <= 32) + stars = stars_max; + else + stars = stars_max / 2; + + for (i = 0; i <= idx_max; i++) { + low = (1ULL << (i + 1)) >> 1; + high = (1ULL << (i + 1)) - 1; + if (low == high) + low -= 1; + val = vals[i]; + width = idx_max <= 32 ? 10 : 20; + printf("%*lld -> %-*lld : %-8d |", width, low, width, high, val); + print_stars(val, val_max, stars); + printf("|\n"); + } +} + +void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, + unsigned int step, const char *val_type) { + int i, stars_max = 40, idx_min = -1, idx_max = -1; + unsigned int val, val_max = 0; + + for (i = 0; i < vals_size; i++) { + val = vals[i]; + if (val > 0) { + idx_max = i; + if (idx_min < 0) + idx_min = i; + } + if (val > val_max) + val_max = val; + } + + if (idx_max < 0) + return; + + printf(" %-13s : count distribution\n", val_type); + for (i = idx_min; i <= idx_max; i++) { + val = vals[i]; + if (!val) + continue; + printf(" %-10d : %-8d |", base + i * step, val); + print_stars(val, val_max, stars_max); + printf("|\n"); + } +} + +unsigned long long get_ktime_ns(void) { + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; +} + +bool is_kernel_module(const char *name) { + bool found = false; + char buf[64]; + FILE *f; + + f = fopen("/proc/modules", "r"); + if (!f) + return false; + + while (fgets(buf, sizeof(buf), f) != NULL) { + if (sscanf(buf, "%s %*s\n", buf) != 1) + break; + if (!strcmp(buf, name)) { + found = true; + break; + } + } + + fclose(f); + return found; +} + +bool fentry_try_attach(int id) { + int prog_fd, attach_fd; + char error[4096]; + struct bpf_insn insns[] = { + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, + }; + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_FENTRY, .attach_btf_id = id, + .log_buf = error, .log_size = sizeof(error), ); + + prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, "test", "GPL", insns, + sizeof(insns) / sizeof(struct bpf_insn), &opts); + if (prog_fd < 0) + return false; + + attach_fd = bpf_raw_tracepoint_open(NULL, prog_fd); + if (attach_fd >= 0) + close(attach_fd); + + close(prog_fd); + return attach_fd >= 0; +} + +bool fentry_can_attach(const char *name, const char *mod) { + struct btf *btf, *vmlinux_btf, *module_btf = NULL; + int err, id; + + vmlinux_btf = btf__load_vmlinux_btf(); + err = libbpf_get_error(vmlinux_btf); + if (err) + return false; + + btf = vmlinux_btf; + + if (mod) { + module_btf = btf__load_module_btf(mod, vmlinux_btf); + err = libbpf_get_error(module_btf); + if (!err) { + btf = module_btf; + } + } + + id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); + + btf__free(module_btf); + btf__free(vmlinux_btf); + return id > 0; +} + +#define DEBUGFS "/sys/kernel/debug/tracing" +#define TRACEFS "/sys/kernel/tracing" + +static bool use_debugfs(void) { + static int has_debugfs = -1; + + if (has_debugfs < 0) + has_debugfs = faccessat(AT_FDCWD, DEBUGFS, F_OK, AT_EACCESS) == 0; + + return has_debugfs == 1; +} + +static const char *tracefs_path(void) { + return use_debugfs() ? DEBUGFS : TRACEFS; +} + +static const char *tracefs_available_filter_functions(void) { + return use_debugfs() ? DEBUGFS "/available_filter_functions" + : TRACEFS "/available_filter_functions"; +} + +bool kprobe_exists(const char *name) { + char addr_range[256]; + char sym_name[256]; + FILE *f; + int ret; + + f = fopen("/sys/kernel/debug/kprobes/blacklist", "r"); + if (!f) + goto avail_filter; + + while (true) { + ret = fscanf(f, "%s %s%*[^\n]\n", addr_range, sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 2) { + fprintf(stderr, "failed to read symbol from kprobe blacklist\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return false; + } + } + fclose(f); + +avail_filter: + f = fopen(tracefs_available_filter_functions(), "r"); + if (!f) + goto slow_path; + + while (true) { + ret = fscanf(f, "%s%*[^\n]\n", sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 1) { + fprintf(stderr, + "failed to read symbol from available_filter_functions\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return true; + } + } + + fclose(f); + return false; + +slow_path: + f = fopen("/proc/kallsyms", "r"); + if (!f) + return false; + + while (true) { + ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name); + if (ret == EOF && feof(f)) + break; + if (ret != 1) { + fprintf(stderr, "failed to read symbol from kallsyms\n"); + break; + } + if (!strcmp(name, sym_name)) { + fclose(f); + return true; + } + } + + fclose(f); + return false; +} + +bool tracepoint_exists(const char *category, const char *event) { + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/events/%s/%s/format", tracefs_path(), + category, event); + if (!access(path, F_OK)) + return true; + return false; +} + +bool vmlinux_btf_exists(void) { + struct btf *btf; + int err; + + btf = btf__load_vmlinux_btf(); + err = libbpf_get_error(btf); + if (err) + return false; + + btf__free(btf); + return true; +} + +bool module_btf_exists(const char *mod) { + char sysfs_mod[80]; + + if (mod) { + snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod); + if (!access(sysfs_mod, R_OK)) + return true; + } + return false; +} + +bool probe_tp_btf(const char *name) { + LIBBPF_OPTS(bpf_prog_load_opts, opts, + .expected_attach_type = BPF_TRACE_RAW_TP); + struct bpf_insn insns[] = { + {.code = BPF_ALU64 | BPF_MOV | BPF_K, .dst_reg = BPF_REG_0, .imm = 0}, + {.code = BPF_JMP | BPF_EXIT}, + }; + int fd, insn_cnt = sizeof(insns) / sizeof(struct bpf_insn); + + opts.attach_btf_id = libbpf_find_vmlinux_btf_id(name, BPF_TRACE_RAW_TP); + fd = bpf_prog_load(BPF_PROG_TYPE_TRACING, NULL, "GPL", insns, insn_cnt, + &opts); + if (fd >= 0) + close(fd); + return fd >= 0; +} + +bool probe_ringbuf() { + int map_fd; + + map_fd = + bpf_map_create(BPF_MAP_TYPE_RINGBUF, NULL, 0, 0, getpagesize(), NULL); + if (map_fd < 0) + return false; + + close(map_fd); + return true; +} diff --git a/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c b/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c new file mode 100644 index 000000000..5cf6d1743 --- /dev/null +++ b/eBPF_Supermarket/kvm_watcher/src/helpers/uprobe_helpers.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2021 Google LLC. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +/* + * Returns 0 on success; -1 on failure. On sucess, returns via `path` the full + * path to the program for pid. + */ +int get_pid_binary_path(pid_t pid, char *path, size_t path_sz) { + ssize_t ret; + char proc_pid_exe[32]; + + if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid) >= + sizeof(proc_pid_exe)) { + warn("snprintf /proc/PID/exe failed"); + return -1; + } + ret = readlink(proc_pid_exe, path, path_sz); + if (ret < 0) { + warn("No such pid %d\n", pid); + return -1; + } + if (ret >= path_sz) { + warn("readlink truncation"); + return -1; + } + path[ret] = '\0'; + + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to a library matching the name `lib` that is loaded into pid's address + * space. + */ +int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz) { + FILE *maps; + char *p; + char proc_pid_maps[32]; + char line_buf[1024]; + char path_buf[1024]; + + if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid) >= + sizeof(proc_pid_maps)) { + warn("snprintf /proc/PID/maps failed"); + return -1; + } + maps = fopen(proc_pid_maps, "r"); + if (!maps) { + warn("No such pid %d\n", pid); + return -1; + } + while (fgets(line_buf, sizeof(line_buf), maps)) { + if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path_buf) != 1) + continue; + /* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */ + p = strrchr(path_buf, '/'); + if (!p) + continue; + if (strncmp(p, "/lib", 4)) + continue; + p += 4; + if (strncmp(lib, p, strlen(lib))) + continue; + p += strlen(lib); + /* libraries can have - or . after the name */ + if (*p != '.' && *p != '-') + continue; + if (strnlen(path_buf, 1024) >= path_sz) { + warn("path size too small\n"); + return -1; + } + strcpy(path, path_buf); + fclose(maps); + return 0; + } + + warn("Cannot find library %s\n", lib); + fclose(maps); + return -1; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the program. + */ +static int which_program(const char *prog, char *path, size_t path_sz) { + FILE *which; + char cmd[100]; + + if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) { + warn("snprintf which prog failed"); + return -1; + } + which = popen(cmd, "r"); + if (!which) { + warn("which failed"); + return -1; + } + if (!fgets(path, path_sz, which)) { + warn("fgets which failed"); + pclose(which); + return -1; + } + /* which has a \n at the end of the string */ + path[strlen(path) - 1] = '\0'; + pclose(which); + return 0; +} + +/* + * Returns 0 on success; -1 on failure. On success, returns via `path` the full + * path to the binary for the given pid. + * 1) pid == x, binary == "" : returns the path to x's program + * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x + * 3) pid == 0, binary == "" : failure: need a pid or a binary + * 4) pid == 0, binary == "bar" : returns the path to `which bar` + * + * For case 4), ideally we'd like to search for libbar too, but we don't support + * that yet. + */ +int resolve_binary_path(const char *binary, pid_t pid, char *path, + size_t path_sz) { + if (!strcmp(binary, "")) { + if (!pid) { + warn("Uprobes need a pid or a binary\n"); + return -1; + } + return get_pid_binary_path(pid, path, path_sz); + } + if (pid) + return get_pid_lib_path(pid, binary, path, path_sz); + + if (which_program(binary, path, path_sz)) { + /* + * If the user is tracing a program by name, we can find it. + * But we can't find a library by name yet. We'd need to parse + * ld.so.cache or something similar. + */ + warn("Can't find %s (Need a PID if this is a library)\n", binary); + return -1; + } + return 0; +} + +/* + * Opens an elf at `path` of kind ELF_K_ELF. Returns NULL on failure. On + * success, close with close_elf(e, fd_close). + */ +Elf *open_elf(const char *path, int *fd_close) { + int fd; + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + fd = open(path, O_RDONLY); + if (fd < 0) { + warn("Could not open %s\n", path); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + *fd_close = fd; + return e; +} + +Elf *open_elf_by_fd(int fd) { + Elf *e; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warn("elf init failed\n"); + return NULL; + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (!e) { + warn("elf_begin failed: %s\n", elf_errmsg(-1)); + close(fd); + return NULL; + } + if (elf_kind(e) != ELF_K_ELF) { + warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e)); + elf_end(e); + close(fd); + return NULL; + } + return e; +} + +void close_elf(Elf *e, int fd_close) { + elf_end(e); + close(fd_close); +} + +/* Returns the offset of a function in the elf file `path`, or -1 on failure. */ +off_t get_elf_func_offset(const char *path, const char *func) { + off_t ret = -1; + int i, fd = -1; + Elf *e; + Elf_Scn *scn; + Elf_Data *data; + GElf_Ehdr ehdr; + GElf_Shdr shdr[1]; + GElf_Phdr phdr; + GElf_Sym sym[1]; + size_t shstrndx, nhdrs; + char *n; + + e = open_elf(path, &fd); + + if (!gelf_getehdr(e, &ehdr)) + goto out; + + if (elf_getshdrstrndx(e, &shstrndx) != 0) + goto out; + + scn = NULL; + while ((scn = elf_nextscn(e, scn))) { + if (!gelf_getshdr(scn, shdr)) + continue; + if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM)) + continue; + data = NULL; + while ((data = elf_getdata(scn, data))) { + for (i = 0; gelf_getsym(data, i, sym); i++) { + n = elf_strptr(e, shdr->sh_link, sym->st_name); + if (!n) + continue; + if (GELF_ST_TYPE(sym->st_info) != STT_FUNC) + continue; + if (!strcmp(n, func)) { + ret = sym->st_value; + goto check; + } + } + } + } + +check: + if (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN) { + if (elf_getphdrnum(e, &nhdrs) != 0) { + ret = -1; + goto out; + } + for (i = 0; i < (int)nhdrs; i++) { + if (!gelf_getphdr(e, i, &phdr)) + continue; + if (phdr.p_type != PT_LOAD || !(phdr.p_flags & PF_X)) + continue; + if (phdr.p_vaddr <= ret && ret < (phdr.p_vaddr + phdr.p_memsz)) { + ret = ret - phdr.p_vaddr + phdr.p_offset; + goto out; + } + } + ret = -1; + } +out: + close_elf(e, fd); + return ret; +} diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c index b44ee3922..edd07f96b 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.bpf.c @@ -16,21 +16,23 @@ // // Kernel space BPF program used for monitoring data for KVM event. -#include "../include/vmlinux.h" +#include "vmlinux.h" #include #include #include -#include "../include/kvm_watcher.h" -#include "../include/kvm_exits.h" -#include "../include/kvm_ioctl.h" -#include "../include/kvm_vcpu.h" -#include "../include/kvm_mmu.h" -#include "../include/kvm_irq.h" -#include "../include/kvm_hypercall.h" +#include "common.h" +#include "kvm_exits.h" +#include "kvm_ioctl.h" +#include "kvm_vcpu.h" +#include "kvm_mmu.h" +#include "kvm_irq.h" +#include "kvm_hypercall.h" +#include "container.h" char LICENSE[] SEC("license") = "Dual BSD/GPL"; const volatile pid_t vm_pid = -1; +const volatile char hostname[64] = ""; static struct common_event *e; // 定义环形缓冲区maps @@ -45,6 +47,13 @@ int BPF_PROG(fentry_kvm_vcpu_halt, struct kvm_vcpu *vcpu) { CHECK_PID(vm_pid); return trace_kvm_vcpu_halt(vcpu); } + +SEC("kprobe/kvm_vcpu_halt") +int BPF_KPROBE(kp_kvm_vcpu_halt, struct kvm_vcpu *vcpu) { + CHECK_PID(vm_pid); + return trace_kvm_vcpu_halt(vcpu); +} + // 追踪vcpu运行信息 SEC("tp/kvm/kvm_vcpu_wakeup") int tp_vcpu_wakeup(struct vcpu_wakeup *ctx) { @@ -67,17 +76,38 @@ SEC("tp/kvm/kvm_entry") int tp_entry(struct exit *ctx) { return trace_kvm_entry(); } + // 记录VCPU调度的信息--进入 +SEC("fentry/vmx_vcpu_load") +int BPF_PROG(fentry_vmx_vcpu_load, struct kvm_vcpu *vcpu, int cpu) { + CHECK_PID(vm_pid); + return trace_vmx_vcpu_load(vcpu, cpu); +} + SEC("kprobe/vmx_vcpu_load") int BPF_KPROBE(kp_vmx_vcpu_load, struct kvm_vcpu *vcpu, int cpu) { CHECK_PID(vm_pid); return trace_vmx_vcpu_load(vcpu, cpu); } + // 记录VCPU调度的信息--退出 +SEC("fentry/vmx_vcpu_put") +int BPF_PROG(fentry_vmx_vcpu_put) { + return trace_vmx_vcpu_put(); +} + SEC("kprobe/vmx_vcpu_put") -int BPF_KPROBE(kp_vmx_vcpu_put, struct kvm_vcpu *vcpu) { +int BPF_KPROBE(kp_vmx_vcpu_put) { return trace_vmx_vcpu_put(); } + +SEC("fentry/mark_page_dirty_in_slot") +int BPF_PROG(fentry_mark_page_dirty_in_slot, struct kvm *kvm, + const struct kvm_memory_slot *memslot, gfn_t gfn) { + CHECK_PID(vm_pid); + return trace_mark_page_dirty_in_slot(kvm, memslot, gfn, &rb, e); +} + SEC("kprobe/mark_page_dirty_in_slot") int BPF_KPROBE(kp_mark_page_dirty_in_slot, struct kvm *kvm, const struct kvm_memory_slot *memslot, gfn_t gfn) { @@ -162,9 +192,15 @@ int BPF_PROG(fexit_vmx_inject_irq, struct kvm_vcpu *vcpu, bool reinjected) { } SEC("fentry/kvm_emulate_hypercall") -int BPF_PROG(fentry_emulate_hypercall, struct kvm_vcpu *vcpu) { +int BPF_PROG(fentry_kvm_emulate_hypercall, struct kvm_vcpu *vcpu) { + CHECK_PID(vm_pid); + return trace_emulate_hypercall(vcpu, &rb, e); +} + +SEC("kprobe/kvm_emulate_hypercall") +int BPF_KPROBE(kp_kvm_emulate_hypercall, struct kvm_vcpu *vcpu) { CHECK_PID(vm_pid); - return entry_emulate_hypercall(vcpu, &rb, e); + return trace_emulate_hypercall(vcpu, &rb, e); } SEC("tp/syscalls/sys_enter_ioctl") @@ -172,10 +208,13 @@ int tp_ioctl(struct trace_event_raw_sys_enter *args) { CHECK_PID(vm_pid); return trace_kvm_ioctl(args); } -SEC("fentry/kvm_arch_vcpu_ioctl_run") -int BPF_PROG(fentry_kvm_arch_vcpu_ioctl_run, struct kvm_vcpu *vcpu) { + +SEC("uprobe") +int BPF_KPROBE(up_kvm_vcpu_ioctl, void *cpu, int type) { CHECK_PID(vm_pid); - return trace_kvm_userspace_entry(vcpu); + if (type != KVM_RUN) + return 0; + return trace_kvm_vcpu_ioctl(); } SEC("tp/kvm/kvm_userspace_exit") @@ -189,8 +228,42 @@ int BPF_PROG(fentry_start_hv_timer, struct kvm_lapic *apic) { return trace_start_hv_timer(apic); } +SEC("kprobe/start_hv_timer") +int BPF_KPROBE(kp_start_hv_timer, struct kvm_lapic *apic) { + CHECK_PID(vm_pid); + return trace_start_hv_timer(apic); +} + SEC("fentry/start_sw_timer") int BPF_PROG(fentry_start_sw_timer, struct kvm_lapic *apic) { CHECK_PID(vm_pid); return trace_start_sw_timer(apic); } + +SEC("kprobe/start_sw_timer") +int BPF_KPROBE(kp_start_sw_timer, struct kvm_lapic *apic) { + CHECK_PID(vm_pid); + return trace_start_sw_timer(apic); +} + +//采集容器的系统用调用信息 +SEC("tracepoint/raw_syscalls/sys_enter") +int tp_container_sys_entry(struct trace_event_raw_sys_enter *args) { + //过滤进程 + bool is_container = is_container_task(hostname); + if (is_container) { + return trace_container_sys_entry(args); + } else { + return 0; + } +} +SEC("tracepoint/raw_syscalls/sys_exit") +int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args) { + //过滤进程 + bool is_container = is_container_task(hostname); + if (is_container) { + return trace_container_sys_exit(args, &rb, e); + } else { + return 0; + } +} \ No newline at end of file diff --git a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c index bcb271968..928a422da 100644 --- a/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c +++ b/eBPF_Supermarket/kvm_watcher/src/kvm_watcher.c @@ -27,9 +27,13 @@ #include #include #include -#include "../include/kvm_watcher.h" +#include "common.h" +#include "trace_helpers.h" +#include "uprobe_helpers.h" #include "kvm_watcher.skel.h" +//可视化调整输出格式 +int is_first = 1; // 创建并打开临时文件 FILE *create_temp_file(const char *filename) { const char *directory = "./temp"; @@ -324,8 +328,11 @@ static struct env { bool execute_ioctl; bool execute_timer; bool verbose; + bool show; + bool execute_container_syscall; int monitoring_time; pid_t vm_pid; + char hostname[64]; enum EventType event_type; } env = { .execute_vcpu_wakeup = false, @@ -343,6 +350,9 @@ static struct env { .verbose = false, .monitoring_time = 0, .vm_pid = -1, + .hostname = "", + .show = false, + .execute_container_syscall = false, .event_type = NONE_TYPE, }; @@ -353,6 +363,7 @@ int option_selected = 0; // 功能标志变量,确保激活子功能 // 具体解释命令行参数 static const struct argp_option opts[] = { {"vcpu_wakeup", 'w', NULL, 0, "Monitoring the wakeup of vcpu."}, + {"container_syscall", 'a', NULL, 0, "Monitoring the syscall of container."}, {"vcpu_load", 'o', NULL, 0, "Monitoring the load of vcpu."}, {"vm_exit", 'e', NULL, 0, "Monitoring the event of vm exit(including exiting to KVM and user " @@ -372,6 +383,7 @@ static const struct argp_option opts[] = { "Monitoring the data of mmio page fault.(The -f option must be " "specified.)"}, {"vm_pid", 'p', "PID", 0, "Specify the virtual machine pid to monitor."}, + {"show", 's', NULL, 0, "Visual display"}, {"monitoring_time", 't', "SEC", 0, "Time for monitoring."}, {"kvm_ioctl", 'l', NULL, 0, "Monitoring the KVM IOCTL."}, {"kvm_timer", 'T', NULL, 0, "Monitoring the KVM hv or software timer."}, @@ -382,6 +394,20 @@ static const struct argp_option opts[] = { // 解析命令行参数 static error_t parse_arg(int key, char *arg, struct argp_state *state) { switch (key) { + case 's': + env.show = true; + break; + case 'a': + SET_OPTION_AND_CHECK_USAGE(option_selected, + env.execute_container_syscall); + char hostname[64]; + int result = gethostname(hostname, sizeof(hostname)); + if (result == 0) { + strcpy(env.hostname, hostname); + } else { + perror("gethostname"); + } + break; case 'H': argp_state_help(state, stderr, ARGP_HELP_STD_HELP); break; @@ -506,6 +532,8 @@ static int determineEventType(struct env *env) { env->event_type = VCPU_LOAD; } else if (env->execute_timer) { env->event_type = TIMER; + } else if (env->execute_container_syscall) { + env->event_type = CONTAINER_SYSCALL; } else { env->event_type = NONE_TYPE; // 或者根据需要设置一个默认的事件类型 } @@ -534,6 +562,12 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { case VCPU_LOAD: { break; } + case CONTAINER_SYSCALL: { + printf("%-8u %-22s %-10lld %-10lld %-16s\n", e->syscall_data.pid, + e->syscall_data.container_id, e->syscall_data.delay, + e->syscall_data.syscall_id, e->syscall_data.comm); + break; + } case HALT_POLL: { // 使用 e->halt_poll_data 访问 HALT_POLL 特有成员 printf("%-18.6f %-15s %-6d/%-8d %-10s %-7d %-7d --> %d \n", @@ -556,6 +590,12 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { } case PAGE_FAULT: { // 使用 e->page_fault_data 访问 PAGE_FAULT 特有成员 + if (env.show) { + printf("%-18.6f %-10u %-6u %-10.4f\n", timestamp_ms, + e->process.pid, e->page_fault_data.count, + NS_TO_US_WITH_DECIMAL(e->page_fault_data.delay)); + break; + } printf("%-18.6f %-15s %-10u %-12llx %-6u %-10.4f ", timestamp_ms, e->process.comm, e->process.pid, e->page_fault_data.addr, e->page_fault_data.count, @@ -660,11 +700,19 @@ static int handle_event(void *ctx, void *data, size_t data_sz) { break; } case IRQ_INJECT: { - printf("%-18.6f %-15s %-10d %-10lld %#-10x %-10d %-10lld %-10s\n", - timestamp_ms, e->process.comm, e->process.pid, - e->irq_inject_data.delay, e->irq_inject_data.irq_nr, - e->irq_inject_data.vcpu_id, e->irq_inject_data.injections, - e->irq_inject_data.soft ? "Soft/INTn" : "IRQ"); + if (env.show) { + printf("%-18.6f %-10d %-10lld %-10d %-10d %-10lld\n", + timestamp_ms, e->process.pid, e->irq_inject_data.delay, + e->irq_inject_data.irq_nr, e->irq_inject_data.vcpu_id, + e->irq_inject_data.injections); + } else { + printf( + "%-18.6f %-15s %-10d %-10lld %#-10x %-10d %-10lld %-10s\n", + timestamp_ms, e->process.comm, e->process.pid, + e->irq_inject_data.delay, e->irq_inject_data.irq_nr, + e->irq_inject_data.vcpu_id, e->irq_inject_data.injections, + e->irq_inject_data.soft ? "Soft/INTn" : "IRQ"); + } break; } case HYPERCALL: { @@ -730,11 +778,17 @@ static int print_event_head(struct env *env) { "DUR_HALT(ms)", "COMM", "PID/TID", "VCPU_ID", "WAIT/POLL", "VAILD?"); break; + case CONTAINER_SYSCALL: + printf("%-8s %-22s %-9s %10s %-16s\n", "PID", "CONTAINER_ID", + "DELAY(us)", "SYSCALLID", "COMM"); + break; case EXIT: - printf("Waiting vm_exit ... \n"); + //可视化调整输出格式 + // printf("Waiting vm_exit ... \n"); break; case VCPU_LOAD: - printf("Waiting vm_vcpu_load ... \n"); + //可视化调整输出格式 + // printf("Waiting vm_vcpu_load ... \n"); break; case HALT_POLL: printf("%-18s %-15s %-15s %-10s %-7s %-11s %-10s\n", "TIME(ms)", @@ -746,18 +800,29 @@ static int print_event_head(struct env *env) { "USERSPACE_ADDR", "SLOT_ID"); break; case PAGE_FAULT: - printf("%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", - "TIME(ms)", "COMM", "PID", "GPA", "COUNT", "DELAY(us)", - "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + if (env->show) { + printf("%-18s %-10s %-6s %-10s \n", "TIME(ms)", "PID", "COUNT", + "DELAY(us)"); + } else { + printf( + "%-18s %-15s %-10s %-12s %-6s %-10s %-20s %-17s %-10s %s\n", + "TIME(ms)", "COMM", "PID", "GPA", "COUNT", "DELAY(us)", + "HVA", "PFN", "MEM_SLOTID", "ERROR_TYPE"); + } break; case IRQCHIP: printf("%-18s %-15s %-10s %-10s %-14s %-10s %-10s\n", "TIME(ms)", "COMM", "PID", "DELAY", "TYPE/PIN", "DST/VEC", "OTHERS"); break; case IRQ_INJECT: - printf("%-18s %-15s %-10s %-10s %-10s %-10s %-10s %-10s\n", - "TIME(ms)", "COMM", "PID", "DELAY", "IRQ_NR", "VCPU_ID", - "INJECTIONS", "TYPE"); + if (env->show) { + printf("%-18s %-10s %-10s %-10s %-10s %-10s \n", "TIME(ms)", + "PID", "DELAY", "IRQ_NR", "VCPU_ID", "INJECTIONS"); + } else { + printf("%-18s %-15s %-10s %-10s %-10s %-10s %-10s %-10s\n", + "TIME(ms)", "COMM", "PID", "DELAY", "IRQ_NR", "VCPU_ID", + "INJECTIONS", "TYPE"); + } break; case HYPERCALL: { printf("Waiting hypercall ... \n"); @@ -791,28 +856,56 @@ static int print_event_head(struct env *env) { return 0; } -/*通过env结构体的属性真值来判断是否加载某个挂载函数*/ static void set_disable_load(struct kvm_watcher_bpf *skel) { + bpf_program__set_autoload(skel->progs.fentry_vmx_vcpu_load, false); + bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_load, false); + bpf_program__set_autoload(skel->progs.fentry_vmx_vcpu_put, false); + bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_put, false); + bpf_program__set_autoload(skel->progs.fentry_kvm_vcpu_halt, false); + bpf_program__set_autoload(skel->progs.kp_kvm_vcpu_halt, false); + bpf_program__set_autoload(skel->progs.fentry_mark_page_dirty_in_slot, + false); + bpf_program__set_autoload(skel->progs.kp_mark_page_dirty_in_slot, false); + bpf_program__set_autoload(skel->progs.fentry_kvm_emulate_hypercall, false); + bpf_program__set_autoload(skel->progs.kp_kvm_emulate_hypercall, false); + bpf_program__set_autoload(skel->progs.fentry_start_hv_timer, false); + bpf_program__set_autoload(skel->progs.kp_start_hv_timer, false); + bpf_program__set_autoload(skel->progs.fentry_start_sw_timer, false); + bpf_program__set_autoload(skel->progs.kp_start_sw_timer, false); + + if (env.execute_vcpu_load) { + SET_KP_OR_FENTRY_LOAD(vmx_vcpu_load, kvm_intel); + SET_KP_OR_FENTRY_LOAD(vmx_vcpu_put, kvm_intel); + } + if (env.execute_vcpu_wakeup) { + SET_KP_OR_FENTRY_LOAD(kvm_vcpu_halt, kvm); + } + if (env.execute_mark_page_dirty) { + SET_KP_OR_FENTRY_LOAD(mark_page_dirty_in_slot, kvm); + } + if (env.execute_timer) { + SET_KP_OR_FENTRY_LOAD(start_hv_timer, kvm); + SET_KP_OR_FENTRY_LOAD(start_sw_timer, kvm); + } + if (env.execute_hypercall) { + SET_KP_OR_FENTRY_LOAD(kvm_emulate_hypercall, kvm); + } + bpf_program__set_autoload(skel->progs.tp_container_sys_entry, + env.execute_container_syscall ? true : false); + bpf_program__set_autoload(skel->progs.tracepoint__syscalls__sys_exit, + env.execute_container_syscall ? true : false); bpf_program__set_autoload(skel->progs.tp_vcpu_wakeup, env.execute_vcpu_wakeup ? true : false); - bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_load, - env.execute_vcpu_load ? true : false); - bpf_program__set_autoload(skel->progs.kp_vmx_vcpu_put, - env.execute_vcpu_load ? true : false); - bpf_program__set_autoload(skel->progs.fentry_kvm_vcpu_halt, - env.execute_vcpu_wakeup ? true : false); bpf_program__set_autoload(skel->progs.tp_exit, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_entry, env.execute_exit ? true : false); - bpf_program__set_autoload(skel->progs.fentry_kvm_arch_vcpu_ioctl_run, + bpf_program__set_autoload(skel->progs.up_kvm_vcpu_ioctl, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_kvm_userspace_exit, env.execute_exit ? true : false); bpf_program__set_autoload(skel->progs.tp_kvm_halt_poll_ns, env.execute_halt_poll_ns ? true : false); - bpf_program__set_autoload(skel->progs.kp_mark_page_dirty_in_slot, - env.execute_mark_page_dirty ? true : false); bpf_program__set_autoload(skel->progs.tp_page_fault, env.execute_page_fault ? true : false); bpf_program__set_autoload(skel->progs.fexit_tdp_page_fault, @@ -837,14 +930,8 @@ static void set_disable_load(struct kvm_watcher_bpf *skel) { env.execute_irq_inject ? true : false); bpf_program__set_autoload(skel->progs.fexit_vmx_inject_irq, env.execute_irq_inject ? true : false); - bpf_program__set_autoload(skel->progs.fentry_emulate_hypercall, - env.execute_hypercall ? true : false); bpf_program__set_autoload(skel->progs.tp_ioctl, env.execute_ioctl ? true : false); - bpf_program__set_autoload(skel->progs.fentry_start_hv_timer, - env.execute_timer ? true : false); - bpf_program__set_autoload(skel->progs.fentry_start_sw_timer, - env.execute_timer ? true : false); } // 函数不接受参数,返回一个静态分配的字符串 @@ -857,11 +944,72 @@ const char *getCurrentTimeFormatted() { tm = localtime(&t); // 格式化时间到静态分配的字符串中 - strftime(ts, sizeof(ts), "%H:%M:%S", tm); + strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", tm); return ts; // 返回指向静态字符串的指针 } +// In order to sort vm_exit maps +int sort_by_key(int fd, struct exit_key *keys, struct exit_value *values) { + int err = 0; + struct exit_key lookup_key = {}; + struct exit_key next_key = {}; + struct exit_value exit_value; + int i = 0, j = 0, count = 0; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + j = i - 1; + struct exit_key temp_key = next_key; + err = bpf_map_lookup_elem(fd, &next_key, &exit_value); + if (err < 0) { + fprintf(stderr, "failed to lookup exit_value: %d\n", err); + return -1; + } + struct exit_value temp_value = exit_value; + while (j >= 0 && + (keys[j].pid > temp_key.pid || (keys[j].tid > temp_key.tid))) { + keys[j + 1] = keys[j]; + values[j + 1] = values[j]; + j--; + } + keys[j + 1] = temp_key; + values[j + 1] = temp_value; + lookup_key = next_key; + count++; + i++; + } + return count; +} + +// clear the specific map +int clear_map(void *lookup_key, void *next_key, enum EventType type, int fd) { + int err; + switch (type) { + case HYPERCALL: + memset(lookup_key, 0, sizeof(struct hc_key)); + break; + case TIMER: + memset(lookup_key, 0, sizeof(struct timer_key)); + break; + case VCPU_LOAD: + memset(lookup_key, 0, sizeof(struct load_key)); + break; + case EXIT: + memset(lookup_key, 0, sizeof(struct exit_key)); + break; + default: + return -1; + } + while (!bpf_map_get_next_key(fd, lookup_key, next_key)) { + err = bpf_map_delete_elem(fd, next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup map: %d\n", err); + return -1; + } + lookup_key = next_key; + } + return 1; +} + int print_hc_map(struct kvm_watcher_bpf *skel) { int fd = bpf_map__fd(skel->maps.hc_map); int count_fd = bpf_map__fd(skel->maps.hc_count); @@ -894,24 +1042,8 @@ int print_hc_map(struct kvm_watcher_bpf *skel) { // // Move to the next key lookup_key = next_key; } - memset(&lookup_key, 0, sizeof(struct hc_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup hc_map: %d\n", err); - return -1; - } - lookup_key = next_key; - } - memset(&lookup_key, 0, sizeof(struct hc_key)); - while (!bpf_map_get_next_key(count_fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(count_fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup hc_count: %d\n", err); - return -1; - } - lookup_key = next_key; - } + clear_map(&lookup_key, &next_key, HYPERCALL, fd); + clear_map(&lookup_key, &next_key, HYPERCALL, count_fd); return 0; } @@ -946,64 +1078,10 @@ int print_timer_map(struct kvm_watcher_bpf *skel) { // Move to the next key lookup_key = next_key; } - - // Clear the timer_map - memset(&lookup_key, 0, sizeof(struct timer_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup timer_map: %d\n", err); - return -1; - } - lookup_key = next_key; - } + clear_map(&lookup_key, &next_key, TIMER, fd); return 0; } -// In order to sort vm_exit maps -int sort_by_key(int fd, struct exit_key *keys, struct exit_value *values) { - int err = 0; - struct exit_key lookup_key = {}; - struct exit_key next_key = {}; - struct exit_value exit_value; - int first = 1; - int i = 0, j; - int count = 0; - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - count++; - if (first) { - first = 0; - bpf_map_lookup_elem(fd, &next_key, &exit_value); - keys[0] = next_key; - values[0] = exit_value; - i++; - lookup_key = next_key; - continue; - } - err = bpf_map_lookup_elem(fd, &next_key, &exit_value); - if (err < 0) { - fprintf(stderr, "failed to lookup exit_value: %d\n", err); - return -1; - } - // insert sort - j = i - 1; - struct exit_key temp_key = next_key; - struct exit_value temp_value = exit_value; - while (j >= 0 && - (keys[j].pid > temp_key.pid || (keys[j].tid > temp_key.tid))) { - keys[j + 1] = keys[j]; - values[j + 1] = values[j]; - j--; - } - i++; - keys[j + 1] = next_key; - values[j + 1] = temp_value; - // Move to the next key - lookup_key = next_key; - } - return count; -} - int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { int fd = bpf_map__fd(skel->maps.load_map); int err; @@ -1011,7 +1089,30 @@ int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { struct load_key next_key = {}; struct load_value load_value = {}; int first = 1; - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + if (env.show) { + if (is_first) { + printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "pid", + "tid", "total_time", "max_time", "min_time", "counts", + "vcpuid", "pcpuid"); + is_first = 0; + } + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &load_value); + if (err < 0) { + fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); + return -1; + } + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", + next_key.pid, next_key.tid, + NS_TO_MS_WITH_DECIMAL(load_value.total_time), + NS_TO_MS_WITH_DECIMAL(load_value.max_time), + NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, + load_value.vcpu_id, load_value.pcpu_id); + + lookup_key = next_key; + } + + } else { if (first) { first = 0; printf("\nTIME:%s\n", getCurrentTimeFormatted()); @@ -1025,29 +1126,22 @@ int print_vcpu_load_map(struct kvm_watcher_bpf *skel) { "------------ " "------------\n"); } - err = bpf_map_lookup_elem(fd, &next_key, &load_value); - if (err < 0) { - fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); - return -1; - } - printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", - next_key.pid, next_key.tid, - NS_TO_MS_WITH_DECIMAL(load_value.total_time), - NS_TO_MS_WITH_DECIMAL(load_value.max_time), - NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, - load_value.vcpu_id, load_value.pcpu_id); - lookup_key = next_key; - } - // clear the maps - memset(&lookup_key, 0, sizeof(struct load_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup counters: %d\n", err); - return -1; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &load_value); + if (err < 0) { + fprintf(stderr, "failed to lookup vcpu_load_value: %d\n", err); + return -1; + } + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u %-12d %-12d\n", + next_key.pid, next_key.tid, + NS_TO_MS_WITH_DECIMAL(load_value.total_time), + NS_TO_MS_WITH_DECIMAL(load_value.max_time), + NS_TO_MS_WITH_DECIMAL(load_value.min_time), load_value.count, + load_value.vcpu_id, load_value.pcpu_id); + lookup_key = next_key; } - lookup_key = next_key; } + clear_map(&lookup_key, &next_key, VCPU_LOAD, fd); return 0; } @@ -1061,67 +1155,76 @@ void __print_exit_map(int fd, enum NameType name_type) { // Iterate over the array __u32 pid = 0; __u32 tid = 0; - for (int i = 0; i < count; i++) { - if (first_run) { - first_run = 0; - printf("\nTIME:%s\n", getCurrentTimeFormatted()); - if (name_type == EXIT_NR) { - printf( - "============================================KVM_EXIT======" - "========" - "==============================\n"); - } else if (name_type == EXIT_USERSPACE_NR) { - printf( - "\n=======================================KVM_USERSPACE_" - "EXIT=======" - "================================\n"); - } else { - return; - } - printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", "TID", - "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT", "REASON"); - printf( - "------------ ------------ ------------ ------------ " - "------------ " - "------------ " - "------------\n"); + if (env.show) { + if (is_first) { + printf("%-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", "TID", + "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT"); + is_first = 0; } - // Print the current entry - if (tid == 0 || tid != keys[i].tid) { - tid = keys[i].tid; - if (pid == 0 || pid != keys[i].pid) { - pid = keys[i].pid; - printf("%-13d", pid); - } else { - printf("%-13s", ""); - } - printf("%-12d %-12.4f %-12.4f %-12.4f %-12u %-12s\n", keys[i].tid, - NS_TO_MS_WITH_DECIMAL(values[i].total_time), - NS_TO_MS_WITH_DECIMAL(values[i].max_time), - NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(keys[i].reason, name_type)); - } else if (tid == keys[i].tid) { - printf("%25s %-12.4f %-12.4f %-12.4f %-12u %-12s\n", "", - NS_TO_MS_WITH_DECIMAL(values[i].total_time), + for (int i = 0; i < count; i++) { + printf("%-12d %-12d %-12.4f %-12.4f %-12.4f %-12u\n", keys[i].pid, + keys[i].tid, NS_TO_MS_WITH_DECIMAL(values[i].total_time), NS_TO_MS_WITH_DECIMAL(values[i].max_time), - NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count, - getName(keys[i].reason, name_type)); + NS_TO_MS_WITH_DECIMAL(values[i].min_time), values[i].count); } - } - // clear the maps - memset(&lookup_key, 0, sizeof(struct exit_key)); - while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { - int err = bpf_map_delete_elem(fd, &next_key); - if (err < 0) { - fprintf(stderr, "failed to cleanup counters: %d\n", err); - return; + + } else { + for (int i = 0; i < count; i++) { + if (first_run) { + first_run = 0; + if (name_type == EXIT_NR) { + printf( + "============================================KVM_EXIT==" + "====" + "========" + "==============================\n"); + } else if (name_type == EXIT_USERSPACE_NR) { + printf( + "\n=======================================KVM_" + "USERSPACE_" + "EXIT=======" + "================================\n"); + } else { + return; + } + printf("%-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", "PID", + "TID", "TOTAL_TIME", "MAX_TIME", "MIN_TIME", "COUNT", + "REASON"); + printf( + "------------ ------------ ------------ ------------ " + "------------ " + "------------ " + "------------\n"); + } + // Print the current entry + if (tid == 0 || tid != keys[i].tid) { + tid = keys[i].tid; + if (pid == 0 || pid != keys[i].pid) { + pid = keys[i].pid; + printf("%-13d", pid); + } else { + printf("%-13s", ""); + } + printf("%-12d %-12.4f %-12.4f %-12.4f %-12u %-12s\n", + keys[i].tid, NS_TO_MS_WITH_DECIMAL(values[i].total_time), + NS_TO_MS_WITH_DECIMAL(values[i].max_time), + NS_TO_MS_WITH_DECIMAL(values[i].min_time), + values[i].count, getName(keys[i].reason, name_type)); + } else if (tid == keys[i].tid) { + printf("%25s %-12.4f %-12.4f %-12.4f %-12u %-12s\n", "", + NS_TO_MS_WITH_DECIMAL(values[i].total_time), + NS_TO_MS_WITH_DECIMAL(values[i].max_time), + NS_TO_MS_WITH_DECIMAL(values[i].min_time), + values[i].count, getName(keys[i].reason, name_type)); + } } - lookup_key = next_key; } + clear_map(&lookup_key, &next_key, EXIT, fd); } int print_exit_map(struct kvm_watcher_bpf *skel) { int exit_fd = bpf_map__fd(skel->maps.exit_map); int userspace_exit_fd = bpf_map__fd(skel->maps.userspace_exit_map); + // printf("\nTIME:%s\n", getCurrentTimeFormatted()); __print_exit_map(exit_fd, EXIT_NR); __print_exit_map(userspace_exit_fd, EXIT_USERSPACE_NR); return 0; @@ -1131,7 +1234,7 @@ void print_map_and_check_error(int (*print_func)(struct kvm_watcher_bpf *), const char *map_name, int err) { OUTPUT_INTERVAL(2); print_func(skel); - if (err < 0) { + if (err < 0 && err != -4) { printf("Error printing %s map: %d\n", map_name, err); } } @@ -1143,21 +1246,23 @@ void print_logo() { system(command); } +int attach_probe(struct kvm_watcher_bpf *skel) { + if (env.execute_exit) { + ATTACH_UPROBE_CHECKED(skel, kvm_vcpu_ioctl, up_kvm_vcpu_ioctl); + } + return kvm_watcher_bpf__attach(skel); +} int main(int argc, char **argv) { // 定义一个环形缓冲区 struct ring_buffer *rb = NULL; struct kvm_watcher_bpf *skel; int err; - - print_logo(); - /*解析命令行参数*/ err = argp_parse(&argp, argc, argv, 0, NULL, NULL); if (err) return err; /*设置libbpf的错误和调试信息回调*/ libbpf_set_print(libbpf_print_fn); - /* Cleaner handling of Ctrl-C */ signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); @@ -1171,7 +1276,7 @@ int main(int argc, char **argv) { /* Parameterize BPF code with parameter */ skel->rodata->vm_pid = env.vm_pid; - + strcpy(skel->rodata->hostname, env.hostname); /* 禁用或加载内核挂钩函数 */ set_disable_load(skel); @@ -1183,12 +1288,11 @@ int main(int argc, char **argv) { } /* 附加跟踪点处理程序 */ - err = kvm_watcher_bpf__attach(skel); + err = attach_probe(skel); if (err) { fprintf(stderr, "Failed to attach BPF skeleton\n"); goto cleanup; } - /* 设置环形缓冲区轮询 */ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); if (!rb) { @@ -1203,6 +1307,8 @@ int main(int argc, char **argv) { fprintf(stderr, "Invalid env parm\n"); goto cleanup; } + if (!env.show) + print_logo(); /*打印信息头*/ err = print_event_head(&env); diff --git a/eBPF_Supermarket/lib/bpftool b/eBPF_Supermarket/lib/bpftool new file mode 160000 index 000000000..1174341ef --- /dev/null +++ b/eBPF_Supermarket/lib/bpftool @@ -0,0 +1 @@ +Subproject commit 1174341ef321a84003c2f0cf80025823145eff22 diff --git a/eBPF_Supermarket/lib/libbpf b/eBPF_Supermarket/lib/libbpf new file mode 160000 index 000000000..02724cfd0 --- /dev/null +++ b/eBPF_Supermarket/lib/libbpf @@ -0,0 +1 @@ +Subproject commit 02724cfd0702c4102138e62c3ae7d2721c7b190e