Jeff Sanders Technical Blog

I am a Microsoft employee that has worked on all aspects of the Web Stack for a long time. I hope these blogs are useful to you! Use this information at your own risk.


<< Go Back

Anaylyze Httpwebrequest Hangs With A Vb Debugdiag Script

- 23 Feb 2009

Here is a VB Version of the JScript DebugDiag script for analyzing WebRequest hangs using DebugDiag http://blogs.msdn.com/jpsanders/archive/2008/10/24/analyzing-httpwebrequest-connection-saturation-in-net.aspx.  It has several improvements over the previous version.  First you will notice many similarities to the previous script.  Some minor changes were added to translate from Jscript to VBscript.  One big change is the .loadby command.  This will load the sos.dll from the same path the mscorwks is loaded.  You would normally run this script on the machine that you took the dump from.  This would ensure that the sos version matches the mscorwks version in the dump.  Another change is to seach not only for GetRequest but for SubmitRequest as well.  Finally I am dumping out more information like when there request is fine and the ConnectionGroupName.

<font color=#ff0000> **Problem! All available connections used up. ** HttpRequest URI:http://10.10.10.10/Service/Service.asmx ServicePoint – ConnectionLimit:12 CurrentConnections:14 ConnectionGroupName: MyConnectionPool </font>

I thought it would be equally important to dump when there were no issues:

 <font color=#008000>**No Hang Problems found ** HttpRequest URI:http://Mydev/output.asmx/test?&Name=test2 ServicePoint – ConnectionLimit:10 CurrentConnections:1 ConnectionGroupName: </font>

Finally, fomatting the URI in a more readable format was fairly simple.  I used the -nofields switch when dumping the string and parsed out the String: text. function getUriString(aObjAddr) Dim aRetStr Dim aCmdOutput Dim aFoundIdx aCmdOutput = g\_Debugger.Execute(“!do -nofields poi(poi(” & aObjAddr & “+” & g\_HttpObjectOffsets.Item(“\_Uri”) & “)+” & g\_UriObjectOffsets.Item(“m_String”) & “)” ) aFoundIdx = InStr(1,aCmdOutput,”String:”,1)

 

if (aFoundIdx <> 0) then   aRetStr=trim( Mid(aCmdOutput, aFoundIdx + Len(“String:”))) end if getUriString = aRetStr end function

 

As with ANY sample code, I leave it to you to debug and test!  I also left some “TODO” sections for you to fill in yourself.  I hope you find it useful.  An industrious person could embed this into the existing DebugDiag scripts to included it in the HangAnalysis portion of the scripts and really have a powerful tool for troubleshooting WebRequest issue.

 

** Full script below ([Copy Code](javascript:CopyCode('VBDDcodesection1');)):**
<font color=#008000 size=2></p> <%</font><font color=#0000ff size=2><font color=#0000ff size=2>@</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>Language</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>=</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>vbscript</font></font> %><%<font color=#0000ff size=2><font color=#0000ff size=2>@</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>Category</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>=</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>Crash/Hang</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>Analyzers</font></font> %><%<font color=#0000ff size=2><font color=#0000ff size=2>@</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>Description</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>=</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>Template</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>System.Net</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>analysis</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>script</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>to</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>build</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>from…</font></font>%><%<font color=#0000ff size=2><font color=#0000ff size=2>@</font></font> <font color=#ff0000 size=2><font color=#ff0000 size=2>TopLevelScript</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>=</font></font> <font color=#0000ff size=2><font color=#0000ff size=2>True</font></font> %><% Option Explicit Dim g_Progress Dim g_Debugger Dim g_HttpObjectOffsets Dim g_SvcPointObjectOffsets Dim g_UriObjectOffsets Dim g_StringObjectOffsets Dim g_MaxConnectionErrorThreads ‘ array of thread id’s that have exceed max connection. Dim g_MaxConnectionWarningThreads ‘ array of thread id’s that have 50% max connection. main() ‘ this kicks off everything sub main() Dim a_DataFile Dim i Dim a_NumberOfDumps Manager.Write “System.Net sample analysis script
” Manager.Progress.SetOverallRange 0,100 Manager.Progress.OverallPosition = 10 Manager.Progress.OverallStatus=”Initializing” a_NumberOfDumps = Manager.DataFiles.Count ‘ for each data file specified… for i=0 to a_NumberOfDumps-1 ‘ get the dump a_DataFile = Manager.DataFiles(i) Manager.Progress.OverallStatus=”Starting analysis of: ” & a_DataFile ‘ get an instance of a debugger set g\_Debugger = Manager.GetDebugger(a\_DataFile)<p mce\_keep="true">Manager.Write “

Analysis of: ” & a\_DataFile & “

”</p> if g_Debugger.IsCrashDump then   Manager.ReportInformation “DebugDiag determined that this dump file (” & a\_DataFile & “) is a <font color=’Red’> crash
” &\_ “dump and this script does not analyze crashes (only hangs). Please run analysis using a crash Script.” end if ‘ try to load the managed debugging extension if(LoadSOS)then ‘ initialize some object information for performance initGlobals ‘ walk all threads but report only specific managed ones walkManagedThreads reportErrors else ‘ todo end if ‘ done with the debugger, so close it Manager.CloseDebugger a_DataFile Manager.Progress.OverallPosition = 100/a_NumberOfDumps * (i+1) next end sub sub initGlobals() set g_HttpObjectOffsets = CreateObject(“Scripting.Dictionary”) ManagedFieldOffsets g\_HttpObjectOffsets, Array(“\_ServicePoint”, “\_Uri”, “\_HttpRequestHeaders”, “\_Proxy”,”\_ConnectionGroupName”),”system.dll!System.Net.HttpWebRequest” set g_SvcPointObjectOffsets = CreateObject(“Scripting.Dictionary”) ManagedFieldOffsets g\_SvcPointObjectOffsets, Array(“m\_ConnectionLimit”, “m_CurrentConnections”),”system.dll!System.Net.ServicePoint” set g_UriObjectOffsets = CreateObject(“Scripting.Dictionary”) ManagedFieldOffsets g\_UriObjectOffsets, Array(“m\_String”), “system.dll!System.Uri” set g_StringObjectOffsets = CreateObject(“Scripting.Dictionary”) ManagedFieldOffsets g\_StringObjectOffsets, Array( “m\_firstChar”), “mscorlib.dll!System.String” g_MaxConnectionErrorThreads = “” g_MaxConnectionWarningThreads = “” end sub

 

‘ this starts the analysis… sub walkManagedThreads() Dim aCmdOutput Dim aStackOutput Dim aThreadInfo Dim aDebugThread Dim aNumThreads Dim aAddr Dim i Dim aFoundIdx Dim bFoundFirstThread bFoundFirstThread =false set aThreadInfo = g_Debugger.ThreadInfo aNumThreads = aThreadInfo.Count Manager.Progress.SetCurrentRange 0,aNumThreads Manager.Progress.CurrentStatus=”Finding threads waiting on ‘System.Net.HttpWebRequest.GetResponse()’ or ‘System.Net.HttpWebRequest.SubmitRequest()'” for i=0 to aNumThreads-1 Manager.Progress.CurrentPosition = i set aDebugThread = aThreadInfo(i) if Not( aDebugThread is nothing ) then aStackOutput = g_Debugger.Execute(“~” & aDebugThread.ThreadID & “e!CLRStack”) aFoundIdx = InStr(aStackOutput,”System.Net.HttpWebRequest.GetResponse”) if (aFoundIdx = 0)then   aFoundIdx = InStr(aStackOutput,”System.Net.HttpWebRequest.SubmitRequest”) end if ‘ highlight the call??? ‘is the tread processing GetResponse? if (aFoundIdx <> 0) then   if Not(bFoundFirstThread) then     bFoundFirstThread=true Manager.Write “

Managed Threads Waiting on ‘System.Net.HttpWebRequest.GetResponse()'


”   end if Manager.Write “<A NAME=’ThreadAnchor” & aDebugThread.ThreadID & “‘>” ‘ dump managed objects on the stack aCmdOutput = g_Debugger.Execute(“~” & aDebugThread.ThreadID & “e!dso”) ‘ Collect the HttpWebRequest objects on the stack. aFoundIdx = InStr(aCmdOutput,”System.Net.HttpWebRequest”) while aFoundIdx <> 0 ‘ if we found an object of interest on the stack, ‘ sav the address fo the object and the thread ID. aAddr = Mid(aCmdOutput,aFoundIdx-9, 8) checkObj aAddr,aDebugThread.ThreadID ‘build error/warning strings here instead. ‘display total number of managed threads? ‘ look for another one… aFoundIdx = 0 ‘aCmdOutput.indexOf(“System.Net.HttpWebRequest”, aFoundIdx + 25) wend Manager.Write regexReplace(aStackOutput, “\n”, “
” ) end if end if next end sub

 

function regexReplace(aSrcStr, aPattern, aReplacement ) dim aRetStr dim aRegEx set aRegEx = New RegExp aRegEx.Pattern=aPattern aRegEx.Global=True aRetStr = aRegEx.Replace( aSrcStr,aReplacement) regexReplace = aRetStr end function

 

sub reportErrors() Dim i Dim threadList if Len(g_MaxConnectionErrorThreads) then Manager.ReportError “The number connections exceeds the number of available connections for these threads:” & g\_MaxConnectionErrorThreads,”Increase the number of connections available per this article: <a TARGET=\_blank href=’http:’msdn.microsoft.com/en-us/library/7af54za5.aspx’>Managing Connections</a>” end if end sub ”””””’/ Managed code helpers ””””””””’/ ‘ use .loadby to find where mscorwks is loaded and then load SOS from the same place function LoadSOS() Dim abSuccess Dim begPos Dim endPos Dim CmdOutput Dim Module abSuccess = false CmdOutput = g_Debugger.Execute(“.loadby sos mscorwks”) ‘todo, check CmdOutput to ensure SOS loaded! LoadSOS = true end function

 

‘ given an Array, populate a dictionary object with offets for these array items to be used later. sub ManagedFieldOffsets( dictObj ,aArray, aObject) Dim aStr Dim i Dim aCmdOutput ‘ get the Class information aCmdOutput = g_Debugger.Execute(“!DumpClass ” + getEEClass(aObject)) for each aStr in aArray   dictObj.Add aStr, findOffset(aCmdOutput,aStr) next End Sub ‘ simple helper functions function getManagedLongVal( aObjAddr, aOffset )   getManagedLongVal = g_Debugger.Execute(“dt long ” & aObjAddr & “+” & aOffset) end function

 

function getManagedStringVal( aObjAddr, aOffset ) Dim retStr Dim aCmdOutput Dim aFoundIdx aCmdOutput = g_Debugger.Execute(“!do -nofields poi(” & aObjAddr & “+” & aOffset & “)”) aFoundIdx = InStr(aCmdOutput,”String:”) if (aFoundIdx <> 0) then retStr=trim( Mid(aCmdOutput, aFoundIdx + Len(“String:”))) end if getManagedStringVal = retStr end function ””””’ Helper Function – findOffset ‘ given a string, extract the field offset specified ‘ this is really an excercise it string parsing function findOffset(aCmdOutput, aField) Dim retStr retStr=”” Dim aFoundIdx Dim aBOL Dim sArray ‘ find the field aFoundIdx = InStr(1,aCmdOutput,aField,1) if aFoundIdx <> 0 then ‘ look backwards from there for the offset. ‘ find the previous line aBOL = InStrRev(aCmdOutput,Chr(10),aFoundIdx,1)+1 ‘need to strip all but one space from the string and make an array of the column values retStr=normalizeWhitespace(Mid(aCmdOutput,aBOL, aFoundIdx-aBOL)) sArray = Split(retStr) retStr=”” ‘ 7 columns total, offset is third column (0 based array) retStr = sArray(2) else   ‘TODO Manager.Write “Did not find Field: ” & aField & “<<<<<<<<<<<<<<” end if findOffset= retStr end function ””””’ Helper Function – getEEClass ‘ given a string, extract the EEClass address ‘ theClass name is direct input to !Name2EE must be MODULENAME!CLASSNAM function getEEClass( theClassName ) Dim retStr retStr=”” Dim aCmdOutput Dim aFoundIdx Dim aEOL aCmdOutput = g_Debugger.Execute(“!Name2EE ” & theClassName) aFoundIdx = InStr(1,aCmdOutput,”EEClass:”,1) if (aFoundIdx <> 0) then   aEOL = InStr(aFoundIdx,aCmdOutput,”Name:”,1)   retStr = Mid(aCmdOutput, aFoundIdx + Len(“EEClass:”) , aEOL – (aFoundIdx + Len(“EEClass:”))) end if getEEClass= retStr end function

‘ utility functions

‘ utility function removing extra whitespace and replacing it with a single space ‘ for example ” some text here” will be converted to ” some text here” function normalizeWhitespace(stringToTrim) dim aRegEx set aRegEx = New RegExp aRegEx.Pattern=”\s+” aRegEx.Global=True normalizeWhitespace = aRegEx.Replace( stringToTrim,” “) end function

 

‘ calculate and save offsets for the Object fields we need ”””””””” Non-generic managed object helpers….

‘ Given an address of an object get the service point object address function getSvcPointFromReq(aHttpObjAddr)

Dim aRetStr aRetStr=”” Dim aCmdOutput aCmdOutput = g\_Debugger.Execute(“dd ” & aHttpObjAddr & “+” & g\_HttpObjectOffsets.Item(“_ServicePoint”) & ” L1″) aRetStr = Mid(aCmdOutput, Len(aCmdOutput) – 8,8) getSvcPointFromReq = Trim(aRetStr) end function

function getUriString(aObjAddr) Dim aRetStr Dim aCmdOutput Dim aFoundIdx aCmdOutput = g\_Debugger.Execute(“!do -nofields poi(poi(” & aObjAddr & “+” & g\_HttpObjectOffsets.Item(“\_Uri”) & “)+” & g\_UriObjectOffsets.Item(“m_String”) & “)” )

aFoundIdx = InStr(1,aCmdOutput,”String:”,1) if (aFoundIdx <> 0) then   aRetStr=trim( Mid(aCmdOutput, aFoundIdx + Len(“String:”))) end if

getUriString = aRetStr

end function

 

function getConnectionGroupName(aObjAddr) getConnectionGroupName=g\_Debugger.Execute(“du poi(poi(” & aObjAddr & “+” & g\_HttpObjectOffsets.Item(“\_ConnectionGroupName”) & “)+” & g\_UriObjectOffsets.Item(“m\_String”) & “)+” & g\_StringObjectOffsets.Item(“m_firstChar”)).substr(9) end function

 

‘ obj is an address we can do !do on to get information from sub checkObj(aObjAddr, threadID) ‘get service point and see if it is OK. Dim bSvcPointErrorOrWarning bSvcPointErrorOrWarning = false Dim maxConn Dim currConn Dim connGroup Dim aCmdOutput Dim i Dim aFoundIdx aCmdOutput = getSvcPointFromReq(aObjAddr) if( (aCmdOutput<>”00000000″) And (aCmdOutput<>”????????”)) then maxConn = getManagedLongVal( aCmdOutput, g\_SvcPointObjectOffsets.Item(“m\_ConnectionLimit”)) currConn = getManagedLongVal( aCmdOutput, g\_SvcPointObjectOffsets.Item(“m\_CurrentConnections”)) connGroup = getManagedStringVal( aObjAddr, g\_HttpObjectOffsets.Item(“\_ConnectionGroupName”)) if(currConn>=maxConn) then ‘ report out info and error g\_MaxConnectionErrorThreads = g\_MaxConnectionErrorThreads & “<a href=’#ThreadAnchor” & threadID & “‘>” & threadID & “</a>, “ Manager.Write “
<font color=’red’>Problem! All available connections used up.
HttpRequest URI:” & getUriString(aObjAddr) & “
ServicePoint – ConnectionLimit:” & maxConn & ” CurrentConnections:” & currConn &”
ConnectionGroupName: ” & connGroup & “</font>

” else if(((currConn)*2)>=maxConn) then ‘ report out info and error g\_MaxConnectionWarningThreads = g\_MaxConnectionWarningThreads & “<a href=’#ThreadAnchor” & threadID + “‘>” & threadID & “</a>, “ Manager.Write “
<font color=’orange’>Warning, half of the availabe connections are being used
HttpRequest URI:” & getUriString(aObjAddr) & “
ServicePoint – ConnectionLimit:” & maxConn & ” CurrentConnections:” & currConn &”
ConnectionGroupName: ” & connGroup & “</font>

” else ‘ no problems found Manager.Write “
<font color=’green’> No Hang Problems found
HttpRequest URI:” & getUriString(aObjAddr) & “
ServicePoint – ConnectionLimit:” & maxConn & ” CurrentConnections:” & currConn &”
ConnectionGroupName: ” & connGroup & “</font>

” end if end if end if End Sub %> </font>
<< Go Back