diff --git a/MagicEyes/src/visualization/vscode_ext/README.md b/MagicEyes/src/visualization/vscode_ext/README.md new file mode 100644 index 000000000..1f1a6c5e1 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/README.md @@ -0,0 +1,46 @@ +# lmp vscode 插件使用指南 + +### 1. 运行效果 + +![](./images/lmp_vscode_ext.gif) + +### 2. 相关提示 + +设置可视化面板存放路径 + +> 注意:别忘记面板路径后面加 "/" + +![](./images/set_panel_addr.png) + +面板命名必须遵循如下规则: + +![](./images/panel_name.png) + +若面板不存在,或路径,或名称不对,将出现如下错误提示: + +![](./images/error_info.png) + +### 3. 开发注意事项 + +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. */ + } +} +``` + + + 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/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/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..957b998e1 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/package.json @@ -0,0 +1,185 @@ +{ + "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 + } + } + }, + "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.4.0", + "cors": "^2.8.5", + "express": "^4.19.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..388c2b339 --- /dev/null +++ b/MagicEyes/src/visualization/vscode_ext/lmp_ext_vscode/src/extension.ts @@ -0,0 +1,393 @@ +// 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: 为了检查面板文件是否存在 + +// 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); + } + + }); + + 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"; + +// 扩展 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 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 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: "点击将呈现net_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: "点击将呈现net_watcher的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_watcher的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: "点击将呈现net_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: "点击将呈现net_watcher的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: "点击将呈现net_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); + } + +} +// 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); + } + +} + */ \ No newline at end of file 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..5eee14503 --- /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.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" + integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== + dependencies: + follow-redirects "^1.15.0" + 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.19.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.0: + 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_grafana-vscode-0.0.16.vsix b/MagicEyes/src/visualization/vscode_ext/lmp_grafana-vscode-0.0.16.vsix new file mode 100644 index 000000000..3f2f86320 Binary files /dev/null and b/MagicEyes/src/visualization/vscode_ext/lmp_grafana-vscode-0.0.16.vsix differ 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 index 4770a3c15..caaf8c408 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/cs_delay.bpf.c @@ -30,9 +30,19 @@ struct { __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; @@ -43,6 +53,7 @@ int BPF_KPROBE(schedule) 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; diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c index b86631002..3aa9dd92e 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/mq_delay.bpf.c @@ -33,6 +33,16 @@ struct { __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); @@ -64,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 @@ -83,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); @@ -97,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); @@ -122,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 @@ -149,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(); @@ -166,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*/ @@ -193,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/preempt.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c index 57043e3bb..bf650a6e0 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/preempt.bpf.c @@ -23,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); @@ -52,6 +62,7 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s // 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/bpf/sar.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c index 2fe6b43f5..eb4012def 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sar.bpf.c @@ -23,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 */ @@ -51,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") 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; @@ -73,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; @@ -94,6 +107,7 @@ int trace_sched_switch2(struct cswch_args *info) { // 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; @@ -124,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); @@ -133,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); @@ -141,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); @@ -159,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); @@ -167,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); @@ -186,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) { @@ -215,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 index 23eaf2200..ee224283a 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/sc_delay.bpf.c @@ -15,27 +15,38 @@ // author: albert_xuu@163.com zhangxy1016304@163.com zhangziheng0525@163.com #include "vmlinux.h" -#include //包含了BPF 辅助函数 +#include #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);//记录时间戳 + +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")//进入系统调用 +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 + 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); @@ -44,13 +55,13 @@ int tracepoint__syscalls__sys_enter(struct trace_event_raw_sys_enter *args){ return 0; } -SEC("tracepoint/raw_syscalls/sys_exit")//退出系统调用 +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 + 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; @@ -59,7 +70,6 @@ int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ }else{ return 0; } - u64 *val2 = bpf_map_lookup_elem(&Events, &pid); if(val2 !=0){ syscall_id = *val2; @@ -67,19 +77,13 @@ int tracepoint__syscalls__sys_exit(struct trace_event_raw_sys_exit *args){ }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/bpf/schedule_delay.bpf.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c index 596158c6e..db1b2a363 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/bpf/schedule_delay.bpf.c @@ -28,14 +28,21 @@ 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) { - struct schedule_ctrl *sched_ctrl; - sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); - if(!sched_ctrl || !sched_ctrl->schedule_func) - return 0; + 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; @@ -62,7 +69,7 @@ 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) { - struct schedule_ctrl *sched_ctrl; + 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; @@ -86,11 +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; - sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); - if(!sched_ctrl || !sched_ctrl->schedule_func) - return 0; - + 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; @@ -138,8 +143,8 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s } delay = current_time - schedule_event->enter_time; struct proc_schedule proc_schedule; - proc_schedule.pid = next_pid; 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); @@ -172,16 +177,27 @@ int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_s } } } + 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; - sched_ctrl = bpf_map_lookup_elem(&schedule_ctrl_map,&ctrl_key); - if(!sched_ctrl || !sched_ctrl->schedule_func) - return 0; - + 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(); diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c index b02a986ce..7c8cde155 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/controller.c @@ -30,6 +30,7 @@ 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; @@ -41,6 +42,7 @@ static struct env { } env = { .usemode = 0, .SAR = false, + .percent = false, .CS_DELAY = false, .SYSCALL_DELAY = false, .MIN_US_SET = false, @@ -58,22 +60,13 @@ static const struct argp_option opts[] = { { "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)" }, - // { "pid", 'p', "PID", 0, "Process ID to trace" }, - // { "tgid", 'P', "TGID", 0, "Thread group 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)" }, - // { "resource", 'r', NULL, 0, "Collects resource usage information about processes" }, - // { "keytime", 'k', "KEYTIME", 0, "Collects keytime information about processes(0:except CPU kt_info,1:all kt_info,any 0 or 1 when deactivated)" }, - // { "lock", 'l', NULL, 0, "Collects lock information about processes" }, - // { "syscall", 's', "SYSCALLS", 0, "Collects syscall sequence (1~50) information about processes(any 1~50 when deactivated)" }, - // { "schedule", 'S', NULL, 0, "Collects schedule information about processes (trace tool process)" }, { NULL, 'h', NULL, OPTION_HIDDEN, "show the full help" }, {}, }; @@ -93,6 +86,8 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 's': env.SAR = true; break; + case 'P': + env.percent = true; case 'c': env.CS_DELAY = true; break; @@ -134,7 +129,7 @@ int deactivate_mode(){ int err; if(env.SAR){ - struct sar_ctrl sar_ctrl = {false,0}; + struct sar_ctrl sar_ctrl = {false,false,0}; err = update_sar_ctrl_map(sar_ctrl); if(err < 0) return err; } @@ -190,9 +185,9 @@ int main(int argc, char **argv) if(env.usemode == 1){ // activate mode if(env.SAR){ - struct sar_ctrl sar_ctrl = {true,SAR_WACTHER}; - err = update_sar_ctrl_map(sar_ctrl); - if(err < 0) return err; + 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){ diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c index 23114d93c..0cdf445f5 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/cpu_watcher.c @@ -55,13 +55,10 @@ struct msg_msg { static struct env { int time; int period; - bool percent; bool enable_proc; bool SAR; bool CS_DELAY; bool SYSCALL_DELAY; - bool MIN_US_SET; - int MIN_US; bool PREEMPT; bool SCHEDULE_DELAY; bool MQ_DELAY; @@ -71,13 +68,10 @@ static struct env { } env = { .time = 0, .period = 1, - .percent = false, .enable_proc = false, .SAR = false, .CS_DELAY = false, .SYSCALL_DELAY = false, - .MIN_US_SET = false, - .MIN_US = 10000, .PREEMPT = false, .SCHEDULE_DELAY = false, .MQ_DELAY = false, @@ -97,9 +91,11 @@ struct mq_delay_bpf *mq_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 prev_watcher = 0;//上一个使用的工具,用于在切换使用功能时,打印不用功能的表头; @@ -130,7 +126,6 @@ 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)" }, { "period", 'i', "INTERVAL", 0, "Period interval in seconds" }, - {"percent", 'P', 0, 0, "Format data as percentages" }, {"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)" }, @@ -153,9 +148,6 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'i': env.period = strtol(arg, NULL, 10); break; - case 'P': - env.percent = true; - break; case 's': env.SAR = true; break; @@ -264,6 +256,28 @@ u64 find_ksym(const char* target_symbol) { 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; @@ -380,7 +394,7 @@ static int print_all() if(env.enable_proc){ time_t now = time(NULL); struct tm *localTime = localtime(&now); - if (env.percent == true){ + 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] = { @@ -567,9 +581,7 @@ void add_entry(int pid, const char *comm, long long delay) { } static int schedule_print() { - int err,key = 0; - struct schedule_ctrl sd_ctrl = {}; err = bpf_map_lookup_elem(schedulemap_fd,&key,&sd_ctrl); if (err < 0) { fprintf(stderr, "failed to lookup infos: %d\n", err); @@ -620,18 +632,34 @@ static int schedule_print() } else{ struct proc_schedule info; + struct proc_id id_key; + struct proc_history prev_info; int key = 0; - int err, fd = bpf_map__fd(sd_skel->maps.threshold_schedule); - err = bpf_map_lookup_elem(fd, &key, &info); + 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.pid!=0) { - if (!entry_exists(info.pid, info.proc_name, info.delay / 1000)) { - printf("%-10d %-16s %15lld\n", info.pid, info.proc_name, info.delay / 1000); - add_entry(info.pid, info.proc_name, info.delay / 1000); + 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"); } + } } @@ -878,7 +906,7 @@ int main(int argc, char **argv) goto sar_cleanup; } sarmap_fd = bpf_map__fd(sar_ctrl_map); - struct sar_ctrl init_value = {false,SAR_WACTHER}; + 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"); @@ -891,9 +919,6 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to attach BPF skeleton\n"); goto sar_cleanup; } - if (env.percent){ - printf(" time proc/s cswch/s runqlen irqTime/%% softirq/%% idle/%% kthread/%% sysc/%% utime/%% sys/%% \n"); - }else{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(); @@ -1013,7 +1038,7 @@ int main(int argc, char **argv) if (err < 0) { break; } - if(env.SCHEDULE_DELAY){ + if(env.SCHEDULE_DELAY&&!sd_ctrl.min_us_set){ sleep(1); } } diff --git a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h index 00a6463ec..eadd0d874 100644 --- a/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h +++ b/eBPF_Supermarket/CPU_Subsystem/cpu_watcher/include/cpu_watcher.h @@ -141,11 +141,21 @@ struct sum_schedule { }; struct proc_schedule { - int pid; + 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]; // 存储最后两个调度的进程信息 +}; + + /*----------------------------------------------*/ /* mq_delay相关结构体 */ /*----------------------------------------------*/ @@ -226,6 +236,7 @@ struct idleStruct { /*----------------------------------------------*/ struct sar_ctrl{ bool sar_func; + bool percent; int prev_watcher; }; diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.bpf.c b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.bpf.c index 50d0bb29c..135a863fd 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.bpf.c +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.bpf.c @@ -1,63 +1,77 @@ -#include "vmlinux.h" -#include //包含了BPF 辅助函数 +#define BPF_NO_GLOBAL_DATA +#include +#include #include -#include "open.h" +#include -char LICENSE[] SEC("license") = "Dual BSD/GPL"; +#define TASK_COMM_LEN 100 +#define path_size 256 -// 定义哈希映射 struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1024); __type(key, pid_t); - __type(value, u64); -} fdtmp SEC(".maps"); + __type(value, char[TASK_COMM_LEN]); +} data SEC(".maps"); + +struct event { + int pid_; + char path_name_[path_size]; + int n_; + char comm[TASK_COMM_LEN]; +}; struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 256 * 1024); -} rb SEC(".maps"); - -SEC("kprobe/do_sys_openat2") -int BPF_KPROBE(do_sys_openat2) -{ - struct fs_t fs; - pid_t pid; - - //pid - pid = bpf_get_current_pid_tgid() >> 32; - fs.pid = pid; - - //uid - fs.uid = bpf_get_current_uid_gid(); - - //fd,file descriptor - int fd = PT_REGS_RC(ctx); - if (fd >= 0) - fs.fd = fd; - else - fs.fd= -1; - - //time - unsigned long long ts = bpf_ktime_get_ns(); - fs.ts = ts; - bpf_map_update_elem(&fdtmp, &pid, &ts, BPF_ANY); - - //从环形缓冲区(ring buffer)中分配一块内存来存储一个名为 struct fs_t 类型的数据,并将该内存块的指针赋值给指针变量 e - struct fs_t *e; +} rb SEC(".maps"); // 环形缓冲区 + + +SEC("tracepoint/syscalls/sys_enter_openat") +int do_syscall_trace(struct trace_event_raw_sys_enter *ctx) +{ + struct event *e; + char comm[TASK_COMM_LEN]; + bpf_get_current_comm(&comm,sizeof(comm)); e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); - if (!e) return 0; - - //给变量e赋值 - e->pid = fs.pid; - e->uid = fs.uid; - e->fd = fs.fd; - e->ts = fs.ts; - bpf_get_current_comm(e->comm, sizeof(e->comm)); - - // 成功地将其提交到用户空间进行后期处理 + if (!e) + return 0; + + char filename[path_size]; + struct task_struct *task = (struct task_struct *)bpf_get_current_task(), + *real_parent; + if (task == NULL) { + bpf_printk("task\n"); + bpf_ringbuf_discard(e, 0); + return 0; + } + int pid = bpf_get_current_pid_tgid() >> 32, tgid; + + bpf_map_update_elem(&data, &pid, &comm, BPF_ANY); + + int ppid = BPF_CORE_READ(task, real_parent, tgid); + + bpf_probe_read_str(e->path_name_, sizeof(e->path_name_), + (void *)(ctx->args[1])); + + bpf_printk("path name: %s,pid:%d,ppid:%d\n", e->path_name_, pid, ppid); + + struct fdtable *fdt = BPF_CORE_READ(task, files, fdt); + if (fdt == NULL) { + bpf_printk("fdt\n"); + bpf_ringbuf_discard(e, 0); + return 0; + } + + unsigned int i = 0, count = 0, n = BPF_CORE_READ(fdt, max_fds); + bpf_printk("n:%d\n", n); + + e->n_ = n; + e->pid_ = pid; + bpf_ringbuf_submit(e, 0); - return 0; + return 0; } +char LICENSE[] SEC("license") = "GPL"; \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.c b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.c index 9a48be42f..ed01c10e5 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.c +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.c @@ -1,10 +1,38 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include #include -#include +#include +#include +#include +#include #include #include -#include -#include "open.skel.h" //包含了 BPF 字节码和相关的管理函数 -#include "open.h" +#include +#include +#include +#include "open.skel.h" +#include +#include +#include + +#define path_size 256 +#define TASK_COMM_LEN 16 + +struct event { + int pid_; + char path_name_[path_size]; + int n_; + char comm[TASK_COMM_LEN]; +}; + +#define warn(...) fprintf(stderr, __VA_ARGS__) + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, + va_list args) +{ + return vfprintf(stderr, format, args); +} static volatile bool exiting = false; @@ -13,18 +41,34 @@ static void sig_handler(int sig) exiting = true; } -static int handle_event(void *ctx, void *data,unsigned long data_sz) +static int handle(void *ctx, void *data, size_t data_sz) { - const struct fs_t *e = data; - printf("pid:%d uid:%llu time:%llu fd:%d comm:%s\n",e->pid,e->uid,e->ts,e->fd,e->comm); - - return 0; -} - + struct event *e = (struct event *)data; + char *filename = strrchr(e->path_name_, '/'); + ++filename; -static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) -{ - return vfprintf(stderr, format, args); + char fd_path[path_size]; + char actual_path[path_size]; + 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); + ssize_t len = readlink(fd_path, actual_path, sizeof(actual_path) - 1); + if (len != -1) { + actual_path[len] = '\0'; + int result = strcmp(e->path_name_, actual_path); + if (result == 0) { + if(bpf_map_lookup_elem(map_fd,&e->pid_,&comm)==0){ + printf("get , filename:%s , fd:%d , pid:%d ,comm:%s\n", e->path_name_, i,e->pid_,comm); + }else{ + fprintf(stderr, "Failed to lookup value for key %d\n", e->pid_); + } + } + } + } + return 0; } int main(int argc, char **argv) @@ -33,50 +77,51 @@ int main(int argc, char **argv) struct open_bpf *skel; int err; - libbpf_set_strict_mode(LIBBPF_STRICT_ALL); - /* 设置libbpf错误和调试信息回调 */ + /* Set up libbpf errors and debug info callback */ libbpf_set_print(libbpf_print_fn); - /* 更干净地处理Ctrl-C - SIGINT:由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 - SIGTERM:请求中止进程,kill命令发送 - */ - signal(SIGINT, sig_handler); //signal设置某一信号的对应动作 + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); - /* 打开BPF应用程序 */ + /* Load and verify BPF application */ skel = open_bpf__open(); if (!skel) { - fprintf(stderr, "Failed to open BPF skeleton\n"); + fprintf(stderr, "Failed to open and load BPF skeleton\n"); return 1; } - - /* 加载并验证BPF程序 */ + + /* Load & verify BPF programs */ err = open_bpf__load(skel); if (err) { fprintf(stderr, "Failed to load and verify BPF skeleton\n"); goto cleanup; } - - /* 附加跟踪点处理程序 */ - err = open_bpf__attach(skel); - if (err) { + + int attach = open_bpf__attach(skel); + if (attach) { fprintf(stderr, "Failed to attach BPF skeleton\n"); + err = -1; goto cleanup; } - - /* 设置环形缓冲区轮询 */ - rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); //ring_buffer__new() API,允许在不使用额外选项数据结构下指定回调 + + int map_fd = bpf_map__fd(skel->maps.data); + if(!map_fd){ + fprintf(stderr, "Failed to find BPF map\n"); + return -1; + } + + rb = ring_buffer__new( + bpf_map__fd(skel->maps.rb), handle, &map_fd, NULL); // 创建一个环形缓冲区,并设置好缓冲区的回调函数 if (!rb) { err = -1; fprintf(stderr, "Failed to create ring buffer\n"); goto cleanup; } - - /* 处理事件 */ + while (!exiting) { - err = ring_buffer__poll(rb, 100 /* timeout, ms */); //ring_buffer__poll(),轮询打开ringbuf缓冲区。如果有事件,handle_event函数会执行 - /* Ctrl-C will cause -EINTR */ + err = ring_buffer__poll(rb, 100); + if (err == -EINTR) { err = 0; break; @@ -85,15 +130,11 @@ int main(int argc, char **argv) printf("Error polling perf buffer: %d\n", err); break; } - - //exiting = true; //使用该程序时,将该行代码注释掉 - } - -/* 卸载BPF程序 */ cleanup: + /* Clean up */ ring_buffer__free(rb); open_bpf__destroy(skel); - + return err < 0 ? -err : 0; -} +} \ No newline at end of file diff --git a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.h b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.h index 835cd90fa..a8194600c 100644 --- a/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.h +++ b/eBPF_Supermarket/Filesystem_Subsystem/fs_watcher/open.h @@ -5,12 +5,11 @@ #define TASK_COMM_LEN 16 #endif -struct fs_t { - int pid; - unsigned long long uid; - int fd; - unsigned long long ts; - char comm[TASK_COMM_LEN]; +struct event { + int pid_; + char path_name_[path_size]; + int n_; + char comm[TASK_COMM_LEN]; }; #endif /* __OPEN_H */ \ No newline at end of file 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/mem_watcher\345\217\257\350\247\206\345\214\226.md" b/eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md similarity index 100% rename from "eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher\345\217\257\350\247\206\345\214\226.md" rename to eBPF_Supermarket/Memory_Subsystem/docs/mem_watcher_vision.md 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 e08470014..06e256892 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_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/' \ @@ -20,7 +20,7 @@ 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) @@ -30,8 +30,9 @@ 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,28 @@ $(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" diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/mem_watcher.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/mem_watcher.bpf.c 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 94% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c index 4ae642682..00ef2efe1 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/memleak.bpf.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/bpf/memleak.bpf.c @@ -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) { @@ -92,6 +106,10 @@ static int gen_alloc_exit2(void *ctx, u64 address) { 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; } 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/mem_watcher.h b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.h rename to eBPF_Supermarket/Memory_Subsystem/mem_watcher/include/mem_watcher.h diff --git a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c index 8f596b55d..2b5f34811 100644 --- a/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c +++ b/eBPF_Supermarket/Memory_Subsystem/mem_watcher/mem_watcher.c @@ -144,6 +144,7 @@ static struct env { bool sysstat; bool memleak; bool kernel_trace; + bool print_time; bool part2; @@ -157,6 +158,7 @@ static struct env { .sysstat = false, .memleak = false, .kernel_trace = true, + .print_time = false, .rss = false, .part2 = false, .choose_pid = 0, @@ -186,8 +188,10 @@ static const struct argp_option opts[] = { {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}, - {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 10}, + + {"time", 't', "TIME-SEC", 0, "Max Running Time(0 for infinite)", 11}, {NULL, 'h', NULL, OPTION_HIDDEN, "show the full help"}, {0}, }; @@ -203,11 +207,12 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'r': env.procstat = 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; case 'R': env.rss = true; break; - case 'l': env.memleak = true; break; - default: return ARGP_ERR_UNKNOWN; + 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; + default: return ARGP_ERR_UNKNOWN; } return 0; } @@ -237,6 +242,10 @@ 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 __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); + // Main function int main(int argc, char **argv) { @@ -521,6 +530,92 @@ int print_outstanding_combined_allocs(struct memleak_bpf *skel, pid_t pid) { return 0; } +// 在更新时间之前获取当前时间并调整为相对于程序启动时的时间 +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 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; + } + + // 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); @@ -813,7 +908,13 @@ static int process_memleak(struct memleak_bpf *skel_memleak, struct env env) { for (;;) { if (!env.kernel_trace) - print_outstanding_combined_allocs(skel_memleak, attach_pid); + 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); 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/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/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/time/Makefile b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile similarity index 97% rename from eBPF_Supermarket/Memory_Subsystem/old_project/time/Makefile rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/Makefile index a018d9a27..6fc92d3a9 100644 --- a/eBPF_Supermarket/Memory_Subsystem/old_project/time/Makefile +++ b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/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/time/time.bpf.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/old_project/time/time.bpf.c rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.bpf.c diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/time/time.c b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/old_project/time/time.c rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.c diff --git a/eBPF_Supermarket/Memory_Subsystem/old_project/time/time.h b/eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.h similarity index 100% rename from eBPF_Supermarket/Memory_Subsystem/old_project/time/time.h rename to eBPF_Supermarket/Memory_Subsystem/old_project/page_fault/page_fault.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/net_watcher/common.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/common.bpf.h index 90ad50757..0c71f53ba 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 -{ +enum { e_ip_rcv = 0, e_ip_local_deliver, e_ip_local_deliver_finish, @@ -74,37 +73,42 @@ 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;// 附加记录计数 + 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_header header; // DNS头部 + char data[64]; // 可变长度数据(域名+类型+类) }; -struct dns{ - u32 saddr; - u32 daddr; +struct dns { + u32 saddr; + u32 daddr; +}; + +struct query_info { + char msql[256]; + u32 size; + u64 start_time; }; // 操作BPF映射的一个辅助函数 @@ -158,6 +162,11 @@ struct { __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); @@ -218,67 +227,83 @@ 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 耗时 +// sql 耗时 struct { - __uint(type, BPF_MAP_TYPE_HASH); - __uint(max_entries, 256*1024); - __type(key, __u32); - __type(value, __u64); + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 256 * 1024); + __type(key, __u32); + __type(value, __u64); } mysql_time SEC(".maps"); -//sql请求数 +// 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); + __type(key, __u32); + __type(value, __u64); } sql_count SEC(".maps"); -//dns计数根据每个saddr、daddr +// dns计数根据每个saddr、daddr struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1024); - __type(key,struct dns); - __type(value,__u64); + __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); + __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"); + 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, - drop_reason = 0,icmp_info = 0 ,tcp_info = 0 ,dns_info = 0 ,stack_info = 0,mysql_info = 0; - -/* help macro */ + 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; -#define FILTER \ - if(filter_dport&&filter_dport!= pkt_tuple.dport) \ - return 0; \ - if(filter_sport&&filter_sport!= pkt_tuple.sport) \ - return 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 \ @@ -349,7 +374,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); \ @@ -365,33 +389,29 @@ const volatile int all_conn = 0, err_packet = 0, extra_conn_info = 0, /* 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)); } @@ -402,9 +422,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); @@ -425,9 +445,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); @@ -440,9 +460,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), @@ -458,31 +478,29 @@ void get_pkt_tuple_v6(struct packet_tuple *pkt_tuple, pkt_tuple->tran_flag = 1; // tcp包 } -int getstack(void *ctx) -{ +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; + 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 = bpf_ringbuf_reserve(&trace_rb, sizeof(*event), 0); + if (!event) + return 1; - event->pid = pid; - event->cpu_id = cpu_id; + event->pid = pid; + event->cpu_id = cpu_id; - if (bpf_get_current_comm(event->comm, sizeof(event->comm))) - event->comm[0] = 0; + 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); + event->kstack_sz = + bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); + bpf_ringbuf_submit(event, 0); - return 0; + return 0; } /* help functions end */ - - #endif diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/data/connects.log b/eBPF_Supermarket/Network_Subsystem/net_watcher/data/connects.log index 733723688..e69de29bb 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/data/connects.log +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/data/connects.log @@ -1 +0,0 @@ -connection{pid="214827",sock="0xffff944e0c3dda00",src="192.168.239.132:35422",dst="20.189.173.1:443",is_server="0",backlog="-",maxbacklog="-",cwnd="-",ssthresh="-",sndbuf="-",wmem_queued="-",rx_bytes="-",tx_bytes="-",srtt="-",duration="-",total_retrans="-",fast_retrans="-",timeout_retrans="-"} diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h index cff8c950f..13e802887 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/mysql.bpf.h @@ -19,55 +19,38 @@ #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 - char comm[16]; 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); - char *sql; + struct query_info info; u32 size = 0; + char *sql; if (command != COM_QUERY) { return 0; } - u64 start_time = bpf_ktime_get_ns() / 1000; - bpf_map_update_elem(&mysql_time, &pid, &start_time, BPF_ANY); - - struct mysql_query *message = - bpf_ringbuf_reserve(&mysql_rb, sizeof(*message), 0); - if (!message) { - return 0; - } - - bpf_probe_read(&message->size, sizeof(message->size), - &com_data->com_query.length); + 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(&message->msql, sizeof(message->msql), sql); - - message->pid = pid; - message->tid = tid; - bpf_get_current_comm(&message->comm, sizeof(comm)); - - bpf_ringbuf_submit(message, 0); + 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(); - u64 *start_time_ptr, duration; - u64 end_time = bpf_ktime_get_ns() / 1000; - start_time_ptr = bpf_map_lookup_elem(&mysql_time, &pid); - if (!start_time_ptr) { + struct query_info *info = bpf_map_lookup_elem(&queries, &tid); + if (!info) { return 0; } - duration = end_time - *start_time_ptr; struct mysql_query *message = bpf_ringbuf_reserve(&mysql_rb, sizeof(*message), 0); if (!message) { @@ -78,13 +61,18 @@ static __always_inline int __handle_mysql_end(struct pt_regs *ctx) { if (count_ptr) { count = *count_ptr + 1; } + message->count = count; bpf_map_update_elem(&sql_count, &tid, &count, BPF_ANY); - - message->duratime = duration; + 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); - bpf_map_delete_elem(&mysql_time, &pid); - return 0; } diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c index 5c67c4090..4ae7d22f6 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.bpf.c @@ -30,6 +30,8 @@ #include "mysql.bpf.h" +#include "redis.bpf.h" + #include "drop.bpf.h" // accecpt an TCP connection @@ -330,4 +332,13 @@ int BPF_KPROBE(query__start) { SEC("uretprobe/_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command") int BPF_KPROBE(query__end){ return __handle_mysql_end(ctx); +} + +SEC("uprobe/call") +int BPF_KPROBE(query__start_redis) { + return __handle_redis_start(ctx); +} +SEC("uretprobe/call") +int BPF_KPROBE(query__end_redis){ + return __handle_redis_end(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 c2ba5490e..b23e6c03e 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.c @@ -44,7 +44,7 @@ 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, 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, - count_info = 0; // flag + redis_info = 0, count_info = 0; // flag static const char *tcp_states[] = { [1] = "ESTABLISHED", [2] = "SYN_SENT", [3] = "SYN_RECV", @@ -78,8 +78,8 @@ static const struct argp_option opts[] = { {"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 语句、Duration Sql耗时、Request " - "Sql请求数"}, + "进程名、Size sql语句字节大小、Sql 语句"}, + {"redis", 'R', 0, 0}, {"count", 'C', "NUMBER", 0, "specify the time to count the number of requests"}, {}}; @@ -141,6 +141,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) { case 'M': mysql_info = 1; break; + case 'R': + redis_info = 1; + break; case 'C': count_info = strtoul(arg, &end, 10); break; @@ -169,6 +172,7 @@ enum MonitorMode { MODE_TCP, MODE_DNS, MODE_MYSQL, + MODE_REDIS, MODE_DEFAULT }; @@ -187,6 +191,8 @@ enum MonitorMode get_monitor_mode() { return MODE_DNS; } else if (mysql_info) { return MODE_MYSQL; + } else if (redis_info) { + return MODE_REDIS; } else { return MODE_DEFAULT; } @@ -227,7 +233,7 @@ void print_logo() { pclose(lolcat_pipe); } -static const char binary_path[] = "/usr/sbin/mysqld"; +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, \ @@ -419,6 +425,7 @@ static void set_rodata_flags(struct netwatcher_bpf *skel) { skel->rodata->dns_info = dns_info; skel->rodata->stack_info = stack_info; skel->rodata->mysql_info = mysql_info; + skel->rodata->redis_info = redis_info; } static void set_disable_load(struct netwatcher_bpf *skel) { @@ -556,6 +563,10 @@ static void set_disable_load(struct netwatcher_bpf *skel) { mysql_info ? true : false); bpf_program__set_autoload(skel->progs.query__end, mysql_info ? true : false); + bpf_program__set_autoload(skel->progs.query__start_redis, + redis_info ? true : false); + bpf_program__set_autoload(skel->progs.query__end_redis, + redis_info ? true : false); } static void print_header(enum MonitorMode mode) { @@ -617,6 +628,14 @@ static void print_header(enum MonitorMode mode) { 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_DEFAULT: printf("===============================================================" "=INFORMATION===================================================" @@ -765,7 +784,7 @@ 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 || drop_reason || icmp_info || tcp_info || - dns_info || mysql_info) + dns_info || mysql_info || redis_info) return 0; const struct pack_t *pack_info = packet_info; if (pack_info->mac_time > MAXTIME || pack_info->ip_time > MAXTIME || @@ -1090,7 +1109,6 @@ static int print_dns(void *ctx, void *packet_info, size_t size) { pack_info->rx); return 0; } -static mysql_query last_query; static int print_mysql(void *ctx, void *packet_info, size_t size) { if (!mysql_info) { @@ -1098,23 +1116,29 @@ static int print_mysql(void *ctx, void *packet_info, size_t size) { } const mysql_query *pack_info = packet_info; - if (pack_info->duratime == 0) { - - memcpy(&last_query, pack_info, sizeof(mysql_query)); + 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; +} - printf("%-20d %-20d %-20s %-20u %-40s", last_query.pid, last_query.tid, - last_query.comm, last_query.size, last_query.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); - memset(&last_query, 0, sizeof(mysql_query)); +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, @@ -1155,7 +1179,8 @@ static int print_trace(void *_ctx, void *data, size_t size) { return 0; } -int attach_uprobe(struct netwatcher_bpf *skel) { +int attach_uprobe_mysql(struct netwatcher_bpf *skel) { + ATTACH_UPROBE_CHECKED( skel, _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command, query__start); @@ -1164,7 +1189,11 @@ int attach_uprobe(struct netwatcher_bpf *skel) { query__end); return 0; } - +int attach_uprobe_redis(struct netwatcher_bpf *skel) { + ATTACH_UPROBE_CHECKED(skel, call, query__start_redis); + ATTACH_UPROBE_CHECKED(skel, call, query__end_redis); + return 0; +} int main(int argc, char **argv) { char *last_slash = strrchr(argv[0], '/'); if (last_slash) { @@ -1187,6 +1216,7 @@ int main(int argc, char **argv) { 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 netwatcher_bpf *skel; int err; /* Parse command line arguments */ @@ -1222,7 +1252,16 @@ int main(int argc, char **argv) { /* Attach tracepoint handler */ if (mysql_info) { - err = attach_uprobe(skel); + 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"); @@ -1237,7 +1276,7 @@ int main(int argc, char **argv) { } enum MonitorMode mode = get_monitor_mode(); - print_logo(); + // print_logo(); print_header(mode); @@ -1297,6 +1336,13 @@ int main(int argc, char **argv) { 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; + } /* Set up ring buffer polling */ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), print_packet, NULL, NULL); if (!rb) { @@ -1318,6 +1364,7 @@ int main(int argc, char **argv) { 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 */); print_conns(skel); sleep(1); /* Ctrl-C will cause -EINTR */ diff --git a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h index de8aa5e7a..8687f3e13 100644 --- a/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/netwatcher.h @@ -180,4 +180,15 @@ typedef struct mysql_query { 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; +}; #endif /* __NETWATCHER_H */ \ No newline at end of file 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..ae4cf559a --- /dev/null +++ b/eBPF_Supermarket/Network_Subsystem/net_watcher/redis.bpf.h @@ -0,0 +1,70 @@ +// 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 4 +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); + //bpf_printk("%s",start.redis[i]); + } + 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_printk("%llu - %llu = %llu",end_time,start->begin_time,message->duratime); + 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/Stack_Analyser/Makefile b/eBPF_Supermarket/Stack_Analyser/Makefile index 83731b94e..4c357c824 100644 --- a/eBPF_Supermarket/Stack_Analyser/Makefile +++ b/eBPF_Supermarket/Stack_Analyser/Makefile @@ -45,6 +45,10 @@ 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 @@ -117,7 +121,7 @@ $(BPFTOOL): $(BPFTOOL_SRC) | $(BPFTOOL_OUTPUT) $(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) \ @@ -125,16 +129,16 @@ $(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 %,$(OUTPUT)/%.o,$(BPF)): $(OUTPUT)/%.o: src/bpf_wapper/%.cpp include/bpf_wapper/%.h $(BPF_SKEL)/%.skel.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 %,$(BPF_SKEL)/%.skel.h,$(BPF)) +$(BIN_OBJ): $(OUTPUT)/%.o: src/%.cpp $(BPF_SKEL_H) $(call msg,CXX,$@) $(Q)$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ @@ -143,7 +147,7 @@ $(OUTPUT)/eBPFStackCollector.o: src/bpf_wapper/eBPFStackCollector.cpp include/bp $(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/bpf/io.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c index 0406c510e..2469004fe 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/io.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/io.h" #include "task.h" diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/llc_stat.bpf.c index eefa368db..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" diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c index 24f2a14e7..d94b29d97 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/memleak.bpf.c @@ -21,7 +21,7 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" #include "bpf_wapper/memleak.h" @@ -31,9 +31,9 @@ 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"; diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c index 5f64815e9..80877b067 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/off_cpu.bpf.c @@ -21,13 +21,13 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "task.h" COMMON_MAPS(u32); COMMON_VALS; // 记录进程运行的起始时间 -BPF_HASH(pid_offTs_map, u32, u64); +BPF_HASH(pid_offTs_map, u32, u64, MAX_ENTRIES/10); const char LICENSE[] SEC("license") = "GPL"; diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/on_cpu.bpf.c index d70b7c23b..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"; diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c index 99f91032c..623bb5ae0 100644 --- a/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c +++ b/eBPF_Supermarket/Stack_Analyser/bpf/probe.bpf.c @@ -22,13 +22,13 @@ #include #include -#include "sa_ebpf.h" +#include "ebpf.h" #include "bpf_wapper/probe.h" #include "task.h" COMMON_MAPS(time_tuple); COMMON_VALS; -BPF_HASH(starts, u32, u64); +BPF_HASH(starts, u32, u64, MAX_ENTRIES/10); static int entry(void *ctx) { diff --git a/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c b/eBPF_Supermarket/Stack_Analyser/bpf/readahead.bpf.c index 41e984ba4..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,8 +31,8 @@ COMMON_MAPS(ra_tuple); COMMON_VALS; -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) 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/include/bpf_wapper/eBPFStackCollector.h b/eBPF_Supermarket/Stack_Analyser/include/bpf_wapper/eBPFStackCollector.h index 292225da1..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 { @@ -62,7 +62,7 @@ class StackCollector uint32_t freq = 49; uint64_t cgroup = 0; uint32_t tgid = 0; - int err = 0; // 用于保存错误代码 + int err = 0; // 用于保存错误代码 bool ustack = false; // 是否跟踪用户栈 bool kstack = false; // 是否跟踪内核栈 @@ -94,25 +94,25 @@ class StackCollector /// @brief 加载、初始化参数并打开指定类型的ebpf程序 /// @param ... 一些ebpf程序全局变量初始化语句 /// @note 失败会使上层函数返回-1 -#define EBPF_LOAD_OPEN_INIT(...) \ - { \ - skel = skel->open(NULL); \ +#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); \ + __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; \ + obj = skel->obj; \ } -#define ATTACH_PROTO \ - { \ - err = skel->attach(skel); \ +#define ATTACH_PROTO \ + { \ + err = skel->attach(skel); \ CHECK_ERR_RN1(err, "Failed to attach BPF skeleton"); \ } 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 83% rename from eBPF_Supermarket/Stack_Analyser/include/sa_common.h rename to eBPF_Supermarket/Stack_Analyser/include/common.h index 449f2f6c3..ad77f45e3 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_common.h +++ b/eBPF_Supermarket/Stack_Analyser/include/common.h @@ -27,22 +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 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/sa_ebpf.h b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h similarity index 91% rename from eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h rename to eBPF_Supermarket/Stack_Analyser/include/ebpf.h index 56ea5549d..84ee017f6 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_ebpf.h +++ b/eBPF_Supermarket/Stack_Analyser/include/ebpf.h @@ -19,7 +19,7 @@ #ifndef STACK_ANALYZER_EBPF #define STACK_ANALYZER_EBPF -#include "sa_common.h" +#include "common.h" #include #define PF_KTHREAD 0x00200000 @@ -39,13 +39,13 @@ /// @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); \ +#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") /** @@ -56,12 +56,12 @@ * 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(tgid_cgroup_map, __u32, \ - char[CONTAINER_ID_LEN]); \ - BPF_HASH(pid_info_map, u32, task_info); +#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; \ @@ -123,7 +123,7 @@ (struct task_struct *)bpf_get_current_task() // 如果没有设置目标进程,则检查被采集进程是否为内核线程,是则退出采集 -#define CHECK_KTHREAD(_task) \ +#define CHECK_KTHREAD(_task) \ if (!target_tgid && BPF_CORE_READ(_task, flags) & PF_KTHREAD) \ return 0; diff --git a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h b/eBPF_Supermarket/Stack_Analyser/include/trace.h similarity index 83% rename from eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h rename to eBPF_Supermarket/Stack_Analyser/include/trace.h index 171bc4ee2..777248f94 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/trace_helpers.h +++ b/eBPF_Supermarket/Stack_Analyser/include/trace.h @@ -4,9 +4,10 @@ #include -#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_SEC 1000000000ULL -struct ksym { +struct ksym +{ const char *name; unsigned long addr; }; @@ -16,11 +17,12 @@ 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 { +struct sym +{ const char *name; unsigned long start; unsigned long size; @@ -30,11 +32,11 @@ struct sym { struct syms; struct syms *syms__load_pid(int tgid); -struct syms *syms__load_file(const char *fname); +struct syms *syms__load_file(const char *fname, int tgid); void syms__free(struct syms *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); 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 syms_cache; @@ -42,7 +44,8 @@ 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 { +struct partition +{ char *name; unsigned int dev; }; @@ -58,7 +61,7 @@ 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); @@ -100,5 +103,11 @@ 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/sa_user.h b/eBPF_Supermarket/Stack_Analyser/include/user.h similarity index 97% rename from eBPF_Supermarket/Stack_Analyser/include/sa_user.h rename to eBPF_Supermarket/Stack_Analyser/include/user.h index b54352a6c..b46c53a76 100644 --- a/eBPF_Supermarket/Stack_Analyser/include/sa_user.h +++ b/eBPF_Supermarket/Stack_Analyser/include/user.h @@ -26,7 +26,14 @@ #include #include -#include "sa_common.h" +#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)); @@ -49,7 +56,7 @@ /// @param info 要打印的错误信息 #define CHECK_ERR(action, cond, ...) \ if (cond) \ - DEAL_ERR(action, __VA_ARGS__) + DEAL_ERR(action, __VA_ARGS__) /// @brief 检查错误,若错误成立则打印带原因的错误信息并使上层函数返回-1 /// @param cond 被检查的条件表达式 @@ -67,4 +74,17 @@ #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/src/bpf_wapper/eBPFStackCollector.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp index d3aa81547..2f99dcc35 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/eBPFStackCollector.cpp @@ -17,14 +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) { @@ -142,39 +143,36 @@ 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()) { - bpf_map_lookup_elem(trace_fd, &id.usid, trace); - for (p = trace + MAX_STACKS - 1; !*p; p--) - ; - std::vector sym_trace(p - trace + 1); - for (int i = 0; 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)) - { - if (sym.name[0] == '_' && sym.name[1] == 'Z') - // 代表是C++符号,则调用demangle解析 - sym.name = demangleCppSym(sym.name); - std::stringstream ss(""); - ss << "+0x" << std::hex << (sym.ip - sym.start); - sym.name += ss.str(); - clearSpace(sym.name); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); - } - else + std::vector sym_trace(p - trace + 1); + for (int i = 0; p >= trace; p--) { - std::stringstream ss(""); - ss << "0x" << std::hex << sym.ip; - sym.name = ss.str(); - g_symbol_parser.putin_symbol_cache(id.pid, addr, sym.name); + struct sym *sym = syms__map_addr(syms, *p); + if (sym) + { + 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); + } + else + sym_trace[i++] = "[unknown]"; } - sym_trace[i++] = sym.name; + traces[id.usid] = sym_trace; } - traces[id.usid] = sym_trace; } if (id.ksid > 0 && traces.find(id.ksid) == traces.end()) { @@ -184,22 +182,9 @@ StackCollector::operator std::string() std::vector sym_trace(p - trace + 1); for (int i = 0; p >= trace; p--) { - uint64_t &addr = *p; - symbol sym; - sym.reset(addr); - std::stringstream ss(""); - if (g_symbol_parser.find_kernel_symbol(sym)) - { - ss << "+0x" << std::hex << (sym.ip - sym.start); - sym.name += ss.str(); - clearSpace(sym.name); - } - else - { - ss << "0x" << std::hex << addr; - sym.name = ss.str(); - } - sym_trace[i++] = 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; } diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/memleak.cpp index 9a9c5ae1f..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) 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 1d9411b3d..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() { @@ -37,13 +37,11 @@ uint64_t *OffCPUStackCollector::count_values(void *data) int OffCPUStackCollector::ready(void) { EBPF_LOAD_OPEN_INIT(); - symbol sym; - sym.name = "finish_task_switch"; - if (!g_symbol_parser.complete_kernel_symbol(sym)) - { + 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; } diff --git a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp index 2fa9e0b67..6b720c6b8 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/bpf_wapper/probe.cpp @@ -17,8 +17,8 @@ // ebpf程序包装类的模板,实现接口和一些自定义方法 #include "bpf_wapper/probe.h" -#include "trace_helpers.h" -#include "uprobe_helpers.h" +#include "trace.h" +#include "uprobe.h" void splitStr(const std::string &symbol, const char split, std::vector &res) { 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 07bb4fba6..000000000 --- a/eBPF_Supermarket/Stack_Analyser/src/dt_symbol.cpp +++ /dev/null @@ -1,587 +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[23]; - 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; -} - -/// @brief 找到指定进程中的虚拟地址对应的在可执行文件中相对于文件开始的偏移,赋给sym.ip -/// @param pid 指定进程的pid -/// @param sym 提供进程虚拟地址 -/// @param file 文件信息 -/// @return 成功找到返回true -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 = "/proc/" + std::to_string(pid) + "/root/" + 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 2c15e5af6..435d380b9 100644 --- a/eBPF_Supermarket/Stack_Analyser/src/main.cpp +++ b/eBPF_Supermarket/Stack_Analyser/src/main.cpp @@ -29,15 +29,14 @@ #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 { @@ -54,89 +53,14 @@ namespace MainConfig bool trace_kernel = false; } -namespace helper -{ -#include -#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) - { - 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; - } -} - -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); - } -} - 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; { + uint64_t IntTmp; + std::string StrTmp; auto OnCpuOption = (clipp::option("on_cpu") .call([] { StackCollectorList.push_back(new OnCPUStackCollector()); }) % @@ -172,31 +96,31 @@ int main(int argc, char *argv[]) COLLECTOR_INFO("llc_stat") & ((clipp::option("-P") & clipp::value("period", IntTmp) - .call([] + .call([IntTmp] { static_cast(StackCollectorList.back()) ->setScale(IntTmp); })) % "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([] - { 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"); + .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([] - { MainConfig::target_cgroup = helper::get_cgroupid(StrTmp.c_str()); printf("Trace cgroup %ld\n", MainConfig::target_cgroup); })) % + .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_tgid)) % @@ -215,7 +139,7 @@ int main(int argc, char *argv[]) "Set the output delay time (seconds); default is 5", (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") @@ -241,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, @@ -260,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) @@ -306,6 +230,19 @@ int main(int argc, char *argv[]) } } + 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, @@ -381,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