Production Debugging for .NET Framework Applications

 

patterns and practices home

Appendix

November 2002

Summary: The appendix contains listings for the code used in the walkthrough application. It also contains a section explaining how to use CorDbg as an alternative to WinDbg for debugging managed code.

This appendix contains the following:

  • Thread state values. This contains a list of possible thread states and their descriptions.
  • Application code. This lists the Visual C# code for the three ASP.NET applications used in the scenarios.
  • Debugging with CorDbg. This shows you how to use CorDbg as an alternative to WinDbg for debugging managed code. It is an addendum to Chapter 3, "Debugging Contention Problems."
  • Exploring further. This contains a list of articles to read for more information.

Contents

Thread State Values

Application Code

Debugging with CorDbg

Exploring Further

Thread State Values

The following table lists the possible thread states that are used to construct the thread state values reported by the !thread command of the SOS.dll debugger extension:

Table 5.1: Thread state values and descriptions

Mnemonic Value Description
TS_Unknown 0x00000000 Threads are initialized to null
TS_StopRequested 0x00000001 Process will be stopped at the next opportunity
TS_GCSuspendPending 0x00000002 Waiting to get to safe spot for garbage collector (GC)
TS_UserSuspendPending 0x00000004 User suspension at the next opportunity
TS_DebugSuspendPending 0x00000008 Indicates whether the debugger is suspending threads
TS_GCOnTransitions 0x00000010 Indicates if a GC is forced on stub transitions (GCStress only)
TS_LegalToJoin 0x00000020 Indicates if it is now legal to attempt a Join()
TS_Hijacked 0x00000080 Return address has been hijacked
TS_Background 0x00000200 Thread is a background thread
TS_Unstarted 0x00000400 Thread has never been started
TS_Dead 0x00000800 Thread is dead
TS_WeOwn 0x00001000 Exposed object initiated this thread
TS_CoInitialized 0x00002000 CoInitialize has been called for this thread
TS_InSTA 0x00004000 Thread hosts an STA
TS_InMTA 0x00008000 Thread is part of the MTA
TS_ReportDead 0x00010000 Thread is in WaitForOtherThreads()
TS_SyncSuspended 0x00080000 Thread is suspended with WaitSuspendEvent
TS_DebugWillSync 0x00100000 Debugger will wait for this thread to sync
TS_RedirectingEntryPoint 0x00200000 Redirecting entry point. Do not call managed entry point when set
TS_SuspendUnstarted 0x00400000 Indicates latching a user suspension on an unstarted thread
TS_ThreadPoolThread 0x00800000 Indicates if this is a thread pool thread
TS_TPWorkerThread 0x01000000 Indicates if this is a thread pool worker thread (if not, it is a thread pool completion port thread)
TS_Interruptible 0x02000000 Thread is sitting in either a Sleep(), Wait(), or Join()
TS_Interrupted 0x04000000 Thread was awakened by an interrupt APC
TS_AbortRequested 0x08000000 Process will be stopped at the next opportunity to trip the thread
TS_AbortInitiated 0x10000000 Set when abort is begun
TS_UserStopRequested 0x20000000 Set when a user stop is requested. This is different from TS_StopRequested
TS_GuardPageGone 0x40000000 Stack overflow, not yet reset
TS_Detached 0x80000000 Thread was detached by DllMain

Application Code

This section of the appendix lists the Visual C#™ development tool code for the three ASP.NET applications used in the scenarios.

Contention.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace Debugging
{
   /// <summary>
   /// Summary description for Contention.
   /// </summary>
   public class Contention : System.Web.UI.Page
   {
      protected System.Web.UI.WebControls.Button btnObtainShared2;
      protected System.Web.UI.WebControls.Button btnFreeShared;
      protected System.Web.UI.WebControls.Button btnObtainShared3;
      protected System.Web.UI.WebControls.Label Label1;
      protected System.Web.UI.WebControls.Button btnObtainShared1;
   
      private void Page_Load(object sender, System.EventArgs e)
      {
         PMHealth.OutputProcessInfoTable(Label1);
      }



      #region Web Form Designer generated code
      override protected void OnInit(EventArgs e)
      {
         //
         // CODEGEN: This call is required by the ASP.NET Web Form 
         //Designer.
         //
         InitializeComponent();
         base.OnInit(e);
      }
      
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {    
         this.btnObtainShared2.Click += new 
System.EventHandler(this.btnObtainShared2_Click);
         this.btnFreeShared.Click += new 
System.EventHandler(this.btnFreeShared_Click);
         this.btnObtainShared3.Click += new 
System.EventHandler(this.btnObtainShared3_Click);
         this.btnObtainShared1.Click += new 
System.EventHandler(this.btnObtainShared1_Click);
         this.Load += new System.EventHandler(this.Page_Load);

      }
      #endregion

      private void btnObtainShared1_Click(object sender, 
System.EventArgs e)
      {
         SharedResource.Wait();
         Label1.Text += "<BR>Obtained Shared Resource 1";
      }

      private void btnObtainShared2_Click(object sender, 
System.EventArgs e)
      {
         SharedResource.Wait();
         Label1.Text += "<BR>Obtained Shared Resource 2";
      }

      private void btnObtainShared3_Click(object sender, 
System.EventArgs e)
      {
         SharedResource.Wait();
         Label1.Text += "<BR>Obtained Shared Resource 3";
      }

      private void btnFreeShared_Click(object sender, 
System.EventArgs e)
      {
         SharedResource.Free();
         Label1.Text = "Freed Shared Resource";
         SharedResource.Reset();
         Label1.Text = Label1.Text += "<BR>Resetting Event";
      }
   }
}
  

Memory.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Management;

/************************************************************
Title: Memory.aspx
Purpose: Running this ASP.NET page can demonstrate memory 
consumption in either small or large increments. 
To consider: How the Garbage Collector cleans up objects stored in 
memory and how the size of those objects determines how, when, and if 
the memory is every freed or reusable.

Partial Lab Instructions:   
In the browser window, click Allocate 20 MB Objects once. Switch back 
to Task Manager. Click the Performance tab and examine the Page File 
Usage History. Click the same button again (2 total). Click the same 
button once more (3 total) and immediately switch to the Performance 
tab of Task Manager. Note: the sudden drop off.
Switch back to the browser window. Click Refresh Stats in the browser 
window.
Note: There were two processes, PID 2740, and now a new process, ID 
3128, and that 2740 was shutdown due to memory limit exceeded. (PID 
numbers will vary from run to run.)
Examine the Application Event Log for Errors. Note: PID 2740 was 
recycled because it exceeded 306 MB (60 of physical RAM)
Click the Refresh button on the browser page.
Using System Monitor. Note: Private Bytes and # Bytes in All Heaps 
are both increasing.
Click the 20 MB Button once more.
Note: the Cache API Entries. And the # Bytes in all heaps. You can 
also look at the Large Object Heap Size to determine that most of the 
GC Heap memory is in the large object heap.

*************************************************************/

namespace Debugging
{
   /// <summary>
   /// Summary description for Memory.
   /// </summary>
   public class Memory : System.Web.UI.Page
   {
      protected System.Web.UI.WebControls.Label Label1;
      protected System.Web.UI.WebControls.Button btn20MB;
      protected System.Web.UI.WebControls.Button btnFree;
      protected System.Web.UI.WebControls.Button btnRefresh;
      protected System.Web.UI.WebControls.Button btn200K;
   
      private void Page_Load(object sender, 
                             System.EventArgs e)
      {
         PMHealth.OutputProcessInfoTable(Label1);
      }


      // Web Form Designer generated code omitted
      private void btn20MB_Click(object sender, 
                                 System.EventArgs e)
      {
         //Allocate 20 MB objects
         //Use a System.Web.SessionState Session object
         //Get the CacheList Session object provided by 
         //ASP.NET
         //If the object is null then populate it with 
         //a unique value.
         if (Session["CacheList"] == null)
         {
            Session["CacheList"] = new ArrayList();
         }
         for (int i = 0; i< 5; i++)
         {
            long objSize = 20000000;
            //Populate the variable with a unique value
            string stime = DateTime.Now.Millisecond.ToString();
            string cachekey = "LOCache-" + i.ToString() + ":" + 
stime;
            //System.Web.Caching.Cache Page.Cache
            //Associate a key with a cached object
            Cache[cachekey] = CreateLargeObject(objSize);
            //Store the cache key in a Session scope
            StoreCacheListInSession(cachekey);
         }
         PMHealth.OutputUpdatedMemoryStatsTable(Label1);   
      }
      private byte[] CreateLargeObject(long size)
      {
         //Create and return a byte of specified size
         byte[] largeByteArray = new byte[size];
         return largeByteArray;
      }
      private void StoreCacheListInSession(string list)
      {
         //Store the list of cache keys in a Session object
         //Specifically, dynamically populate an ArrayList with 
         //cached values and add to Session variable
         ArrayList arr = (ArrayList)Session["CacheList"];
         arr.Add(list);
         Session["CacheList"] = arr;
      }

      private void btnFree_Click(object sender, 
                                 System.EventArgs e)
      {
         //Display message if the Session object is null.
         if (Session["CacheList"] == null)
         {
            Label1.Text += "<TABLE BORDER>";
            Label1.Text += "<TR><TD>No Entries in Cache (CacheList is 
null)</TD></TR></TABLE>";

            PMHealth.OutputGCTotalMemoryandCollect(Label1);
         }
         else
         {
            ArrayList arr = (ArrayList)Session["CacheList"];
            if (arr.Count == 0)
            {
               Label1.Text += "<TABLE BORDER>";
               Label1.Text += "<TR><TD>No Entries in Cache 
(CacheList.Count is 0)</TD></TR></TABLE>";

               PMHealth.OutputGCTotalMemoryandCollect(Label1);
            }
            else
            {
               //If Session object is populated, clear its contents.
               Label1.Text += "<TABLE BORDER>";
               Label1.Text += "<TR><TD><FONT COLOR=GREEN>";
               Label1.Text += "Before Emptying Cache";
               Label1.Text += "</FONT></TD></TR>";
               Label1.Text += "<TR><TD><FONT COLOR=GREEN>";
               //Retrieve the number of bytes currently thought to
               //be allocated and convert to KB
               Label1.Text += "GC.TotalMemory </TD><TD>" + 
(GC.GetTotalMemory(false) / 1000).ToString() + " KB";
               Label1.Text += "</FONT></TD></TR>";
               Label1.Text += "<TR><TD><FONT COLOR=GREEN>";
               //Get the amount of physical memory mapped to the 
               //process context and convert to KB
               Label1.Text += "WorkingSet</TD><TD>" + 
(Environment.WorkingSet / 1000).ToString() + " KB";
               Label1.Text += "</FONT></TD></TR></TABLE>";

               foreach (string s in arr)
               {
                  if (Cache[s] != null)
                  {
                     //Remove each item from the cache 
                     //using the cache key
                     Cache.Remove(s);
                     Label1.Text += "<TABLE BORDER>";
                     Label1.Text += "<TR><TD>";
                     Label1.Text += 
                           "Emptying out cachekey " + s;
                     Label1.Text += "</TD></TR></TABLE>";
                  }
               }
               //Remove elements from the arraylist and  
               //Session object
               arr.Clear();
               Session["CacheList"] = arr;
               PMHealth.OutputGCTotalMemoryandCollect(Label1);
            }
         }
      }

      private void btnRefresh_Click(object sender, 
                                    System.EventArgs e)
      {
         PMHealth.OutputUpdatedMemoryStatsTable(Label1);
      }

      private void btn200K_Click(object sender, 
                                 System.EventArgs e)
      {
         //Allocate 200K objects
         //Use a System.Web.SessionState.HttpSessionState 
         //Page.Session
         //Get the CacheList Session object provided by 
         //ASP.NET. If the object is null then populate it 
         //with a unique value.
         if (Session["CacheList"] == null)
         {
            Session["CacheList"] = new ArrayList();
         }
         for (int i = 0; i< 5; i++)
         {
            long objSize = 200000;
            //Populate the variable with a unique value
            string stime = 
                   DateTime.Now.Millisecond.ToString();
            string cachekey = "LOCache-" + i.ToString() + 
                              ":" + stime; 
            //System.Web.Caching.Cache Page.Cache
            //Associate a key with a cached object
            Cache[cachekey] = CreateLargeObject(objSize);
            //Store the cache key in a Session scope
            StoreCacheListInSession(cachekey);
         }  
      }
   }
}
  

Unexpected.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using DebuggingCOMLib;

namespace Debugging
{
   /// <summary>
   /// Summary description for Unexpected.
   /// </summary>
   public class Unexpected : System.Web.UI.Page
   {
      protected System.Web.UI.WebControls.Label Label1;
      protected System.Web.UI.WebControls.Button btnSTA;
      protected System.Web.UI.WebControls.Button btnMTA;
   
      private void Page_Load(object sender, System.EventArgs e)
      {
         PMHealth.OutputProcessInfoTable(Label1);
      }

      // Web Form Designer generated code omitted

      private void btnSTA_Click(object sender, 
                                System.EventArgs e)
      {
         DebuggingCOMLib.STAClass staobj = 
                     new DebuggingCOMLib.STAClass();
         staobj.CallCOM(1);
         Label1.Text += "STA Call Completed successfully";
      }

      private void btnMTA_Click(object sender, 
                                System.EventArgs e)
      {
         DebuggingCOMLib.MTAClass mtaobj = 
                      new DebuggingCOMLib.MTAClass();
         mtaobj.CallCOM(1);
         Label1.Text += "MTA Call Completed sucessfully";
      }
   }
}
  

Pmhealth.cs

Pmhealth.cs contains the code that displays the Process Model statistics for the ASP.NET applications used in the scenario ASP.NET pages.

using System;
using System.Web;
using System.Runtime.InteropServices;

namespace Debugging
{
  /// <summary>
  /// Summary description for PMHealth.
  /// </summary>
  public class PMHealth
  {
    static PMHealth()
    {
      //
      // TODO: Add constructor logic here
      //
    }
    public static void 
OutputProcessInfoTable(System.Web.UI.WebControls.Label lbl)
    {
      //ProcessModelInfo Class inherits from the System.Web 
      //Namespace.  
      //The static method GetHistory populates the 
      //ProcessInfo object.
      //Properties of ProcessInfo provide information about 
      //the ASP.NET worker processes.
      ProcessInfo[] history = 
ProcessModelInfo.GetHistory(100);
      for( int i=0; i<history.Length; i++ )
      {
        lbl.Text += "<TABLE BORDER>";
        lbl.Text += "<TR><TD>";
        //System.DateTime ProcessInfo.StartTime gets 
        //the time at which the process started.
        lbl.Text += "StartTime<TD>"      + 
history[i].StartTime.ToString()       + "<BR>";
        lbl.Text += "<TR><TD>";
        //System.TimeSpan ProcessInfo.Age gets the 
        //length of time the process has been running.
        lbl.Text += "Age<TD>"            + 
history[i].Age.ToString()             + "<BR>";
        lbl.Text += "<TR><TD>";
        //ProcessInfo.ProcessID. Get the ID assigned 
        //to the process.
        lbl.Text += "ProcessID<TD>"      + 
history[i].ProcessID.ToString()       + "<BR>";
        lbl.Text += "<TR><TD>";
        //ProcessInfo.RequestCount.  Get the number of 
        //start requests.
        lbl.Text += "RequestCount<TD>"   + 
history[i].RequestCount.ToString()    + "<BR>";
        lbl.Text += "<TR><TD>";
        //System.String Memory.GetProcessStatus.  Get 
        //the current status of a process.
        lbl.Text += "Status<TD>"         + 
GetProcessStatus( history[i].Status ) + "<BR>";
        lbl.Text += "<TR><TD>";
        //System.String Memory.GetShutdownReason. Get 
        //the reason why a process shut down.
        lbl.Text += "ShutdownReason<TD>" + 
GetShutdownReason( history[i].ShutdownReason) + "<BR>";
        lbl.Text += "<TR><TD>";
        //ProcessInfo.PeakMemoryUsed gets the maximum 
        //amount of memory the process has used.
        lbl.Text += "PeakMemoryUsed<TD>" + 
history[i].PeakMemoryUsed.ToString()  + "<BR>";
        lbl.Text += "</TABLE><P><P>";
      }

    }
    private static String GetProcessStatus( ProcessStatus ps )
    {
      //ProcessStatus indicates the current status of a 
      //process.
      String s = "Unknown";
      switch(ps)
      {
        //The process is running
        case ProcessStatus.Alive:
          s = "Alive";
          break;

        
        //The process has shut down normally after 
        //receiving a shut down message from the IIS process.
        case ProcessStatus.ShutDown:
          s = "Shutdown";
          break;

        //The process has begun to shut down.
        case ProcessStatus.ShuttingDown:
          s = "Shutting Down";
          break;

        //The process was forced to terminate by the 
        //IIS process.
        case ProcessStatus.Terminated:
          s = "Terminated";
          break;
      }
      return s;
    }

    private static String GetShutdownReason( 
ProcessShutdownReason psr )
    {
      //ProcessShutdownReason indicates why a process has 
      //shut down.
      String s = "Unknown";
      switch(psr)
      {

          //The process was restarted because a 
          //deadlock was suspected
        case ProcessShutdownReason.DeadlockSuspected:
          s = "Deadlock Suspected";
          break;

          //The process exceeded the allowable 
          //idle time.
        case ProcessShutdownReason.IdleTimeout:
          s = "Idle Timeout";
          break;

          //The process exceeded the per-process 
          //memory limit.
        case ProcessShutdownReason.MemoryLimitExceeded:
          s = "Memory Limit Exceeded";
          break;

          //No reason available
        case ProcessShutdownReason.None:
          s = "N/A";
          break;

          //The process was restarted because it 
          //didn't respond to a ping from the IIS process
        case ProcessShutdownReason.PingFailed:
          s = "Ping Failed";
          break;

          //Requests assigned to the process 
          //exceeded the allowable number in the queue.
        case ProcessShutdownReason.RequestQueueLimit:
          s = "Request Queue Limit";
          break;

          //Requests executed by the process 
          //exceeded the allowable limit.
        case ProcessShutdownReason.RequestsLimit:
          s = "Requests Limit";
          break;

          //The process restarted because it was 
          //alive longer than allowed.
        case ProcessShutdownReason.Timeout:
          s = "Timeout";
          break;

          //The process shut down unexpectedly.
        case ProcessShutdownReason.Unexpected:
          s = "Unexpected";
          break;
      }
      return s;
    }
    public static void 
OutputUpdatedMemoryStatsTable(System.Web.UI.WebControls.Label lbl)
    {
      //Update the Memory Statistics
      lbl.Text += "<TABLE BORDER>";
      lbl.Text += "<TR><TD><FONT COLOR=BLUE>";
      lbl.Text += "Updated Memory Stats";
      lbl.Text += "</FONT></TD><TD></TD></TR>";
      lbl.Text += "<TR><TD><FONT COLOR=BLUE>";
      //Retrieve the number of bytes currently thought to 
      //be allocated and convert to KB
      lbl.Text += "GC.TotalMemory </TD><TD>" + 
(GC.GetTotalMemory(false) / 1024).ToString() + " KB";
      lbl.Text += "</FONT></TD></TR>";
      lbl.Text += "<TR><TD><FONT COLOR=BLUE>";
      //Get the amount of physical memory mapped to the 
      //process context and convert to KB
      lbl.Text += "Private Bytes</TD><TD>" + 
GetMemoryInfoFromOS() + " KB";
      lbl.Text += "</FONT></TD></TR></TABLE>";
    }

    public static void 
OutputGCTotalMemoryandCollect(System.Web.UI.WebControls.Label lbl)
    {
      lbl.Text += "<TABLE BORDER>";
      lbl.Text += "<TR><TD><FONT COLOR=RED>";
      //Display allocated memory before calling Garbage 
      //Collector
      //Retrieve the number of bytes currently thought to
      //be allocated and convert to KB
      lbl.Text += "GC.TotalMemory </TD><TD>" + 
(GC.GetTotalMemory(false) / 1024).ToString() + " KB";
      lbl.Text += "</FONT></TD></TR></TABLE>";
      //Force Garbage Collection of all Generations
      //NOTE: Calling System.GC.Collect() manually can 
      //degrade performance of your application
      //      We call it in this lab so that you don't have 
      //      to wait for a Generation 2 collection to
      //    occur before you can see the results in 
      //    the heap.
      GC.Collect();
      lbl.Text += "<TABLE BORDER>";
      lbl.Text += "<TR><TD><FONT COLOR=GREEN>";
      //Display allocated memory after calling Garbage 
      //Collector
      lbl.Text += "After GC.Collect";
      lbl.Text += "</FONT></TD><TD></TD></TR>";
      lbl.Text += "<TR><TD><FONT COLOR=GREEN>";
      //Retrieve the number of bytes currently thought to 
      //be allocated and convert to KB
      lbl.Text += "GC.TotalMemory </TD><TD>" + 
(GC.GetTotalMemory(false) / 1024).ToString() + " KB";
      lbl.Text += "</FONT></TD></TR>";
      lbl.Text += "<TR><TD><FONT COLOR=GREEN>";
      //Get the amount of physical memory mapped to the 
      //process context and convert to KB
      lbl.Text += "Private Bytes</TD><TD>" + 
GetMemoryInfoFromOS() + " KB";
      lbl.Text += "</FONT></TD></TR></TABLE>";
    }
    [StructLayout(LayoutKind.Sequential)]
      public class PROCESS_MEMORY_COUNTERS
    {
      public int cb;
      public int PageFaultCount;
      public int PeakWorkingSetSize;
      public int WorkingSetSize;
      public int QuotaPeakPagedPoolUsage;
      public int QuotaPagedPoolUsage;
      public int QuotaPeakNonPagedPoolUsage;
      public int QuotaNonPagedPoolUsage;
      public int PagefileUsage;
      public int PeakPagefileUsage;
    }
    [DllImport("psapi.dll")]
    public static extern int GetProcessMemoryInfo (
      int hProcess,
      [Out] PROCESS_MEMORY_COUNTERS counters,
      int size
      );

    private static int GetMemoryInfoFromOS()
    {
      PROCESS_MEMORY_COUNTERS counters = new 
PROCESS_MEMORY_COUNTERS();
      GetProcessMemoryInfo (-1, counters, 40);
      if (counters != null && counters.PagefileUsage != 0)
        return counters.PagefileUsage/1024;
      return 0;
    }
  }
}
  

Debugging with CorDbg

This section is an addendum to Chapter 3, "Debugging Contention Problems."

CorDbg provides an alternative to WinDbg for debugging managed code using a command line interface instead of a GUI. The difference between using this tool and using WinDbg is that you do not have to create a dump file or terminate the process.

This section shows you how to use CorDbg to attach to the Aspnet_wp.exe process, list the loaded modules in the process, and display call stack traces for specified threads. After listing specific CorDbg commands, there are examples of how to use these commands. Finally, there is an example showing you how to automate this process using a batch file.

Note   For more information on CorDbg, see the Microsoft® .NET Framework SDK documentation and the CorDbg program Help.

The following table lists the commonly used CorDbg commands.

Table5.2: CorDbg commands and descriptions

Command Description
pro [cessenum] Enumerates all managed processes and the application domains in each process.
a [ttach] pid Attaches the debugger to the running process specified by the pid argument.
l [ist] mod Lists the loaded assemblies in the process by AppDomain.
w [here] [count] Displays a stack trace for the current thread. If you do not specify an argument, the tool displays a complete stack trace. If you specify an argument, the tool displays the specified number of stack frames.
de [tach] Detaches the debugger from the current process. The process automatically continues and runs as if a debugger is not attached to it.
q [uit] Stops the current process (if detach was not previously executed) and exits the debugger.
? [command �] Displays descriptions for the specified commands. If you do not specify any arguments, Cordbg.exe displays a list of debugger commands.

Using CorDbg to Display Call Stack Information

Using CorDbg, you obtain the call stacks for the three threads created by the button-click events in Contention.aspx.

To display call stack information

  1. To start the Aspnet_wp.exe process so that you can attach to it, browse to https://localhost/Debugging/Contention.aspx.

  2. Click the three Obtain Shared Resource buttons on Contention.aspx. The page does not return data.

  3. Open the Visual Studio .NET Command Prompt, and then type cordbg to start the debugger.

    Note   You need to have the .NET Framework SDK \Bin folder in your path to be able to run CorDbg. If you are using Visual Studio® .NET, you can open a Visual Studio .NET Command Prompt window, which has the path set correctly.

    You should see a startup banner and prompt like the following:

    Microsoft (R) Common Language Runtime Test Debugger Shell Version 1.0.3705.0
    Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
    
    (cordbg)
    
    
  4. Using the CorDbg pro command, find the Aspnet_wp.exe process ID among the listed running processes.

     (cordbg) pro
    
    PID=0xab8 (3820)  
    Name=C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\aspnet_wp.exe
            ID=2  AppDomainName=/LM/w3svc/1/root/Debugging-1-
    126695838789138931
            ID=1  AppDomainName=DefaultDomain
    
    

    Note   If you have other managed processes running, you will see other entries listed here. To ensure you are using the Aspnet_wp.exe process ID related to the correct browser session, close any other browser windows and other managed applications, and then restart IIS.

  5. Attach the debugger to the running Aspnet_wp.exe process by using the attach command followed by the Aspnet_wp.exe process ID returned in the previous step.

    Note that the symbols are not loaded for runtime components, so don't worry about the "cannot load symbol" warning messages. The first few lines of output from the command are shown below:

     (cordbg) a 3820
    Process 3820/0xab8 created.
    ...
    
    
  6. List the loaded modules in the process using the l mod command.

    The command line is shown below:

     (cordbg) l mod
    
    
  7. Display and examine the call stack trace for all current threads using the *w command. Specify an argument of 200; this lists up to 200 levels of stack trace.

     (cordbg) *w 200
    
    
  8. Now detach from the process, and quit the debugger:

     (cordbg) de
    (cordbg) q
    
    

Using a Script File

An alternative to running each of the commands individually is to use a .cmd file that includes the commands and writes the output to a disk.

The code listing for managed_threads.cmd follows the output shown below. To run this file, make sure that managed_threads.cmd is located in the C:\Debuggers folder.

To run the script

  1. Open a command prompt window, and then type iisreset at the prompt to restart IIS.

  2. Browse to https://localhost/Debugging/contention.aspx.

  3. Click the Obtain Shared Resource 1, 2,and 3 buttons.

  4. At the command line, type the following:

    C:\Debuggers>managed_threads.cmd 3820
    
    

    Make sure that you replace "3280" with the ID of your Aspnet_wp.exe process.

The following listing shows the contents of the output file (C:\Debuggers\managed_threads.txt), with commands shown in bold:

Microsoft (R) Common Language Runtime Test Debugger Shell Version 
1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

(cordbg) a 3820
Process 3820/0xeec created.
Warning: couldn't load symbols for 
c:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system\1.0.3300.0__b77a5c561934e089\system.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.drawing\1.0.3300.0__b03f5f7f11d50a3
a\system.drawing.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.directoryservices\1.0.3300.0__b03f5f7f11d50a3a\
system.directoryservices.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.messaging\1.0.3300.0__b03f5f7f11d50a3a\
system.messaging.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.serviceprocess\1.0.3300.0__b03f5f7f11d50a3a\
system.serviceprocess.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.data\1.0.3300.0__b77a5c561934e089\system.data.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.web\1.0.3300.0__b03f5f7f11d50a3a\system.web.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.thunk.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.web.regularexpressions\1.0.3300.0__b03f5f7f11d50a3a
\system.web.regularexpressions.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.xml\1.0.3300.0__b77a5c561934e089\system.xml.dll
Warning: couldn't load symbols for 
c:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system\1.0.3300.0__b77a5c561934e089\system.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.drawing\1.0.3300.0__b03f5f7f11d50a3a\system.drawing.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.directoryservices\1.0.3300.0__b03f5f7f11d50a3a\
system.directoryservices.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.messaging\1.0.3300.0__b03f5f7f11d50a3a\
system.messaging.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.serviceprocess\1.0.3300.0__b03f5f7f11d50a3a\
system.serviceprocess.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.data\1.0.3300.0__b77a5c561934e089\system.data.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.web\1.0.3300.0__b03f5f7f11d50a3a\system.web.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.xml\1.0.3300.0__b77a5c561934e089\system.xml.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.web.regularexpressions\1.0.3300.0__b03f5f7f11d50a3a
\system.web.regularexpressions.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.thunk.dll
Warning: couldn't load symbols for 
c:\windows\assembly\gac\system.web.services\1.0.3300.0__b03f5f7f11d50a3a\
system.web.services.dll
Warning: couldn't load symbols for 
c:\windows\microsoft.net\framework\v1.0.3705\temporary asp.net 
files\debugging\34374a37\79f52287\assembly\dl\2b680d64\198fa8c2_9d17c201\
interop.debuggingcomlib.dll
[thread 0xf30] Thread created.
Unable to  determine existence of prolog, if any
[thread 0xe00] Thread created.
[thread 0xf40] Thread created.
[thread 0x6f4] Thread created.
[thread 0xd78] Thread created.
[thread 0xf30] 
Thread 0xf30 R   0x7FFE0304:  <unknown>
(cordbg) l mod
Module 0x00174f0c, 
c:\windows\assembly\gac\system.drawing\1.0.3300.0__b03f5f7f11d50a3a\
system.drawing.dll -- AD #2 -- Symbols not loaded
Module 0x00135824, 
c:\windows\assembly\gac\system.serviceprocess\1.0.3300.0__b03f5f7f11d50a3a\
system.serviceprocess.dll -- AD #1 -- Symbols not loaded
Module 0x00179404, 
c:\windows\assembly\gac\system.serviceprocess\1.0.3300.0__b03f5f7f11d50a3a\
system.serviceprocess.dll -- AD #2 -- Symbols not loaded
Module 0x0016f3cc, 
c:\windows\assembly\gac\system.xml\1.0.3300.0__b77a5c561934e089\
system.xml.dll -- AD #1 -- Symbols not loaded
Module 0x000b5ff4, 
c:\windows\assembly\gac\system\1.0.3300.0__b77a5c561934e089\
system.dll -- AD #1 -- Symbols not loaded
Module 0x00177514, 
c:\windows\assembly\gac\system.data\1.0.3300.0__b77a5c561934e089\
system.data.dll -- AD #2 -- Symbols not loaded
Module 0x0011897c, 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.thunk.dll -- AD #1 -- Symbols not loaded
Module 0x001190ec, 
c:\windows\assembly\gac\system.data\1.0.3300.0__b77a5c561934e089\
system.data.dll -- AD #1 -- Symbols not loaded
Module 0x00090ac4, 
c:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll -- AD #1–Symbols not loaded
Module 0x015dc294, 
c:\windows\assembly\gac\system.web.services\1.0.3300.0__b03f5f7f11d50a3a\
system.web.services.dll -- AD #2 -- Symbols not loaded
Module 0x0017ad4c, 
c:\windows\assembly\gac\system.web.regularexpressions\1.0.3300.0__b03f5f7f11d50a3a
\system.web.regularexpressions.dll -- AD #2 -- Symbols not loaded
Module 0x0017ae74, 
c:\windows\assembly\gac\system.xml\1.0.3300.0__b77a5c561934e089\
system.xml.dll -- AD #2 -- Symbols not loaded
Module 0x015db804, 
c:\windows\microsoft.net\framework\v1.0.3705\temporary asp.net 
files\debugging\34374a37\79f52287\pvnt75wl.dll -- AD #2
Module 0x0017f99c, 
c:\windows\microsoft.net\framework\v1.0.3705\temporary asp.net 
files\debugging\34374a37\79f52287\assembly\dl\18040135\6ef006c4_9d17c201\
debugging.dll -- AD #2
Module 0x00178fbc, 
c:\windows\assembly\gac\system.web\1.0.3300.0__b03f5f7f11d50a3a\
system.web.dll -- AD #2 -- Symbols not loaded
Module 0x001755fc, 
c:\windows\assembly\gac\system.directoryservices\1.0.3300.0__b03f5f7f11d50a3a\
system.directoryservices.dll -- AD #2 -- Symbols not loaded
Module 0x00171efc, 
c:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll -- AD #2–Symbols not loaded
Module 0x0016a894, 
c:\windows\microsoft.net\framework\v1.0.3705\temporary asp.net 
files\debugging\34374a37\79f52287\assembly\dl\2b680d64\198fa8c2_9d17c201\
interop.debuggingcomlib.dll -- AD #2 -- Symbols not loaded
Module 0x00142ccc, 
c:\windows\assembly\gac\system.web\1.0.3300.0__b03f5f7f11d50a3a\
system.web.dll -- AD #1 -- Symbols not loaded
Module 0x0010b2ac, 
c:\windows\assembly\gac\system.directoryservices\1.0.3300.0__b03f5f7f11d50a3a\
system.directoryservices.dll -- AD #1 -- Symbols not loaded
Module 0x0016ba8c, 
c:\windows\assembly\gac\system\1.0.3300.0__b77a5c561934e089\system.dll–AD #2 -- Symbols not loaded
Module 0x00142ba4, 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.dll -- AD #1 -- Symbols not loaded
Module 0x0011880c, 
c:\windows\assembly\gac\system.messaging\1.0.3300.0__b03f5f7f11d50a3a\
system.messaging.dll -- AD #1 -- Symbols not loaded
Module 0x000bd6b4, 
c:\windows\assembly\gac\system.drawing\1.0.3300.0__b03f5f7f11d50a3a\
system.drawing.dll -- AD #1 -- Symbols not loaded
Module 0x015da5b4, 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.thunk.dll -- AD #2 -- Symbols not loaded
Module 0x015d6dbc, 
c:\windows\assembly\gac\system.enterpriseservices\1.0.3300.0__b03f5f7f11d50a3a\
system.enterpriseservices.dll -- AD #2 -- Symbols not loaded
Module 0x0017718c, 
c:\windows\microsoft.net\framework\v1.0.3705\temporary asp.net 
files\debugging\34374a37\79f52287\0rzdgjzl.dll -- AD #2
Module 0x001770fc, 
c:\windows\assembly\gac\system.messaging\1.0.3300.0__b03f5f7f11d50a3a\
system.messaging.dll -- AD #2 -- Symbols not loaded
Module 0x00142c34, 
c:\windows\assembly\gac\system.web.regularexpressions\1.0.3300.0__b03f5f7f11d50a3a
\system.web.regularexpressions.dll -- AD #1 -- Symbols not loaded
(cordbg) *w 200


Thread 0xd78 R   0x7FFE0304:  <unknown>
Thread 0xd78 Current State:Normal
*   0x7FFE0304:  <unknown>
--- Unmanaged transition ---
0)  mscorlib!System.Threading.WaitHandle::WaitOne +0036 [no source 
information available]
1)  debugging!Debugging.SharedResource::Wait +005b in 
C:\Inetpub\wwwroot\Debugging\SharedResource.cs:55
2)  debugging!Debugging.Contention::btnObtainShared3_Click +0013 in 
c:\inetpub\wwwroot\debugging\contention.aspx.cs:71
    sender=(0x012113a8) <System.Web.UI.WebControls.Button>
    e=(0x012193c4) <System.EventArgs>
3)  system.web!System.Web.UI.WebControls.Button::OnClick +006d [no 
source information available]
4)  system.web!System.Web.UI.WebControls.Button::System.Web.UI.
IPostBackEventHandler.RaisePostBackEvent +003a [no source information available]
5)  system.web!System.Web.UI.Page::RaisePostBackEvent +0013 [no 
source information available]
6)  system.web!System.Web.UI.Page::RaisePostBackEvent +0022 [no 
source information available]
7)  system.web!System.Web.UI.Page::ProcessRequestMain +04f0 [no 
source information available]
8)  system.web!System.Web.UI.Page::ProcessRequest +0077 [no source 
information available]
9)  system.web!System.Web.UI.Page::ProcessRequest +0013 [no source 
information available]
10)  system.web!CallHandlerExecutionStep::Execute +00b4 [no source 
information available]
11)  system.web!System.Web.HttpApplication::ExecuteStep +0058 [no 
source information available]
12)  system.web!System.Web.HttpApplication::ResumeSteps +00f3 [no 
source information available]
13)  system.web!System.Web.HttpApplication::System.Web.
IHttpAsyncHandler.BeginProcessRequest +00e3 [no source information available]
14)  system.web!System.Web.HttpRuntime::ProcessRequestInternal +01e0 
[no source information available]
15)  system.web!System.Web.HttpRuntime::ProcessRequest +003d [no 
source information available]
16)  system.web!System.Web.Hosting.ISAPIRuntime::ProcessRequest +00b7 
[no source information available]
--- Managed transition ---
  0x00421EAE:  <unknown>



Thread 0xf30 R   0x7FFE0304:  <unknown>
Thread 0xf30 Current State:Normal
*   0x7FFE0304:  <unknown>
--- Unmanaged transition ---
0)  mscorlib!System.Threading.WaitHandle::WaitOne +0036 [no source 
information available]
1)  debugging!Debugging.SharedResource::Wait +005b in 
C:\Inetpub\wwwroot\Debugging\SharedResource.cs:55
2)  debugging!Debugging.Contention::btnObtainShared1_Click +0014 in 
c:\inetpub\wwwroot\debugging\contention.aspx.cs:59
    sender=(0x011e91c4) <System.Web.UI.WebControls.Button>
    e=(0x011f136c) <System.EventArgs>
3)  system.web!System.Web.UI.WebControls.Button::OnClick +006d [no 
source information available]
4)  system.web!System.Web.UI.WebControls.Button::System.Web.UI.
IPostBackEventHandler.RaisePostBackEvent +003a [no source information available]
5)  system.web!System.Web.UI.Page::RaisePostBackEvent +0013 [no 
source information available]
6)  system.web!System.Web.UI.Page::RaisePostBackEvent +0022 [no 
source information available]
7)  system.web!System.Web.UI.Page::ProcessRequestMain +04f0 [no 
source information available]
8)  system.web!System.Web.UI.Page::ProcessRequest +0077 [no source 
information available]
9)  system.web!System.Web.UI.Page::ProcessRequest +0013 [no source 
information available]
10)  system.web!CallHandlerExecutionStep::Execute +00b4 [no source 
information available]
11)  system.web!System.Web.HttpApplication::ExecuteStep +0058 [no 
source information available]
12)  system.web!System.Web.HttpApplication::ResumeSteps +00f3 [no 
source information available]
13)  system.web!System.Web.HttpApplication::System.Web.
IHttpAsyncHandler.BeginProcessRequest +00e3 [no source information available]
14)  system.web!System.Web.HttpRuntime::ProcessRequestInternal +01e0 
[no source information available]
15)  system.web!System.Web.HttpRuntime::ProcessRequest +003d [no 
source information available]
16)  system.web!System.Web.Hosting.ISAPIRuntime::ProcessRequest +00b7 
[no source information available]
--- Managed transition ---
  0x00421EAE:  <unknown>



Thread 0x6f4 R   0x7FFE0304:  <unknown>
Thread 0x6f4 Current State:Normal
*   0x7FFE0304:  <unknown>
--- Unmanaged transition ---
0)  mscorlib!System.Threading.WaitHandle::WaitOne +0036 [no source 
information available]
1)  debugging!Debugging.SharedResource::Wait +005b in 
C:\Inetpub\wwwroot\Debugging\SharedResource.cs:55
2)  debugging!Debugging.Contention::btnObtainShared2_Click +0013 in 
c:\inetpub\wwwroot\debugging\contention.aspx.cs:65
    sender=(0x012043ec) <System.Web.UI.WebControls.Button>
    e=(0x0120c888) <System.EventArgs>
3)  system.web!System.Web.UI.WebControls.Button::OnClick +006d [no 
source information available]
4)  system.web!System.Web.UI.WebControls.Button::System.Web.UI.
IPostBackEventHandler.RaisePostBackEvent +003a [no source information available]
5)  system.web!System.Web.UI.Page::RaisePostBackEvent +0013 [no 
source information available]
6)  system.web!System.Web.UI.Page::RaisePostBackEvent +0022 [no 
source information available]
7)  system.web!System.Web.UI.Page::ProcessRequestMain +04f0 [no 
source information available]
8)  system.web!System.Web.UI.Page::ProcessRequest +0077 [no source 
information available]
9)  system.web!System.Web.UI.Page::ProcessRequest +0013 [no source 
information available]
10)  system.web!CallHandlerExecutionStep::Execute +00b4 [no source 
information available]
11)  system.web!System.Web.HttpApplication::ExecuteStep +0058 [no 
source information available]
12)  system.web!System.Web.HttpApplication::ResumeSteps +00f3 [no 
source information available]
13)  system.web!System.Web.HttpApplication::System.Web.
IHttpAsyncHandler.BeginProcessRequest +00e3 [no source information available]
14)  system.web!System.Web.HttpRuntime::ProcessRequestInternal +01e0 
[no source information available]
15)  system.web!System.Web.HttpRuntime::ProcessRequest +003d [no 
source information available]
16)  system.web!System.Web.Hosting.ISAPIRuntime::ProcessRequest +00b7 
[no source information available]
--- Managed transition ---
  0x00421EAE:  <unknown>



Thread 0xe00 R   0x7FFE0304:  <unknown>
Thread 0xe00 Current State:Background
*   0x7FFE0304:  <unknown>



Thread 0xf40 R   0x7FFE0304:  <unknown>
Thread 0xf40 Current State:Background
*   0x7FFE0304:  <unknown>

(cordbg) de
(cordbg) q
  

The managed_threads.cmd Script

This is the code listing for the script that produced the output in the previous section:

@echo off
if "%_echo%"=="1" echo on
setlocal

REM FILENAME: MANAGED_THREADS.CMD
REM PURPOSE:  To Dump the managed threads for an .NET Process
REM DATE:     5/9/2002
REM AUTHOR:   SIE, GSSC, Microsoft Corporation


if /i "%1" == "" (
   GOTO :HELP
) else (
  cordbg !a %1 !l mod !*w 200 !de !q >managed_threads.txt
  echo managed threads output is in managed_threads.txt
  GOTO :EOF
)



:HELP
echo.
echo USAGE: %~xn0 [process ID from TLIST.EXE]
echo.
echo %~xn0 will create a log of the managed threads in a .NET Application
echo.
echo For Example:
tlist
goto :EOF

:EOF
endlocal
  

Exploring Further

For more information, read the following articles:

patterns and practices home