使用vistualbox建立kgdb调试环境

来源:互联网 发布:云计算安全和大数据 编辑:程序博客网 时间:2024/05/18 23:16

        好吧,我不得不承认没有一种调试技术是万能呢,昨天刚说了UML调试linux内核,今天就又要用kgdb调试了……kgdb吧,是linux内核的私生子,他被linux内核细心的呵护和照顾着,用kgdb调试内核确实是比较主流的一种做法。既然是主流,你可以不喜欢,但你不能不了解(你得先了解主流,才能说你是喜欢主流还是非主流吧)。借着今天帮BOSS搭建kgdb调试环境之际,将方法记录下来,以备后用,并分享与大家欢迎指正和补充。

        用vistualbox啊kvm啊什么的搭建kgdb调试环境的时候有些情况下会不太方便,比如和虚拟机之间的串口通信有的是网卡模拟的,那么你如果要调试网卡驱动,那还要想办法解决这个,在这点上谁有什么好的方法可以讨论。那么开始kgdb的搭建之旅。


搭建环境:在两个virtualbox虚拟机之间搭建kgdb调试环境,设其中被调试的那台(安装kgdb内核)为LabDebian-ee,设其中用gdb去调试跟踪的那台为LabDebian-er。两台系统上都安装了debian6.0系统。


before-start:

        首先一个linux的内核源码是必不可少的,你需要一个你想要调试的源码,如果你没有这样的源码可以参考www.kernel.org。如果你在编译kgdb相关内核之前从来没有尝试编译过linux内核,那么请你先去练习一下编译一个可用的linux内核并安装启动,然后再回来。好了,现在我认为你已经有编译内核的基础,那么开始编译kgdb调试用内核。


start:

        我们这里使用的是debian6.0 squeeze,所以下面的有些操作是和系统有关的,如果你不是这个系统可能命令的使用上会有些许的差异,但意思都一样。

现在LabDebian-ee上操作,下面的前两步可以不在LabDebian-ee上执行,因为编译会比较繁琐,在一台虚拟机上执行会比较慢,所以可以在一台和LabDebian-ee平台一样的机器上编译,然后再拷贝到LabDebian-ee上。

        第一步,配置kgdb相关的内核选项,在你想要编译的内核目录下执行

                make menuconfig

        如果你没有安装ncures-dev的lib库,这一步会报错,请先安装所需要的软件包,如果你没有安装gcc和make等,那要么你这是一个刚安装好的系统,要么goto before-start。在成功执行后你会看到熟悉的内核配置界面,有关kgdb的选项可以参考‘/kgdb’的帮助信息,以及‘?’查看到的信息。有关kgdb的选项如下:

General setup

        --> Prompt for development and/or incomplete code/driver [ y ] 这个不用解释了吧……用解释的话你也goto begin-start吧。

Kernel hacking

        --> Kernel debugging [ y ]  打开内核调试

        --> Compile the kernel with debug info [ y ]使得编译的内核包含一些调试信息

        --> Compile the kernel with frame pointers [ y ] 使得内核使用帧指针寄存器来维护堆栈,从而就可以正确的执行堆栈回溯,即函数调用的栈信息

        --> KGDB: kernel debugging with remote gdb [ y ] 使得内核支持kgdb调试

                --> KGDB: use kgdb over the serial console [ y ] 通过串口调试kgdb

                --> KGDB:internal test suite [ n/y ]  这个选项是用于调试KGDB本身的,如果你需要调试KGDB那么就选这个,否则就不用选了。

        --> Write protect kernel read-only data structures [ n ] 管选项是将内核的一些内存区域空间设置为只读,这样可能导致kgdb设置软断点的功能工作不好,所以鉴于这个内核是调试用的,建议去掉。

        这些是和kgdb相关的基本选项,如果你还有什么其它的调试需求请自行选择。都选择好后保存退出,配置就结束了。


        第二步,编译内核。在内核目录下执行

                make -j8  ( 如果你的机器的处理器很厉害可以多来几个j,如果你的机器性能有限就少写点,因为我的机器是64位8核处理器16G内存,所以就写了一个8。)

        现在你可以去喝杯茶了,出去溜一圈什么的,编译所需的时间比UML编译时要多好多好多倍,在我机器上我大概等待了10分钟。


        第三步,安装编译好的内核。我们需要在LabDebian-ee虚拟机上安装这个内核,所以如果你是在别的地方编译的内核,那么现在该把它拷贝到虚拟机里了。

如果你不理解上面的一句,请不要作下面的操作,否则后果可能会比较严重(比如你把自己主机的内核覆盖了,还没覆盖好……)

        然后在内核目录下执行

                make install  (注意在执行这一步之前备份/boot/下的可能会重名的文件。)

                make modules_install  (同样注意别覆盖/lib/modules/下的重名文件。)

        安装内核和模块等后制作一个对应的initrd,我是在debian6.0 squeeze下,所以我执行

                mkinitramfs  -o  /boot/initrd.img-$name   $name  ($name是你编译后的内核的名字,比如你安装到/boot/中的内核叫vmlinuz-2.6.32-5-amd64,那么$name就是2.6.32-5-amd64)

        如果你的系统是别的系统,请根据具体情况制作initrd。

        更新grub,执行

                update-grub2 


       第四步,配置串口通信。这里是配置两个virtualbox虚拟机进行串口通信的方法。将LabDebian-ee和LabDebian-er之间配置串口通信。首先在关闭LabDebian-er的前提下配置串口,在virtualbox的LabDebian-er配置界面下配置如下


        在串口配置栏下启用串口,然后端口编号选择COM1,端口模式是Host Pipe,一定要选上“创建通道”,在端口文件写上/tmp/vbox。

        好吧,解释一下:COM1对应你主机的/dev/ttyS0第一个串口设备,如果你的主机不是这样那你自己修改。Host Pipe是选择pipe模式,用/tmp/vbox作为管道,两个使用同一管道的虚拟机可以通信。/tmp/vbox可以指定到别的位置上,只要你有权创建就行,选上“创建通道”就是说这个虚拟机启动时会创建这个文件,这个文件的类型是socket类型的。

        接下来在LabDebian-ee上配置什么呢?聪明的你应该想到了,肯定会和LabDebian-er的host-pipe文件是同一个,然后唯一的不同点就是不要够选“创建通道”


        为什么我们把“创建通道”设置在LabDebian-er上?因为我觉得应该让LabDebian-er先启动。而LabDebian-ee上安装有等待调试的内核,如果你按照kgdb方式启动会在其中一步时阻塞住等待LabDebian-er的连接,所以LabDebian-ee会在你调试期间经常重启,而LabDebian-er只要启动后就不用重启了,它只需要重新gdb vmlinux就行了。下面将讲到调试的办法,到时会更容易理解。这里说明,要按照上面的配置后,先启动LabDebian-er。

        第五步,开始kgdb调试。

        1、 启动LabDebian-er虚拟机。启动起来后就先不管它了。

        2、 启动LabDebian-ee虚拟机,注意在启动到grub界面时,如下:


        这是我LabDebian-ee的grub界面,现在找到你编译并安装的那个kgdb调试用内核,我的是第三行那个白色高亮显示的那个,在这行按下键盘“e”按键,进入编辑启动项的界面,如下:


在这个界面下找到linux /boot/vmlinuz-………………那行,在其后面添加如下内容" kgdboc=ttyS0,115200 kgdbwait ",如上图。然后按组合键“Ctrl+x”按照这个配置启动内核(如果你希望每次都这样启动,你可以把这个配置写在grub的启动配置文件里,就不用每次都重新输入了。)。然后就会在看到这样一行内容时停下


        从字面意思就可以明白kgdb在等待远程gdb的连接,下面回到LabDebian-er虚拟机,LabDebian-ee在等待这它的gdb连接。那怎么连接呢,先把你编译调试kgdb的内核代码拷贝到LabDebian-er虚拟机上,然后进入内核目录,执行

                gdb vmlinux

        启动gdb后进入gdb命令界面,在其中输入如下指令:

                (gdb) set remotebaud 115200

                (gdb) target remote /dev/ttyS0

        然后回车,下面就开始你的kgdb之旅吧。注意一定要在编译好的内核源码中这样作,因为gdb调试时需要借助源码,使用过gdb的人应该明白。

        再看看LabDebian-ee已经开始继续启动了,你的kgdb调试正式开始了,这仅仅是一个开始,调试会遇到很多问题,欢迎大家前来留言讨论。

        这里我只写了两个virtualbox虚拟机用kgdb调试的搭建过程,如何用kvm搭建,或者如何用主机和virtualbox虚拟机直接搭建,大家可以自行尝试。应该只在配置串口的地方有差异。欢迎补充、指正。


----------------------         我 是 补 充 内 容 分 界 线          ---------------------------

        好吧,我自己先补充一点。在这次配置的时候我首先是想让host机和虚拟机通过串口连接,但是昨天一天我实在没设置成功,让host机和virtualbox虚拟机的串口连接起来通信。我让虚拟机使用Host device的形式使用/dev/ttyS0,但是这样是行不通的,这样相当于host机和虚拟机共用/dev/ttyS0设备,并不是可以相互通信。那么剩下就是使用host pipe了,使用host pipe能够很简单的实现两个virtualbox虚拟机之间的通信,但是怎么实现host机和虚拟机的通信呢?

        首先通过minicom 能很简单的实现,用这样的工具能够让这个host pipe(就是上面的/tmp/vbox)成为主机和虚拟机间的连接桥梁,但是用gdb怎么指定使用host pipe呢。就像我们上面说的,虚拟机停在等待kgdb连接的地方了,接下来主机怎么通过gdb连接的虚拟机的kgdb?首先我确定这个问题肯定有解,因为minicom 能实现,最最费劲的方法就是自己模仿minicom 写一个程序,让host机的/dev/ttyS0和host pipe文件连接起来。这个办法显然是一个最没办法时的办法,我想首先从gdb的特性和那个host pipe文件的特性下手。让我们看一下。

        在远程连接时gdb使用的是target remote命令,那么target remote在gdb help中的解释是Use a remote computer via a serial line, using a gdb-specific protocol。这显然没有什么帮助,因为我现在不想直接用串口,我是想用那么host pipe文件。那么再看一下那个host pipe文件的属性,简单的先看一下ls -l能告诉我们什么。

                ls -l /tmp/vbox

        得到如下回答:

        srwxr-xr-x 1 zorro zorro 0 10月 25 12:52 /tmp/vbox

        第一个字母就是关键,'s',代表它是一个local domain socket类型的文件。好了,问题范围出来了,能否用gdb连接local domain socket文件。从经验上我们知道gdb可以使用target remote连接到ip port,比如

        (gdb)target remote localhost:4321

        那么可以说其实gdb的target remote就是想找到一个能连接的标准输入输出,一个设备或一个端口等,那么我们就向target remote返回一个stdio就好了,把/tmp/vbox这个local domain socket和gdb的target remote想要的stdio关联起来。这不禁让我想起了强大的socat(socket cat),socket的特点就是在两个流之间建立一个双向的 通道,socket的地址类型很 多,有ip,tcp,udp,pipe,exec,open,等等很多,socat命令里必须有两个流,那么我们可以这样写:

                socat  stdio  unix-connect:/tmp/vbox

        第一个流就是stdio,第二个流就是/tmp/vbox这个domain socket文件,unix-connect:的意思就是说后面那个文件是unix domain stream socket并且接受连接的文件。好了,现在把这个命令和target remote连接起来就成了

                (gdb) target remote | socat  stdio  unix-connect:/tmp/vbox

        如果我们上面的推理都正确,那么那个在苦苦等待着host机连接的kgdb内核就要有希望了。我心里也没底,来实验一下吧。首先说一下执行这条命令前的环境:

        有一个virtualbox虚拟机,对于串口的配置指定了host pipe类型,并选择创建通道,通道文件是/tmp/vbox。然后启动它,按照我们上面说的,它是一个装有kgdb调试内核的系统,启动时加入kgdb启动选项,现在它处于苦苦的等待连接中……(好可怜哭,我们快点成全它和主机吧)

        主机的某个目录下有一个我们编译好的内核的复制本,进入后执行gdb vmlinux,进入gdb命令界面。

                (gdb) set remotebaud 115200

        这句和上面一样,之后就要设置连接了,我之前实验的都不行,比如:

                (gdb) target remote /tmp/vbox

                (gdb) target remote unix:/tmp/vbox

                (gdb) target remote /dev/ttyS0

        以上这三句都不能后虚拟机连接上,唉,可怜的虚拟机。

        下面我们要实验我们上面的推论,那么改为执行:

                (gdb) target remote | socat  stdio  unix-connect:/tmp/vbox

        回车,竟然成功了!!额……我的意思是说果然成功了吐舌头,激动了……没hold住。看到了如下画面,并且还next了几下都没问题:


        真的照作的朋友可能会问,现在虚拟机怎么还没动静?你看看上面的打印函数,还在kgdb里没进入下面的内核启动呢,不信你敲continue命令,你就回看到虚拟机开始启动了,host机嘛……好像我刚才忘设置断点了……。呵呵,现在主机和virtualbox虚拟机通过host pipe连接进行kgdb调试就此配置通过了。

        顺便提一句,现在网上可能有很多kvm和host机用kgdb调试的说明,我看过一些,里面的串口连接大部分是用tcp端口模拟的,在启动虚拟机的时候大部分会有类似如下的选项配置:

                -serial tcp::9876,server

        然后在主机用gdb连接的时候执行:

                (gdb) target remote localhost:9876

        这样做也是可以的,但我们的人中有人提出使用它调试网络驱动时会出现一点问题。我没亲自实验过,对于每一种配置有兴趣的人可以尝试一下,欢迎分享一下体验。


        总结:

        上面我们说了用kgdb调试内核的方法,在搭建环境的时候其实就那么几点:

         1、 配置kgdb和你需要的内核选项。

         2、 编译内核成功。

         3、在一个用于运行kgdb内核的系统上安装编译好的内核。如果你需要编译调试模块,那么你可能还需要把编译好的内核放到例如/usr/src/等目录下,方便你编译模块。

         4、在另一个用于gdb跟踪的系统上拷贝一个编译内核的副本。

         5、在两个系统间建立串口或虚拟的串口连接。

         6、按照kgdb的启动选项启动kgdb内核。

         7、用gdb vmlinux开始调试。

        前五步都是搭建,后两步是执行。它们共性的地方在于内核的配置和编译是差不多的,除了每个人有不同的其它配置要求以外。还有执行是相同的。不同的搭建方法的主要区别就在于串口连接的建立上。那么我们上面说了三种连接方式,算上实体连接其实是四种:

         1、 两台虚拟机之间通过host pipe连接。

         2、host机和虚拟机之间通过host pipe连接。

         3、host机和虚拟机之间通过tcp端口连接。

         4、两台实体机通过串口连接。

        好有什么方法的或上述描述还有什么不合适的地方还请大家补充和指正。谢谢

        

        


原创粉丝点击