转储的英文单词是dump,但这边我们说的dump不是SQL Server本身的DUMP备份命令,而是指通过sqldumper.exe中的dump。那什么是dump呢,dump指的是将某种
内容转换为另外一种更具可读性的方式。在ORACLE中,有专门的dump命令可以dump出数据文件等的内容,其trace也相当于另外一种dump。通过dump,我们便可以了解
整个系统的运行原理。
SQL Server这方面的资料很少,当然,这也符合了微软不开源的策略。不过这几年来,关于这方面的资料比较多了,通过google可以获得相关的内容。
最早对此感兴趣的是碰到了很多人经常问的.mdmp文件,mdmp的叫mini dmp,也可以叫memory dmp,这是由于SQL Server 在运行过程中,遇到了一些bug或者错误而进行转储以便记录出错信息的文件。一般对这类文件的处理,都是建议打包后提交给微软分析的。在无法获得微软帮助的情况,就需要自己对此类文件进行分析了,然后找出问题原因,
从而进行解决。
一、SQLDumper.exe介绍前面介绍了SQL Server 会在运行时自动产生一些dump文件,我们也可以手工产生dump文件,产生dump文件的方式,就是通过Sqldumper来进行的。自 SQL Server 2000 Service Pack 3 (SP3) 起,Microsoft SQL Server 2000 中开始附带 Sqldumper.exe。Sqldumper.exe 可根据任一 Microsoft Windows 应用程序的需要生成转储文件。Sqldumper.exe不仅可以转储SQL Server,还可以转储其他的windows application。
我使用的环境是SQL Server 2008,因此SQLDumper位于C:\Program Files\Microsoft SQL Server\100\Shared下,我们可以运行SQLDumper /? 查看其使用方法C:\Program Files\Microsoft SQL Server\100\Shared>SqlDumper.exe /?Usage: SqlDumper.exe [ProcessID [ThreadId [Flags[:MiniDumpFlags] [SqlInfoPtr [DumpDir [ExceptionRecordPtr [ContextPtr [ExtraFile]]]]]]]] [-I<InstanceName>] [-S<ServiceName>][-remoteservers:[print|dump|freeze|resume|remote:guid\dumporigin\signature\localId\port\operationType]] Flags: dbgbreak = 0x0001 nominidump = 0x0002 validate_image = 0x0004 referenced_memory = 0x0008 all_memory = 0x0010 dump_all_threads = 0x0020 match_file_name = 0x0040 no_longer_used_flag = 0x0080 verbose = 0x0100 wait_at_exit = 0x0200 send_to_watson = 0x0400 defaultflags = 0x0800 maximumdump = 0x1000 mini_and_maxdump = 0x2000 force_send_to_watson= 0x4000 full_filtered_dump = 0x8000 MiniDumpFlags: Normal = 0x0000 WithDataSegs = 0x0001 WithFullMemory = 0x0002 WithHandleData = 0x0004 FilterMemory = 0x0008 ScanMemory = 0x0010 WithUnloadedModules = 0x0020 WithIndirectlyReferencedMemory = 0x0040 FilterModulePaths = 0x0080 WithProcessThreadData = 0x0100 WithPrivateReadWriteMemory = 0x0200 WithoutOptionalData = 0x0400 WithFullMemoryInfo = 0x0800 WithThreadInfo = 0x1000从上面的命令可以看出,要想对某一application进行dump,需要先找出其pid(processes id),然后加上一些Flags的控制标识来控制dump内容。比如,我现在想对我的SQL Server 进行dump,先找到SQL Server 的pid 为900,想dump所有的内存信息,那就可以用下面的命令来进行:C:\Program Files\Microsoft SQL Server\100\Shared>SqlDumper.exe 900 0x0010其中,0x0010 表示all_memory,这样,在C:\Program Files\Microsoft SQL Server\100\Shared目录下会产生SQLDmprnnn.mdmp的文件,这就是转储文件(.mdmp)。以下是几个比较常见的dump 标识:0x0120 - Minidump,这个flag只dump 一个Process的堆栈和载入模块的信息,这是最小的转储方式,也是SQL Server自动产生dump文件的方式。0x01100 – Full Dump,这个flag会转储Process的整块信息,如果在64位的系统上,其文件大小还是很大的。0x8100 – Filtered Dump,这个flag用来转储用于于其他服务器用途(包括过程缓存)的内存信息二、SQL Server方式的DUMP使用Sqldumper只是手工产生dump文件的一种方式,当然,产生dmp文件的方式还是很多的,SQL Server内部也提供了这样的工具。主要是DBCC STACKDUMP 和dbcc dumptrigger 这两个命令。当然还可以通过TraceFlag来控制是否产生dmp文件或者遇到什么错误时才产生文件。比如,我们想产生一个Full Dump,必须打开Trace Flag 2544 和 2546dbcc traceon(2544, -1) godbcc traceon(2546, -1) godbcc stackdump
dbcc traceon(2544, -1) godbcc traceon(2546, -1) go--设置dump的触发器为错误802dbcc dumptrigger('set', 802) go-- 查看当期的dump触发器内容dbcc traceon(3604, -1) godbcc dumptrigger('display') godbcc traceoff(3604, -1) go-- 关闭当期的dump触发器。dbcc dumptrigger('clear', 802) go
四、使用windbg
在了解上述知识后,就可以使用windbg来进行分析了。1,windbg环境的配置到微软的网站下载windbg后直接安装,安装完成后,需要配置symbols的path,打开windbg,File --> Symbols File Path 在弹出的对话框输入srv*c:\symbols*http://msdl.microsoft.com/download/symbols其中c:\symbols是本地硬盘的文件夹,在使用时,windbg会到http://msdl.microsoft.com/download/symbols下载相关的symbols,也可以自己手工下载相关操作系统的symbols。配置好,可以使用.reload命令来强制下载某个symbols,如:.reload /f sqlservr.exe具体的命令可以参考windbg的帮助文档。2,打开mdmp文件打开windbg,File --> Open Crash Dump,选择mdump文件,在弹出的对话框里点击yes3,分析mdmp在下面的对话框输入 ~ 会出现线程的信息0:000> ~. 0 Id: 384.608 Suspend: 1 Teb: 7ffdd000 Unfrozen 1 Id: 384.698 Suspend: 1 Teb: 7ffda000 Unfrozen 2 Id: 384.6a8 Suspend: 1 Teb: 7ffd9000 Unfrozen 3 Id: 384.6a4 Suspend: 1 Teb: 7ffd8000 Unfrozen 4 Id: 384.6b0 Suspend: 1 Teb: 7ffd7000 Unfrozen 5 Id: 384.6ac Suspend: 1 Teb: 7ffd6000 Unfrozen 6 Id: 384.6c8 Suspend: 1 Teb: 7ffd5000 Unfrozen 7 Id: 384.6dc Suspend: 1 Teb: 7ffd4000 Unfrozen 8 Id: 384.6e0 Suspend: 1 Teb: 7ffd3000 Unfrozen 9 Id: 384.108 Suspend: 1 Teb: 7ff9f000 Unfrozen 10 Id: 384.6e8 Suspend: 1 Teb: 7ff9e000 Unfrozen 11 Id: 384.6e4 Suspend: 1 Teb: 7ff9d000 Unfrozen 12 Id: 384.604 Suspend: 1 Teb: 7ff9c000 Unfrozen 13 Id: 384.714 Suspend: 1 Teb: 7ff9b000 Unfrozen 14 Id: 384.718 Suspend: 1 Teb: 7ff9a000 Unfrozen 15 Id: 384.71c Suspend: 1 Teb: 7ff99000 Unfrozen 16 Id: 384.720 Suspend: 1 Teb: 7ff98000 Unfrozen 17 Id: 384.728 Suspend: 1 Teb: 7ffdc000 Unfrozen 18 Id: 384.730 Suspend: 1 Teb: 7ff97000 Unfrozen 19 Id: 384.74c Suspend: 1 Teb: 7ff96000 Unfrozen 20 Id: 384.784 Suspend: 1 Teb: 7ff95000 Unfrozen 21 Id: 384.788 Suspend: 1 Teb: 7ff94000 Unfrozen 22 Id: 384.1e0 Suspend: 1 Teb: 7ff93000 Unfrozen 23 Id: 384.284 Suspend: 1 Teb: 7ff92000 Unfrozen 24 Id: 384.280 Suspend: 1 Teb: 7ff91000 Unfrozen 25 Id: 384.23c Suspend: 1 Teb: 7ff8f000 Unfrozen 26 Id: 384.3d0 Suspend: 1 Teb: 7ff8e000 Unfrozen 27 Id: 384.3d4 Suspend: 1 Teb: 7ff8d000 Unfrozen 28 Id: 384.3d8 Suspend: 1 Teb: 7ff8c000 Unfrozen 29 Id: 384.204 Suspend: 1 Teb: 7ff8b000 Unfrozen 30 Id: 384.43c Suspend: 1 Teb: 7ff8a000 Unfrozen 31 Id: 384.450 Suspend: 1 Teb: 7ff89000 Unfrozen 32 Id: 384.454 Suspend: 1 Teb: 7ff88000 Unfrozen 33 Id: 384.458 Suspend: 1 Teb: 7ff87000 Unfrozen 34 Id: 384.45c Suspend: 1 Teb: 7ff86000 Unfrozen 35 Id: 384.464 Suspend: 1 Teb: 7ff84000 Unfrozen 36 Id: 384.44c Suspend: 1 Teb: 7ff83000 Unfrozen 37 Id: 384.1e8 Suspend: 1 Teb: 7ffdb000 Unfrozen 38 Id: 384.1cc Suspend: 1 Teb: 7ff82000 Unfrozen 39 Id: 384.1684 Suspend: 1 Teb: 7ff80000 Unfrozen 40 Id: 384.c38 Suspend: 1 Teb: 7ff90000 Unfrozen 41 Id: 384.1048 Suspend: 1 Teb: 7ff85000 Unfrozen 42 Id: 384.140c Suspend: 1 Teb: 7ff7f000 Unfrozen 43 Id: 384.a18 Suspend: 1 Teb: 7ff81000 Unfrozen在我的这个例子中,我的spid在循环运行一个select命令,从sysprocesses中,可以看到spid对应的kpid是488spid kpid51 052 488488转化为16进制刚好为1e8 ,对应的序号是37。那我们如果想看线程37的内容,可以先使用 ~37s命令切换到线程37的上下文中0:000> ~37seax=00000000 ebx=3f20f344 ecx=1f8dcf08 edx=00000001 esi=000009b5 edi=00000000eip=7c92e514 esp=3f20f238 ebp=3f20f29c iopl=0 nv up ei ng nz ac pe cycs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297ntdll!KiFastSystemCallRet:7c92e514 c3 ret看起来是CPU的寄存器信息,可惜这些还看不太懂,不然可以更深入了。接着用k命令,查看具体的函数调用信息0:037> kChildEBP RetAddr 3f20f234 7c92df5a ntdll!KiFastSystemCallRet3f20f238 7c8025db ntdll!ZwWaitForSingleObject+0xc3f20f29c 7c802542 kernel32!WaitForSingleObjectEx+0xa83f20f2b0 011e7ced kernel32!WaitForSingleObject+0x123f20f324 011e7ddb sqlservr!Np::StatusWriteNoComplPort+0x9f3f20f354 011e7ea2 sqlservr!SNIStatusWriteNoComplPort+0x823f20f374 012a8ae0 sqlservr!TDSSNIClient::WriteStatus+0x6a3f20f4a0 0153d30c sqlservr!write_data+0x1a63f20f4d0 0117492e sqlservr!flush_buffer+0xdf3f20f6a0 015490b6 sqlservr!CKatmaiTds::SendRowImpl+0x2faf3f20f6ac 01532f0d sqlservr!CValOdsRow::SetDataX+0x293f20f6bc 01532d8b sqlservr!SetMultData+0x1e3f20f734 0154962f sqlservr!CEs::GeneralEval4+0xd03f20f740 01547825 sqlservr!CEs::Eval+0x133f20f7f8 015499af sqlservr!CXStmtQuery::ErsqExecuteQuery+0x4093f20f85c 015401c3 sqlservr!CXStmtSelect::XretExecute+0x2683f20f8f8 01540cc0 sqlservr!CMsqlExecContext::ExecuteStmts<1,1>+0x28d3f20f9e0 01540686 sqlservr!CMsqlExecContext::FExecute+0x70e3f20fa84 0153cf8c sqlservr!CSQLSource::Execute+0x5983f20fc08 01539f79 sqlservr!process_request+0x2f0从下面的内容,可以看出几点(个人观点:) )3f20f354 011e7ea2 sqlservr!SNIStatusWriteNoComplPort+0x823f20f374 012a8ae0 sqlservr!TDSSNIClient::WriteStatus+0x6a3f20f4a0 0153d30c sqlservr!write_data+0x1a63f20f4d0 0117492e sqlservr!flush_buffer+0xdf3f20f7f8 015499af sqlservr!CXStmtQuery::ErsqExecuteQuery+0x4093f20f85c 015401c3 sqlservr!CXStmtSelect::XretExecute+0x2683f20f8f8 01540cc0 sqlservr!CMsqlExecContext::ExecuteStmts<1,1>+0x28d3f20f9e0 01540686 sqlservr!CMsqlExecContext::FExecute+0x70e从底往上看,可以看到这是一个select动作,进行select时,先对内存的一些缓存进行清除(flush_buffer),接着便是写入数据(write_data),然后再发送写状态(TDSSNIClient::WriteStatus),由于一直循环所以会有写未完成的提示(SNIStatusWriteNoComplPort)。这也基本符合一个select的动作。如果遇到错误时,在函数调用中一般会抛出raiseerror等内容,类似如下:00000000`220ce2d0 00000000`013a3d41 sqlservr!ex_raise2+0xcdd8bf00000000`220ce630 00000000`02deb8ce sqlservr!ex_raise+0x51 这时,基本可以判断出现问题的原因了。3,其他windbg的功能是很强大的,是通往sql server内部一个强大工具。要想了解的话,估计得好好研究下<windows internal>,有兴趣的可以自行深入。