分类 调试 下的文章

什么是OverflowException

就是溢出异常。这个一般是当在线程检查的上下文中执行的算术、强制转换或转换运算导致溢出时引发的异常。

继承



Object

简介

在过去的几年中,崩溃转储成为我们调试活动的一个重要部分。当我们的软件在客户的机器出现故障时,创建应用程序状态的快照并使用在开发人员机器上运行的传统调试器对其进行分析的可能性是非常宝贵的。第一代崩溃转储(通常称为“完全用户转储”)捕获了整个进程虚拟内存的内容。尽管对于事后调试毫无疑问是有用的,但这样的转储常常变得如此巨大,以至于不可能或至少不方便将它们以电子方式传输给软件开发人员。此外,没有以编程方式创建此类转储的公共API,我们必须依赖外部工具(如Dr.Watson或Userdump)来创建它们。
一个新的崩溃转储系列,叫做“minidumps”,与Windows XP一起出现在我们面前。minidump是高度可定制的。在最流行的配置中,小型转储包含的信息刚好足以恢复失败进程中所有线程的调用堆栈,并在失败时检查本地变量的值。这种转储很小(通常只有几千字节),因此很容易以电子方式将它们传输给软件开发人员。但如果需要,小型转储可以包含比旧式崩溃转储更多的信息(例如,小型转储可以包含有关进程使用的内核对象的信息)。此外,可再发行的DbgHelp.dll公开了一个用于以编程方式创建小型转储的公共API,我们不再依赖外部工具。
小型转储的可定制性给我们带来了一个问题:我们需要多少关于应用程序状态的信息才能有效地进行调试,同时使小型转储尽可能小?虽然对调用堆栈和局部变量值的了解通常足以调试简单的访问冲突,但更困难的问题将需要额外的信息。例如,我们可能需要查看全局变量的值,检查堆的完整性,或者分析进程虚拟内存的布局。同时,如果可执行文件本身在开发人员的计算机上可用,则可执行模块的代码部分可能是多余的。
幸运的是,DbgHelp函数(MiniDumpWriteDump和MiniDumpCallback)提供了这样的控制级别,甚至更多。在本文中,我们将探讨如何使用这些函数来创建小转储,这些小转储虽然小,但仍然包含足够的信息,可以进行有效的调试。我们还将看到小型转储中可以包含哪些类型的数据,以及如何使用流行的调试器(WinDbg和VS.NET)来查看这些数据。

Minidump Types

让我们从一些代码开始。图1包含MiniDumpWriteDump函数的声明,图2显示了如何使用该函数创建一个简单的minidump。

 Figure 1:

BOOL MiniDumpWriteDump(
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

Figure
2:void CreateMiniDump( EXCEPTION_POINTERS*pep )
{
//Open the file HANDLE hFile= CreateFile( _T("MiniDump.dmp"), GENERIC_READ |GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );if( ( hFile != NULL ) && ( hFile !=INVALID_HANDLE_VALUE ) )
{
//Create the minidump MINIDUMP_EXCEPTION_INFORMATION mdei;

mdei.ThreadId
=GetCurrentThreadId();
mdei.ExceptionPointers
=pep;
mdei.ClientPointers
=FALSE;

MINIDUMP_TYPE mdt
=MiniDumpNormal;

BOOL rv
=MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
hFile, mdt, (pep
!= 0) ? &mdei : 0, 0, 0);if( !rv )
_tprintf( _T(
"MiniDumpWriteDump failed. Error: %u \n"), GetLastError() );else_tprintf( _T("Minidump created.\n") );//Close the file CloseHandle( hFile );

}
else{
_tprintf( _T(
"CreateFile failed. Error: %u \n"), GetLastError() );
}

}

Memory窗口可用于查看特定位置的原始内存数据。前面介绍的Watch、Autos和Locals窗口可以查看位于内存特定位置的变量值,而Memory窗口则会显示存储在内存中的数据概貌。

打开窗口

  • 通过菜单栏打开
  • 快捷键打开
    Ctrl+Alt+M 数字(1...4)
    按下组合键后,状态栏如下

     

     然后在按数字键
    Memory 1多一组快捷键 Alt+6

窗口的使用

 

窗口的上方是工具栏:

  • Address
    要观察虚拟地址值,十六进制。可以将代码里的某变量按住拖动到地址栏,也可以手动填写,当你填写为十进制数时会制动转为十六进制
  • Columns
    内存数据按字节分列,这里可以设置一屏显示的列数,有如下选择

左侧是以观察的地址为首地址的地址列

 

右侧是滚动条,用户可以使用它观察周边位置的内存数据。唯一要注意的是它的使用并不常见。电脑的内存空间很大,因此如果内存包含标准的滚动栏,就很难在内存中找到指定的内容。因此,Memory窗口使用弹簧式滚动条,该滚动条的手柄一直将保持在列表中间。

右键点击窗口区域,弹出如下菜单

 

简介

调试应用程序时,调试器必须加载可执行模块的符号,以便能够显示有意义的调用堆栈、当前源代码行、变量值等。如果您曾经调试过在另一个系统上创建的小型转储,那么您已经知道除了符号之外,调试器还需要访问创建转储时由应用程序加载的相同版本的模块。如果调试器找不到与模块完全相同的版本(即匹配的模块),则无法加载模块的符号,从而严重限制了成功调试的可能性。在本文中,我们将讨论VS.NET和WinDbg调试器用来识别和定位匹配模块的规则。我们将看到如何告诉调试器在哪里查找匹配的模块。我们还将讨论无法找到匹配模块的情况,并尝试找到解决方法。

模块匹配

调试器如何决定模块是否匹配?他们如何知道需要什么版本的模块?让我们看看Minidump里面。每个小型转储都包含转储进程加载的模块列表。此列表存储为小型转储模块结构的数组(如DbgHelp文档中所述)。此结构声明如下:

typedef struct_MINIDUMP_MODULE {
ULONG64 BaseOfImage;
ULONG32 SizeOfImage;
ULONG32 CheckSum;
ULONG32 TimeDateStamp;
RVA ModuleNameRva;
VS_FIXEDFILEINFO VersionInfo;
MINIDUMP_LOCATION_DESCRIPTOR CvRecord;
MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;
ULONG64 Reserved0;
//Reserved for future use. ULONG64 Reserved1; //Reserved for future use. } MINIDUMP_MODULE, *PMINIDUMP_MODULE;