!2 添加锁分析任务,完善死锁检测功能模块

Merge pull request !2 from mcy-kylin/master
This commit is contained in:
穆瑞超 2022-12-09 01:10:40 +00:00 committed by Gitee
commit 93fc339f87
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
40 changed files with 2513 additions and 176 deletions

View File

@ -17,16 +17,11 @@ deadlock-detect 是一款基于OpenKylin社区[kylin-code](https://gitee.com/mcy
版本要求:>=16.14.x and <17
获取地址https://nodejs.org/dist/
2. 编译view前端
```shell
cd view-ui
npm run build
cp dist detect-plugin/ -a
```
3. 编译打包插件
```shell
cd detect-plugin
yarn compile
yarn package
vsce package
```
2. 编译及安装
依赖npm yarn vsce vue
编译:在当前目录执行`./build.sh`后自动编译打包,编译完成的插件位于`detect-plugin`目录下。
安装:在当前目录执行 ```code-oss --install-extension detect-plugin/*.vsix```即可。
3. 使用方式
查看插件首页[细节内容](detect-plugin/README.md)

11
build.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
# sudo apt install sshpass
cd view-ui
npm install
npm run build
rm -rf ../detect-plugin/dist/
cp dist/ ../detect-plugin/ -r
cd ../detect-plugin
yarn
vsce package

View File

@ -0,0 +1,24 @@
{
"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"
]
}

5
detect-plugin/.vscode/extensions.json vendored Normal file
View File

@ -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"]
}

35
detect-plugin/.vscode/launch.json vendored Normal file
View File

@ -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}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js",
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "tasks: watch-tests"
}
]
}

13
detect-plugin/.vscode/settings.json vendored Normal file
View File

@ -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"
}

40
detect-plugin/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,40 @@
// 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": []
}
]
}

View File

@ -0,0 +1,12 @@
.vscode/**
.vscode-test/**
node_modules/**
src/**
.gitignore
.yarnrc
webpack.config.js
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts

1
detect-plugin/.yarnrc Normal file
View File

@ -0,0 +1 @@
--ignore-engines true

View File

@ -0,0 +1 @@
MIT

58
detect-plugin/README.md Normal file
View File

@ -0,0 +1,58 @@
# C/C++ 程序锁分析
#### [仓库](https://gitee.com/openkylin/deadlock-detect)
## 介绍
deadlock-detect 是一款基于OpenKylin社区kylin-code的拓展插件用于C/C++程序的死锁检测及锁状态分析帮助开发者在Linux系统下识别多线程死锁状态、互斥锁自旋锁等状态异常问题并对程序加锁位置分析。
---
## 功能
- C/C++ 多线程程序锁分析
- 可视化观察临界区进入次数、耗时、条件变量等待时长
- 线程退出未释放锁异常
- 可执行程序死锁检测
- 进程死锁检测
- 远程环境进程死锁检测
## 使用方法
#### 检查依赖
依赖binutils、libc-bin、sshpass软件包
安装方式```sudo apt install binutils libc-bin sshpass -y```
内核版本在4.19以上支持BPF内核选项```CONFIG_BPF_EVENTS=y```
#### C/C++ 多线程序锁分析
* 打开目标C/C++工程,点击`终端`->`配置任务` 选择`deadlockdetect: 可执行程序锁检测`
task.json文件中自动添加的锁检测任务如下
```json
{
"type": "deadlockdetect",
"command": "被检测可执行程序",
"args": [
"可执行程序参数(可选)"
],
"options": {
"env": {
"程序执行所需额外环境变量如 DISPALY" : "1"
}
},
"problemMatcher": [],
"label": "deadlockdetect: 可执行程序锁检测"
}
```
根据实际情况输入待检测程序,其中`args`,`options` 为空时可以删除。有多个检测任务时可以修改不同的label字段。
* 执行锁检测任务
1. 选择`终端`->`运行任务`->`选择相应锁检测任务`;
2. 输入当前用户密码;
3. IDE终端中会显示被检测程序相关输出;
4. 当被测程序终止时,若检测到锁信息会有在相关页面展示;
5. 也可选择在终端中右键`终止终端` 或选择菜单栏中`终端`->`终止任务`,结束被测程序执行,若检测到锁信息会有在相关页面展示。
#### 进程死锁检测
打开命令面板,选择`C/C++程序死锁检测`,默认会打开一个页面,按要求添加`进程Pid`、`用户密码`即可。
#### 远程进程死锁检测
确保远程主机及用户可以`SSH`登录 本地依赖工具`sshpass`
打开命令面板,选择`C/C++程序死锁检测`,默认会打开一个页面,按要求添加`进程Pid`、`远程用户`、`远程IP地址`、`远程登录密码`即可。

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,49 @@
#!/usr/bin/bash
ARCH=`arch`
if [ "$ARCH" == "aarch64" ];then
ARCH="arm64"
fi
DIR=`dirname $0`
BINLOADER=$DIR/${ARCH}/binloader
HIJACKLIBPATH=$DIR/${ARCH}/hijack_pthread.so
CLEANFILE=`ls /tmp/lock.info.* 2>/dev/null`
for tmpfile in ${CLEANFILE}
do
pid=`echo $tmpfile | awk -F '.' '{print $3}'`
[ -e /proc/$pid ] || rm -rf $tmpfile
done
if [ -n "`ps -ef | grep 'deadlockcheck --threadlib' | grep -v grep`" ];then
exit 100
fi
if [ $# -lt 1 ];then
# echo "Error params"
exit 101
fi
BINPATH=$1
if [ ! -e ${BINPATH} ] || [ "a" == "a${BINPATH}" ];then
# echo "No File"
exit 102
fi
if [ -z "`file ${BINPATH} | grep ELF`" ];then
# echo "Not ELF"
exit 103
fi
LIBPTHREAD_PATH=`ldd ${BINPATH} | grep pthread | awk '{print $3}'`
if [ -z "${LIBPTHREAD_PATH}" ];then
# echo "No pthread"
exit 104
fi
if [ -z $HIJACK ];then
${BINLOADER} $@
else
export LD_PRELOAD=${HIJACKLIBPATH}
${BINLOADER} $@
fi

View File

@ -0,0 +1,9 @@
#!/bin/bash
set -e
USER_DIR=$(cd $(dirname $0); cd ..; pwd)
cat $(dirname $0)/.user.pwd | sudo -S ls /proc/$1 > /dev/null
cat $(dirname $0)/.user.pwd | sudo -S $(dirname $0)/detect_deadlock $1

View File

@ -0,0 +1,48 @@
#!/usr/bin/bash
ARCH=`arch`
if [ "$ARCH" == "aarch64" ];then
ARCH="arm64"
fi
DIR=`dirname $0`
CHECKTOOL=$DIR/${ARCH}/deadlockcheck
if [ -n "`ps -ef | grep 'deadlockcheck --threadlib' | grep -v grep`" ];then
exit 99
fi
SUPPORTBPF=`grep -r "^CONFIG_BPF_EVENTS=" /boot/config-$(uname -r)`
if [ -z "$SUPPORTBPF" ];then
echo "Current Kernel not support ebpf"
exit 100
fi
if [ $# -lt 1 ];then
# echo "Error params"
exit 101
fi
while ((1 == 1))
do
if [ "$1" -gt 0 ] 2>/dev/null;then
break
else
exit 102
fi
done
if [ ! -e /proc/$1/exe ];then
echo "Process exe not exist"
exit 103
fi
LIBPTHREAD_PATH=`ldd /proc/$1/exe | grep pthread | awk '{print $3}'`
if [ -z "${LIBPTHREAD_PATH}" ];then
echo "No pthread"
exit 104
fi
BINPATH=`readlink /proc/$1/exe`
echo "/proc/$1/root${LIBPTHREAD_PATH}|${BINPATH}|$1"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,22 +2,67 @@
"name": "deadlock-detect",
"displayName": "deadlock-detect",
"description": "Deadlock detect for C/C++ program which use the posix thread library for Linux system platform.",
"version": "0.0.1",
"publisher": "kylin-code",
"version": "0.0.2",
"engines": {
"vscode": "^1.54.0"
},
"license": "SEE LICENSE IN LICENSE.txt",
"repository": {
"type": "git",
"url": "https://gitee.com/openkylin/deadlock-detect.git"
},
"categories": [
"Other"
],
"activationEvents": [
"onCommand:deadlock-detect.open"
"onCommand:deadlock-detect.open",
"onLanguage:c",
"onLanguage:cpp",
"onCommand:workbench.action.tasks.runTask"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "deadlock-detect.open",
"title": "C/C++ 死锁检测"
"title": "C/C++程序死锁检测"
},
{
"command": "deadlock-detect.analysepid",
"title": "C/C++程序进程锁分析"
}
],
"taskDefinitions": [
{
"type": "deadlockdetect",
"required": [
"command"
],
"properties": {
"command": {
"type": "string",
"description": "%deadlockdetect.taskDefinitions.command.description%"
},
"args": {
"type": "array",
"description": "%deadlockdetect.taskDefinitions.args.description%"
},
"options": {
"type": "object",
"description": "%deadlockdetect.taskDefinitions.options.description%",
"properties": {
"cwd": {
"type": "string",
"description": "%deadlockdetect.taskDefinitions.options.cwd.description%"
},
"env": {
"type": "object",
"description": "%deadlockdetect.taskDefinitions.options.env.description%"
}
}
}
}
}
]
},
@ -33,19 +78,22 @@
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.54.0",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.1",
"@types/node": "16.x",
"@types/vscode": "^1.54.0",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"@vscode/test-electron": "^2.1.5",
"eslint": "^8.20.0",
"glob": "^8.0.3",
"mocha": "^10.0.0",
"typescript": "^4.7.4",
"ts-loader": "^9.3.1",
"typescript": "^4.7.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"@vscode/test-electron": "^2.1.5"
"webpack-cli": "^4.10.0"
},
"dependencies": {
"yaml": "^2.1.3"
}
}

View File

@ -0,0 +1,7 @@
{
"deadlockdetect.taskDefinitions.command.description": "这里是要检测的可执行程序(必选) 相对路径或绝对路径",
"deadlockdetect.taskDefinitions.args.description": "这里是可执行程序的参数(可选)",
"deadlockdetect.taskDefinitions.options.description": "这里是一些可选型",
"deadlockdetect.taskDefinitions.options.cwd.description": "程序的运行目录",
"deadlockdetect.taskDefinitions.options.env.description": "程序的环境变量"
}

View File

@ -0,0 +1,162 @@
import * as vscode from 'vscode';
import * as child from 'child_process';
import {execDetect, testPwd, prase_yaml2html} from './utils';
import { dirname, join } from 'path';
import * as fs from 'fs';
const checkTool: string = join(dirname(__dirname), "detect-tools/pidcheck.sh");
var intervalObj: NodeJS.Timer;
function execCheckTool(pwd: string, checkedPid: number, cb: (code: number, da:string)=> void): child.ChildProcess {
return child.exec(`echo ${pwd} | sudo -S ${checkTool} ${checkedPid}`, (err, out, e)=>{
child.exec(`sudo -k`);
if(err?.code){
cb(err.code, "");
return;
}
if(out.length){
cb(0, out);
}
});
}
function createTerm(checkedPid:number, passwd: string): vscode.Pseudoterminal {
const writeEmitter = new vscode.EventEmitter<string>();
const closeEmitter = new vscode.EventEmitter<number>();
const pty: vscode.Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => {
writeEmitter.fire(`正在分析进程${checkedPid}锁信息,关闭终端或输入q后停止...\r\n`);
function intervalFunc(we: vscode.EventEmitter<string>) {
we.fire('...');
}
intervalObj = setInterval(intervalFunc, 1500, writeEmitter);
},
close: () => {
child.exec(`echo ${passwd} | sudo -S killall -2 deadlockcheck`);
closeEmitter.fire(0);
},
handleInput: data => {
if (data === 'q') {
writeEmitter.fire(`\r\n已停止进程${checkedPid}锁分析\r\n`);
clearInterval(intervalObj);
child.exec(`echo ${passwd} | sudo -S killall -2 deadlockcheck`);
closeEmitter.fire(0);
}
}
};
return pty;
}
function localPidAnalyse(pid: number, passwd:string, context: vscode.ExtensionContext) {
// var term = new MyCustomBuildTaskTerminal(pid);
// term.open(undefined);
execCheckTool(passwd, pid, (code, out)=>{
if(out.length === 0){
switch (code) {
case 99:
vscode.window.showWarningMessage("当前已存在检测任务,请稍后再试");
case 100:
vscode.window.showWarningMessage("当前Kernel不支持");
break;
case 101:
case 102:
vscode.window.showWarningMessage("参数错误");
break;
case 103:
vscode.window.showWarningMessage(`当前不存在${pid}进程`);
break;
case 104:
vscode.window.showWarningMessage("当前进程未引用pthread动态库");
break;
default:
vscode.window.showWarningMessage("其他未知异常");
break;
}
return;
}
let pthreadlib = out.split('|');
if(pthreadlib.length < 2){
vscode.window.showWarningMessage(`${pid}进程检测程序无法识别`);
return;
}
var pty = createTerm(pid, passwd);
vscode.window.createTerminal({name:`检测进程 ${pid}`, pty}).show();
execDetect(passwd, pthreadlib[0], pid, (out2)=>{
if(out2.length === 0){
vscode.window.showWarningMessage(`未检测到进程${pid}相关锁信息`);
return;
}
if(out2.match(/warn/)?.length){
vscode.window.showWarningMessage(`被检测进程${pid}已退出`);
pty.close();
}
if(out2.match(/fatal/)?.length){
vscode.window.showWarningMessage("被检测程序存在严重问题!");
}else if(out2.match(/deadlock/)?.length){
vscode.window.showWarningMessage("被检测程序存在死锁!");
}
if(out2.match(/normal|fatal|deadlock/)?.length){
const webPanel = vscode.window.createWebviewPanel(
'detectResultWebview',
"检测结果",
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
console.log(out2);
webPanel.webview.html = prase_yaml2html(pthreadlib[1], context.extensionPath, webPanel.webview, out2);
}else{
vscode.window.showInformationMessage("未检测到相关锁信息");
}
});
});
}
// 本地进程锁分析
export function doLocalPidLockAnalyse(context: vscode.ExtensionContext) {
vscode.window.showInputBox({
ignoreFocusOut:true, // 默认false设置为true时鼠标点击别的地方输入框不会消失
placeHolder:'请输入本地进程PID', // 在输入框内的提示信息
prompt:'请输入本地进程PID',
validateInput: function (text) {
if(!isNaN(Number(text))){
return undefined;
}
return text;
}
}).then((data)=>{
if(data === undefined || data?.length === 0)
{
return;
}
let pid = Number(data);
fs.access(`/proc/${pid}`, fs.constants.F_OK, (err)=>{
if(err){
vscode.window.showErrorMessage(`进程${pid}不存在!`);
return;
}
vscode.window.showInputBox({
password: true,
ignoreFocusOut:true, // 默认false设置为true时鼠标点击别的地方输入框不会消失
placeHolder:'请输入密码', // 在输入框内的提示信息
prompt:'请输入当前用户密码'
}).then((data)=>{
if(data === undefined || data.length === 0){
return;
}
testPwd(data, (ok)=>{
if(!ok){
vscode.window.showErrorMessage("当前用户密码错误!");
return;
}
localPidAnalyse(pid, data, context);
});
});
});
});
}

View File

@ -0,0 +1,291 @@
import * as vscode from 'vscode';
import * as child from 'child_process';
import {execDetect, archs, testPwd, prase_yaml2html} from './utils';
import { dirname, join } from 'path';
import { kill } from 'process';
import * as fs from 'fs';
type Options = {
cwd?: string;
env?: {
[name: string]: any;
};
};
var isNotSupport = false;
const arch = archs.get(process.arch);
const binLoader: string = join(dirname(__dirname), "detect-tools/bincheck_loader.sh");
const hijackLib: string = join(dirname(__dirname), "detect-tools", arch? arch : "x86_64", "hijack_pthread.so");
const hijackTool: string = join(dirname(__dirname), "detect-tools", arch? arch : "x86_64", "detectlock");
interface DetectTaskDefinition extends vscode.TaskDefinition {
command: string;
args?: string[];
options?: Options;
}
export class DetectTaskProvider implements vscode.TaskProvider {
static customBuildScriptType = 'deadlockdetect';
private tasks: vscode.Task[] | undefined;
private sharedState: string | undefined;
private cmd: child.ChildProcessWithoutNullStreams | undefined;
constructor(private workspaceRoot: string, private context: vscode.ExtensionContext, private _isNotSupport: boolean) {
isNotSupport = this._isNotSupport;
}
public async provideTasks(): Promise<vscode.Task[]> {
return this.getTasks();
}
public resolveTask(_task: vscode.Task): vscode.Task | undefined {
const command: string = _task.definition.command;
if (command) {
const definition: DetectTaskDefinition = <any>_task.definition;
return this.getTask(definition.command, definition.args ? definition.args : [], definition.options ? definition.options : {}, definition);
}
return undefined;
}
private getTasks(): vscode.Task[] {
if (this.tasks !== undefined) {
return this.tasks;
}
this.tasks = [];
var option: Options = {
};
this.tasks!.push(this.getTask("被检测可执行程序", ["可执行程序参数(可选)"], option));
return this.tasks;
}
private getTask(command: string, args: string[], option: Options, definition?: DetectTaskDefinition): vscode.Task {
if (definition === undefined) {
definition = {
type: DetectTaskProvider.customBuildScriptType,
command: command,
args: args,
options: option,
};
}
return new vscode.Task(definition, vscode.TaskScope.Workspace, "可执行程序锁检测",
DetectTaskProvider.customBuildScriptType, new vscode.CustomExecution(async (): Promise<vscode.Pseudoterminal> => {
// When the task is executed, this callback will run. Here, we setup for running the task.
return new CustomBuildTaskTerminal(this.cmd, this.context, command, args, option, () => this.sharedState, (state: string) => this.sharedState = state);
}));
}
}
class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
private writeEmitter = new vscode.EventEmitter<string>();
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
private closeEmitter = new vscode.EventEmitter<number>();
onDidClose?: vscode.Event<number> = this.closeEmitter.event;
private passwd: string | undefined;
private checkedPid: number = 0;
private hadChecked: boolean = false;
constructor(private cmd: child.ChildProcessWithoutNullStreams | undefined, private context: vscode.ExtensionContext, private command: string, private args: string[], private option: Options, private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) {
}
open(initialDimensions: vscode.TerminalDimensions | undefined): void {
// At this point we can start using the terminal.
if (this.command.length) {
let found = false;
vscode.workspace.workspaceFolders?.forEach((value)=>{
// value.uri.fsPath
if(this.command[0] === '/' && !found){
this.option.cwd = value.uri.fsPath;
found = true;
}
if(!found){
let absPath = join(value.uri.fsPath, this.command);
if(fs.existsSync(absPath)){
this.option.cwd = value.uri.fsPath;
this.command = absPath;
found = true;
}
}
});
if(!found || !fs.existsSync(this.command)){
vscode.window.showWarningMessage("未找到被检测二进制文件, 请输入相对路径或绝对路径");
this.closeEmitter.fire(0);
return;
}
// vscode.workspace.workspaceFolders
vscode.window.showInputBox({
password:true, // 输入内容是否是密码
ignoreFocusOut:true, // 默认false设置为true时鼠标点击别的地方输入框不会消失
placeHolder:'请输入用户密码', // 在输入框内的提示信息
prompt:'请输入当前用户的密码',
}).then((pwd)=>{
if(pwd === undefined || pwd?.length === 0)
{
this.closeEmitter.fire(0);
return;
}
testPwd(pwd, (ok)=>{
if(ok){
this.passwd = pwd;
this.doExec();
return;
}
vscode.window.showErrorMessage("用户密码不匹配");
this.closeEmitter.fire(0);
});
});
}else{
vscode.window.showWarningMessage("未发现被检测二进制文件");
this.closeEmitter.fire(0);
}
}
private hijackCheck(){
// 注入方式
child.exec(`${hijackTool} ${this.checkedPid}`, (err, o, e)=>{
if(o.match(/normal|deadlock/)?.length){
const webPanel = vscode.window.createWebviewPanel(
'detectResultWebview',
"检测结果",
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
webPanel.webview.html = prase_yaml2html(this.command, this.context.extensionPath, webPanel.webview, o);
return;
}
vscode.window.showInformationMessage("未检测到相关锁信息");
});
}
close(): void {
if(this.cmd){
kill(this.checkedPid, 9);
vscode.window.showWarningMessage("检测任务已退出");
if(isNotSupport && !this.hadChecked){
this.hadChecked = true;
this.hijackCheck();
}
}
}
// 开始执行待检测程序
private async doExec(): Promise<void> {
return new Promise<void>((resolve) => {
this.writeEmitter.fire('正在检测 ...\r\n');
let localEnv: NodeJS.ProcessEnv = {};
for(var key in process.env){
localEnv[key] = process.env[key];
}
if(this.option.env){
Object.keys(this.option.env).forEach(key => {
let value = this.option.env?.[key];
localEnv[key] = value;
});
}
let hadPid = false;
if(isNotSupport){
// 库注入
this.hadChecked = false;
localEnv["HIJACK"] = "true";
}
let coption: child.SpawnOptionsWithoutStdio = {
env: localEnv
};
if(this.option.cwd?.length){
coption.cwd = this.option.cwd;
}
this.cmd = child.spawn(binLoader, [this.command, ...this.args], coption);
this.cmd.on('error', (err)=>{
this.writeEmitter.fire('执行失败 ... ' + err.message);
});
this.cmd.on('exit', (code) =>{
let errmsg = "";
switch (code) {
case 100:
errmsg = "有程序正在检测请稍后...";
break;
case 101:
errmsg = "待检测可执行程序不能为空 !";
break;
case 102:
errmsg = "待检测可执行程序未找到,请确认可执行程序路径是否存在 !";
break;
case 103:
errmsg = "待检测可执行程序不是二进制文件 !";
break;
case 104:
errmsg = "待检测可执行程序未引用pthread动态库 !";
break;
default:
break;
}
if(errmsg.length){
vscode.window.showErrorMessage(errmsg);
}
if(errmsg.length === 0 && isNotSupport && !this.hadChecked){
// 注入后锁分析
this.hadChecked = true;
this.hijackCheck();
}
this.writeEmitter.fire('被检测程序已终止 ... \r\n');
const date = new Date();
this.setSharedState(date.toTimeString() + ' ' + date.toDateString());
this.closeEmitter.fire(0);
resolve();
});
this.cmd.stdout.on('data', (data) =>{
// childPid: 17558
let buff: Buffer = data;
let str: string = buff.toString('utf8');
let arrStr = str.split(/\r?\n/);
arrStr.forEach((s)=>{
this.writeEmitter.fire(s+'\r\n');
});
});
this.cmd.stderr.on('data', (data) =>{
let buff: Buffer = data;
let str: string = buff.toString('utf8');
if(!hadPid){
let result = str.split("|");
if(result.length >= 4){
hadPid = true;
this.checkedPid = parseInt(result[3].trim());
if(isNotSupport){
// 注入方式
return;
}
execDetect(this.passwd, result[1].trim(), this.checkedPid, (detectResult)=>{
if(detectResult.match(/fatal/)?.length){
kill(this.checkedPid, 9);
vscode.window.showWarningMessage("被检测程序存在严重问题!");
}else if(detectResult.match(/deadlock/)?.length){
kill(this.checkedPid, 9);
vscode.window.showWarningMessage("被检测程序存在死锁!");
}
if(detectResult.match(/normal|fatal|deadlock/)?.length){
const webPanel = vscode.window.createWebviewPanel(
'detectResultWebview',
"检测结果",
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
webPanel.webview.html = prase_yaml2html(this.command, this.context.extensionPath, webPanel.webview, detectResult);
}else{
vscode.window.showInformationMessage("程序已退出,但未检测到相关锁信息");
}
});
}
return;
}
let arrStr = str.split(/\r?\n/);
arrStr.forEach((s)=>{
this.writeEmitter.fire(s+'\r\n');
});
});
});
}
}

View File

@ -3,13 +3,26 @@
import * as vscode from 'vscode';
import path = require('path');
import fs = require("fs");
import {DetectTaskProvider} from './detectTaskProvider';
import { homedir } from 'os';
import {exec, execSync} from 'child_process';
import {testPwd} from './utils';
import {doLocalPidLockAnalyse} from './analysePid';
// linux命令操作模块
var child = require('child_process');
const { pid } = require('process');
let detectTaskProvider: vscode.Disposable | undefined;
function getWebViewContent(context: vscode.ExtensionContext, templatePath: string) {
const resourcePath = path.join(context.extensionPath, templatePath);
const resourcePath = path.join(path.dirname(__dirname), templatePath);
const dirPath = path.dirname(resourcePath);
let html = fs.readFileSync(resourcePath, 'utf-8');
// vscode不支持直接加载本地资源需要替换成其专有路径格式这里只是简单的将样式和JS的路径替换
html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => {
if($2[0] === '/'){
$2 = "." + $2;
}
return $1 + vscode.Uri.file(path.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() + '"';
});
return html;
@ -22,6 +35,125 @@ export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "deadlock-detect" is now active!');
let arch = process.arch;
if (arch == 'x64'){
arch = 'x86_64';
}
function testPwd1(cb: (ok: boolean)=>void): void {
try{
execSync(`cat ${__dirname}/../detect-tools/.user.pwd | sudo -S ls /root/`);
cb(true);
}catch (error: any) {
cb(false);
}
return;
}
function local_detect_deadlock (message: any ,panel: vscode.WebviewPanel){
let form=message.form;
let cmdout = '';
fs.writeFileSync(`${__dirname}/../detect-tools/.user.pwd`, `${form.pwd}`, { encoding: 'utf8', flag: 'w' });
if(form.CmdType===1){
testPwd1((ok)=>{
if(!ok){
vscode.window.showErrorMessage("用户密码不匹配");
return;
}
if(fs.existsSync(`/proc/${form.pid}`))
{
try{
cmdout = execSync(`cat ${__dirname}/../detect-tools/.user.pwd | sudo -S ${__dirname}/../detect-tools/${arch}/detect_deadlock ${form.pid}`).toString();
form.data =cmdout;
form.success='success';
panel.webview.postMessage({
res: form});
}catch (error: any) {
form.success='error';
panel.webview.postMessage({
res: form});
}
}else{
vscode.window.showErrorMessage(`当前不存在${form.pid}进程`);
}
});
}
fs.unlinkSync(`${__dirname}/../detect-tools/.user.pwd`);
}
function remote_clean(form: any){
execSync(`sshpass -f ${__dirname}/../detect-tools/.user.pwd ssh -o StrictHostKeyChecking=no ${form.user}@${form.ip} 'rm -rf .user.pwd detect.sh detect_deadlock'`);
fs.unlinkSync(`${__dirname}/../detect-tools/.user.pwd`);
}
function remote_detect_deadlock(message: any, panel: vscode.WebviewPanel){
let form=message.form;
let cmdout = '';
let remote_arch = '';
// sudo apt install sshpass
fs.writeFileSync(`${__dirname}/../detect-tools/.user.pwd`, `${form.pwd}`, { encoding: 'utf8', flag: 'w' });
try{
remote_arch = execSync(`sshpass -f ${__dirname}/../detect-tools/.user.pwd ssh -o StrictHostKeyChecking=no ${form.user}@${form.ip} 'uname -m'`).toString();
}catch (error: any) {
vscode.window.showErrorMessage(`${error.message}`);
fs.unlinkSync(`${__dirname}/../detect-tools/.user.pwd`);
return;
}
if (remote_arch.includes('aarch64')){
remote_arch = 'arm64';
}else if (remote_arch.includes('x64') || remote_arch.includes('x86_64')){
remote_arch = 'x86_64';
}
try{
cmdout = execSync(`sshpass -f ${__dirname}/../detect-tools/.user.pwd scp -o StrictHostKeyChecking=no ${__dirname}/../detect-tools/.user.pwd ${__dirname}/../detect-tools/detect.sh ${__dirname}/../detect-tools/${remote_arch}/detect_deadlock ${form.user}@${form.ip}:./`).toString();
}catch (error: any) {
let msg = `${error.message}`;
// console.log(`sshpass -f ${__dirname}/.user.pwd scp -o StrictHostKeyChecking=no ${__dirname}/.user.pwd ${__dirname}/../detect-tools/detect.sh ${__dirname}/../detect-tools/${arch}/detect_deadlock ${form.user}@${form.ip}:/tmp`);
if(msg.includes('Permission denied, please try again')){
vscode.window.showErrorMessage("远程主机用户名或密码错误");
}else if(msg.includes('scp:')){
vscode.window.showErrorMessage("远程主机无法拷贝文件, 请确认登录用户是否拥有权限.");
}else {
vscode.window.showErrorMessage(msg);
}
fs.unlinkSync(`${__dirname}/../detect-tools/.user.pwd`);
return;
}
cmdout = '';
try{
cmdout = execSync(`sshpass -f ${__dirname}/../detect-tools/.user.pwd ssh -o StrictHostKeyChecking=no ${form.user}@${form.ip} 'bash detect.sh ${form.pid};'`).toString();
}catch (error: any) {
let msg = `${error.message}`;
// console.log(`sshpass -f ${__dirname}/.user.pwd ssh -o StrictHostKeyChecking=no ${form.user}@${form.ip} '[ ! -d .deadlock-detect ] && mkdir .deadlock-detect || ls .deadlock-detect > /dev/null; mv /tmp/detect.sh .deadlock-detect; bash .deadlock-detect/detect.sh ${form.pid}'`);
if(msg.includes('ls:')){
vscode.window.showErrorMessage(`远程主机不存在${form.pid}进程`);
}else if(msg.includes('Permission denied, please try again')){
vscode.window.showErrorMessage("远程主机用户名或密码错误");
}else {
console.log(msg);
vscode.window.showErrorMessage(`远程主机无法路由,或其他未知错误`);
form.success='error';
panel.webview.postMessage({
res: form});
}
remote_clean(form);
return;
}
{
form.data = cmdout;
form.success='success';
panel.webview.postMessage({
res: form});
}
remote_clean(form);
}
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
@ -35,14 +167,72 @@ export function activate(context: vscode.ExtensionContext) {
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: false,
retainContextWhenHidden: true,
}
);
panel.webview.html = getWebViewContent(context, "src/dist/index.html");
panel.webview.onDidReceiveMessage(message => {
switch(message.form.CmdType) {
case 1://local pid dead-detect
local_detect_deadlock(message,panel);
break;
case 3://remote pid dead-detect
remote_detect_deadlock(message,panel);
break;
}
});
panel.webview.html = getWebViewContent(context, 'dist/index.html');
});
// 进程锁分析
exec(`which sshpass`, (e, o, err)=>{
if(e){
vscode.window.showWarningMessage('当前环境缺失sshpass工具,C/C++程序远程死锁检测功能受限,请执行 sudo apt install sshpass 安装.');
return;
}
});
exec(`uname -r`, (e, o, err)=>{
if(e){
vscode.window.showWarningMessage("检测环境失败, C/C++程序锁分析插件功能受限");
return;
}
var kernelOptions: string[] = [
"CONFIG_UPROBES=y",
"CONFIG_UPROBE_EVENTS=y",
"CONFIG_TRACEPOINTS=y",
"CONFIG_PERF_EVENTS=y",
"CONFIG_BPF_EVENTS=y"
];
var isNotSupport = false;
o = o.replace("\n", "");
fs.readFile(`/boot/config-${o}`, function (err, buff) {
if(err){
isNotSupport = true;
}else{
kernelOptions.forEach(elem=>{
if(!buff.includes(elem)){
isNotSupport = true;
}
});
}
if(!isNotSupport){
context.subscriptions.push(vscode.commands.registerCommand('deadlock-detect.analysepid',()=>{
doLocalPidLockAnalyse(context);
}));
}else{
context.subscriptions.push(vscode.commands.registerCommand('deadlock-detect.analysepid',()=>{
vscode.window.showWarningMessage("由于内核限制, 当前不支持对进程锁分析");
}));
}
const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) ? vscode.workspace.workspaceFolders[0].uri.fsPath : homedir();
detectTaskProvider = vscode.tasks.registerTaskProvider(DetectTaskProvider.customBuildScriptType, new DetectTaskProvider(workspaceRoot, context, isNotSupport));
});
});
context.subscriptions.push(disposable);
}
// this method is called when your extension is deactivated
export function deactivate() {}
export function deactivate() {
if(detectTaskProvider){
detectTaskProvider.dispose();
}
}

421
detect-plugin/src/utils.ts Normal file
View File

@ -0,0 +1,421 @@
import * as child from 'child_process';
import * as vscode from 'vscode';
import { dirname, join } from 'path';
import * as YAML from 'yaml';
export const archs = new Map([
["x86_64", "x86_64"],
["x64", "x86_64"],
["arm64", "arm64"],
["aarch64", "arm64"]
]);
const myArch = archs.get(process.arch);
const detectTool: string = join(dirname(__dirname), "detect-tools", myArch ? myArch: "x86_64", "deadlockcheck");
export function execDetect(pwd: string | undefined, libPthreadPath: string, checkedPid: number, cb: (da:string)=> void):child.ChildProcess {
var task = child.exec(`echo ${pwd} | sudo -S ${detectTool} --threadlib ${libPthreadPath} --pid ${checkedPid}`, (err, out, e)=>{
if(err?.message){
vscode.window.showErrorMessage('附加到被检测程序失败, '+err.message);
return;
}
// if(out.length){
cb(out);
// }
child.exec(`sudo -k`);
});
return task;
}
export function testPwd(pwd:string, cb: (ok: boolean)=>void): void {
if(pwd?.length){
child.exec(`echo ${pwd} | sudo -S echo okok`, (err, stdout, stderr)=>{
if(stdout.includes('okok')){
child.exec(`sudo -k`, (e1, stdout1, stderr3)=>{
cb(true);
});
return;
}
cb(false);
});
}
}
function circledNodesPosition(x:number, y: number, index: number, nodesLen: number) {
const avd = 360 / nodesLen;
const ahd = avd * Math.PI / 180;
return {
x: Math.sin((ahd * index)) * 15 + x,
y: Math.cos((ahd * index)) * 15 + y
};
}
// warn: "process exit"
// normalInfo:
// mutex:
// - pid: 12619
// details:
// 0x7f6a92f4c990: 5-7673-0
// 0x7f6a92f4c968: 1-3764-0
// 0x7ffc74e6ab98: 1-7411-0
// totalTime: 18848
// condWaitTime: 0
// - pid: 12618
// details:
// 0x7f6a92f4c990: 6-10005-0
// 0x7f6a92f4c968: 1-95491-0
// 0x7ffc74e6ab98: 2-43556-8000086897
// totalTime: 149052
// condWaitTime: 8000086897
// - pid: 12560
// details:
// 0x7f6a92f4c968: 2-36323-0
// totalTime: 36323
// condWaitTime: 0
// 输出死锁时的信息
function product_deadlockinfo_to_html(cmd: string, echartsPath: string, deadlockDesc: string, nodesInfo: object[], linksInfo: object[]) {
let _nodesInfo = JSON.stringify(nodesInfo);
let _linksInfo = JSON.stringify(linksInfo);
return `
<!DOCTYPE html>
<html lang="zh-CN" style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="deadlockinfo" style="width:60%;margin:auto">
<h3>
</h3>
<p>${cmd}</p>
<h3>
</h3>
<ul>
${deadlockDesc}
</ul>
</div>
<br><br>
<div id="container" style="height: 70%;width:60%;margin:auto"></div>
<script type="text/javascript" src="${echartsPath}"></script>
<script type="text/javascript">
var dom = document.getElementById('container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
option = {
title: {
text: '死锁关系图'
},
tooltip: {},
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
roam: true,
symbolSize: 70,
label: {
show: true
},
edgeSymbol: ['none', 'arrow'],
edgeSymbolSize: [4, 10],
edgeLabel: {
normal: {
show: true,
textStyle: {
fontSize: 14
},
formatter: "{c}"
}
},
data: ${_nodesInfo},
links: ${_linksInfo},
lineStyle: {
opacity: 0.9,
width: 2,
curveness: 0
}
}
]
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
window.addEventListener('resize', myChart.resize);
</script>
</body>
</html>
`;
}
// 输入正常锁信息的html
function product_normalinfo_to_html(cmd: string, echartsPath: string, mutexInfo:string, dimensionsList: string[], sourceList: object[], seriesList: object[]) {
let _dimensionsList = JSON.stringify(dimensionsList);
let _sourceList = JSON.stringify(sourceList);
let _seriesList = JSON.stringify(seriesList);
return `
<!DOCTYPE html>
<html lang="zh-CN" style="height: 55%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<br><br>
<div style="width:60%;margin:auto">
<h3>
</h3>
<p>${cmd}</p>
<h3>线</h3>
<ul>
${mutexInfo}
</ul>
</div>
<br><br><br>
<div id="container" style="height: 100%;width:60%;margin:auto"></div>
<script type="text/javascript" src="${echartsPath}"></script>
<script type="text/javascript">
var dom = document.getElementById('container');
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
var app = {};
var option;
option = {
title: {
text: '临界区耗时'
},
legend: {},
tooltip: {},
dataset: {
dimensions: ${_dimensionsList},
source: ${_sourceList}
},
xAxis: { type: 'category' },
yAxis: {},
// Declare several bar series, each will be mapped
// to a column of dataset.source by default.
series: ${_seriesList}
};
if (option && typeof option === 'object') {
myChart.setOption(option);
}
window.addEventListener('resize', myChart.resize);
</script>
</body>
</html>
`;
}
// 线程退出但是未释放锁
// fatal:
// - pid: 23773
// mutexes:
// - 0x7ffd08d622d8
// 输出线程退出但是未释放锁的html
function product_fatalinfo_to_html(cmd: string, fatalInfo:string) {
return `
<!DOCTYPE html>
<html lang="zh-CN" style="height: 55%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<br><br>
<div style="height: 100%;width:60%;margin:auto">
<h3>
</h3>
<p>${cmd}</p>
<h3></h3>
<ul>
${fatalInfo}
</ul>
</div>
</body>
</html>
`;
}
// 发生死锁
// deadlock:
// cycle:
// - 0x7ffdd4f33b50
// - 0x7ffdd4f33b28
// threadid:
// - 23446
// - 23445
interface DetailInfo {
[key:string]: string
}
interface MutexInfo{
pid: number,
details: DetailInfo,
totalTime: number,
condWaitTime: number
}
interface DeadlockInfo{
cycle: number [],
threadid: number[]
}
interface FatalInfo{
pid: number,
mutexes: number[]
}
interface SourceInfo {
[key:string]: any
};
function parse_mutexInfo(mutexObj:MutexInfo[], sinfoList:SourceInfo[], dimensionsList:string[], seriesList: object[]):string {
let miStr: string = ``;
let mutexSet = new Set<string>();
mutexObj.forEach(obj =>{
let sinfo:SourceInfo = {};
sinfo['product'] = `线程 ${obj.pid}`;
if(obj.condWaitTime){
miStr += `
<li>线 ${obj.pid} ${obj.totalTime/1000000} ${obj.condWaitTime/1000000} .
<ul>
`;
}else{
miStr += `
<li>线 ${obj.pid} ${obj.totalTime/1000000} .
<ul>
`;
}
for (const key1 in obj.details) {
let info = obj.details[key1].split('-');
if(info.length >= 3){
let key = '互斥量 0x'+Number(key1).toString(16);
sinfo[key] = Number(info[1])/1000000;
mutexSet.add(key);
if(Number(info[2])){
miStr += `
<li>
(${key}) ${info[0]} , ${sinfo[key]} , ${Number(info[2])/1000000} .
</li>
`;
}else{
miStr += `
<li>
(${key}) ${info[0]} , ${sinfo[key]} .
</li>
`;
}
}
}
miStr += `
</ul>
</li>
`;
sinfoList.push(sinfo);
});
dimensionsList.push('product');
mutexSet.forEach((mutex)=>{
dimensionsList.push(mutex);
});
for (let index = 0; index < dimensionsList.length-1; index++) {
seriesList.push({type:'bar'});
}
return miStr;
}
function parse_fatalInfo(fatalInfoList:FatalInfo[]) {
let fatalStr =``;
fatalInfoList.forEach(fatal=>{
fatalStr += `
<li> 线 ${fatal.pid} 退,
<ul>
`;
let tmp = '';
fatal.mutexes.forEach(m=>{
tmp = '0x' + m.toString(16);
fatalStr += `<li> ${tmp} </li>`;
})
fatalStr += `
</ul>
</li>
`;
})
return fatalStr;
}
// <li>
// 线程 1005 已经拥有互斥量(0x2342fff), 继续申请互斥量(0x2342fffff)
// </li>
// <li>
// 线程 1002 已经拥有互斥量(0x2342fff), 继续申请互斥量(0x2342fffff)
// </li>
// <li>
// 线程 1001 已经拥有互斥量(0x2342fff), 继续申请互斥量(0x2342fffff)
// </li>
function parse_deadlockInfo(dlInfo:DeadlockInfo, dataList: object[], linksList: object[]) {
let miStr = ``;
if(dlInfo.cycle.length != dlInfo.threadid.length)
{
return miStr;
}
dlInfo.threadid.forEach((value, index)=>{
if(dlInfo.cycle[index]){
let tmp1 = '0x'+dlInfo.cycle[index].toString(16);
let tmp2 = '0x'+dlInfo.cycle[(index+1)%dlInfo.cycle.length].toString(16);
let val = `线程 ${value} 已经拥有互斥量(${tmp1}), 继续申请互斥量(${tmp2})`;
miStr += `
<li>
${val}.
</li>`;
let pos = circledNodesPosition(300, 300, index, dlInfo.cycle.length);
dataList.push({
name: `互斥量 \n(${tmp1})`,
x: pos.x,
y: pos.y,
});
linksList.push({
source: index,
target: (index+1)%dlInfo.cycle.length,
value: `线程 ${value}`,
lineStyle: {
curveness: -0.2
}
});
}
});
return miStr;
}
export function prase_yaml2html(cmd: string, uri: string, webView: vscode.Webview, str: string) {
let ymlObj = YAML.parse(str);
const diskPath = vscode.Uri.file(join(uri, 'static', 'echarts.min.js'));
const scriptUri = diskPath.with({ scheme: 'vscode-resource' }).toString();
if(ymlObj.normalInfo?.mutex){
let sinfoList:SourceInfo[] = [];
let dimensionsList:string[] = [];
let seriesList: object[] = [];
let miStr = parse_mutexInfo(ymlObj.normalInfo.mutex, sinfoList, dimensionsList, seriesList);
return product_normalinfo_to_html(cmd, scriptUri, miStr, dimensionsList, sinfoList, seriesList);
}
if(ymlObj.fatal){
return product_fatalinfo_to_html(cmd, parse_fatalInfo(ymlObj.fatal));
}
if(ymlObj.deadlock){
let dataList: object[] = [];
let linksList: object[] = [];
let dlStr = parse_deadlockInfo(ymlObj.deadlock, dataList, linksList);
return product_deadlockinfo_to_html(cmd, scriptUri, dlStr, dataList, linksList);
}
return ``;
}

45
detect-plugin/static/echarts.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -171,7 +171,7 @@
resolved "https://registry.npmmirror.com/@types/node/-/node-16.11.58.tgz#0a3698dee3492617a8d5fe7998d18d7520b63026"
integrity sha512-uMVxJ111wpHzkx/vshZFb6Qni3BOMnlWLq7q9jrwej7Yw/KvjsEbpxCCxw+hLKxexFMc8YmpG8J9tnEe/rKsIg==
"@types/vscode@^1.71.0":
"@types/vscode@^1.54.0":
version "1.71.0"
resolved "https://registry.npmmirror.com/@types/vscode/-/vscode-1.71.0.tgz#a8d9bb7aca49b0455060e6eb978711b510bdd2e2"
integrity sha512-nB50bBC9H/x2CpwW9FzRRRDrTZ7G0/POttJojvN/LiVfzTGfLyQIje1L1QRMdFXK9G41k5UJN/1B9S4of7CSzA==
@ -2024,6 +2024,11 @@ yallist@^4.0.0:
resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^2.1.3:
version "2.1.3"
resolved "https://registry.npmmirror.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207"
integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==
yargs-parser@20.2.4:
version "20.2.4"
resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"

View File

@ -12,10 +12,12 @@
"echart": "^0.1.3",
"echarts": "^5.3.3",
"element-ui": "^2.15.9",
"vscode": "^1.1.37",
"vue": "2.6.14"
},
"devDependencies": {
"@types/node": "^16.11.25",
"@types/vscode-webview": "^1.57.0",
"@vitejs/plugin-legacy": "^1.7.1",
"@vue/runtime-dom": "^3.2.31",
"typescript": "~4.7.3",
@ -802,6 +804,14 @@
"node": ">= 8.0.0"
}
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"engines": {
"node": ">= 6"
}
},
"node_modules/@types/node": {
"version": "16.11.56",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.56.tgz",
@ -814,6 +824,12 @@
"integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==",
"dev": true
},
"node_modules/@types/vscode-webview": {
"version": "1.57.0",
"resolved": "https://registry.npmmirror.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz",
"integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==",
"dev": true
},
"node_modules/@vitejs/plugin-legacy": {
"version": "1.8.2",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-1.8.2.tgz",
@ -1194,6 +1210,17 @@
"node": ">= 0.6"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
@ -1254,12 +1281,31 @@
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
},
"node_modules/browserslist": {
"version": "4.21.3",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.3.tgz",
@ -1278,6 +1324,11 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/cache-content-type": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/cache-content-type/-/cache-content-type-1.0.1.tgz",
@ -1343,6 +1394,16 @@
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"node_modules/commander": {
"version": "2.15.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/consolidate": {
"version": "0.16.0",
"resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.16.0.tgz",
@ -1489,6 +1550,14 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/diff": {
"version": "3.5.0",
"resolved": "https://registry.npmmirror.com/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
@ -1628,6 +1697,19 @@
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
},
"node_modules/es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"node_modules/es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
"dependencies": {
"es6-promise": "^4.0.3"
}
},
"node_modules/esbuild": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz",
@ -2002,7 +2084,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
@ -2036,6 +2117,11 @@
"node": ">=10"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
@ -2065,6 +2151,22 @@
"node": ">=6.9.0"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
"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"
},
"engines": {
"node": "*"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
@ -2080,6 +2182,14 @@
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmmirror.com/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"engines": {
"node": ">=4.x"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
@ -2096,7 +2206,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
@ -2191,6 +2300,40 @@
"node": ">= 0.6"
}
},
"node_modules/http-proxy-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
"dependencies": {
"@tootallnate/once": "1",
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
@ -2428,6 +2571,118 @@
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q=="
},
"node_modules/mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==",
"deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
"dependencies": {
"minimist": "0.0.8"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/mocha/-/mocha-5.2.0.tgz",
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
"dependencies": {
"browser-stdout": "1.3.1",
"commander": "2.15.1",
"debug": "3.1.0",
"diff": "3.5.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.2",
"growl": "1.10.5",
"he": "1.1.1",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "5.4.0"
},
"bin": {
"_mocha": "bin/_mocha",
"mocha": "bin/mocha"
},
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/mocha/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/mocha/node_modules/glob": {
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"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"
},
"engines": {
"node": "*"
}
},
"node_modules/mocha/node_modules/he": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/he/-/he-1.1.1.tgz",
"integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==",
"bin": {
"he": "bin/he"
}
},
"node_modules/mocha/node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/mocha/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/mocha/node_modules/supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@ -2475,6 +2730,14 @@
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/only": {
"version": "0.0.2",
"resolved": "https://registry.npmmirror.com/only/-/only-0.0.2.tgz",
@ -2488,6 +2751,14 @@
"node": ">= 0.8"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
@ -2645,7 +2916,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -2659,6 +2929,15 @@
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@ -2973,6 +3252,96 @@
"node": ">= 8"
}
},
"node_modules/vscode": {
"version": "1.1.37",
"resolved": "https://registry.npmmirror.com/vscode/-/vscode-1.1.37.tgz",
"integrity": "sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg==",
"deprecated": "This package is deprecated in favor of @types/vscode and vscode-test. For more information please read: https://code.visualstudio.com/updates/v1_36#_splitting-vscode-package-into-typesvscode-and-vscodetest",
"dependencies": {
"glob": "^7.1.2",
"http-proxy-agent": "^4.0.1",
"https-proxy-agent": "^5.0.0",
"mocha": "^5.2.0",
"semver": "^5.4.1",
"source-map-support": "^0.5.0",
"vscode-test": "^0.4.1"
},
"bin": {
"vscode-install": "bin/install"
},
"engines": {
"node": ">=8.9.3"
}
},
"node_modules/vscode-test": {
"version": "0.4.3",
"resolved": "https://registry.npmmirror.com/vscode-test/-/vscode-test-0.4.3.tgz",
"integrity": "sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==",
"deprecated": "This package has been renamed to @vscode/test-electron, please update to the new name",
"dependencies": {
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1"
},
"engines": {
"node": ">=8.9.3"
}
},
"node_modules/vscode-test/node_modules/agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dependencies": {
"es6-promisify": "^5.0.0"
},
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/vscode-test/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/vscode-test/node_modules/http-proxy-agent": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
"integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
"dependencies": {
"agent-base": "4",
"debug": "3.1.0"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/vscode-test/node_modules/https-proxy-agent": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dependencies": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/vscode-test/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/vscode/node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/vue": {
"version": "2.6.14",
"resolved": "https://registry.npmmirror.com/vue/-/vue-2.6.14.tgz",
@ -3041,6 +3410,11 @@
"integrity": "sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA==",
"dev": true
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz",
@ -3625,6 +3999,11 @@
"picomatch": "^2.2.2"
}
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
},
"@types/node": {
"version": "16.11.56",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.11.56.tgz",
@ -3637,6 +4016,12 @@
"integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==",
"dev": true
},
"@types/vscode-webview": {
"version": "1.57.0",
"resolved": "https://registry.npmmirror.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz",
"integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==",
"dev": true
},
"@vitejs/plugin-legacy": {
"version": "1.8.2",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-legacy/-/plugin-legacy-1.8.2.tgz",
@ -3974,6 +4359,14 @@
"negotiator": "0.6.3"
}
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"requires": {
"debug": "4"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz",
@ -4028,12 +4421,31 @@
}
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
},
"browserslist": {
"version": "4.21.3",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.3.tgz",
@ -4046,6 +4458,11 @@
"update-browserslist-db": "^1.0.5"
}
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"cache-content-type": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/cache-content-type/-/cache-content-type-1.0.1.tgz",
@ -4098,6 +4515,16 @@
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"commander": {
"version": "2.15.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"consolidate": {
"version": "0.16.0",
"resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.16.0.tgz",
@ -4209,6 +4636,11 @@
"resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmmirror.com/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
"dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz",
@ -4335,6 +4767,19 @@
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
},
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
"requires": {
"es6-promise": "^4.0.3"
}
},
"esbuild": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz",
@ -4518,8 +4963,7 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"estree-walker": {
"version": "2.0.2",
@ -4544,6 +4988,11 @@
"universalify": "^2.0.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
@ -4563,6 +5012,19 @@
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"requires": {
"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"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
@ -4575,6 +5037,11 @@
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmmirror.com/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
@ -4587,8 +5054,7 @@
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"has-symbols": {
"version": "1.0.3",
@ -4661,6 +5127,34 @@
}
}
},
"http-proxy-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
"integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
"requires": {
"@tootallnate/once": "1",
"agent-base": "6",
"debug": "4"
}
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"requires": {
"agent-base": "6",
"debug": "4"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
@ -4856,6 +5350,94 @@
"mime-db": "1.52.0"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q=="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==",
"requires": {
"minimist": "0.0.8"
}
},
"mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/mocha/-/mocha-5.2.0.tgz",
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
"requires": {
"browser-stdout": "1.3.1",
"commander": "2.15.1",
"debug": "3.1.0",
"diff": "3.5.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.2",
"growl": "1.10.5",
"he": "1.1.1",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "5.4.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"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"
}
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/he/-/he-1.1.1.tgz",
"integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@ -4891,6 +5473,14 @@
"ee-first": "1.1.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"only": {
"version": "0.0.2",
"resolved": "https://registry.npmmirror.com/only/-/only-0.0.2.tgz",
@ -4901,6 +5491,11 @@
"resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
@ -5023,8 +5618,7 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.0.2",
@ -5032,6 +5626,15 @@
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@ -5246,6 +5849,77 @@
}
}
},
"vscode": {
"version": "1.1.37",
"resolved": "https://registry.npmmirror.com/vscode/-/vscode-1.1.37.tgz",
"integrity": "sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg==",
"requires": {
"glob": "^7.1.2",
"http-proxy-agent": "^4.0.1",
"https-proxy-agent": "^5.0.0",
"mocha": "^5.2.0",
"semver": "^5.4.1",
"source-map-support": "^0.5.0",
"vscode-test": "^0.4.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"vscode-test": {
"version": "0.4.3",
"resolved": "https://registry.npmmirror.com/vscode-test/-/vscode-test-0.4.3.tgz",
"integrity": "sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==",
"requires": {
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1"
},
"dependencies": {
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"requires": {
"es6-promisify": "^5.0.0"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"http-proxy-agent": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
"integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
"requires": {
"agent-base": "4",
"debug": "3.1.0"
}
},
"https-proxy-agent": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}
}
},
"vue": {
"version": "2.6.14",
"resolved": "https://registry.npmmirror.com/vue/-/vue-2.6.14.tgz",
@ -5302,6 +5976,11 @@
"integrity": "sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA==",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz",

View File

@ -12,6 +12,7 @@
"echart": "^0.1.3",
"echarts": "^5.3.3",
"element-ui": "^2.15.9",
"vscode": "^1.1.37",
"vue": "2.6.14"
},
"devDependencies": {
@ -24,6 +25,7 @@
"vite-plugin-vue2": "^1.9.3",
"vue-template-babel-compiler": "^1.2.0",
"vue-template-compiler": "2.6.14",
"vue-tsc": "^0.37.3"
"vue-tsc": "^0.37.3",
"@types/vscode-webview": "^1.57.0"
}
}

View File

@ -1,28 +1,29 @@
<template>
<div id="app">
<template>
<el-tabs v-model="activeName" @tab-click="handleClick" style="width: 80%;margin: auto;">
<el-tabs v-model="activeName" @tab-click="handleClick" type="border-card" style="width: 80%;margin: auto;">
<el-tab-pane label="本地检测" name="first">
<div style="width: 60%;margin:auto">
<br />
<el-radio v-model="radio" label="1">检测指定进程</el-radio>
<el-radio v-model="radio" label="2">检测可执行程序</el-radio>
<div id="pid" v-if="radio == '1'">
<div id="pid">
<br />
<el-input v-model="local_pid_input" placeholder="请输入被检测进程PID"></el-input>
</div>
<div id="comm" v-if="radio == '2'">
<br />
<el-input v-model="local_cmd_input" placeholder="请输入被检测程序命令行"></el-input>
<br /><br />
<el-input v-model="local_env_input" placeholder="请输入被检测程序环境变量"></el-input>
</div>
<br />
<el-input placeholder="请输入管理员密码" v-model="local_passwd_input" show-password></el-input>
<el-input placeholder="请输入用户密码" v-model="local_passwd_input" show-password></el-input>
<br /> <br />
<el-button type="primary" v-on:click="begin_local_check(1,radio)">开始检测</el-button>
<el-button type="primary" v-on:click="begin_local_check()">开始检测</el-button>
</div>
<br />
<el-card class="box-card" style="width: 80%;margin: auto;">
<div slot="header" class="clearfix" v-if="loading == false">
<span>{{ local_thread_data }}</span>
</div>
<div v-if="loading == false && pid == false" v-for="o in 4" :key="o" class="text item">
{{'线程信息: ' + o }}
</div>
</el-card>
<el-card id="main_local" style="width: 80%; height: 400px; margin: auto;"></el-card >
</el-tab-pane>
<el-tab-pane label="远程检测" name="second">
@ -36,20 +37,17 @@
<br /> <br />
<el-button type="primary" @click="begin_remote_check()">开始检测</el-button>
</div>
<br />
<el-card class="box-card" style="width: 80%;margin: auto;">
<div>
<span>线程死锁关系</span>
<br>
<span>{{ remote_thread_data }}</span>
</div>
</el-card>
<el-card id="main_remote" style="width: 80%; height: 400px; margin: auto;"></el-card>
</el-tab-pane>
</el-tabs>
<el-card v-loading="loading" element-loading-text="正在检测..." class="box-card" style="width: 80%;margin: auto;">
<div slot="header" class="clearfix" v-if="loading == false">
<span>检测结果</span>
</div>
<div v-if="loading== false" v-for="o in 4" :key="o" class="text item">
{{'线程信息: ' + o }}
</div>
</el-card>
<el-card id="main" style="width: 80%; height: 400px; margin: auto;">
</el-card >
</template>
</div>
</template>
@ -101,137 +99,323 @@ function TableData () {
export default {
data() {
return {
local_pid_input: -1,
local_pid_input: '',
local_passwd_input: '',
local_cmd_input: '',
local_env_input: '',
remote_account_input: '',
remote_passwd_input: '',
remote_addr_input: '',
remote_pid_input: -1,
radio: '1',
remote_pid_input: '',
loading: true,
pid: false,
afterchecked: false,
deadlockgraph: {},
deadlockinfo: [],
lockinfo:[]
lockinfo:[],
activeName: "first",
local_thread_data: '',
remote_thread_data: '',
thread_info: []
};
},
mounted() {
this.chartDraw();
this.listener();
this.messageListen();
},
methods:{
begin_local_check(isLocal, isPid){
let td = new TableData();
if(isLocal){
}else if(!isLocal){
td.CmdType = cmd_type.REMOTE_PID;
td.Pid = remote_pid_input;
td.Addr = remote_addr_input;
td.User = remote_
}
this.$vscode.postMessage(data);
messageListen() {
window.addEventListener("message", (event) => {
if (event.data.res.CmdType == 1) {
if(event.data.res.success=='success') {
this.drawDeadGraph(event.data.res.data);
}
// else if (event.data.res.success=='error') {
// this.$message({
// showClose: true,
// message:"local-deadlock-detect command exec failed!",
// type: event.data.res.success=='success'?'success':'error'
// });
// }
// else {
// this.$message({
// showClose: true,
// message: event.data.res.msg,
// type: event.data.res.success=='success'?'success':'error'
// });
// }
}
else if (event.data.res.CmdType == 3) {
if(event.data.res.success=='success'){
this.drawDeadGraph(event.data.res.data);
}
// else if (event.data.res.success=='error') {
// this.$message({
// showClose: true,
// message:"local-deadlock-detect command exec failed!",
// type: event.data.res.success=='success'?'success':'error'
// });
// }
// else {
// this.$message({
// showClose: true,
// message: event.data.res.msg,
// type: event.data.res.success=='success'?'success':'error'
// });
// }
}
})
},
chartDraw() {
var myChart = echarts.init(document.getElementById('main'));
var option;
option = {
title: {
text: '死锁关系图'
},
tooltip: {},
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
symbolSize: 50,
roam: false,
label: {
show: true
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
edgeLabel: {
fontSize: 20
},
data: [
{
name: 'Node 1',
x: 300,
y: 300
},
{
name: 'Node 2',
x: 800,
y: 300
},
{
name: 'Node 3',
x: 550,
y: 100
},
{
name: 'Node 4',
x: 550,
y: 500
}
],
// links: [],
links: [
{
source: 0,
target: 1,
symbolSize: [5, 20],
label: {
show: true
},
lineStyle: {
width: 5,
curveness: 0.2
}
},
{
source: 'Node 2',
target: 'Node 1',
label: {
show: true
},
lineStyle: {
curveness: 0.2
}
},
{
source: 'Node 1',
target: 'Node 3'
},
{
source: 'Node 2',
target: 'Node 3'
},
{
source: 'Node 2',
target: 'Node 4'
},
{
source: 'Node 1',
target: 'Node 4'
}
],
lineStyle: {
opacity: 0.9,
width: 2,
curveness: 0
begin_local_check(){
let td = new TableData();
let local_form = {};
let re = this.local_info_verify(this.local_pid_input, this.local_passwd_input);
if(re == '-1')
return;
local_form.pid = this.local_pid_input;
local_form.pwd = this.local_passwd_input;
local_form.CmdType = cmd_type.LOCAL_PID;
this.$vscode.postMessage({form: local_form});
},
begin_remote_check(){
let remote_form = {};
let re = this.remote_info_verify(this.remote_pid_input, this.remote_passwd_input, this.remote_account_input, this.remote_addr_input);
if(re == '-1')
return;
remote_form.pid = this.remote_pid_input;
remote_form.pwd = this.remote_passwd_input;
remote_form.CmdType = cmd_type.REMOTE_PID;
remote_form.user = this.remote_account_input;
remote_form.ip = this.remote_addr_input;
this.$vscode.postMessage({form: remote_form});
},
local_info_verify(pid, pwd) {
if(!pid){
this.$message({
showClose: true,
message:'请输入pid',
type:'warning',
});
return '-1';
}
if(!pwd){
this.$message({
showClose: true,
message:'请输入用户密码',
type:'warning',
});
return '-1';
}
return '0';
},
remote_info_verify(pid, pwd, user, ip) {
if(!pid){
this.$message({
showClose: true,
message:'请输入pid',
type:'warning',
});
return '-1';
}
if(!pwd){
this.$message({
showClose: true,
message:'请输入用户密码',
type:'warning',
});
return '-1';
}
if(!user){
this.$message({
showClose: true,
message:'请输入远程用户账号!',
type:'warning',
});
return '-1';
}
if(!ip){
this.$message({
showClose: true,
message:'请输入远程IP地址!',
type:'warning',
});
return '-1';
}
return '0';
},
lockInfoInit(data){
if (this.activeName == "first")
this.local_thread_data = '';
if (this.activeName == "second")
this.remote_thread_data = '';
this.thread_info = [];
let sub=data.split(/\n/);
console.log(sub);
this.pid = true;
this.loading = false;
sub.forEach(item=>{
if(/LWP/g.test(item)){
let obj={};
obj.threadID=item.split(/LWP /)[1].split(/\)/)[0];
obj.lockID=item.split(/\{/)[1].split(/\}/)[0];
obj.ownID=item.split(/LWP /)[2].split(/\)/)[0];
this.thread_info.push(obj);
if (this.activeName == "first") {
this.local_thread_data = `${this.local_thread_data}线程${obj.threadID}等待锁${obj.lockID},锁${obj.lockID}被线程${obj.ownID}拥有;\n`;
console.log(this.local_thread_data);
} else {
this.remote_thread_data = `${this.remote_thread_data}线程${obj.threadID}等待锁${obj.lockID},锁${obj.lockID}被线程${obj.ownID}拥有;`;
console.log(this.remote_thread_data);
}
}
]
};
option && myChart.setOption(option);
}
});
},
drawDeadGraph(data){
if(/没有发现死锁/g.test(data)){
let myChart = echarts.init(document.getElementById(this.activeName == "first" ? "main_local" : "main_remote"));
myChart.setOption({}, true);
myChart.clear();
if (this.activeName == "first") {
this.local_thread_data = '';
} else {
this.remote_thread_data = '';
}
this.$message({
showClose: true,
message:'没有发现死锁!',
type:'warning',
});
return '-1';
}
this.lockInfoInit(data);
let echartsData=this.getdeadgraphData(this.thread_info);
this.deadgraph(echartsData);
},
getdeadgraphData(lockInfo){
let data=[];
let createdNodeArr=[];
let threadPos={};
let lockPos={};
let thPosX=30;
let thPosY=30;
let linkDataArr=[];
lockInfo.forEach((item,index)=>{
thPosX+=50;
let obj={
name: item.threadID,
// x: threadPos[item.threadID],
x: thPosX,
y: thPosY,
symbol:'rect',
};
data.push(obj);
createdNodeArr.push(item.threadID);
if(index==lockInfo.length-1){
thPosY=0;
}else{
thPosY+=50;
}
if(!lockPos[item.ownID]){
lockPos[item.ownID]=thPosY;
}else{
lockPos[item.ownID]+=50;
lockPos[item.ownID]+=thPosY;
}
obj={
name: '锁'+item.lockID,
// x: threadPos[item.threadID],
x:thPosX,
// y: lockPos[item.ownID],
y:thPosY,
symbol:'circle',
};
data.push(obj);
createdNodeArr.push('锁'+item.lockID);
let linkObj={
source: item.threadID,
target: '锁'+item.lockID,
label: {
show: true,
formatter:'等待'
},
lineStyle: {
curveness: 0.2
}
}
let linkObj1={
source: item.ownID,
target: '锁'+item.lockID,
label: {
show: true,
formatter:'拥有'
},
lineStyle: {
curveness: 0.2
}
}
linkDataArr.push(linkObj,linkObj1);
})
let returnData={
data:data,
linkDataArr:linkDataArr
};
return returnData;
}
,
deadgraph(echartsData) {
let myChart = echarts.init(document.getElementById(this.activeName == "first" ? "main_local" : "main_remote"));
let option = {
title: {
text: '死锁关系图'
},
tooltip: {},
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
symbolSize: 50,
roam: true,
label: {
show: true
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
itemStyle: {
color: '#bbb',
show: true
},
edgeLabel: {
fontSize: 20
},
data: echartsData.data,
links: echartsData.linkDataArr,
lineStyle: {
opacity: 0.9,
width: 1,
curveness: 0.2
}
}
]
};
myChart.setOption(option, true);
}
}
};
</script>
</script>

View File

@ -4,9 +4,11 @@ import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
const vscode = acquireVsCodeApi()
Vue.prototype.$vscode = vscode
new Vue({
el: '#app',
render: h => h(App)