VS Code作為宇宙第一編輯器,在眾多插件的加持下,具有了調(diào)試、單元測(cè)試等等功能,使其越來越像一個(gè)IDE。
然而很多人其實(shí)并不會(huì)使用VS Code的調(diào)試功能,只是把VS Code當(dāng)做了一個(gè)帶有語(yǔ)法補(bǔ)全的編輯器。這實(shí)際上極大地浪費(fèi)了VS Code的功能,尤其是對(duì)于C/C++開發(fā)者來說,使用命令行的GDB調(diào)試遠(yuǎn)不如使用VS Code內(nèi)嵌的GDB圖形化界面調(diào)試來的舒服。
本文就講介紹如何使用VS Code調(diào)試C/C++項(xiàng)目
1. 概述通常我們?cè)谡{(diào)試一個(gè)C/C++工程的時(shí)候,大體上的流程可以分為兩步:
例如對(duì)于一個(gè)CMake
組織的C/C++項(xiàng)目,這兩大步具體包含的流程如下(編寫CMakeLists.txt
是在編碼階段,編碼是與調(diào)試獨(dú)立的階段):
build
文件夾:mkdir -p build
build
文件夾:cd build
cmake ..
make
gdb
對(duì)于不同的項(xiàng)目(npm
項(xiàng)目、C#
項(xiàng)目、java
項(xiàng)目等等),可能在啟動(dòng)調(diào)試器前的準(zhǔn)備工作不同,但是大體上都可以分為進(jìn)行調(diào)試前需要進(jìn)行的一系列任務(wù),以及結(jié)合具體參數(shù)啟動(dòng)時(shí)調(diào)試器
因此,對(duì)于這兩個(gè)階段,VSCode中提供了tasks.json
和launch.json
兩個(gè)文件來分別描述調(diào)試前的準(zhǔn)備工作
以及以指定的參數(shù)啟動(dòng)調(diào)試器
VSCode使用tasks.json
來描述啟動(dòng)調(diào)試前的準(zhǔn)備工作。
tasks.json
的結(jié)構(gòu)一般如下
{"version": "2.0.0",
"tasks": [
],
"inputs": [
]
}
version
標(biāo)簽指定了Tasks.json
的版本,因?yàn)椴煌陌姹局С值臉?biāo)簽不一樣,所以需要使用version
標(biāo)簽指明版本。
目前version
支持2.0.0
版本,所以直接指定version
為2.0.0
即可。
tasks
標(biāo)簽是一個(gè)列表,我們?cè)谄渲卸x不同的task
,而關(guān)于具體的task
如何定義則見下
我們以創(chuàng)建build
文件夾這個(gè)任務(wù)為例
{"label": "create dir",
"type": "shell",
"command": "mkdir",
"args": [
"-p",
"build"
],
"windows": {"args": [
"-Force",
"build"
],
"options": {"shell": {"executable": "powershell.exe"
}
},
}
}
1) label標(biāo)簽label
標(biāo)簽定義了一個(gè)任務(wù)的名字,稍后我們能用通過名字取定位一個(gè)任務(wù),從而實(shí)現(xiàn)諸如將多個(gè)任務(wù)合并為一個(gè)組,而后執(zhí)行一組任務(wù)這樣的操作。
label
標(biāo)簽的值是隨我們自己喜歡,想寫什么就寫什么的。
type
標(biāo)簽指定了一個(gè)任務(wù)的類型。所有的任務(wù)大致上可以分為兩類:
Shell
中執(zhí)行的命令,值為shell
MySQL
數(shù)據(jù)庫(kù)的程序,那么就需要在調(diào)試前啟動(dòng)MySQL
數(shù)據(jù)庫(kù),則此時(shí)MySQL
數(shù)據(jù)庫(kù)就是進(jìn)程形式的任務(wù)。進(jìn)程形式的任務(wù)的值為process
command
標(biāo)簽指定了需要執(zhí)行的命令或者程序。
Shell
中的命令的話,那么command
的值為需要執(zhí)行的命令。command
的值為需要執(zhí)行的可執(zhí)行程序的位置,可執(zhí)行程序可以是有x權(quán)限的.sh
,也可以是.exe
等可執(zhí)行程序。args
標(biāo)簽指定了執(zhí)行的命令或者程序時(shí)傳入的命令行參數(shù)。在具體執(zhí)行的時(shí)候會(huì)把多個(gè)參數(shù)用空格連接起來而后執(zhí)行。
結(jié)合command
標(biāo)簽,我們執(zhí)行的命令就是下面這句話
mkdir -p build
5 ) windows標(biāo)簽windows
標(biāo)簽指定了只有在windows
系統(tǒng)上的配置。我們?cè)?code>windows標(biāo)簽中指定了兩個(gè)標(biāo)簽options
標(biāo)簽和args
標(biāo)簽。
對(duì)于args
標(biāo)簽就意味著在其他系統(tǒng)(Linux
/MacOS
)上,使用-p build
作為命令行參數(shù),而在Windows
系統(tǒng)上,使用-Force build
作為命令行參數(shù)。
這是因?yàn)樵?code>Linux/MacOS
系統(tǒng)上,創(chuàng)建一個(gè)文件夾使用下面的命令就行了
mkdir -p 文件夾名
但是在Windows
平臺(tái)上,創(chuàng)建一個(gè)文件夾需要使用下面的命令
mkdir -Force 文件夾名
對(duì)于options
標(biāo)簽就意味著只有在Windows
平臺(tái)上才會(huì)有這個(gè)標(biāo)簽。
options
標(biāo)簽指明了運(yùn)行命令的shell
的位置(shell
標(biāo)簽)、運(yùn)行命令的環(huán)境變量(env
標(biāo)簽)以及運(yùn)行命令的文件夾(cwd
標(biāo)簽)。當(dāng)然這里只用了shell
這一個(gè)標(biāo)簽。
使用shell
標(biāo)簽的原因是因?yàn)樵?code>Windows上有兩個(gè)命令行,一個(gè)是cmd
一個(gè)是powershell
。而mkdir
這個(gè)命令是在powershell
中的,因此我們需要特殊指明在Windwos
上需要使用powershell.exe
作為Shell的解釋器
input
標(biāo)簽用于生成一個(gè)選項(xiàng)卡,接收用戶的輸入,一般是和args
標(biāo)簽一起使用我們稍后再講解這個(gè)標(biāo)簽的用法。
launch.json
的結(jié)構(gòu)一般如下
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "gdb",
"request": "launch",
"name": "GDB",
"program": "${workspaceFolder}/${command:AskForProgramName}",
"stopOnEntry": true,
"arguments": "",
"preLaunchTask": ""
}
]
}
launch.json
中的version
標(biāo)簽和tasks.json
中的version
標(biāo)簽作用是一樣的,一般都用0.2.0
。
configuration
標(biāo)簽中定義了開始啟動(dòng)調(diào)試器時(shí)候的具體的配置信息。具體來說,可以有多套配置信息。即configuration
標(biāo)簽下可以有多個(gè)條目。
name
標(biāo)簽定義了一套配置信息的名稱,這個(gè)名稱稍后可以在左邊的運(yùn)行與調(diào)試
頁(yè)面中看到。
type
標(biāo)簽指定了調(diào)試時(shí)啟動(dòng)的調(diào)試器:
對(duì)于C/C++
項(xiàng)目來說,type
的值指定為cppdbg
或者是cppvsdbg
Windows
上開發(fā)一般用的編譯器都是Visual Studio
中自帶的msvc
編譯器,適用的調(diào)試器也是Visual Studio
自帶的,此時(shí)就需要把值設(shè)為cppvsdbg
上用的一般都是
gcc,
MacOS上用的編譯器一般都是
clang,對(duì)應(yīng)的調(diào)試器分別是
gdb和
lldb,此時(shí)需要把值設(shè)為
cppdbg對(duì)于Python
項(xiàng)目來說,type
的值指定為python
,因?yàn)?code>python解釋器自帶了pdb
這個(gè)調(diào)試器
…
剩下的具體查詢手冊(cè):https://code.visualstudio.com/docs
3 ) request標(biāo)簽request
標(biāo)簽指明了調(diào)試器調(diào)試程序的方式。具體來說有兩種:
launch
:表示調(diào)試器直接啟動(dòng)程序進(jìn)行調(diào)試,類似于使用命令gdb helloworld
,將會(huì)直接運(yùn)行命令helloworld
attach
:有時(shí)候,我們需要調(diào)試的程序運(yùn)行在遠(yuǎn)程服務(wù)器上,此時(shí)在服務(wù)器上已經(jīng)運(yùn)行了一個(gè)gdb
,而且服務(wù)器上的gdb
把調(diào)試服務(wù)暴露在某一個(gè)端口上,此時(shí)我們?cè)诒緳C(jī)上運(yùn)行gdb
的時(shí)候,通過鏈接遠(yuǎn)程服務(wù)器該端口,從而實(shí)現(xiàn)用本地的gdb
調(diào)試遠(yuǎn)程服務(wù)器上的程序。此時(shí),遠(yuǎn)程服務(wù)器上的gdb
稱為gdb server
。這種調(diào)試方式稱為attach
,即把調(diào)試器附加到一個(gè)gdb server
上去。一般在本機(jī)做調(diào)試的時(shí)候值都是launch
。
program
標(biāo)簽指定了我們需要調(diào)試的程序。注意,如果request
標(biāo)簽的值是attach
的話,那么就不能使用program
標(biāo)簽。
CMake
中有EXECUTABLE_OUTPUT_PATH
宏,我們可以指定EXECUTABLE_OUTPUT_PATH
宏的值從而指定可執(zhí)行文件輸出的路徑,也可以通過${}
來讀取EXECUTABLE_OUTPUT_PATH
宏的值來打印到屏幕上或者用于為其他宏賦值。
類似的,VSCode
中也有功能類似的宏,workspaceFolder
這個(gè)宏就表示了當(dāng)前打開的目錄。我們也可以使用${}
來獲取這個(gè)宏的值。
command:AskForProgramName
這個(gè)宏的作用就是在程序運(yùn)行的時(shí)候在上面彈出來一個(gè)選項(xiàng)卡,詢問用戶需要調(diào)試的程序的名字。
例如我們直接對(duì)著launch.json
這個(gè)程序按下F5
,然后就會(huì)彈出來一個(gè)選項(xiàng)卡讓我們輸入需要調(diào)試的程序的名字
stopAtEntry
標(biāo)簽表示在進(jìn)入到主程序之后就會(huì)停下來,對(duì)于C/C++
來說就是在進(jìn)入main
之后就停下來。
但是一般我們都是打上斷點(diǎn),然后直接運(yùn)行到斷點(diǎn)處,所以這個(gè)stopAtEntry
的值一般用的都是false
。
這個(gè)標(biāo)簽我沒用過,所以我也搞不清楚,如果要傳參的給程序的話,用args
標(biāo)簽
preLaunchTask
標(biāo)簽可以說是最重要的標(biāo)簽之一,它溝通了launch.json
和tasks.json
這兩個(gè)文件。
前面我們?cè)?code>tasks.json中定義了一系列任務(wù),而launch.json
中的這個(gè)標(biāo)簽說明了在啟動(dòng)調(diào)試器前需要執(zhí)行的tasks.json
中的那個(gè)任務(wù)。
所以利用這個(gè)標(biāo)簽,我們就可以實(shí)現(xiàn)從調(diào)試前的準(zhǔn)備工作再到啟動(dòng)調(diào)試器這一連串的任務(wù)。
4. 一個(gè)Toy Example: echo 宏下面展示一個(gè)Toy Example來展示tasks.json
和launch.json
的workflow
Toy Example中tasks.json
的內(nèi)容如下
{"version": "2.0.0",
"tasks": [
{"label": "example",
"command": "echo",
"args": [
"${file}\n",
"${fileBasename}\n",
"${fileBasenameNoExtension}\n",
"${fileDirname}\n",
]
}
]
}
具體來說我們就是想要執(zhí)行一下下面的命令
echo "${file}\n" "${fileBasename}\n" "${fileBasenameNoExtension}\n" "${fileDirname}\n"
主要是看一看這四個(gè)宏的值分別是什么
B. launch.json的內(nèi)容Toy Example中lauch.json
的內(nèi)容如下
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "gdb",
"request": "launch",
"name": "Toy Example",
"program": "${workspaceFolder}/${file}",
"stopOnEntry": true,
"preLaunchTask": "example"
}
]
}
具體來說就是在啟動(dòng)調(diào)試器之前運(yùn)行一下上面定義的example
這個(gè)task。
我們接下來寫一個(gè)hello_world.c
,里面的內(nèi)容如下:
#includeint main(int argc, char *argv[]){for (int i = 0; i< argc; i++)
printf("%s\n", argv[i]);
printf("Hello World!\n");
return 0;
}
D. 開始調(diào)試首先在運(yùn)行和調(diào)試
界面把調(diào)試的配置選定為Toy Example
,然后編輯器打開hello_world.c
接下來按F5
開始調(diào)試
此時(shí)我們?cè)诮K端就能夠看到執(zhí)行的任務(wù)以及輸出
很清楚就能看到,上面四個(gè)宏的值分別是
${file} : /Users/jack/project/test/vscode_test/hello_world.c
${fileBasename} : hello_world.c
${fileBasenameNoExtension} : hello_world
${fileDirname} : /Users/jack/project/test/vscode_test
5. 一個(gè)Toy Example:編譯文件我們對(duì)上面的Toy Example進(jìn)行修改,增加一個(gè)自動(dòng)編譯的功能
A. tasks.json的內(nèi)容我們給tasks.json
新加一個(gè)task,即自動(dòng)編譯,此外我們修改一下輸出宏的task
{"version": "2.0.0",
"tasks": [
{"label": "echo",
"command": "echo",
"args": [
"${file}\n",
"${pathSeparator}\n",
"${fileBasenameNoExtension}\n",
"${fileDirname}${pathSeparator}${fileBasenameNoExtension}.o\n"
]
},
{"label": "build",
"command": "gcc",
"args": [
"${file}",
"-o",
"${fileDirname}${pathSeparator}${fileBasenameNoExtension}.o"
]
}
]
}
B. launch.json的內(nèi)容我們?cè)俳olaunch.json中新加一個(gè)配置信息
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "gdb",
"request": "launch",
"name": "Echo Macros",
"program": "${workspaceFolder}/${file}",
"stopOnEntry": true,
"preLaunchTask": "echo"
},
{"type": "gdb",
"request": "launch",
"name": "Gcc Compile",
"program": "${workspaceFolder}${pathSeparator}${fileBasenameNoExtension}.o",
"stopAtEntry": false,
"preLaunchTask": "build"
}
]
}
C. 運(yùn)行Echo Macros首先輸出一下在build
這個(gè)task中使用到的宏。
具體來說在運(yùn)行和調(diào)試
界面選擇配置為Echo Macros
然后按下F5
開始運(yùn)行
可以看到,上面四個(gè)宏的值是
${file} : /Users/jack/project/test/vscode_test/hello_world.c
${pathSeparator} : /
${fileBasenameNoExtension} : hello_world
${fileDirname}${pathSeparator}${fileBasenameNoExtension}.o : /Users/jack/project/test/vscode_test/hello_world.o
D. 運(yùn)行Gcc Compile接下來我們運(yùn)行Gcc Compile
,類似的,還是先在運(yùn)行和調(diào)試
界面選擇Gcc Compile
,然后按下F5
開始運(yùn)行
而后我們就會(huì)發(fā)現(xiàn)在文件夾下就出現(xiàn)了編譯后的文件
我們上面做到了編譯程序,而在編譯之后我們需要干的就是去調(diào)試這個(gè)程序。
首先需要明白的是,我們?nèi)绻胍褂?code>gdb、lldb
等調(diào)試器去調(diào)試一個(gè)程序的時(shí)候,我們必須要在編譯的時(shí)候指定-g
參數(shù),這樣編譯器(例如gcc
、clang
)在編譯的時(shí)候就會(huì)把源代碼、符號(hào)表等等信息寫入到程序里面去。
而后在調(diào)試的時(shí)候,我們使用命令gdb xxxx
/lldb xxxx
,gdb
/lldb
就回去讀取源代碼和符號(hào)表,從而開始調(diào)試。
我們首先新增加一個(gè)名為debug_build
的task,具體來說就是在編譯的時(shí)候加上-g
參數(shù)
{"version": "2.0.0",
"tasks": [
{"label": "debug_build",
"command": "gcc",
"args": [
"${file}",
"-g",
"-o",
"${fileDirname}${pathSeparator}${fileBasenameNoExtension}.o"
]
}
]
}
B. launch.json的內(nèi)容為了要進(jìn)行debug,我們?cè)趌aunch.json中新加入一項(xiàng),這一項(xiàng)可能會(huì)有些復(fù)雜
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "cppdbg",
"request": "launch",
"name": "LLDB Debug",
"program": "${workspaceFolder}${pathSeparator}${fileBasenameNoExtension}.o",
"stopAtEntry": false,
"preLaunchTask": "debug_build",
"cwd": "${workspaceFolder}",
"MIMode": "lldb"
},
]
}
詳細(xì)的解釋如下:
"type":"cppdbg"
:新加入的這一項(xiàng)的類型是cppdgb
,表示C/C++ Debug
。因?yàn)槲覀冃绿砑拥倪\(yùn)行配置的目的就是給C/C++
程序Debug,所以我們讓這一項(xiàng)的類型是cppdgb
。如果我們是別的項(xiàng)目的話,例如是node.js
的項(xiàng)目的話,那么我們讓這個(gè)運(yùn)行配置的type
是node
即可"cwd:"${workspaceFolder}"
:因?yàn)樵陂_始調(diào)試的時(shí)候我們需要在指定的文件夾下運(yùn)行調(diào)試器,所以就需要使用cwd
標(biāo)簽指定工作目錄,一般制定成項(xiàng)目的根目錄,也就是workspaceFolder
就行了"MIMode":"lldb"
:不同的系統(tǒng)上使用的調(diào)試器不同,MacOS
、Linux
、Windows
使用的調(diào)試器分別是lldb
、gdb
、msvc
/gdb
(msvc
是Visual Studio
帶的調(diào)試器,gdb
是MinGW
帶的調(diào)試器),所以我們需要使用MIMode
標(biāo)簽指定使用的調(diào)試器的類型。此外,我們其實(shí)還可以使用miDebuggerArgs
和miDebuggerPath
來專門制定調(diào)用調(diào)試器時(shí)候傳入的參數(shù)以及調(diào)試器的路徑。
因?yàn)槲覍戇@篇博客時(shí)候用的是Mac
,所以用的調(diào)試器就是LLDB
我們給前面的程序加上一個(gè)斷點(diǎn),然后選擇運(yùn)行配置為LLDB Debug
,然后按下F5
開始調(diào)試。
接下來我們就進(jìn)入了調(diào)試頁(yè)面:
我們上面調(diào)試了一個(gè)程序。但是在現(xiàn)實(shí)中,我們往往在調(diào)試前是需要順序執(zhí)行多個(gè)命令的,而不是簡(jiǎn)單的編譯。
我們接下來給出的Toy Example在啟動(dòng)調(diào)試前就將順序執(zhí)行兩步命令:
bin
文件夾bin
文件夾中我們?cè)?code>tasks.json中創(chuàng)建下面的兩個(gè)任務(wù)
{"version": "2.0.0",
"tasks": [
{"label": "create_bin",
"type": "shell",
"command": "mkdir",
"args": [
"-p",
"${workspaceFolder}${pathSeparator}/bin"
]
},
{"label": "debug_build",
"type": "shell",
"command": "gcc",
"group": "build",
"args": [
"${file}",
"-g",
"-o",
"${fileDirname}${pathSeparator}bin${pathSeparator}${fileBasenameNoExtension}.o"
],
"dependsOn": "create_bin",
},
],
}
create_bin
任務(wù)就是老三樣,沒啥好說的。
關(guān)鍵就在于修改之后的debug_build
任務(wù),debug_build
任務(wù)中新增加了一個(gè)dependsOn
標(biāo)簽,這個(gè)標(biāo)簽說明了在運(yùn)行debug_build
任務(wù)之前需要運(yùn)行的任務(wù)。
在這里就表示在運(yùn)行debug_build
任務(wù)之前,需要運(yùn)行create_bin
任務(wù)。
launch.json
中的內(nèi)容保持不變,還是LLDB Debug
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "cppdbg",
"request": "launch",
"name": "LLDB Debug",
"program": "${workspaceFolder}${pathSeparator}bin${pathSeparator}${fileBasenameNoExtension}.o",
"stopAtEntry": false,
"preLaunchTask": "debug_build",
"cwd": "${workspaceFolder}",
"MIMode": "lldb",
},
]
}
C. 運(yùn)行LLDB Debug運(yùn)行LLDB Debug
的結(jié)果如下,可以發(fā)現(xiàn)首先bin
文件被創(chuàng)建了,接著可執(zhí)行文件輸出到了bin
文件夾中,而后開始debug
我們上面講了四個(gè)Toy Example,介紹了VSCode的tasks.json
和launch.json
最基本的功能,接下來我們就把這些功能結(jié)合到一起,用VSCode調(diào)試一個(gè)真實(shí)的CMake
工程。
下面這個(gè)工程的目的就是編譯就是一個(gè)名為Wish
的自己寫的shell
腳本的項(xiàng)目,編譯完成后將在本機(jī)得到一個(gè)可以運(yùn)行的shell
CMake
工程的結(jié)構(gòu)如下
tree ./
./
├── CMakeLists.txt
├── main.c
├── wish.c
└── wish.h
0 directories, 4 files
項(xiàng)目的源文件一共有四個(gè),其中:
CMakeLists.txt
定義了項(xiàng)目結(jié)構(gòu)wish.c
和wish.h
定義了libwish
靜態(tài)庫(kù)main.c
調(diào)用了libwish
庫(kù)CMakeLists.txt
中的內(nèi)容如下:
project(WISH)
cmake_minimum_required(VERSION 3.9)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(
libwish STATIC
wish.c
)
add_executable(
wish
main.c
)
target_link_libraries(wish libwish)
2 ) wish.h 和 wish.c// wish.h
#ifndef _WISH_H
#include#include#include#include#include#include#include#include#include#include#define _WISH_H 1
#define WISH_EXIT_SUCCESS 0
#define WISH_EXIT_FAILURE -1
#define WISH_BUF_SIZE 1024
#define WISH_MY_SEARCH 1
#define WISH_BUILTIN_NUM sizeof(builtin_str) / sizeof(char *)
#define WISH_MAX_WORD 20
#define WISH_MAX_FNAME 1024
#define WISH_MAX_PATH 128
#define WISH_DEBUG 1
// Base Functions
void wish_loop(void); // main loop of wish
char *wish_read_line(void); // read user input line
char **wish_split_line(char *line); // split user input line into words
int wish_redirection(char *args[]); // parse user input token to
int wish_execute(char *args[], int rarg_sht); // execute user input command
int wish_launch(char *args[], int rarg_sht); // launch other program
char *wish_search(char *cmd); // search program in PATH
void wish_error(); // report error
void wish_line(int);
int wish_cd(char **args);
int wish_exit(char **args);
int wish_path(char **args);
int wish_help(char **args);
int wish_env(char **args);
#endif
// wish.c
#include "wish.h"
// path
char *path[WISH_MAX_PATH] = {[0] = "/bin"
};
// Shell Builtin Funtions
char *builtin_str[] = {"cd",
"exit",
"path",
"help",
"wenv"
};
int (*builtin_func[])(char **) = {&wish_cd,
&wish_exit,
&wish_path,
&wish_help,
&wish_env
};
void wish_loop(void){char *line;
char **args;
bool status;
do
{printf("wish>");
line = wish_read_line();
args = wish_split_line(line);
int i = -1;
if ((i = wish_redirection(args)) != -1){args[i++] = 0;
}
status = wish_execute(args, i);
free(line);
for (int i = 0; i< WISH_MAX_WORD; i++)
if (NULL != args[i])
free(args[i]);
free(args);
} while (status);
if (!status)
exit(WISH_EXIT_FAILURE);
return;
}
char *wish_read_line(void){int position = 0;
int bufsize = WISH_BUF_SIZE;
char * buffer = (char *) malloc(sizeof(char) * bufsize);
if(NULL == buffer){fprintf(stderr, "wish: Memory allocation failed for read line.\n");
exit(WISH_EXIT_FAILURE);
}
char c;
while (true)
{// read a char
c = getchar();
if (c == EOF || c == '\n'){buffer[position++] = '\0';
return buffer;
} else
buffer[position++] = c;
// resize buffer
if (position >= bufsize){bufsize += WISH_BUF_SIZE;
char * temp = (char *) malloc(sizeof(char) * bufsize);
if(NULL == temp){fprintf(stderr, "wish: Memory allocation failed for read line.\n");
exit(EXIT_FAILURE);
}
// copy and reset pointer to new buffer
int num = position;
while (num >0){temp[num] = buffer[num];
num--;
}
free(buffer);
buffer = temp;
}
}
}
char **wish_split_line(char *line){char **words = (char **) malloc(sizeof(char *) * WISH_MAX_WORD);
for (int i = 0; i< WISH_MAX_WORD; i++)
words[i] = (char *)0;
int j = 0, k = 0;
int len = strlen(line);
char *temp = (char *) malloc(sizeof(char) * len);
for (int i = 0; i< len + 1; i++){temp[j] = line[i];
if (temp[j] == ' ' || temp[j] == '\t' || temp[j] == '\0'){temp[j] = '\0';
words[k] = (char *) malloc(sizeof(char) * (i + 1));
strncpy(words[k], temp, i);
words[k][i] = '\0';
j = 0, k += 1;
} else
j += 1;
}
free(temp);
return words;
}
int wish_redirection(char **args){int i = 0;
while (args[i] != 0)
{if (args[i][0] == '>')
return i;
i += 1;
}
return -1;
}
int wish_execute(char *args[], int rarg_sht){if (NULL == args[0])
return 1;
// run builtin command
for (int i = 0; i< WISH_BUILTIN_NUM; i++)
if (strcmp(args[0], builtin_str[i]) == 0)
return (*builtin_func[i])(args);
return wish_launch(args, rarg_sht);
}
int wish_launch(char *args[], int rarg_sht){// search path
int i = 0;
char *executable_path = (char *) malloc(sizeof(char) * WISH_MAX_FNAME);
for (int j = 0; j< WISH_MAX_FNAME; j++)
executable_path[j] = '\0';
char * temp_path = (char *) malloc(sizeof(char) * WISH_MAX_FNAME);
while (path[i] != NULL) {// copy path[i] to temp and then concate
if (strncpy(temp_path, path[i], strlen(path[i])) == NULL){wish_error();
wish_line(__LINE__);
return WISH_EXIT_FAILURE;
}
int len = strlen(temp_path);
temp_path[len] = '/';
temp_path[len + 1] = '\0';
if (strcat(temp_path, args[0]) == NULL){wish_error();
wish_line(__LINE__);
return WISH_EXIT_FAILURE;
}
// check privilege
if(access(temp_path, X_OK) == 0){if (strcpy(executable_path, temp_path) == NULL){wish_error();
wish_line(__LINE__);
return WISH_EXIT_FAILURE;
}
break;
}
i++;
}
free(temp_path);
// print error if not found
if (executable_path[0] == '\0'){free(executable_path);
wish_error();
wish_line(__LINE__);
return EXIT_FAILURE;
}
int status;
pid_t son_pid, wait_pid;
son_pid = fork();
if (son_pid == 0){// child process
// redirection
if (-1 != rarg_sht && NULL != args[rarg_sht]){// get real path
char rp[WISH_MAX_FNAME];
realpath(args[rarg_sht], rp);
if (NULL == freopen(rp, "w", stdout))
fprintf(stderr, "wish: redirection file %s open fail!\n", rp);
}
// run cmd
int (*func)();
if (WISH_MY_SEARCH == 1)
func = execvp;
else
func = execv;
if (func(args[0], args) == -1){// wish_error();
// wish_line(__LINE__);
return 1;
}
// if run the following code, then it is wrong
exit(WISH_EXIT_FAILURE);
} else if (son_pid< 0)
perror("wish: son process create fail by fork");
else {do {wait_pid = waitpid(son_pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return true;
}
char *wish_search(char *cmd){return (char *)0;
}
void wish_error(){char *err_msg = "An error has occurred\n";
write(STDERR_FILENO, err_msg, strlen(err_msg));
}
void wish_line(int lineno){#ifdef WISH_DEBUG
fprintf(stderr, "in line %d\n", lineno);
#endif
}
int wish_cd(char *args[]){if (NULL == args[1])
wish_error();
else
if (chdir(args[1]) != 0)
wish_error();
return 1;
}
int wish_exit(char *args[]){if (NULL == args[1])
exit(0);
wish_error();
return 1;
}
int wish_path(char *args[]){int i = 0;
while ((path[i] = args[i+1]) != NULL)
i++;
return 1;
}
int wish_env(char *args[]){if (NULL == args[1])
return 1;
else {char * env = getenv(args[1]);
printf("%s:\n", args[1]);
printf("%s\n", env);
}
return 1;
}
int wish_help(char *args[]){printf("WISH written by Shihong Wang.\n");
printf("Usage: command argument [enter]\n");
printf("Builtin commands:\n");
for (int i = 0; i< WISH_BUILTIN_NUM; i++)
printf("\t%s", builtin_str[i]);
printf("\nRefer man page of other command.\n");
return 1;
}
3 ) main.c#include "wish.h"
extern char **environ;
int main(int argc, char *argv[])
{if (argc == 1)
{// command loop mode
wish_loop();
}
else
{// read-parse-execute mode
FILE *file;
if (NULL == (file = fopen(argv[1], "r")))
{fprintf(stderr, "wish: read-parse mode %s file not exists!\n", argv[1]);
exit(WISH_EXIT_FAILURE);
}
// read a line, parse and execute
int status;
char ** args;
size_t len = 0;
ssize_t read;
char *line = (char *) malloc(sizeof(char) * WISH_BUF_SIZE);
while ((read = getline(&line, &len, file)) != -1)
{int j = -1;
while (line[++j] != '\n');
line[j] = '\0';
args = wish_split_line(line);
int i = -1;
if ((i = wish_redirection(args)) != -1){args[i++] = 0;
}
status = wish_execute(args, i);
for (int i = 0; i< WISH_MAX_WORD; i++)
if (NULL != args[i])
free(args[i]);
free(args);
}
free(line);
}
return WISH_EXIT_SUCCESS;
}
B. tasks.json的內(nèi)容我們?cè)谡{(diào)試前,需要:
build
文件夾build
文件夾使用cmake
配置項(xiàng)目make
或者cmake --build ./ --target all
進(jìn)行編譯因此,我們需要再tasks.json
中定義三個(gè)任務(wù)
{"version": "2.0.0",
"tasks": [
{"label": "create_build",
"type": "shell",
"command": "mkdir",
"args": [
"-p",
"${workspaceFolder}/build"
],
"detail": "創(chuàng)建build文件夾",
},
{"label": "cmake_configure",
"type": "shell",
"command": "cmake",
"options": {"cwd": "${workspaceFolder}/build"
},
"args": [
"-DCMAKE_BUILD_TYPE=${input:CMAKE_BUILD_TYPE}",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", // 生成compile_commands.json 供c/c++擴(kuò)展提示使用
"../"
],
"dependsOn": "create_build",
"detail": "CMake配置項(xiàng)目"
},
{"label": "make_build",
"type": "shell",
"command": "make",
"options": {"cwd": "${workspaceFolder}/build"
},
"args": [
"all"
],
"dependsOn": "cmake_configure",
"detail": "Make構(gòu)建項(xiàng)目"
}
],
"inputs": [
{"id": "CMAKE_BUILD_TYPE",
"type": "pickString",
"description": "選擇項(xiàng)目的編譯類型(CMake Build Type)",
"options": [
"Debug",
"Release",
"RelWithDebInfo",
"MinSizeRel",
],
"default": "Debug"
}
]
}
關(guān)于input
標(biāo)簽,參考手冊(cè)的這一節(jié):https://code.visualstudio.com/docs/editor/variables-reference
launch.json
的內(nèi)容如下
{// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{"type": "cppdbg",
"request": "launch",
"name": "LLDB Debug",
"program": "${workspaceFolder}/bin/wish",
"stopAtEntry": true,
"preLaunchTask": "make_build",
"cwd": "${workspaceFolder}",
"MIMode": "lldb",
},
]
}
D. 開始調(diào)試按下F5
開始調(diào)試
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-9m1QUYpQ-1671721662119)(https://jack-1307599355.cos.ap-shanghai.myqcloud.com/%E5%B1%8F%E5%B9%95%E5%BD%95%E5%88%B62022-10-11-%E4%B8%8B%E5%8D%8811.28.15%20(1)].gif)
9. CMake工程常用的tasks.json和launch.json下面給出一個(gè)CMake
工程常用的tasks.json
和launch.json
// tasks.json
{// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{// 在根文件夾中執(zhí)行創(chuàng)建文件夾build的命令
// 除windows系統(tǒng)外執(zhí)行的命令為`mkdir -p build`
// windows系統(tǒng)是在powershell中執(zhí)行命令`mkdir -Force build`
"label": "build_dir",
"command": "mkdir",
"type": "shell",
"args": [
"-p",
"build"
],
"windows": {"options": {"shell": {"executable": "powershell.exe"
}
},
"args": [
"-Force",
"build"
],
}
},
{// 在build文件夾中調(diào)用cmake進(jìn)行項(xiàng)目配置
// 除windows系統(tǒng)外執(zhí)行的命令為`cmake -DCMAKE_BUILD_TYPE=../`
// windows系統(tǒng)是在visual stuido的環(huán)境中執(zhí)行命令`cmake -DCMAKE_BUILD_TYPE=../ -G "CodeBlocks - NMake Makefiles"`
"label": "cmake",
"type": "shell",
"command": "cmake",
"args": [
"-DCMAKE_BUILD_TYPE=${input:CMAKE_BUILD_TYPE}",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", // 生成compile_commands.json 供c/c++擴(kuò)展提示使用
"../"
],
"options": {"cwd": "${workspaceFolder}/build",
},
"windows": {"args": [
"-DCMAKE_BUILD_TYPE=${input:CMAKE_BUILD_TYPE}",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"../",
"-G",
"\"CodeBlocks - NMake Makefiles\""
],
"options": {"shell": {// 需要根據(jù)安裝的vs版本調(diào)用vs工具命令提示符,根據(jù)自己的計(jì)算機(jī)上的路徑進(jìn)行修改
"executable": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat",
"args": [
"${input:PLATFORM}", //指定平臺(tái)
"-vcvars_ver=${input:vcvars_ver}", //指定vc環(huán)境版本
"&&"
]
}
},
},
"dependsOn": [
"build_dir" // 在task `build_dir` 后執(zhí)行該task
]
},
{// 在build文件夾中調(diào)用cmake編譯構(gòu)建debug程序
// 執(zhí)行的命令為`cmake --build ./ --target all --`
// windows系統(tǒng)如上需要在visual stuido的環(huán)境中執(zhí)行命令
"label": "build",
"group": "build",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"./",
"--target",
"all",
"--"
],
"options": {"cwd": "${workspaceFolder}/build",
},
"problemMatcher": "$gcc",
"windows": {"options": {"shell": {"executable": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat",
"args": [
"${input:PLATFORM}",
"-vcvars_ver=${input:vcvars_ver}",
"&&"
]
}
},
"problemMatcher": "$msCompile"
},
"dependsOn": [
"cmake" // 在task `cmake` 后執(zhí)行該task
]
},
{"label": "Open Terminal",
"type": "shell",
"command": "osascript -e 'tell application \"Terminal\"\ndo script \"echo hello\"\nend tell'",
"problemMatcher": []
}
],
"inputs": [
{"id": "CMAKE_BUILD_TYPE",
"type": "pickString",
"description": "指定 CMAKE_BUILD_TYPE 的值",
"options": [
"Debug",
"Release",
"RelWithDebInfo",
"MinSizeRel",
],
"default": "Debug"
},
{"id": "PLATFORM",
"type": "pickString",
"description": "指定 PLATFORM 的值",
"options": [
"x86",
"amd64",
"arm",
"x86_arm",
"x86_amd64",
"amd64_x86",
"amd64_arm",
],
"default": "amd64"
},
{"id": "vcvars_ver",
"type": "pickString",
"description": "指定 vcvars_ver 的值",
"options": [
"14.2", // 2019
"14.1", // 2017
"14.0", // 2015
],
"default": "14.2"
}
]
}
注意,如果是需要以Attach Debug
方式啟動(dòng)的調(diào)試的話,運(yùn)行中的進(jìn)程在編譯的時(shí)候必須要加上-g
以將符號(hào)表寫入到程序中,從而能夠debug
程序,若使用CMake
工具的話,需要指定使用Debug
方式來構(gòu)建程序,而非MinSizeRel
等其他構(gòu)建方式
// launch.json
{// 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": "Launch Debug",
//調(diào)試類型,除使用msvc進(jìn)行調(diào)試外,均為該類型
"type": "cppdbg",
"request": "launch",
//指定C/C++程序位置
"program": "${workspaceFolder}/bin/${input:executable}",
//指定運(yùn)行參數(shù)
"args": [
"test.bin",
"sorted.bin"
],
"stopAtEntry": false,
//指定工作目錄
"cwd": "${workspaceFolder}",
//在調(diào)試前會(huì)先調(diào)用build_debug這個(gè)task編譯構(gòu)建程序
"preLaunchTask": "build",
"environment": [],
//macOS的特定配置
"osx": {//指定使用lldb進(jìn)行調(diào)試
"MIMode": "lldb",
// 使用外部終端
"externalConsole": true,
},
//linux的特定配置
"linux": {//指定使用gdb調(diào)試
"MIMode": "gdb",
"setupCommands": [
{"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
//windows的特定配置
"windows": {//指定使用msvc進(jìn)行調(diào)試
"type": "cppdbg",
//指定C/C++程序位置
"program": "${workspaceFolder}/build/${workspaceFolderBasename}.exe",
}
},
{//名稱
"name": "Attach Debug",
//調(diào)試類型,除使用msvc進(jìn)行調(diào)試外,均為該類型
"type": "cppdbg",
"request": "attach",
//指定C/C++程序位置
"program": "${workspaceFolder}/bin/${input:executable}",
//指定要attach的線程
"processId":"${command:pickProcess}",
"osx": {//指定使用lldb進(jìn)行調(diào)試
"MIMode": "lldb",
// 使用外部終端
"externalConsole": true,
},
//linux的特定配置
"linux": {//指定使用gdb調(diào)試
"MIMode": "gdb",
"setupCommands": [
{"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
//windows的特定配置
"windows": {//指定使用msvc進(jìn)行調(diào)試
"type": "cppdbg",
//指定C/C++程序位置
"program": "${workspaceFolder}/build/${workspaceFolderBasename}.exe",
}
}
],
"inputs": [
{"id": "executable",
"type": "pickString",
"description": "可執(zhí)行文件的名稱",
"default": "posrt",
"options": [
"psort"
]
}
]
}
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
名稱欄目:VSCode調(diào)試C/C++項(xiàng)目-創(chuàng)新互聯(lián)
轉(zhuǎn)載來源:http://vcdvsql.cn/article42/ddgihc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、網(wǎng)站導(dǎo)航、品牌網(wǎng)站設(shè)計(jì)、虛擬主機(jī)、商城網(wǎng)站、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容