SQL Server转储介绍III

obuntu 2011-08-24 12:16:22
加精
之前曾介绍过SQL Server的DUMP文件,现在来看一个从DUMP文件获取查询语句的例子。手工生成DUMP文件可以参考前面的帖子(SQL Server 转储介绍 ISQL Server 转储介绍II),在WIN7或者Windows Server 2008上,还可以在任务管理器用右键进行文件转储。在分析DUMP文件之前,需要设置好windbg的使用环境,主要是设置symbol的路径,以前的帖子里也有介绍:)。


然后就可以使用windbg进行DUMP文件分析了,打开windbg->File->Open Crash Dump 打开具体的转储文件。然后找到具体执行SQL 语句的线程,寻找方法也很简单,在windbg的命令行里面输入~*kv,找到有sqlservr!CMsqlExecContext::ExecuteStmts<1,1>的线程就可以了。然后再用~ns,设置到线程n。
设置到具体线程后,用kv命令查看调用栈信息。接下来就可以根据调用栈来获取具体的SQL 执行语句了。
0:000> ~46s
ntdll!ZwWaitForSingleObject+0xa:
00000000`76f6f6fa c3 ret
0:046> kvnf
# Memory Child-SP RetAddr : Args to Child : Call Site
00 00000000`0fe3b688 000007fe`fd6f10ac : 00000000`80cb61a0 00000000`0fe3b780 00000000`00000001 00000000`0144105c : ntdll!ZwWaitForSingleObject+0xa
01 8 00000000`0fe3b690 00000000`013b889e : 00000000`000007d0 00000000`012b5f80 00000000`00000000 00000000`00000669 : KERNELBASE!WaitForSingleObjectEx+0x79
02 a0 00000000`0fe3b730 00000000`013b8799 : 00000000`00000000 00000000`007c8c00 ffffffff`ffffffff 00000000`0fe3b870 : sqlservr!Np::StatusWriteNoComplPort+0x6e
03 b0 00000000`0fe3b7e0 00000000`013b85cc : 00000000`007ce240 00000000`007ce240 00000000`0fe3b8b0 00000000`00000000 : sqlservr!SNIStatusWriteNoComplPort+0x59
04 60 00000000`0fe3b840 00000000`0101f671 : 00000000`00000000 00000000`00001000 00000000`00000000 00000000`8135cad0 : sqlservr!TDSSNIClient::WriteStatus+0x99
05 30 00000000`0fe3b870 00000000`00f93ba3 : 00000000`80c70750 00000000`80c70c40 00000000`00000001 00000000`80c70750 : sqlservr!write_data+0x1bf
06 1e0 00000000`0fe3ba50 00000000`013acaea : 00000000`80cb61a0 00043300`00100004 00000000`00000000 00000000`00000000 : sqlservr!flush_buffer+0xf3
07 50 00000000`0fe3baa0 00000000`00fb3b69 : 00000000`00000000 00000000`00000000 00000000`8136b690 00000000`00000000 : sqlservr!CKatmaiTds::SendRowImpl+0x19c
08 280 00000000`0fe3bd20 00000000`00f9287e : 00000000`00000000 00000000`00000000 00000000`80c70140 00000000`80c704f0 : sqlservr!CXStmtQuery::ErsqExecuteQuery+0x5ce8
09 2f10 00000000`0fe3ec30 00000000`00f90fe3 : 00000000`8135ecb0 00000000`0144cc96 00000000`80a72140 00000000`00000000 : sqlservr!CMsqlExecContext::ExecuteStmts<1,1>+0xb6c
0a 290 00000000`0fe3eec0 00000000`00f91499 : 00000000`80a72140 00000000`8135d3d0 00000000`8135d300 00000000`00000000 : sqlservr!CMsqlExecContext::FExecute+0x593
0b 180 00000000`0fe3f040 00000000`00f93ff2 : 00000000`00000000 00000000`8135d3d0 00000000`8135dc30 00000000`00000000 : sqlservr!CSQLSource::Execute+0x2f9
0c 120 00000000`0fe3f160 00000000`00f8ebbb : 00000000`8135cfa0 00000000`007c8c00 00000000`00000000 00000000`00000000 : sqlservr!process_request+0x370
0d 2c0 00000000`0fe3f420 00000000`00f12abb : 00000000`00000000 00000000`00000000 00000000`00479988 00726574`6e696f50 : sqlservr!process_commands+0x2b2
0e 200 00000000`0fe3f620 00000000`00f10fda : 00000000`00000000 00000000`00000000 00000000`80cb61a0 00000000`80cb61a0 : sqlservr!SOS_Task::Param::Execute+0x11b
0f 120 00000000`0fe3f740 00000000`00f12665 : 00000000`0fe3f7f8 00000000`00479948 00000000`00479948 00000000`00000000 : sqlservr!SOS_Scheduler::RunTask+0xca
10 90 00000000`0fe3f7d0 00000000`014babb0 : 00000000`00479940 00000000`80cb61a0 00000000`00479948 00000000`00418270 : sqlservr!SOS_Scheduler::ProcessTasks+0x95
11 70 00000000`0fe3f840 00000000`014bc4b0 : 00000000`80cb61a0 00000000`00480080 000007ff`fff9e000 000007ff`fff9f4a8 : sqlservr!SchedulerManager::WorkerEntryPoint+0x110
12 c0 00000000`0fe3f900 00000000`014ba060 : 00000000`80cb61a0 000007ff`fff9f4a8 00000000`00418190 000007fe`fd6f6cf9 : sqlservr!SystemThread::RunWorker+0x60
13 30 00000000`0fe3f930 00000000`014ba9ef : 000007ff`fff9f4a8 000007ff`fff9f4a8 00000000`056f3e30 00000000`00418190 : sqlservr!SystemThreadDispatcher::ProcessWorker+0x12c
14 90 00000000`0fe3f9c0 00000000`680137d7 : 000007ff`fff9f4a8 00000000`0081ac10 00000000`00000000 00000000`00000000 : sqlservr!SchedulerManager::ThreadEntryPoint+0x12f
15 90 00000000`0fe3fa50 00000000`68013894 : 00000000`680c95c0 00000000`0081ac10 00000000`00000000 00000000`00000000 : msvcr80!endthreadex+0x47
16 30 00000000`0fe3fa80 00000000`7685f56d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : msvcr80!endthreadex+0x104
17 30 00000000`0fe3fab0 00000000`76f52cc1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
18 30 00000000`0fe3fae0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

用kvnf命令查看的调用者说明如下:
第一列“#”,表示调用栈的序号;第二列"memory"表示栈所占用的空间;第三列Child-SP则是函数的入栈地址,在有的平台上会显示为ChildEBP,EBP和ESP在汇编里面就是存放栈的具体地址的;第四列的“RetAddr”表示函数返回地址;第五列的“Args to Child ”表示对应函数的参数地址,只显示前3个,但这三个地址在有些情况下会不准确;第六列的“Call Site”则是具体的函数调用名,像sqlservr!CMsqlExecContext::ExecuteStmts<1,1>中的sqlservr表示具体的执行程序,CMsqlExecContext表示具体的类,ExecuteStmts表示具体的方法(从中也可以看出SQL Server是用C++来编写的~)。

接下来就可以根据调用栈来获取实际的SQL 语句了。
在这里我们关注的是process_request这个函数:
0c 120 00000000`0fe3f160 00000000`00f8ebbb : 00000000`8135cfa0 00000000`007c8c00 00000000`00000000 00000000`00000000 : sqlservr!process_request+0x370
从第一个参数地址入手,按照下面的步骤就可以获取实际的SQL 执行语句。
0:046> dd 8135cfa0+0x20 l1
00000000`8135cfc0 8135cea0
0:046> dd 8135cea0+0x28 l1
00000000`8135cec8 80317970
0:046> dd 80317970+0x200 l1
00000000`80317b70 813645b0
0:046> dd 813645b0+0x20 l1
00000000`813645d0 81364610
0:046> dd 81364610 l1
00000000`81364610 813646a0
0:046> du 813646a0
00000000`813646a0 "while 1=1..select * from obuntu_"
00000000`813646e0 "testOR_CōOP._ON_?脶"

上述命令中的dd和du是查看内存的指令,更灵活的用法可以参考windbg的手册。至于为什么在地址后面要加上一些偏移(如0x20,0x28,0x2000),我们一般是无法获悉的,但一般情况下,我们可以按照上述步骤获取SQL 语句。我也是下面的这两篇帖子中看到分析SQL dump文件,比较感兴趣,所以给大家介绍下。
《How do I find what queries were executing in a SQL memory dump?》
《Finding which queries were executing from a SQL Memory Dump – revisited》


其中第二个帖子里面,因为作者用到了private symbol(微软不公开的),也就是说他有SQL Server的源码,所以我们没办法重现。而第一个帖子里面的使用方法,在我的环境里是可以实现的。其实分析这样的dump文件跟版本和编译环境是有很大关系的,我的环境参数如下:
--SQL Server:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (X64) Jul 9 2008 14:17:44 Copyright (c) 1988-2008 Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
--OS:
Windows 7 Version 7600 MP (4 procs) Free x64
Product: WinNt, suite: SingleUserTS Personal
kernel32.dll version: 6.1.7600.16385 (win7_rtm.090713-1255)


本来想通过反汇编研究下为什么取那样的偏移,无奈道行尚浅,看到几千上万的汇编代码直接晕过去了。不过我们可以通过编写个C程序,用windbg分析下调用栈的情况。

C代码如下:

#include <stdio.h>

void add(int *a, int *b,int *c)
{
*c = *a + *b;
}

int main()
{
int a = 10;
int b = 5;
int c;
add(&a,&b,&c);

printf("c:%d\n",c);

}


编译执行后,用windbg->File->Open Executeable打开编译后的exe文件,然后用bp命令在exe文件里面的add函数打上断点,然后输入g命令直接运行,windbg会在add函数里面停住,接着用kv命令看下具体的调用栈信息。然后可以用dd命令查看add函数对应的三个参数地址的实际值。具体如下:



在图中,可以看到“Args to Child”列里面的第一个地址的值为0000000a,也就是十进制10,刚好是a的值;第二个地址的值为00000005,十进制5,是b的值;第三个地址的值为cccccccc,这是因为我们还没执行到让参数c获取值的地方,系统会自动给这样的参数赋值为cccccccc。

所以,对于SQL Server文件,我们也可以根据这样的方法来一个个分析函数的值,但花费的时间相对来说会很多的。同时,根据上述例子打断点的思想,我们如果从一个因异常而dump出的文件中分析出了出错的地方,还可以在windbg->File->Attach to a Process,附加SQL Server的进行,进行单步跟踪,同时根据反汇编信息获得具体的错误内容。(但请注意,如果用windbg附加一个正在运行的进程时,在windbg退出后,也会推出进程,所以千万不要在生产环境上做这样的一个实验!!!!)。

其实,SQL Server是一个比较稳定的数据库系统,如果出现了dump文件,一般要么是碰到微软的BUG,要么是数据库出现了致命的问题。对于这种东西,如果没有微软的支持,我们是很难定位出具体的问题所在。所以,我们只能规避它,要么重装系统,要么重装SQL Server,要么重建数据库,如果还搞定不了的话,还是求助微软吧。

写这帖子,只是给有兴趣的人一点参考,也希望能共同探索~。现在流行微博,也发一个,大家有兴趣的话,可以多多交流:) http://weibo.com/huangqingxin
...全文
2542 80 打赏 收藏 转发到动态 举报
写回复
用AI写文章
80 条回复
切换为时间正序
请发表友善的回复…
发表回复
wildwild1 2011-09-02
  • 打赏
  • 举报
回复
大早上看到这个,挺幸运的
习惯性蹭分 2011-09-02
  • 打赏
  • 举报
回复
头晕了,有木有。
chtzhking 2011-09-02
  • 打赏
  • 举报
回复
学习,收藏
l785228475 2011-09-01
  • 打赏
  • 举报
回复
Hold不住了
JesseNi328 2011-09-01
  • 打赏
  • 举报
回复
dump是一定要看到啊,像C++的程序,只能创建dump去查看错误
kiss筱魔 2011-08-31
  • 打赏
  • 举报
回复
深奥!
obuntu 2011-08-30
  • 打赏
  • 举报
回复

好久不见~
[Quote=引用 67 楼 claro 的回复:]

很好! 学习!
[/Quote]
obuntu 2011-08-30
  • 打赏
  • 举报
回复

谢谢王大哥捧场~
[Quote=引用 64 楼 baoqiangwang 的回复:]

拜读一下,obuntu又精进了不少!
[/Quote]
andy0718 2011-08-29
  • 打赏
  • 举报
回复
楼主发的东西很有用啊
学习了
js_csharp 2011-08-29
  • 打赏
  • 举报
回复
厉害 看不懂
stonecpu 2011-08-29
  • 打赏
  • 举报
回复
支持,不过小弟看不懂,原来传说中的牛人还真不是一般的牛啊。
dezay 2011-08-28
  • 打赏
  • 举报
回复
mark
yuanwza 2011-08-28
  • 打赏
  • 举报
回复
学习。。。
claro 2011-08-27
  • 打赏
  • 举报
回复
很好! 学习!
my365testing 2011-08-27
  • 打赏
  • 举报
回复
对我有些深。。。
24K純帥 2011-08-27
  • 打赏
  • 举报
回复
学习了,SQL牛人~
csdn_风中雪狼 2011-08-26
  • 打赏
  • 举报
回复
太深奥了,看不懂
  • 打赏
  • 举报
回复
拜读一下,obuntu又精进了不少!
obuntu 2011-08-26
  • 打赏
  • 举报
回复
谢谢~

[Quote=引用 60 楼 hsh03081051 的回复:]

楼主发的东西很有用啊
学习了
[/Quote]
obuntu 2011-08-26
  • 打赏
  • 举报
回复

学习,对windbg不熟,平时很少用。
[Quote=引用 46 楼 skyworth98 的回复:]

支持不在生产环境上做试验,至于windbg退出时,被调试的进程跟着退出还是有解决办法的,那就是先执行.detach命令.
[/Quote]
加载更多回复(57)

34,590

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧