在wsl上写mp的辛酸血泪史
November 7, 2019About 8 min
在wsl上写mp的辛酸血泪史
大一学习ECE120时用vscode写C程 (课程的mp, machine problem) 的工作流,做了简单的记录。主要包括自动编译调试脚本,valgrind调试输出。以及在不同虚拟机平台调试C程的方法。因为是仅针对课程内容的环境调试,内容比较个性化,适合初学者使用,也能更加熟悉vscode的运行逻辑。但是针对较大的项目建议使用cmake和clion等成熟框架IDE。
引子:我为啥又折腾了
学校给的垃圾虚拟机真的太蛋疼了,没有root权限,不能装软件,还是32位的,ssh全程调试都不行。老老实实的做得要全用gdb来debug,都9102年了,纯命令行debug,您是人🐎?作为一个被vscode宠坏的人,没有ui界面是不行的。所以。。。又折腾了。。。
这个流程看起来有点复杂,其实花点时间搞清vscode的逻辑后就很清晰了。只是把原来要一遍一遍输入的命令组合调用。而且一旦熟练了可以省下更多的时间。
Warning
大工程还是老老实实用ide,不然会死🐴
目录
@[toc]
0. 总览

在vscode上写c/cpp的细致教程参考这一篇:VSCode 如何编写运行 C、C++ 程序? - 知乎
1. 总结出来的流程
- 配置 ‘Makefile’ 复制一遍, 把 “main” 变成自己的
mymain
- 配置自己的
mymain.c
, 一般来说直接复制main
, 把里面不能编译的 i386 代码删除, 把不测试的部分先注释掉, 写好一部分测试一部分 - 配置
tasks.json
,用到valgrind的, 把(mpX) valgrind leak check
配置了 - 配置
launch.json
, 每个mp新建一个launch, 复制前一个, 改掉program和args - 开始写代码
- 保证内存管理
- 先把
load
和free
写了, 直接测试tasks里的(mpX) valgrind
- 会生成一个
.valgrind
文件, 打开这个文件, 有高亮支持(需要vscode插件支持) - 看这个文件, 也可以用
F5
, 保证内存控制正确
- 先把
- 测试功能
- 正常写就行了
- 不用单步调试的话, 直接用valgrind, 可以看输出
- F5进单步调试慢慢看
- 代码注意点:
- 记得写注释
- 低耦合, 分离模块
- 保证内存管理
- 功能完善之后, 可以写测试文件
- 有gold的可以用
test.sh
test.sh
放进Makefile
- 有gold的可以用
- 功能全部完成后,
valgrind
最后再检查一次内存 - svn上传, 虚拟机里最终检查
- 最后检查注释, 上传
2. vscode的配置文件
tasks文件
task文件简单来说就是你要在命令行执行的命令。除了传统的gcc编译命令之外,也可以调用make命令。这里的作用是在进入调试(launch)之前,先把保存好的文件编译一遍,保证测试的是当前修改完的代码。调用方式是 ctrl
+ B
下面注释解释了三个功能块:基本功能(gcc为例),组合功能(clean and make为例),valgrind功能(valgrind在vscode中没有很好的集成,不像clion方便,但是通过这里的task和插件 vscode-valgrind
做到valgrind显示结果且高亮)
{
"version": "2.0.0",
"tasks": [
//////////////////////////////////////
//示例: 把gcc filename.c -o filename -g -Wall 变成tasks格式
{"label": "g++ build active file",//名字:在后面的launch中用到,意思是执行launch命令之前,先跑一遍这个task命令
"type": "shell",
"command": "/usr/bin/g++", //要用的程序,gcc,make和其他的都可以,只要terminal能能用他就能用
"args": [ //传入的参数
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-g",
"-Wall"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": "build" //最好选"build"这样在都在一起方便看
},
//////////////////////////////////////
{"label": "gcc build active file",
"type": "shell",
"command": "/usr/bin/gcc",
"args": [
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-g",
"-Wall"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": "build"
},
{"label": "make",
"type": "shell",
"command": "/usr/bin/make",
"options": {
"cwd": "${fileDirname}"
},
"group": "build",
"problemMatcher": [
"$gcc"
]
},
{"label": "make_clean",
"type": "shell",
"command": "/usr/bin/make",
"args": [
"clean"
],
"options": {
"cwd": "${fileDirname}"
},
"group": "build"
},
//////////////////////////////////////
{"label": "clean and make", //意思是把上面两个tasks连在一起,按照顺序用(也可以并行“parallel”)
"dependsOn":["make_clean","make"],
"dependsOrder": "sequence",
"problemMatcher":[]
},
//////////////////////////////////////
{"label": "valgrind start gdbserver",//这个是错误示例,不要用!vscode的gdb调试似乎不支持gdb的server
"type": "shell",
"command": "/usr/bin/valgrind",
"args": [
"--tool=memcheck",
"--vgdb=yes",
"--vgdb-error=0",
"--leak-check=full",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
}
},
//////////////////////////////////////
{"label": "(mpX) valgrind leak check", // 针对mp的valgrind命令
"type": "shell",
"command": "/usr/bin/valgrind",
"args": [
"--tool=memcheck",
"--leak-check=full",
"--track-origins=yes",
"${fileDirname}/mp11",//TODO: change the number here
"graph", //TODO:这里相当于测试时给mp11传入的参数,要修改为对应mp的
"requests",
">", //后面三行让valgrind的输出写入名为mp11.valgrind的文件,在另一个窗口打开这个文件就能看到高亮的输出了,见前文图右上角
"${fileDirname}/mp11.valgrind",//TODO: change the number here
"2>&1",
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher":[],
"group": "build",
},
{"label": "valgrind leak check", //对任意文件的valgrind命令
"type": "shell",
"command": "/usr/bin/valgrind",
"args": [
"--tool=memcheck",
"--leak-check=full",
"${fileDirname}/${fileBasenameNoExtension}",
"2>&1",
"&"
],
"options": {
"cwd": "${fileDirname}"
},
"group": "build",
},
{"label": "valgrind",//gcc和valgrind组合
"dependsOrder": "sequence",
"dependsOn": [
"gcc build active file",
"valgrind leak check"
],
"group": "build"
},
{"label": "(mpX) make and valgrind",//make和valgrind组合
"dependsOrder": "sequence",
"dependsOn": [
"make",
"(mpX) valgrind leak check"
],
"group": "build",
"problemMatcher":[]
},
//////////////////////////////////////
{"label": "lc3as assemble", //lc3的一些命令,用不到的可以删了
"type": "shell",
"command": "/home/gavin/bin/lc3as",
"args": [
"${file}"
],
"options": {
"cwd": "${fileDirname}"
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"group": "build",
"problemMatcher": []
},
{"label": "lc3sim-tk simulate",
"type": "shell",
"command": "DISPLAY=:0",
"args": [
"/home/gavin/bin/lc3sim-tk",
"${fileBasenameNoExtension}.obj"
],
"options": {
"cwd": "${fileDirname}"
},
"presentation": {
"echo": false,
"reveal": "never"
}
},
{"label": "lc3sim simulate",
"type": "shell",
"command": "/home/gavin/bin/lc3sim",
"args": [
"${fileBasenameNoExtension}.obj"
],
"options": {
"cwd": "${fileDirname}"
}
},
{"label": "lc3 tk debug",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"lc3as assemble",
"lc3sim-tk simulate"
],
"problemMatcher": [],
"group": "build"
},
{"label": "lc3 CLI debug",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"lc3as assemble",
"lc3sim simulate"
],
"problemMatcher": []
}
]
}
launch文件
launch简单来说就是gdb时给的命令。基本就是一个mp建一个新的,mp小工程没必要用cmake,但好处在于按F5能进入gui调试界面,友好许多。
//launch 文件示例,每个mp都不一样,具体按照要求来
//看mp11大概指导怎么写就行了,别的mp的基本都是重复
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
//////////////////////////////////////
//示例:原本输入命令为./mp11 graph request如何变成launch的形式
"name": "mp11", //debug栏中显示的名称
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/mp11", //TODO:要运行的文件
"args": [ //文件名后传入的参数,根据mp要求文档来
"graph",
"requests",
//"mygraph",
//"myrequests",
],
"stopAtEntry": false, //开始调试时在文件起点暂停与否
"cwd": "${fileDirname}", //调试时的目录,查找data文件确定相对路径
"environment": [], //一般不用填
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "clean and make", //这个很重要,要在tasks.json中编辑
"miDebuggerPath": "/usr/bin/gdb"
},
//////////////////////////////////////
{
"name": "mp10",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/mp10", //TODO:change this within test/mpX
"args": [
// "tests/maze3.txt" //TODO:change this within test/
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "clean and make",
"miDebuggerPath": "/usr/bin/gdb"
},
{
"name": "mp9",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/mp9", //TODO:change this within test/mpX
"args": [
"tests/maze3.txt" //TODO:change this within test/
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "clean and make",
"miDebuggerPath": "/usr/bin/gdb"
},
{
"name": "mp8",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/mp8", //TODO:change this within test/mpX
"args": [
"sudoku1.txt"
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "clean and make",
"miDebuggerPath": "/usr/bin/gdb"
},
{
"name": "mp7",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/mp7", //TODO:change this within test/mpX
"args": [
"/home/gavin/2019_FALL/ece220fa19/mp7/Images/tajmahal.png",
"/home/gavin/2019_FALL/ece220fa19/mp7/Images/my_tajmahal.png",
"0",
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "clean and make",
"miDebuggerPath": "/usr/bin/gdb"
},
{
"name": "mp6",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/gameoflife",
"args": [
"<",
"life4.dat"
],
"stopAtEntry": true,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "make_ignore_warning",
"miDebuggerPath": "/usr/bin/gdb"
},
{"name": "g++ build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
// "args": ["${fileDirname}/testfour.txt"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "g++ build active file",
"miDebuggerPath": "/usr/bin/gdb"
},
{"name": "gcc build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "gcc build active file",
"miDebuggerPath": "/usr/bin/gdb"
},
{"name": "cppdbg launch", //官方示例
"type": "cppdbg",
"request": "launch",
"program": "enter program name, for example ${workspaceFolder}/a.out",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"pipeTransport": {
"debuggerPath": "/usr/bin/gdb",
"pipeProgram": "/usr/bin/ssh",
"pipeArgs": [],
"pipeCwd": ""
},
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
]
}
3. 细数踩过的坑
archlinux(官方ECE220虚拟机)
用ssh连接vscode- 没有openssh-server
尝试获得root权限- 不可能的,要用来考试的机子不会放权限的
安装vscode- root没有不能用pacman
- 直接build没有找到,可能可以,但是既然他要用来考试,一般是找不到了
ubuntu
✔快乐32位虚拟机
- 还是向oracle VM屈服了,装了32位的16.04
- 但是笔记本性能真的不太好,内存显存cpu给足了感觉还是卡
- 好就好在32位的程序能跑
- 为啥用vm就能装32位的呢???
尝试ssh连接vscode- 不支持32位的ssh server
- 研究了一下下虚拟机的网络连接模式,小有收获
✔安装vscode
- 直接用apt装成功了
- 也可以直接编译32位的文件,但是如果有错误【指针指向\0或者别的】会直接爆掉,虚拟机直接异常关闭。de一次bug就要重启
- 界面不太流畅,经常有闪烁的黑框,而且32位的vscode在1.35之后就不更新了
目前找到的最好的解决方案,如果不出奇怪错误的话
wsl
- ✔64位编译32程序
- 缺少multilib库
- makefile编译指令 -m32 -m64
- 最后还是要去32位的机器上跑test,就是写起来比较流畅
- docker玩交叉开火
- 还没玩,说不定是新世界