如何从内核调试器访问用户模式调试器
在某些情况下,您可能希望使用用户模式调试器从内核调试器中调试进程。它可能是你有一个加载内核模式驱动程序的应用程序,并且你希望能够调试应用程序的用户模式方面,然后进入内核以跟踪对内核的调用。
这是你需要做的!
通过串行电缆(调制解调器电缆)、USB电缆或FireWire电缆连接内核调试器,并将您的计算机配置为内核调试。要在Vista或Windows 2008上启用调试选项,必须使用bcdedit.exe文件因为那些操作系统不再使用启动.ini文件。下面是一个例子:
bcdedit /debug {<guid>} <ON | OFF>bcdedit/dbgsettings SERIAL DEBUGPORT:1 BAUDRATE:115200
转储或实时系统的远程(内核或用户模式)调试
在某些情况下,我们会执行实时调试以确定服务器故障的根本原因。在内存转储的情况下,文件通常非常大,因此通过网络复制文件,即使是在最快的广域网连接上,也可能需要很长时间。解决方案是远程调试。
你应该怎么做?
首先,需要两个人:一个是远程人员,另一个是通过使用安装在他们机器上的调试器调试进程来帮助远程用户的专家。
假设你是帮助远程人的专家。流程如下:
1、远程用户使用windbg打开转储文件、调试进程或内核调试远程位置的计算机。
2、远方的人决定,“我需要帮助!”
3、远程用户只在windbg提示符下需输入..server tcp:port=9999。
注意下面的输出。
服务器已启动。客户机可以使用这些命令行中的任何一个进行连接
0: <debugger> -remote tcp:Port=9999,Server=MyServerName
!address -summary报错"The current target does not provide full memory information. No meaningful summary available. "
今天分析一个dmp文件时,想看下内存的使用情况,于是执行!address -summary,结果却有如下输出:
0:000> !address -summary
The current target does not provide full memory information. No meaningful summary available.
Use !address with no arguments to display the available virtual memory map of the target.
之前收集到的dmp文件没有这样问题,于是问了下支持的同事,这个dmp文件的获取方法是怎样的。支持的同事反应不是自己抓的,是我们的程序自动产生的。看来程序在生成dmp时,Minidump的的选项设置有问题。
用工具打开dmp看看
赶紧查看一下代码
果然设置选项不够,赶紧加上MiniDumpWithFullMemoryInfo选项,制造一个异常,在看看
用Windbg加载,并执行!address -summary
0:000> !address -summary
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free 123 75782000 ( 1.835 GB) 91.78%
<unknown> 124 5954000 ( 89.328 MB) 53.04% 4.36%
Image 306 3c17000 ( 60.090 MB) 35.68% 2.93%
Heap 42 c96000 ( 12.586 MB) 7.47% 0.61%
Stack 18 600000 ( 6.000 MB) 3.56% 0.29%
Other 8 5c000 ( 368.000 kB) 0.21% 0.02%
TEB 6 e000 ( 56.000 kB) 0.03% 0.00%
PEB 1 3000 ( 12.000 kB) 0.01% 0.00%
--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_MAPPED 57 5497000 ( 84.590 MB) 50.22% 4.13%
MEM_IMAGE 327 3c83000 ( 60.512 MB) 35.93% 2.95%
MEM_PRIVATE 121 1754000 ( 23.328 MB) 13.85% 1.14%
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE 123 75782000 ( 1.835 GB) 91.78%
MEM_COMMIT 447 8197000 ( 129.590 MB) 76.94% 6.33%
MEM_RESERVE 58 26d7000 ( 38.840 MB) 23.06% 1.90%
--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READONLY 188 3f7c000 ( 63.484 MB) 37.69% 3.10%
PAGE_EXECUTE_READ 68 2f22000 ( 47.133 MB) 27.98% 2.30%
PAGE_READWRITE 144 11be000 ( 17.742 MB) 10.53% 0.87%
PAGE_WRITECOPY 29 666666000 ( 1.066 MB) 0.63% 0.05%
PAGE_READWRITE|PAGE_GUARD 12 1e000 ( 120.000 kB) 0.07% 0.01%
PAGE_EXECUTE_READWRITE 4 a000 ( 40.000 kB) 0.02% 0.00%
PAGE_NOACCESS 2 2000 ( 8.000 kB) 0.00% 0.00%
--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free 7eb0000 56d90000 ( 1.357 GB)
<unknown> 4e00000 2000000 ( 32.000 MB)
Image 75231000 54d000 ( 5.301 MB)
Heap 7a79000 376000 ( 3.461 MB)
Stack 37e0000 fd000 (1012.000 kB)
Other 7fcc0000 33000 ( 204.000 kB)
TEB e19000 3000 ( 12.000 kB)
PEB e16000 3000 ( 12.000 kB)
看到了内存的摘要信息了。
虽然我们希望dmp文件尽量小,但一些必要的信息还是需要的。
.Net中的异常处理:高级异常
这种处理的有效性主要取决于所选择的语言和平台,因此,详细了解它们的正确用法和行为非常重要,这样我们的用户和其他开发人员在诊断代码中的问题时免受痛苦。
在本文中,我们将了解C和.NET在错误处理方面的作用。
词汇表
CLR:公共语言运行时的缩写,是.NET运行时,它负责执行用所有.NET语言编译的应用程序。除了虚拟机和实时编译器之外,它还具有额外的职责,如内存管理、安全性等。
BCL:Base Class Library的缩写,是.NET framework的核心库。除了直接使用CLR操作之外,它还公开了原始数据类型和构建和运行应用程序的基本功能。也称为mscorlib。
FCL:Framework类库的缩写,是我们大多数人在.NET中所知道的“框架”。使用BCL作为构建块,它公开了大量具有各种特性的名称空间,比如系统IO, 系统安全, 系统文本,等等。
TPL:Task Parallel Library的缩写,是一个包含由异步关键字和API提供的功能的库。它是随着.NET版本4.5和C#5一起发布的。
SEH:Structured Exception Handling的缩写,是Windows的原生异常子系统,它在操作系统级别处理软件和硬件异常
MDA:托管调试助手的缩写。这些是特殊的调试扩展,向VisualStudio调试器提供与CLR执行状态相关的信息,后者由内部助手和资产公开。
重新审查异常
在.NET中,尤其是在C#中,异常是使用try、catch和finally块来处理的。首先,try块将包含预期引发异常的代码,其次是catch块,它将指定异常类型和在try块内引发与指定类型匹配的异常时将执行的代码块:
Random rnd = newRandom();try{
Console.WriteLine(1 / rnd.Next(-100, 101));
}catch(DivideByZeroException ex) {
Debug.WriteLine(“A division by zero attempt has occurred”);
}
调试优化x64代码的挑战
如果到目前为止您还没有机会调试优化的x64代码,请不要再等待太久,也不要落后于时代!由于类似x64 fastcall的调用约定加上大量的通用寄存器,在调用堆栈中的任意点查找变量值确实非常困难。
在本文中,我想详细介绍一些我最喜欢的调试优化x64代码的技术。但是在深入研究这些技术之前,让我们先对x64调用约定有一个快速的概述。
x64调用约定
熟悉x86平台上fastcall调用约定的人将认识到与x64调用约定的相似之处。通常,您必须了解x86平台上的多个调用约定,而在x64平台上,目前只有一个。在这种情况下,通过__declspec(naked)调用(当然不包括直接调用)可以实现编码
我不会详细介绍x64呼叫约定的所有细微差别,因此我建议您查看以下链接(http://msdn.microsoft.com/en-us/library/ms794533.aspx). 但是通常,函数的前四个参数是通过寄存器rcx、rdx、r8和r9传递的。如果函数接受四个以上的参数,则这些参数将传递到堆栈上。(熟悉x86 fastcall调用约定的人,其中前两个参数是在ecx和edx中传递的,熟悉这种约定的人会认识到它们的相似之处)。
为了帮助说明x64调用约定是如何工作的,我创建了一些简单的示例代码。虽然代码是人为设计的,与真实世界中的代码相去甚远,但它演示了在实际世界中可能遇到的一些场景。代码如下所示。
#include <stdlib.h>#include<stdio.h>#include<windows.h>__declspec(noinline)voidFunctionWith4Params(int param1, int param2, intparam3,intparam4 )
{
size_t lotsOfLocalVariables1=rand();
size_t lotsOfLocalVariables2=rand();
size_t lotsOfLocalVariables3=rand();
size_t lotsOfLocalVariables4=rand();
size_t lotsOfLocalVariables5=rand();
size_t lotsOfLocalVariables6=rand();
DebugBreak();
printf("Entering FunctionWith4Params( %X, %X, %X, %X )\n",
param1, param2, param3, param4 );
printf("Local variables: %X, %X, %X, %X, %X, %X \n",
lotsOfLocalVariables1, lotsOfLocalVariables2,
lotsOfLocalVariables3, lotsOfLocalVariables4,
lotsOfLocalVariables5, lotsOfLocalVariables6 );
}
__declspec(noinline)voidFunctionWith5Params(int param1, int param2, intparam3,int param4, intparam5 )
{
FunctionWith4Params( param5, param4, param3, param2 );
FunctionWith4Params( rand(), rand(), rand(), rand() );
}
__declspec(noinline)voidFunctionWith6Params(int param1, int param2, intparam3,int param4, int param5, intparam6 )
{
size_t someLocalVariable1=rand();
size_t someLocalVariable2=rand();
printf("Entering %s( %X, %X, %X, %X, %X, %X )\n","FunctionWith6Params",
param1, param2, param3, param4, param5, param6 );
FunctionWith5Params( rand(), rand(), rand(),
param1, rand() );
printf("someLocalVariable1 = %X, someLocalVariable2 = %X\n",
someLocalVariable1, someLocalVariable2 );
}intmain(int /*argc*/, TCHAR** /*argv*/)
{//I use the rand() function throughout this code to keep//the compiler from optimizing too much. If I had used//constant values, the compiler would have optimized all//of these away. int params[] ={ rand(), rand(), rand(),
rand(), rand(), rand() };
FunctionWith6Params(params[0], params[1], params[2],params[3], params[4], params[5] );return 0;
}