Page 1 of 1

ros 基本调试 wiki翻译

Posted: Thu Mar 03, 2011 5:22 am
by reactoscn
原文地址:http://www.reactos.org/wiki/Debugging
欢迎拍砖。。
本文讲述了调试ROS的各种方法和必要步骤。
目录
1.简介
2.可用的调试方法
2.1通过文本消息调试
2.1.1串行端口
2.1.1.1虚拟机
2.1.1.2真实电脑——物理串行线
2.1.1.3基于freebsd的串行终端
2.1.2将调试文字输出到文件
2.1.3将调试文字输出到屏幕
2.1.4修改波特率(数据传输速率)
2.2KDBG
2.3GDB
2.4WINDBG
3.生成更多输出
3.1编译时开启verbosity功能
3.1.1 reactos类型
3.1.1.1加入我们自己的debug消息
3.1.2 wine的类型
3.2 运行时开启verbosity
3.3 进入内置的内核调试器
3.3.1 动态调试
3.3.1.1 在用户态异常中断
3.3.2 静态调试
3.4 生成backtrace
3.5 翻译地址
3.6 开启内核trace
简介
如果你想为ros做贡献——不论是加入到源代码的开发中,还是帮助测试ros的关键部分——你都需要了解如何生成重要的调试日志。
对于开发者来说,如果想准确地找出和判断ros的运行状态,调试信息是非常重要的。从操作系统得到默认的调试输出很容易,但是这些输出对于定位问题尤其是bugs没有太大作用。
本文旨在告诉读者如何生成有用的调试信息,通过这些信息可以直接判断出操作系统的运行状态。
几种调试方法
调试ros的方法有很多,它们都被列举在下面,其中有些方法比其他方法需要更多的知识。
通过文本信息调试
如果要从ros接收调试信息,这是最简单的方式。
串行端口
如果想从ros中接收调试信息,串行端口无疑是最常用的一种方法。基于你是在虚拟机还是在真实电脑上运行ros,接收信息有不同的方法。如果你计划在虚拟机上运行,为了和虚拟串口建立连接,应该使用com0com(http://www.reactos.org/wiki/Com0com),而非命名管道。
虚拟机
如果想从虚拟机获得串口输出,可以访问下面虚拟机调试专栏
QEMU http://www.reactos.org/wiki/QEMU#Grabbi ... g_messages
VirtualBox http://www.reactos.org/wiki/VirtualBox# ... bug_output
VMware http://www.reactos.org/wiki/VMware#Getting_debug_output
真实电脑——物理串口线
如果想通过串口从真实电脑接受调试信息,你需要有一条物理串口线。这种方法还需要你有两台电脑(一台安装ros,另一台接受调试信息)。
需要一条名为“Null-Modem”的串口线,你可以在电脑公司以低于10美元的价格买到(这个东西。。。反正我是从来没听说过。。)当然,你也可以选择自己制作一条,请访问 http://jesusnjim.com/electronics/reacto ... cable.html

用这条线将两台电脑的第一个串口连接起来。
在接受调试信息的电脑上使用类似PUTTY的终端程序或者是windows自带的超级终端,将设置修改为以115200的波特率监听第一个串口(COM1[3F8/IRQ4])。然后,在测试电脑上运行ros,你就可以接收到调试信息了。如果接收不到,请检查硬件和freeldr.ini的配置。
串行硬件比较复杂,请耐心一点。下面几条请牢记:
■计划好哪个连接是DTE,哪个是DCE,它们各包括什么类型,牢记你在这两台电脑上各使用了哪个端口(1还是2)。
■制作正确的null modem线。制作null modem线有多种方法,采用不同的方法做出来的线都是不一样的。
■使用的线越短越好。
■使用HyperTerminal或Minicom等串行终端程序来接受调试信息,如果收不到调试信息,请检查上面几步。
■GDB远程命令以$开始,以;结束。这样你才能识别出调试信息。
注意:如果你用的是HyperTerminal,应该使用下列设置:
■比特数:115200
■据位:8位
■数:无
■位:1
■控制:硬件
FreeBSD上的串行终端
在终端输入:
$ sudo cu -s 115200 -e -o -t -l /dev/cuau0
其中的/dev/cuau0是你的串口设备名称,可以在dmesg命令的输出中找到COM口的正确名称。
将调试文本输出到文件
在r43333及以后版本,可以在启动菜单中选择ReactOS(Log file)。这样调试信息就会被写入到名为debug.log的文件中去
这种方法有一定局限性,那就是致命的系统错误信息不会被写入到文件中去。
将调试文本输出到屏幕
将freeldr.ini修改为如下形式,使之含有一个入口。
——————————————————————————————————
[ReactOS_Debug]
BootType=ReactOS
SystemPath=multi(0)disk(0)rdisk(0)partition(1)\ReactOS
Options=/DEBUG /DEBUGPORT=SCREEN /SOS
——————————————————————————————————
修改波特率
如果你认为115200太慢而且你的串行连接支持像虚拟串口一样更高的传输速度的话,你可以修改它。
1、打开位于ros安装根目录的freeldr.ini。
2、定位到[ReactOS_Debug]。
3、修改"/BAUDRATE=921600"这里(在hyperterminal和putty中已测试成功)。
4、保存文件。
5、修改你使用的终端程序的波特率。
KDBG
进入此链接(http://www.reactos.org/wiki/Kdbg)以查看关于内置内核调试器的更多信息。
GDB
欲使用GDB作为内核调试器,请看这里(http://www.reactos.org/wiki/GDB)。
所需要的:
■GDB(已经被包含在ros编译环境中,http://www.reactos.org/wiki/Build_Environment
■QEMU http://www.reactos.org/wiki/QEMU
启动QEMU时,加入如下命令行参数:
-s -S
则QEMU就会以“停止”状态启动,此状态允许你使用GDB进行连接,然后我们再启动GDB。
(假设你正处在rosBE的命令行下),输入“gdb”以启动gdb。
输入“file ./output-i386/ntoskrnl/ntoskrnl.exe”来告知gdb去哪里载入关于内核的信息。
如果你喜欢intel的语法,输入“set disassembly-flavor intel”。
输入“target remote localhost:1234”以将GDB和QEMU连接。
输入“c”(意为继续)来让GDB指示QEMU开始(或是继续)模拟的运行。
若要人工暂停执行,确保gdb正拥有焦点,按下<CTRL>+<C>。
WinDbg
更多关于windbg的信息请访问: http://www.reactos.org/wiki/WinDBG
Windbg
更多关于windbg的信息请看这里。
windbg被支持得还不是很好。在x86架构上你需要一个win2003版本的kdcom.dll文件,编译的时候还需要加上WINDK=1开关。而在64位的ros上,windbg是最主要的调试方法,和x64版本的kdcom.dll有很好的兼容性。
生成更多输出
如果想要得到更多有意义的输出,有必要额外开启verbosity功能。
编译时开启verbosity
几乎所有的ros模块都使用了内置的"ros类型"调试功能,"ros类型"有如下特点:
• 每个文件通常都定义了verbosity级别。
• 一共只有两个消息级别。
1. 总是可用(DPRINT1)
2. 仅当NDEBUG未被定义时才可用(DPRINT)
通过下面这两行代码,遵循以上特点的文件可以被轻易识别出来。
#define NDEBUG
#include 〈debug.h〉
如果想要完全开启verbosity功能,只需要将"#define NDEBUG"这行注释掉即可,注意在提交补丁时要将注释符号去掉。
加入我们的调试消息
首先要确保已经包含了debug.h头文件
#include 〈debug.h〉
使用DPRINT或DPRINT1,两者功能都和printf类似,但是代码不同 。
WINE格式
在boot/bootdata/hivesys.inf 此文件中,提供了在用户程序中开启debug channel的例子
; Debug channels
HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\Environment","DEBUGCHANNEL",0x00020000,"+ole,+rpc"
运行时开启verbosity
在任何特殊的组件的编译过程中开启调试verbosity的最简单方式是使用DEBUGCHANNEL环境变量. 例如,如果想从MSI中得到所有调试消息,只需在CMD下运行:
set DEBUGCHANNEL=+msi
然后,运行你想测试的程序. 可以从多种组件添加调试消息,例如:
set DEBUGCHANNEL=+msi,+rpc,+ole
可以在下面的链接中获得一份可被调试的组件的清单。
http://wiki.winehq.org/DebugChannels
关闭CMD后, 调试verbosity将会恢复成默认值。
<这里描述关于如何设置verbosity级别和开启所有组件的调试的细节>
进入内置的内核调试器
当操作系统不能再正常运行时,为了避免破坏数据,错误检查就产生并终止系统的运行了。通常系统会出现蓝屏,但如果内核调试器处在激活状态,系统会提示你如何去得知系统现在的状态。默认情况下,ros的debug编译版本会开启内置的内核调试器——KDBG(http://www.reactos.org/wiki/Kdbg),而release版本不会有这一功能,只会出现蓝屏。
如果要强制进行错误检查,可以采取下面两种方式:
动态方式
若你拥有debug编译版本的ros,欲停止系统运行并启动内核调试器,可以用键盘输入:
tab+k
注意默认状态下kdbg的输出是通过串口,但接受输入是通过键盘。
若想将接受输入方式也改为通过窗口,需要在启动ros时加入启动选项"ReactOS (RosDbg)",或是在freeldr.ini文件中的启动选项中加入 /KDSERIAL 命令。
处理用户态异常
可以设置KDB处理用户态异常的方式。分别为never,umode,kmode和always。
 never kdbg不会处理发生的异常。
 umode kdbg会处理用户态异常。
 kmode kdbg会处理内核态异常。
 always kdbg会处理所有异常。
若想将kdbg默认的处理方式”kmode”修改为”always”,可以打开调试器并输入:
set condition * first always
输入”cont”以继续运行。
静态方式
当操作系统运行到你想调试的地方时,这种方式可以停止系统的运行,并立即得到backtrace从而知道哪些代码出现了问题。
此方式是通过在代码中调用KeBugCheck()或ASSERT()来实现的。
生成backtrace
欲生成backtrace,需要进入KDBG的命令提示状态,输入”bt”,并按<return>键,会得到如下结果:
(drivers\filesystems\vfat\rw.c:809) <\ReactOS\system32\kernel32.dll>
Entered debugger on embedded INT3 at 0x0008:0x800935f2.
kdb:> bt
Eip:
<ntoskrnl.exe:935f3 (lib\rtl\i386\debug_asm.S:31 (DbgBreakPoint@0))>
Frames:
<vfatfs.sys:97de (drivers/filesystems/vfat/misc.c:111 (VfatDispatchRequest))>
<vfatfs.sys:9b25 (drivers/filesystems/vfat/misc.c:167 (VfatBuildRequest))>
<ntoskrnl.exe:3ab23 (ntoskrnl/io/iomgr/irp.c:1088 (IofCallDriver))>
<ntoskrnl.exe:36206 (ntoskrnl/io/iomgr/iofunc.c:686 (IoSynchronousPageWrite))>
<ntoskrnl.exe:59daa (ntoskrnl/mm/section.c:6330 (MmspWriteDataSectionPages))>
<ntoskrnl.exe:244c6 (ntoskrnl/ex/work.c:162 (ExpWorkerThreadEntryPoint))>
<ntoskrnl.exe:70e90 (ntoskrnl/ps/thread.c:134 (PspSystemThreadStartup))>
<ntoskrnl.exe:7b142 (ntoskrnl\ke\i386\ctxswitch.S:258 (KiThreadStartup@156))>
kdb:>
bt命令会显示出当前线程的backtrace,所以你还需要使用”thread attach”命令。请阅读kdbg手册以获得更多细节。(http://www.reactos.org/wiki/Kdbg
仔细观察后可发现,错误检查是通过INT3 操作产生的。下一行表明Eip指向了系统停止之前的最后地址。
紧跟其后的便是框架。这是生成backtrace的重要组成部分,包含了生成错误检查所需要的所有函数地址。对开发者了解codeflow是非常重要的信息。
翻译地址
偶尔我们需要手动翻译地址。当错误检查产生,而kdbg又没有开启时,系统会显示类似下面的堆状态:
(subsystems\win32\csrss\win32csr\conio.c:1101) Console_Api Ctrl-C

*** Fatal System Error: 0x00000001
(0x80079279,0x00000000,0x0000FFFF,0x00000000)

<\SystemRoot\System32\NTOSKRNL.EXE: 29bb>
<\SystemRoot\System32\HAL.DLL: 4749>
<\SystemRoot\System32\NTOSKRNL.EXE: 54cb4>
<\SystemRoot\System32\NTOSKRNL.EXE: 582bf>
<\SystemRoot\System32\NTOSKRNL.EXE: 583fd>
<\SystemRoot\System32\NTOSKRNL.EXE: 89956>
<\SystemRoot\system32\drivers\videoprt.sys: 2417>
<\SystemRoot\system32\drivers\vbemp.sys: 17f5>
<\SystemRoot\system32\drivers\vbemp.sys: 19cf>
<\SystemRoot\system32\drivers\videoprt.sys: 1c48>
<\SystemRoot\System32\NTOSKRNL.EXE: 34c17>
<\SystemRoot\System32\NTOSKRNL.EXE: 21e0>
<\SystemRoot\System32\NTOSKRNL.EXE: 2908>
<\SystemRoot\System32\NTOSKRNL.EXE: 29bb>
<\SystemRoot\System32\NTOSKRNL.EXE: 85fa8>
这结果和kdbg的”bt”命令得到的结果十分类似。问题是,这里只给出了地址。因为对每个人的编译结果来说,这些地址都是不一样的,这些信息对于想要知道系统状态的人来说没有什么用。
解决上面问题的办法是将地址翻译为可读的函数名,通过一个名为“raddr2line”的工具即可。这个工具是从unix里的“addr2line”修改而来的。它会将输入的地址转换为文件名和行号。通过使用可执行文件的调试信息,它可将地址翻译为可读信息并输出到控制台中。这些信息可以和堆状态地址一同复制到调试日志中去。
Raddr2line被包含在了ros编译环境中,使用方法如下:
raddr2line <file> <address>
我们转换一下上面堆状态中最后的地址:
C:\Users\Ged\MyFiles\ReactOS\clean_source>raddr2line ntoskrnl.exe 85fa8

C:\Users\Ged\MyFiles\ReactOS\clean_source\output-i386\ntoskrnl\ntoskrnl.exe
obj-i386\ntoskrnl\ex\zw.S:253 (ZwClearEvent)
地址0x85fa8翻译结果是文件 ntoskrnl\ex\zw.S的第253行(各人的结果可能不同)
这些信息可以被加到上面的堆状态中,如下:
<\SystemRoot\System32\NTOSKRNL.EXE: 29bb> <enter next one here>
<\SystemRoot\System32\NTOSKRNL.EXE: 85fa8> obj-i386\ntoskrnl\ex\zw.S:253 (ZwClearEvent)
开启内核tracing
欲知如何开启内核tracing,请看这篇文章(http://www.reactos.org/wiki/Enable_kernel_tracing
如何阅读或调试错误检查产生的信息
错误检查代码的参数其16进制定义在:
\include\reactos\mc\bugcodes.mc
or, generated .h-version for i386: \obj-i386\include\reactos\bugcodes.h
有些信息有详细的说明。