cc682/NetRoc
http://netroc682.spaces.live.com/
调试器操作(用户模式)
本节包含以下主题:
被创建的进程行为
控制进程和线程
重新附加到目标进程
调试托管代码
被创建进程的行为
由调试器创建的进程(也称为产生的进程)的行为和不是调试器创建的进程有轻微不同。
调试器创建的进程使用特殊的调试堆,而不是使用标准的堆API。在Microsoft Windows XP和之后版本Windows中,可以通过_NO_DEBUG_HEAP 环境变量或 -hd
命令行选项强制子进程使用标准堆。
同样,由于子目标程序是调试器的子进程,所以继承了调试器的权限。这种权限可能使得目标进程能做一些通常情况下不能进行的操作。例如,目标进程可能可以影响其它被保护的进程。
控制进程和线程
当进行用户模式调试时,可以激活、显示、冻结、解冻、挂起、恢复进程和线程。
当前正被调试的进程称为当前活动进程。类似的,当前调试器正在控制的线程称为当前线程或活动线程。很多调试器命令的行为由当前进程和当前线程决定。
调试开始时,当前进程是调试器附加到的进程或因为异常中断到调试器的进程。同样,当前线程是当调试器附加到进程时的线程或产生异常的线程。但是,可以利用调试器改变当前线程和进程,也可以分别冻结或解冻线程。
在内核模式调试下,不使用本节描述的方法来控制线程和进程。关于在内核模式下操作进程和线程的更多信息,查看改变上下文。
显示进程和线程
使用如下方法显示进程和线程信息:
设置当前进程和当前线程
使用如下方法改变当前进程和线程:
冻结和挂起线程
调试器可以通过挂起或冻结线程来改变它的执行。这两种操作有一些不同的地方。
每个线程都有一个关联的挂起计数(suspend count)。如果这个数字是大于等于1,则系统不会运行该线程。如果计数小于等于0,系统会在适当的时机运行该线程。
一般来说,每个线程的挂起计数都是0。当调试器附加到进程时,会将它的所有线程的挂起计数加1。如果调试器停止对进程的附加,会将所有挂起计数减1。当调试器执行进程时,会临时将所有的挂起计数减少1。
使用下面一些方法可以用调试器控制任何一个线程的挂起计数:
一般用这些命令来将指定线程的挂起计数从1加到2。当调试器执行或停止附加进程时,该线程由于挂起计数为1,即使进程中其他线程都开始执行,该线程仍然保持挂起。
在进行非侵入式调试时也可以挂起线程。
调试器也可以冻结线程。该行为和以某些方式挂起线程类似。但是,"冻结"仅仅是一种调试器设置。Windows系统不会知道该线程有任何不同点。
默认情况下,所有线程都是非冻结的。当调试器运行进程时,被冻结的线程不会运行。但是,当调试器停止对该进程的附加时,所有线程都会变为非冻结状态。
使用下面一些方法来冻结和解冻各个线程:
在任何情况下,当调试器中断目标时,该进程中的所有线程永远不会被执行。线程的挂起计数仅在调试器运行进程或者停止进程附加时有效。冻结状态仅在调试器运行进程时有效。
其他命令中的线程和进程
在很多其他命令中也可以指定进程或线程。更多信息,查看各个命令的主题。
在很多命令和扩展命令前都可以加上~e (Thread-Specific Command) 限定词。该限定词使得命令对指定线程起作用。在想对一个以上线程使用某个命令时它非常有用。例如,下面的命令对被调使得所有线程使用!gle 扩展命令。
~*e !gle
多系统
调试器可以同一时刻附加到多个目标。当这些处理包含不止一台计算机上的dump文件或活动目标时,调试器的每个行为都以一个系统、进程和线程为基准。关于这类调试的更多信息,查看调试多个目标。
重新附加到目标程序
如果调试器在用户模式调试时冻结了,或因为其他原因停止响应(即崩溃),可以将一个新调试器附加到已存在的进程上。
注意 该方法仅在Microsoft Windows XP和之后版本Windows中支持。该方法不管进程是调试器创建的还是附加上去的,也不管是否使用了-pd选项。
使用以下步骤来重新将调试器附加到目标程序:
- 对目标程序确认进程ID。
打开一个新的CDB或WinDbg。使用-pe命令行选项。
Debugger -pe -p PID
也可以使用其他命令行选项。
可以在一个静止的调试器中使用.attach (Attach to Process) 命令和-e选项。
- 附加完成后,结束原来的调试器进程。
- 如果进程没有正常响应,可能是因为挂起计数太高。可以使用~m (Resume Thread) 命令来减少挂起计数。关于挂起计数的更多信息,查看控制进程和线程。
如果原来的调试器还在正常工作,该方法可能无效。两个调试器会竞争调试事件,并且Windows操作系统没有必要将所有调试事件都通知新调试器。
如果原来的调试器在附加新调试器之前就已经结束,目标程序也会被结束掉。 (但是,如果调试器以-pd选项附加上去并正常结束,目标程序会继续运行。这种情况下,第二个调试器可以不使用-pe选项附加到目标程序。)
如果已经在调试一个进程并想停止附加,但保持进程在调试状态冻结,可以使用.abandon (Abandon Process) 命令。该命令之后,任何Windows调试器都可以用本主题描述的方法重新附加到进程上。
调试托管代码
WinDbg、CDB和NTSD也可以对包含托管代码的目标程序进行有限制的调试。
介绍托管代码
托管代码是和Microsoft .NET 公用语言运行时(CLR)一同执行的代码。.NET CLR 管理程序的原始代码和数据,并且提供类似垃圾回收和平台无关代码这样的高级支持。
需要该运行时的编译后代码称为托管代码。不需要该运行时的代码成为非托管代码。只包含托管代码的应用程序称为托管应用程序。
托管的.NET应用程序可以在支持.NET CLR 的任何平台上运行,因为编译器生成的二进制代码是平台无关的。托管程序中的二进制代码是Microsoft中间语言(Microsoft intermediate language (MSIL))。这种二进制代码还包含对象信息和其他引用(称为元数据)。
托管应用程序和传统的应用程序有所不同,因为很多程序执行的细节是在运行时决定的,例如数据结构如何分布和本地代码如何生成和使用。当这种程序执行的时候,运行时决定程序运行的数据使用和代码使用,生成和平台相关的本地代码。从MSIL产生本地代码的过程称为托管或just-in-time (JIT) 编译(有时也称JITting)。运行时用来进行这种翻译的组件成为JIT编译器。
当JIT编译器为某个方法编译了MSIL后,该方法的存根被编译后代码的地址取代。不管之后这个方法什么时候被调用,执行的都是本地代码,而JIT编译器不需要再重复这个步骤。
构建托管代码
可以使用各个软件厂商提供的各个编译器来构建托管代码。特别是,Microsoft Visual Studio .NET可以使用4种不同语言来生成托管代码:
- 带托管扩展的C++
- C#
- Visual Basic
- JScript
默认的,Microsoft Visual C++ .NET不会构建托管应用程序。必须通过图形界面或命令行开关来指定这样的构建。
调试托管代码
可以使用Sos.dll 扩展来调试托管代码。该扩展在Windows调试工具包安装目录的\clr10 子目录下。
要使用该扩展,可以加载它之后输入!clr10\sos.help。
该命令会列出所有可用的扩展命令和它们的参数。