commit
93fc339f87
21
README.md
21
README.md
|
@ -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)
|
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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"]
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
--ignore-engines true
|
|
@ -0,0 +1 @@
|
|||
MIT
|
|
@ -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.
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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": "程序的环境变量"
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ``;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
<el-input placeholder="请输入用户密码" v-model="local_passwd_input" show-password></el-input>
|
||||
<br /> <br />
|
||||
<el-input v-model="local_env_input" placeholder="请输入被检测程序环境变量"></el-input>
|
||||
<el-button type="primary" v-on:click="begin_local_check()">开始检测</el-button>
|
||||
</div>
|
||||
<br />
|
||||
<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-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,43 +99,285 @@ 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_
|
||||
messageListen() {
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.data.res.CmdType == 1) {
|
||||
if(event.data.res.success=='success') {
|
||||
this.drawDeadGraph(event.data.res.data);
|
||||
}
|
||||
this.$vscode.postMessage(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 = {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
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: '死锁关系图'
|
||||
},
|
||||
|
@ -149,89 +389,33 @@ export default {
|
|||
type: 'graph',
|
||||
layout: 'none',
|
||||
symbolSize: 50,
|
||||
roam: false,
|
||||
roam: true,
|
||||
label: {
|
||||
show: true
|
||||
},
|
||||
edgeSymbol: ['circle', 'arrow'],
|
||||
edgeSymbolSize: [4, 10],
|
||||
itemStyle: {
|
||||
color: '#bbb',
|
||||
show: true
|
||||
},
|
||||
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'
|
||||
}
|
||||
],
|
||||
data: echartsData.data,
|
||||
|
||||
links: echartsData.linkDataArr,
|
||||
|
||||
lineStyle: {
|
||||
opacity: 0.9,
|
||||
width: 2,
|
||||
curveness: 0
|
||||
width: 1,
|
||||
curveness: 0.2
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
option && myChart.setOption(option);
|
||||
myChart.setOption(option, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue