正值暑假,准备做一些体系结构方面的实验。之前使用的模拟器是GEM5[1],使用起来非常不方便,无法并行模拟不说,运行起来还贼慢。一个实验全系统模式(Full System, FS)下需要跑俩星期,而且经常跑到一半发现参数错了或者需要代码调整,又得重新开始,费工费力。即使是使用系统调用模式(System-call Emulation, SE),也难以通过IDE进行DEBUG。虽然也略懂一些gdb的调试方法,但是与IDE的图形化调试相比效率还是低了很多。
为了提高一下科研效率,想要更换一款更加易于修改、而且运行快一点的模拟器。通过调研,发现了一款模拟器,zsim[2]。zsim同样使用了SE的执行模式,并且效率非常高,支持多线程并发模拟。可惜的是,zsim与GEM5一样,都使用了基于python的scons[3]进行代码构建,虽然一直不理解为什么那么多程序一定得用scons构架代码,但是scons对IDE调试支持的缺乏对希望使用IDE的我来说是非常不便的。
还好,zsim的代码量比较少,而且大部分都是c/cpp,只需要少量修改即可适用于IDE调试。经过两天的不断尝试,终于完成了。现将过程记录下来,供需要使用IDE调试pintool或者zsim的小伙伴参考。
准备工作
从学习编程以来一直用Visual Studio进行编程和调试,用着顺手,所以准备使用VS作为IDE。由于zsim需要Linux下的头文件<syscall.h>,需要远程连接Linux。这里使用了VisualGDB[4]作为远程连接的工具。
windows端:
安装Microsoft Visual Studio 和VisualGDB
XMing [5](可选,用以将结果输出在其他terminal上)
这里使用的版本是VS2013专业版和VisualGDB5.1.6
Linux端:
使用Ubuntu 14.04.6 server系统,server版本没有GUI可以大量减少空间占用(虚拟机用户福音)
尽量不要使用docker安装ubuntu,否则运行zsim会出现如下错误
[panic] personality() call failed: Operation not permitted
搜索后发现似乎与docker相关[6],尚未解决,最终换了虚拟机版本的Ubuntu
安装完ubuntu后,首先安装必要的ssh组件:
sudo apt-get update
sudo apt-get install openssh-server
随后运行ssh服务,如果使用root登录,记得在/etc/ssh/sshd_config文件中添加PermitRootLogin yes
sudo service ssh start
在windows终端中测试ssh连接畅通,并保存RSA key:
ssh user@ubuntu-host-ip
随后,通过ssh可以在Ubuntu系统中安装zsim相关依赖包
sudo apt-get install gcc g++ gdb git
sudo apt-get install scons libconfig-dev libconfig++-dev
sudo apt-get install libhdf5-dev libhdf5-serial-dev
sudo apt-get install libelfg0 libelfg0-dev
下载pin[7],这里选择的是2.14版本
tar -xvf pin-2.14-71313-gcc.4.4.7-linux.tar
export PINPATH=/vine/pin-2.14-71313-gcc.4.4.7-linux(指向解压后pin的位置)
下载zsim,并在ubuntu上build成功一次,编译debug版本(添加--d选项)
git clone https://github.com/s5z/zsim.git
cd zsim
scons --d -j8
编译完成后,将整个zsim-master文件夹拷贝到Windows上备用
如果在Ubuntu上运行出现以下错误
vine@ubuntu-linux:/vine/zsim$ scons --d
scons: Reading SConscript files ...
Building debug zsim at build/debug
gcc: error: /usr/include/asm/unistd.h: No such file or directory
gcc: fatal error: no input files
compilation terminated.
Traceback (most recent call last):
File "../../misc/list_syscalls.py", line 7, in <module>
denseList = ["INVALID"]*(max([num for (num, name) in sysList]) + 1)
ValueError: max() arg is an empty sequence
解决方案如下,修改misc/list_syscalls.py:4,以避免后面-Werror产生的错误
syscallCmd = "gcc -E -dD /usr/include/asm/unistd.h | grep __NR"
修改为:
syscallCmd = "gcc -E -dD /usr/include/asm-generic/unistd.h | grep __NR"
1.准备工作立libzsim.so库文件的VS工程
打开VS,新建Linux Project,命名为libzsim
选择Application-Use GNU Make,语言选择c++11及以上
配置好ssh,下一步
Transferred files这里修改成如下内容,以传输全部文件到ubuntu上
*.*;*
删除默认的.cpp文件,选择删除而不是移除
由于zsim基于pin,源码中包含多个main入口,因此不能直接将整个工程拖到VS中,需要删除额外的文件。参与编译libzsim.so的源文件如下,可以通过在src/SConscript中加入print查看
编译libzsim.so需要的cpp文件如下:
只保留需要的c/cpp文件和全部.h头文件
需要注意的地方
删除: virt/syscall_name.cpp.in
确认: virt/syscall_name.cpp已经生成(在ubuntu上编译一次后生成)
将zsim/build/debug/version.h也添加至工程中
保留原有文件夹结构先复制到VS工程文件夹中,再拖入vs工程中,这样只有一个main入口,并且结合先前传输文件的设置,VisualGDB会将全部文件和文件结构保留传输到ubuntu上
修改galloc.cpp:35,删除原来的#include语句,将g_heap/dlmalloc.h.c的文件内容全部复制到这个位置,并将g_heap/dlmalloc.h.c从工程中删除
#include "g_heap/dlmalloc.h.c"
如果使用原有的#include方式,将产生multiple definition错误
最后修改工程属性(工程上点右键->VisualGDB Project Properties),按照下面设置
Makefile settings选项卡:
Project type选择Shared library, Output file name填libzsim.so
删除Preprocessor macros中的"DEBUG=1",否则会和源代码中的DEBUG宏重复定义产生warning,导致-Werror产生错误(或者在CXXFLAGS中删掉-Werror)
Include directories:
/vine/pin-2.14-71313-gcc.4.4.7-linux/source/include/pin
/vine/pin-2.14-71313-gcc.4.4.7-linux/source/include/pin/gen
/vine/pin-2.14-71313-gcc.4.4.7-linux/extras/components/include
/vine/pin-2.14-71313-gcc.4.4.7-linux/extras/xed-intel64/include
/tmp/VisualGDB/c/Projects/libzsim/libzsim(工程文件在ubuntu上的目录位置)
Library directories:
/usr/lib
/usr/lib/x86_64-linux-gnu
/vine/pin-2.14-71313-gcc.4.4.7-linux/extras/xed-intel64/lib
/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/lib
/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/lib-ext
/usr/local/lib
/usr/lib
Library names:
config++ pin xed pindwarf elf dl rt hdf5 hdf5_hl
CXXFLAGS:
-g -O0 -std=c++0x
-Wall -Wno-unknown-pragmas -fomit-frame-pointer -fno-stack-protector
-MMD -DBIGARRAY_MULTIPLIER=1
-DUSING_XED -DTARGET_IA32E -DHOST_IA32E
-Werror -fPIC -DTARGET_LINUX
-DPIN_PATH="/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/bin/pinbin"
-DZSIM_PATH="/tmp/VisualGDB/c/Projects/libzsim/libzsim/Debug/libzsim.so"
-DMT_SAFE_LOG
其中有一个需要注意的地方就是-DZSIM_PATH需要修改为实际生成的libzsim.so的位置
-DZSIM_PATH="/tmp/VisualGDB/c/Projects/libzsim/libzsim/Debug/libzsim.so"
LDFLAGS:
-Wl,-gc-sections
-Wl,--hash-style=sysv
-Wl,-Bsymbolic
-Wl,--version-script=/vine/pin-2.14-71313-gcc.4.4.7-linux/source/include/pin/pintool.ver
-shared
注意:上面几项内容中均不含换行,换行只是为了介绍清楚方便大家核对
点击确认后,如果出现Toolchain Test Failed,但是Detailed log里面又没有内容,可以直接Ignore(如下图所示),如果有内容需要按照内容修改。常见的错误就是因为没有安装依赖,如libconfig++,libhdf5等,或者路径错误等
Debug settings选项卡
这里Start GDB in the following mode选择"Attach to an existing instance", Executable with symbols输入pin里面的intel64/bin/pinbin,我这里是
/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/bin/pinbin
进程附加选择"Ask for a process to attach when debugging is started",如果安装了xming的,可以选择在Forward program output to中选择xming
勾选"Run debugger as root with sudo"
此时,点击“生成->生成解决方案”即可以生成libzsim.so文件
3.生成zsim 的VS工程
在解决方案资源管理器中“解决方案”上点右键,新建Linux Project工程,命名为zsim. 和前面libzsim.so工程的新建方法一致,然后删除默认加入的.cpp文件。注意Files to transfer修改为
*.*;*
新建项目完成后,在zsim项目上点右键->生成依赖项->项目依赖项,勾选libzsim,保证libzsim.so在每次编译时先于zsim生成
zsim编译需要的cpp文件如下:
将这些.cpp文件和需要的.h文件,以及tests文件夹一起,保留文件夹结构,先复制到Windows上zsim工程的工程目录下,再拖进zsim项目中
需要确认tests文件夹下的几个文件也出现在工程文件目录中,这样VisualGDB可以将tests文件夹也复制到生成目录下
最终zsim工程文件如下所示:
最后修改工程属性(zsim工程上点右键->VisualGDB Project Properties)
同样,删除Preprocessor macros下的"DEBUG=1"
Project type选择Executable,Output file name输入zsim
Include directories与libzsim.so工程一致,Library directories添加libzsim.so库文件生成文件所在位置:
/usr/lib
/usr/lib/x86_64-linux-gnu
/vine/pin-2.14-71313-gcc.4.4.7-linux/extras/xed-intel64/lib
/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/lib
/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/lib-ext
/usr/local/lib
/usr/lib
/tmp/VisualGDB/c/Projects/libzsim/libzsim/Debug
Library names:
config++ pthread zsim
CXXFLAGS:(注意需要添加--static)
-g -O0 -std=c++0x --static
-Wall -Wno-unknown-pragmas -fomit-frame-pointer -fno-stack-protector
-MMD -DBIGARRAY_MULTIPLIER=1
-DUSING_XED -DTARGET_IA32E -DHOST_IA32E -fPIC -DTARGET_LINUX
-DPIN_PATH="/vine/pin-2.14-71313-gcc.4.4.7-linux/intel64/bin/pinbin"
-DZSIM_PATH="/tmp/VisualGDB/c/Projects/libzsim/libzsim/Debug/libzsim.so"
-DMT_SAFE_LOG
Debug settings选项卡
Start GDB in the following mode选择“Debug a new instance”, Main executable arguments填要运行的.cfg配置文件。如果前面保留文件夹结构将tests文件夹拖进工程中,只需要使用
/tmp/VisualGDB/$(ProjectDirUnixStyle)/tests/simple.cfg
同样,Program output可以根据xming安装情况选择
至此,zsim 的VS工程文件创建完成
4.开始DEBUG!
首先,修改zsim工程下的pin_cmd.cpp:31,添加如下内容
args.push_back("-injection");
args.push_back("child");
args.push_back("-pause_tool");
args.push_back("20");
其中“-injection child”可以根据pin的版本修改,有一些pin版本不需要添加
如果出现下述错误,则一定需要添加
vine@ubuntu-linux:/tmp/VisualGDB/c/Projects/libzsim/zsim$ ./Debug/zsim tests/simple.cfg
[H] Starting zsim, built Wed Jul 22 09:44:07 CST 2020 (rev no git repo)
[H] Creating global segment, 1024 MBs
[H] Global segment shmid = 1605648
[H] Deadlock detection OFF
E: 4.4 is not a supported linux release
[H] Child 15168 done
[H] All children done, exiting
“-pause_tool”后的20为等待debugger连接的时间,可以适当调整
将zsim工程设置为启动项目,点击调试,找到下面的VisualGDB Remote Console
复制add-symbol-file及后面的内容,并添加至"VisualGDB Project Properties->GDB startup commands"中下的第二个文本框中
第二行填写b main,可以使程序自动中断在main函数上(也可以根据实际需求修改)
保存配置后,通过ssh在Ubuntu上运行zsim,出现等待连接提示:
vine@ubuntu-linux:/tmp/VisualGDB/c/Projects/libzsim/zsim$ ./Debug/zsim tests/simple.cfg
[H] Starting zsim, built Wed Jul 22 09:44:07 CST 2020 (rev no git repo)
[H] Creating global segment, 1024 MBs
[H] Global segment shmid = 1802256
[H] Deadlock detection OFF
Pausing for 30 seconds to attach to process with pid 15820
To load the tool's debug info to gdb use:
add-symbol-file /tmp/VisualGDB/c/Projects/libzsim/libzsim/Debug/libzsim.so 0x7ffff64c90a0 -s .data 0x7ffff6d56b00 -s .bss 0x7ffff6d58ec0
修改libzsim.so工程为启动工程,点击调试,第一次会要求输入ubuntu管理员账号的密码,随后会出现进程附加选择窗口
选择Ubuntu上输出pid后对应数字所在进程(上例PID 为15820)
此时会出现帧不在模块中的提示,不用担心,点击继续,等待“-pause_tool”的时间结束
时间结束后,VS自动中断至main函数位置,此时可以开始完美DEBUG了!
5.不太完美的一键DEBUG设置
虽然上面已经可以开始进行调试,但是可以发现过程比较繁琐:需要先通过ssh在Ubuntu上运行zsim程序,再通过VS进行.so文件的进程附加和调试。
有没有什么办法可以一键DEBUG呢?经过多次尝试,目前有一个不太完美的解决方案。
为了解决这个问题有两个思路:
-> 通过VisualGDB在zsim生成后,后台ssh运行zsim,获得需要debug的进程PID后发送给VS 的GDB进行自动附加
->进入GDB命令行后调用shell或python命令,根据shell执行结果确定PID,并返回进行附加
最终,经过多次尝试,第二种难以实现,采用第一种方法,但是不太完美
如果有小伙伴找到完美解决办法,也可以给我留言
方法如下:
找到zsim工程中的zsim_harness.cpp:235,修改为如下内容
int cpid = fork();
if (cpid) { //parent
// *****ADD
std::ofstream ofs;
ofs.open("/tmp/VisualGDB/c/Projects/libzsim/zsim/Debug/command.pid", std::ios::out);
ofs << "PID = ";
ofs << (cpid + 2);
ofs.close();
// *****END
assert(cpid > 0);
childInfo[procIdx].pid = cpid;
childInfo[procIdx].status = PS_RUNNING;
} else { //child
这里的思路为当pin程序在开始执行.cfg中配置的进程时,将进程号写入文件中,写入的目录及文件名可以自行定义
打开libzsim工程->VisualGDB Project Properties->Debug settings,将进程附加选项原本的"Ask for a process to attach when debugging is started"改成"Attach to a process with the following PID",数字默认填0
选择Custom debug steps,在Custom debug actions选项卡的Before launching GDB中选择Edit..
添加第一个命令:
具体命令为
screen -m -d /tmp/VisualGDB/c/Projects/libzsim/zsim/Debug/zsim /tmp/VisualGDB/c/Projects/libzsim/zsim/tests/simple.cfg
这个命令的作用是利用screen命令,实现ssh断开后的zsim后台运行[8]
为此需要Ubuntu安装好依赖
sudo apt-get install screen
其实还有一种方法更为完美[9],但是在我的电脑上刚开始可以,Ubuntu reboot一遍后无法后台运行了。小伙伴们可以试试,如果下面的命令可以ssh后台运行zsim,则不需要添加上面的screen命令了,添加下面这个即可
sh -c 'nohup /tmp/VisualGDB/c/Projects/libzsim/zsim/Debug/zsim /tmp/VisualGDB/c/Projects/libzsim/zsim/Debug/tests/simple.cfg' > /dev/null 2>&1 &
一般情况下ssh下执行的程序会由fork()创建程子进程,在ssh结束后,子进程也会被强制结束
需要使用ssh后台运行(即ssh session结束后,程序还需要在运行)的原因,是VisualGDB需要等待ssh执行结束后才能进行下一项(不知道新的版本有没有改进)而且无法使用ssh -f的方法强制后台保留,因此,必须让ssh又运行程序,又完全不返回内容才能继续
因此使用command & 的方法也不行,VisualGDB仍然会等待zsim完全结束后才执行附加进程命令,而那时候需要附加的进程已经没了,造成错误
上述两个命令选择一个能ssh后台执行zsim的命令之后,添加第二个命令:
这个命令的作用是通过shell的cat命令,读取刚才zsim写入的PID文件,并设置为$AttachPID环境变量,VisualGDB再根据$AttachPID,自动附加到进程。需要注意的是这里可以设置正则表达式匹配,要和前面的文件输出格式匹配。这里使用的"PID = ?"的格式
执行的流程是这样
- VS点击调试
- 生成libzsim.so
- 生成zsim pintool
- (开始调试前)后台运行zsim,得到要DEBUG的PID,并写入到文件
- (开始调试前)VisualGDB读取写的PID的文件,并加载到变量$AttachPID
- (开始调试)GDB附加到Ubuntu上PID=$AttachPID的进程上,完成调试
这里的问题是zsim_harness.cpp:235附近,cpid和.cfg中需要执行命令间的PID关系
经过试验,在没有别的进程干扰下,是顺序关系,只需要按照图中修改代码即可
有干扰的情况,小概率会失败(所以不完美)
猜测:使用screen时,screen本身PID+1,再运行新的程序再+1,所以.cfg中最终运行的程序PID是+2的;nohup本身不影响PID,所以只需要+1?
.cfg中存在多个process的情况,根据顺序再加就可以
希望能帮助到想要用IDE调试zsim或者pintool的小伙伴们!
码字不易,请勿转载
参考
- ^1 http://www.gem5.org
- ^2 https://github.com/s5z/zsim
- ^3 https://www.scons.org
- ^4 https://visualgdb.com
- ^5 https://sourceforge.net/projects/xming/
- ^6 https://github.com/travis-ci/travis-ci/issues/9061
- ^7 https://software.intel.com/content/www/us/en/develop/articles/pin-a-dynamic-binary-instrumentation-tool.html
- ^8 https://serverfault.com/questions/21806/how-can-i-launch-a-screen-session-with-a-command-over-ssh-on-a-remote-server-fro
- ^9 https://stackoverflow.com/questions/29142/getting-ssh-to-execute-a-command-in-the-background-on-target-machine