部分观点可能过于激进,读者自行取舍。尽管我认为程序是写出来的,不是调出来的(和市场上的观点正好相反),优质的程序应该在模块设计和实现时应保证不出现什么bug。这当然只是理想情况,还得取决于个人水平。程序应在实现阶段得到最大保证,而不是过度依赖调试器。一个彻底的反调试派,据说真正的高手会在cpu旁放一颗爆米花:)转载请保留原创:http://www.cppblog.com/jinglexy,MSN & Email:jinglexy at yahoo dot com dot cn
如果系统处于仿真的初期节点,例如操作系统的初始化,调试还是很有必要的。这里是我在linux环境调试操作系统的相关工具,os开发者可参考,欢迎指正:)
(1)工具安装:
linux安装:bochs-2.3,insight-6.6,gcc-3.4(使用g++和as(binutils包中))
windows安装:Xmanager Enterprise2.1
因为网管没有lotus和clearcase在linux系统下的支持,所以只好用两个操作系统了。这样也比较好,一个用于program,一个用于调试,毕竟bochs挺耗cpu的,就让她干活好了。
bochs安装:./configure --with-all-libs --enable-vbe --enable-gdb-stub && make && make install
insight-6.6安装:包含了tck/tk,gdb-6.6,bfd等工具,使用insight时最好这样设置环境变量:
export LC_ALL=en_US
否则运行时可能会报错:
Tcl_Init failed: can't read "env(TCL_LIBRARY)": no such variable
配置xserver用于远程访问Linux图形界面,这样可以在windows上通过ssh执行linux的图形界面程序。
(2)相关文件:
bochs配置文件添加如下节:
gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
gdb调试脚本:
gdb的命令集可以写入到一个文件中去,这样避免了在启动时输入一大堆命令,100%鼠标操作?faint
保存所有命令到一个文件,每行一个命令,如下:
file ./vmjinix
target remote 127.0.0.1:1234
dir ./arch/i386
dir ./init
dir ./kernel
dir ./drivers
dir ./drivers/video
dir ./drivers/video/console
show dir
break start_kernel
continue
list 0
gdb和gdb前端执行如下:
gdb -q -x gdb.command
insight -q -x gdb.command
其他脚本(磁盘自动创建分区,自动安装grub,拷贝内核,及Makefile脚本),这些贴出来太长,花了好几个小时写好的,需要可以和我联系(MSN & Email:jinglexy at yahoo dot com dot cn)
(3)调试方法
将内核(jinix-1.2.1是我正在编写的一个C++ 开源OS,欢迎参与)拷贝到linux主机,配置samba共享,这样可以在windows上开发(推荐使用slickedit 2007,哪位有linux上的2007版本可否发一个给我)。
使用xshell(ssh方式)登录到linux主机上,编译和调试都在这里了。
在ssh上执行bochs -f bochsrc.txt.linux,
在ssh上执行insight -q -x gdb.command
截图如下:
汇编语言节点也可用使用bochs+gdb调试,在gdb断点时候执行:
disassemble $pc $pc+100(从当前断点处反汇编100字节)
需要注意的是,在os的汇编初始化的前期阶段,分页机制往往未开启,符号和地址不能一一对应,
这个时候不能进行源码级汇编调试,只能用最即便的反汇编调试了。
bochs-2.3中好像有个bug没有解决,nexti执行和stepi在call的时候居然一样,如果要断点到指定行,可以使用物理地址断点。
(4)文章会不断更新,如有什么好的想法可以在原博客讨论:
http://www.cppblog.com/jinglexy
(5)整理的一份常用gdb指令
x /4wx ds:0x1234 x是线性地址空间
xp /4wx 0x1234 xp是物理地址空间
backtrace
print variable 打印变量值
print variable@10 打印变量后面的10个整数值
set variable=2 赋值
whatis variable 显示变量类型
ptype variable 显示数据结构(变量类型加强版)
断点类型:
break init_kernel.cpp:start_kernel 断点在文件的函数
break init_kernel.cpp:101 断点在文件的101行
break init_kernel.cpp:101 if var==100 条件断点
break *0xc0102030
info break 查看所有断点
delete breakpoint 3
delete breakpoint 删除所有断点
isable breakpoint 2
enable breakpoint 2
search string1 搜索字符串,从list结束行开始
reverse-search string1 方向搜索
set history expansion on 使用历史命令
clear 删除刚才停止处的断点
continue 从断点开始继续执行
info break 显示当前断点清单,包括到达断点处的次数等
info files 显示被调试文件的详细信息
info func 显示所有的函数名称
info local 显示当函数中的局部变量信息
info prog 显示被调试程序的执行状态
info var 显示所有的全局和静态变量名称
info all
run
continue
step, next, stepi, nexti i后缀表示执行一条汇编指令
/*********************************************************************************
格式说明 /
/*********************************************************************************
x /nuf addr检查位于线性地址addr处的内存内容,若addr不指定,则默认为下一个单元地址。
xp /nuf addr检查位于物理地址addr处的内存内容。
其中的可选参数n、u和f的分别可为:
n欲显示内存单元的计数值,默认值为1。
u表示单元大小,默认选择为'w':
b (Bytes)1字节;
h (Halfwords)2字节;
w (Words)4字节;
g (Giantwords)8字节。
注意:这些缩略符与Intel的不同,主要是为了与GDB调试器的表示法一致。
f显示格式,默认选择为'x':
x (hex)显示为十六进制数(默认选择);
d (decimal)显示为十进制数;
u (unsigned)显示成无符号十进制数;
o (octal)显示成八进制数;
t (binary)显示成二进制数。
c (char)显示字节代码对应的字符。若不是可显示字符代码,就直接显示代码。
*********************************************************************************/
说明一下:bochs 和 insight本身单个执行就非常慢,而且insight是通过ssh方式链接远程xserver执行,所以速度巨慢,本文所述完全可以全部在linux上操作,不限于平台。