Implementing custom Membership Provider and Role Provider for Authenticating ASP.NET MVC Applications by Matt Wrock

The .NET framework provides a provider model that allows developers to implement common user management and authentication functionality in ASP.NET applications. Out of the box, the framework ships with a few providers that allow you to easily wire up a user management system with very little to zero back end code. For instance, you can use the SqlMembershipProvider and the SqlRoleProvider which will create a database for you with tables for maintaining users and their roles. There are also providers available that use Active Directory as the user and role data store.

This is great in many cases and can save you from writing a lot of tedious plumbing code. However, there are many instances where this may not be appropriate:

  • I personally don’t feel comfortable having third party code create my user schema. Perhaps I’m being overly sensitive here.
  • You don’t use Sql Server or Active Directory. If you are using MySql to manage your user data, neither the out of the box providers will work for you.
  • You already have your own user data and schema which you want to continue using. You will need to implement your own providers to work with your schema.

I fall into the first and third scenarios listed above. I have multiple applications that all run on top of a common user/role management database. I want to be able to use Forms Authentication to provide my web security and I want all user validation and role checking to leverage the database schema that I already have. Fortunately, creating custom Membership and Role providers proved to be rather easy and this post will walk you through the necessary steps to get up and running.

Defining Users, Roles and Rights

My schema has the following key tables:
A users table which defines individual users and their user names, human names, passwords and role.

  • A role table that defines individual roles. It’s a simple lookup table with role_id and role_name columns.
  • A right table which is also a simple lookup table with Right_id and right_name columns.
  • A role_right table which defines many to many relationships between roles and rights. It has a role_id and right_id columns. Individual roles contain a collection of one to N number of rights.

These tables are mapped to classes via nHibernate.

Here is the User class:

public class User : IPrincipal    {
  protected User() { }
  public User(int userId, string userName, string fullName, string password)
  {
    UserId = userId;
    UserName = userName;
    FullName = fullName;
    Password = password;
  }
  public virtual int UserId { get; set; }
  public virtual string UserName { get; set; }
  public virtual string FullName { get; set; }
  public virtual string Password { get; set; }
  public virtual IIdentity Identity { get; set; }
  public virtual bool IsInRole(string role)
  {
    if (Role.Description.ToLower() == role.ToLower())
      return true;
    foreach (Right right in Role.Rights)
    {
      if (right.Description.ToLower() == role.ToLower())
        return true;
    }
    return false;
  }
}

You will notice that User derives from IPrincipal. This is not necessary to allow User to operate with my MembershipProvider implementation, but it allows me the option to use the User class within other frameworks that work with IPrincipal. For example, I may want to be able to directly interact with User when calling the Controller base class User property. This just requires me to implement the Identity property and the bool IsInRole(string roleName) method. When we get to the RoleProvider implementation, you will see that the IsInRole implementation is used there. You may also notice something else that may appear peculiar: a role here can be either a role or a right. This might be a bit of a hack to shim my finer grained right based schema into the ASP.NET role based framework, but it works for me.

Here are the Role and Right classes. They are simple data objects:

public  class Role
{
  protected Role() { }

  public Role(int roleid, string roleDescription)
  {
    RoleId = roleid;
    Description = roleDescription;
  }

  public virtual int RoleId { get; set; }
  public virtual string Description { get; set; }
  public virtual IList<Right> Rights { get; set; }
}

public class Right
{
  protected Right() { }
  public Right(int rightId, string description)
  {
    RightId = rightId;
    Description = description;
  }

  public virtual int RightId { get; set; }
  public virtual string Description { get; set; }
}

Implementing the providers

So now with the basic data classes behind us, we can implement the membership and role providers. These implementations must derrive from MembershipProvider and RoleProvider. Note that these base classes contain a lot of methods that I have no use for. The Membership Provider model was designed to handle all sorts of user related functionality like creating users, modifying paswords, etc. I just need basic logon and role checking; so a lot of my methods throw NotImplementedExceptions. However, all methods required to handle authentication and role checking are implemented.

Here is the Membership Provider:

public class AdminMemberProvider : MembershipProvider
{
  public override string ApplicationName
  {
    get { throw new NotImplementedException(); }
    set { throw new NotImplementedException(); }
  }
  public override bool ChangePassword(string username, string oldPassword, string newPassword)
  {
    throw new NotImplementedException();
  }
  public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
  {
    throw new NotImplementedException();
  }
  public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
  {
    throw new NotImplementedException();
  }      
  public override bool DeleteUser(string username, bool deleteAllRelatedData)
  {
    throw new NotImplementedException();
  }
  public override bool EnablePasswordReset
  {
    get { throw new NotImplementedException(); }
  }
  public override bool EnablePasswordRetrieval
  {
    get { throw new NotImplementedException(); }
  }
  public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
  {
    throw new NotImplementedException();
  }
  public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
  {
    throw new NotImplementedException();
  }
  public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
  {
    throw new NotImplementedException();
  }
  public override int GetNumberOfUsersOnline()
  {
    throw new NotImplementedException();
  }
  public override string GetPassword(string username, string answer) 
  {
    throw new NotImplementedException();
  }
  public override MembershipUser GetUser(string username, bool userIsOnline)
  {
    throw new NotImplementedException();
  }
  public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
  {
    throw new NotImplementedException();
  }
  public override string GetUserNameByEmail(string email)
  {
    throw new NotImplementedException();
  }
  public override int MaxInvalidPasswordAttempts
  {
    get { throw new NotImplementedException(); }
  }
  public override int MinRequiredNonAlphanumericCharacters
  {
    get { throw new NotImplementedException(); }
  }
  public override int MinRequiredPasswordLength
  {
    get { throw new NotImplementedException(); }
  }
  public override int PasswordAttemptWindow
  {
    get { throw new NotImplementedException(); }
  }
  public override MembershipPasswordFormat PasswordFormat
  {
    get { throw new NotImplementedException(); }
  }
  public override string PasswordStrengthRegularExpression
  {
    get { throw new NotImplementedException(); }
  }
  public override bool RequiresQuestionAndAnswer
  {
    get { throw new NotImplementedException(); }
  }
  public override bool RequiresUniqueEmail
  {
    get { throw new NotImplementedException(); }
  }
  public override string ResetPassword(string username, string answer)
  {
    get { throw new NotImplementedException(); }
  }
  public override bool UnlockUser(string userName)
  {
    get { throw new NotImplementedException(); }
  }
  public override void UpdateUser(MembershipUser user)
  {
    get { throw new NotImplementedException(); }
  }
  IUserRepository _repository;
  public AdminMemberProvider() : this(null) { }
  public AdminMemberProvider(IUserRepository repository) : base()
  {
    _repository = repository ?? UserRepositoryFactory.GetRepository();
  }
  public User User { get; private set; }
  public UserAdmin.DataEntities.User CreateUser(string fullName,string passWord, string email)
  {
    return (null);
  }
  public override bool ValidateUser(string username, string password)
  {
    if(string.IsNullOrEmpty(password.Trim())) return false;
    string hash = EncryptPassword(password);
    User user = _repository.GetByUserName(username);
    if (user == null) return false;
    if (user.Password == hash)
    {
      User = user;
      return true;
    }
    return false;
  }
  
  /// <summary>
  /// Procuses an MD5 hash string of the password
  /// </summary>
  /// <param name="password">password to hash</param>
  /// <returns>MD5 Hash string</returns>
  protected string EncryptPassword(string password)
  {
    //we use codepage 1252 because that is what sql server uses
    byte[] pwdBytes = Encoding.GetEncoding(1252).GetBytes(password);
    byte[] hashBytes = System.Security.Cryptography.MD5.Create().ComputeHash(pwdBytes);
    return Encoding.GetEncoding(1252).GetString(hashBytes);
  }
}

The key method implemented here is ValidateUser. This uses the Repository pattern to query the user by name and then compares the password in the repository with the password passed to the method. My default repository is backed by nHibernate, but it could be plain ADO or a fake repository for testing purposes. You will need to implement your own UserRepositoryFactory based on your data store. Note that I am encrypting the passed password before the comparison. This is because our passwords are hashed in the database.

Here is the Role Provider:

public class AdminRoleProvider : RoleProvider
{
  IUserRepository _repository;
  public AdminRoleProvider(): this(UserRepositoryFactory.GetRepository()) { }
  public AdminRoleProvider(IUserRepository repository) : base()
  {
    _repository = repository ?? UserRepositoryFactory.GetRepository();
  }
  public override bool IsUserInRole(string username, string roleName)
  {
    User user = _repository.GetByUserName(username);
    if(user!=null)
      return user.IsInRole(roleName);
    else
      return false;
  }
  public override string ApplicationName
  {
    get { throw new NotImplementedException(); }
    set { throw new NotImplementedException(); }
  }
  public override void AddUsersToRoles(string[] usernames, string[] roleNames)
  {
    throw new NotImplementedException();
  }
  public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
  {
    throw new NotImplementedException();
  }
  public override void CreateRole(string roleName)
  {
    throw new NotImplementedException();
  }
  public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
  {
    throw new NotImplementedException();
  }
  public override bool RoleExists(string roleName)
  {
    throw new NotImplementedException();
  }
  public override string[] GetRolesForUser(string username)
  {
    User user = _repository.GetByUserName(username);
    string[] roles = new string[user.Role.Rights.Count + 1];
    roles[0] = user.Role.Description;
    int idx = 0;
    foreach (Right right in user.Role.Rights)
      roles[++idx] = right.Description;
    return roles;
  }
  public override string[] GetUsersInRole(string roleName)
  {
    throw new NotImplementedException();
  }
  public override string[] FindUsersInRole(string roleName, string usernameToMatch)
  {
    throw new NotImplementedException();
  }
  public override string[] GetAllRoles()
  {
    throw new NotImplementedException();
  }
}

Again, many methods of the base class are unimplemented because I did not need the functionality.

Wiring the providers in web.config

This is all the code we need for the “model” level user authentication and role checking. Next we have to wire these classes up in our web.config so that the application knows to use them. The following should be inside of <system.web>:

<authentication mode="Forms" >
  <forms loginUrl="~/LoginAccount/LogOn" path="/" />
</authentication>
<authorization>
  <deny users="?"/>
</authorization>
<membership defaultProvider="AdminMemberProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear/>
    <add name="AdminMemberProvider" type="UserAdmin.DomainEntities.AdminMemberProvider, UserAdmin" />
  </providers>
</membership>
<roleManager defaultProvider="AdminRoleProvider" enabled="true" cacheRolesInCookie="true">
  <providers>
    <clear/>
    <add name="AdminRoleProvider" type="UserAdmin.DomainEntities.AdminRoleProvider, UserAdmin" />
  </providers>
</roleManager>

Brining in the Controller and View

The only thing left now is to code the controllers. First our LoginAccountController needs to be able to log users in and out of the application:

Here is our login form in the view:

public class LoginAccountController : Controller
{
  UserMemberProvider provider = (UserMemberProvider) Membership.Provider;
  public LoginAccountController() {}
  public ActionResult LogOn() { return View(); }

  [AcceptVerbs(HttpVerbs.Post)]
  public ActionResult LogOn(string userName, string password, string returnUrl)
  {
    if (!ValidateLogOn(userName, password))
      return View();
    UserAdmin.DataEntities.User  user = provider.GetUser();
    FormsAuthentication.SetAuthCookie(user.UserName, false);
    if (!String.IsNullOrEmpty(returnUrl) && returnUrl != "/")
      return Redirect(returnUrl);
    else
      return RedirectToAction("Index", "Home");
  }

  public ActionResult LogOff()
  {
    FormsAuthentication.SignOut();
    return RedirectToAction("Index", "Home");
  }

  private bool ValidateLogOn(string userName, string password)
  {
    if (String.IsNullOrEmpty(userName))
    {
      ModelState.AddModelError("username", "You must specify a username.");
    }
    if (String.IsNullOrEmpty(password))
    {
      ModelState.AddModelError("password", "You must specify a password.");
    }
    if (!provider.ValidateUser(userName, password))
    {
      ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
    }
    return ModelState.IsValid;
  }
}
<div id="errorpanel">
    <%= Html.ValidationSummary() %></div>

<div class="signinbox">
   <% using (Html.BeginForm()) { %>
     <table>
       <tr>
         <td>Email:</td>
         <td>
           <%= Html.TextBox("username", null, new { @class = "userbox"}) %>
         </td>
        </tr>
        <tr>
          <td>Password:</td>
          <td>
            <%= Html.Password("password", null, new { @class = "password"}) %>
          </td>
        </tr>
        <tr>
          <td>
            <input type="image" src="/Content/images/buttonLogin.gif"
              alt="Login" width="80" height="20" border="0" id="Image1"
              name="Image1">
          </td>
          <td align="right" valign="bottom"></td>
        </tr>
      </table>
   <% } %>
</div>

That’s it. It’s really pretty simple.

The Perfect Build Part 2: Version Control by Matt Wrock

Over a year ago, my team was using Visual Source Safe (VSS) for version control. I’m not going to spend time discussing why we wanted to migrate away from VSS. The reasons should be obvious to anyone who has worked with VSS and especially to those who have worked with both VSS and another source control system like SVN, CVS or GIT.

We decided to migrate to Subversion. At the time there were only two options we were considering: TFS and Subversion (SVN). We chose SVN because it seemed much lighter than TFS, it had wide industry adoption, another business unit in our organization had been happily using it for years and I could not find anything stating SVN sucks. If I were making the same decision today, I would probably have chosen GIT. Especially since shortly after our SVN migration, we spawned a team in Vietnam that is somewhat bandwidth constrained.

The source Safe repository we had been using had been in existence for over eight years and had survived several managerial “dynasties.” In short, the repository structure was completely disorganized. We did not intend to simply transplant our VSS structure to SVN. We wanted to use the migration as an opportunity to reorganize our repository into something much more usable that would support a smooth code promotion process. As a side effect of this decision, we also had no intention of migrating our VSS history to the SVN repository. There are tools that will assist in doing this. Our plan was to keep the VSS repository alive in a read only state as a reference for project history. As new development progresses on the new SVN repository, the need to refer to VSS would become more and more infrequent.

Structure Requirements

My development team consists of a few sub teams (2 to 4 developers each) that each build and maintain multiple applications. We also have common libraries that are shared across teams. As we planned our structure, we had the following requirements:

  1. We wanted to have a structure that would allow us to commit common code to a single trunk so that when other teams checked out the trunk, they would be guaranteed to have the latest code.
  2. We wanted checkouts to be simple where developers could get everything they need by doing a single checkout of the trunk.
  3. We wanted to be able to lock down an app at a certain point in its release cycle so that changes to common code from another team would not propogate to another team’s app when they were in final QA.
  4. We wanted to have snap shots of every app at each release.

 

“Suggested” SVN Structure

Many blogs and the SVN Book suggest a structure that looks like the following:

/projA/trunk
/projA/branches
/projA/tags
/projB/trunk
/projB/branches
/projB/tags

I’m sure this structure works great in the majority of cases where you have a single dev team or are working on a single application or if you have multiple teams that have strictly partitioned code bases. However, it would be awkward to apply it to our environment. How would you handle shared libraries here?

Our Structure

/prod
/prod/app1/20090903
/prod/app1/20090917
/prod/app2/20090903
/sandbox
/sandbox/users/mwrock
/staging
/staging/app1
/staging/app2
/trunk
/trunk/libraries
/trunk/desktop
/trunk/docs
/trunk/services
/trunk/sql
/trunk/testing
/trunk/thirdparty
/trunk/web

Here we have the following root folders:

  • Trunk:  This holds the latest code revisions suitable for integration into the mainline. There is a single trunk shared by all projects and teams. Developers only have to checkout this single folder and they have everything they need.
  • Sandbox: These are personal development areas used for branching long running changes that you want to keep separate from the trunk until they are ready to be merged back to the trunk.
  • Staging: This is the final QA/UAT area. The trunk is copied here once development is thought to be stable and ready for final testing. This protects the release from development commited to the trunk by other teams. When a release is in this stage, you do not want unknown commits from someone else entering your code base.
  • Prod: This contains production releases. Each application has its own folder under prod and each release has a folder named after the date of its release. The staging branch is copied to these release tags upon deployment and they represent a snapshot of the code at the time of release. The prod area is a historical record of exactly what was released and when.

Limitations

This structure has worked great for us. However no structure is perfect for everyone. For instance if you have separate teams working on applications with no shared code and that are completely unrelated, it may be better to separate the code bases of such teams into completely separate repositories. Another limitation we have run into is with offshore development. We have a team in Vietnam that has a much thinner bandwidth pipe and it takes a couple hours for them to check out the entire trunk. Admittedly, my personal experience with SVN is not vast and there are likely better ways to organize a repository for our team. But the fact remains that this has worked well for us and has had tremendously improved the state of our configuration management.

In my next post in this series, I will discuss how our integration server interacts with our repository and automates builds and deployments.

The Perfect Build Part 1 by Matt Wrock

A year ago, my team was using Visual Source Safe as our version control repository and our builds were built and deployed manually using sophisticated tools like windows explorer and remote desktop. This was not a happy time.

  • VSS was awfully slow and practically impossible to work with remotely.
  • VSS branching, tagging and merging capabilities were primitive at best.
  • Our manual deployments were not scripted and therefore not easily repeatable and certainly not guaranteeably repeatable.
  • Deployments were inadequately documented and it was often impossible to know exactly what was deployed and when.
  • Complicated deployments were time consuming, error prone and just plain stressful for the build engineer.

Well all of this has changed now. We migrated to Subversion for source control and implemented CruiseControl and NANT as an integration server and deployment solution. Looking back on this migration, I can honestly say that it represents the biggest leap forward we have made in terms of process improvement. This is a first in a series of blog posts where I will document this process in detail. I will outline how we tackled the following:

  • Repository structure: We did not want to merely copy our VSS repository tree. We wanted to create something fundamentlly more useful.
  • Generic NANT scripts to handle Visual Studio Projects and Solutions
  • Managing configuratioin files in different environments (dev, staging and production)
  • Automated tagging production deployments
  • Cruisecontrol organizaion

Some may say, "Its only source control, what's the big deal?" Well our source is our product. Any inability to produce our product or any risk to the integrity of our product is a big deal. As developers, we interact with source control throughout every working day and it is imperative that these interactions are simple, reliable and valuable. If there is waste in terms of time or lost code at this layer of the develoopment process, its like a death by a thousand cuts to the whole dev team.

This was a migration that I took very seriously. Like most software shops, we are always very busy and juggle lots of projects at the same time. This migration provided no direct revenue increase to our business and was always difficult to prioritize. I was often asked "why don't we just migrate now? Just have Joe Jr. Developer port the code from VSS to SVN and be done with it." This was something I really wanted to think through, research and do right. I knew it was not going to be a "set and forget" operation. It needed to be planned, implemented and enforced and baby sat. My team, including myself, had been using VSS for years and were not familiar with the branch/merge paradigm of SVN. I knew we needed to understand these concepts and use them to their fullest. I also knewthat developers were going to experience hickups with usig SVN and its related tools. I needed to educate myself and make myself availale as a mentor of this technology although I was a novice. There was no budget to bring in a consultant.

So I researched on the web, I talked to colleagues with experience in SVN in varying environments, and I mapped out my own custom solution. My tech department is composed of different teams that produce and maintain several different applications that are web based, desktop and background services. We also have some common libraries shared and maintained by all teams and we share third party tools and libraries some of which are open source and require constant updates to new versions. We needed a solution that would allow us to build separate applications maintained by separate teams with code shared accross the organization. We needed a setup that would allow developers to commit code to a centralized Trunk that had all of the most recent changes but would present these changes to different applications in a controlled manner.

I beieve we have acieved exactly this and more. So what do we have now?

  • We have a source control repository that informs us of exactly what is on an individual dev's environment, on our integrated dev box, on staging and on production.
  • We have a setup that allows us to see exactly what code was deployed on which application on which date.
  • Commits to SVN are automaticaly built and deployed to our integration dev server
  • Commits to staging are automaically built and deployed to our staging servers.
  • With a click of a button in CruiseControl, an application can be deployed to our production server farms
  • Each environment can have its own versioned configuration
  • Rolling back to a previous deployment is trivial

My next post in this series will focus on the SVN structure we developed and how it partitions our different environments and applications but ensures that each new build incorporates the latest comon code.

Debugging Windows Services in Visual Studio by Matt Wrock

One challenge involved in developing windows service applications is debugging them in Visual Studio. I don't know why Visual Studio does not provide better support for this, but I've seen some creative techniques employed to make debugging windows services possible. One popular method is to put a really long thread.sleep(30000) in the OnStart event and then install the service, start it and attach the debugger to the service's process hoping that it will take less than 30 seconds to start it, find the process and attach.

There is a better way and it is quite trivial.

There is one prerequisite: Make sure you do not have service logic in your OnStart() method. This turns out to be a good thing either way. I've seen 100 line service OnStart methods that put a good deal if not all of the logic into this one method. Well what if you want to reuse this logic in a console, WPF or win forms app? Its not a very flexible methodology. I believe that a windows service project should never contain more than your installer and entry point class and the entry point class only handles start, stop and pause by caling into a completely separate assembly. However, before over philosophizing this, lets quickly jump into how to setup your service project so that it can be easily dubugged in VS.

Follow these steps:

  1. Change your Main() entry point method to Main(string[] args)
  2. Change the Output Type property of the servce project from Windows Aplication to Console Application
  3. Your Main(string[] args) method shouldlook something like:
        static void Main(string[] args)        {            if (args.Length > 0)            {                MyApp app = new MyApp();                app.Run();            }            else            {                ServiceBase[] ServicesToRun;                ServicesToRun = new ServiceBase[]                 {                     new Service1()                 };                ServiceBase.Run(ServicesToRun);            }        }

Finaly, in the Debug section of the project properties, provide a command line argument.

Your OnStart() should contain the same app startup code as you Main method:

        protected override void OnStart(string[] args)
        {
            MyApp app = new MyApp();
            app.Run();
        }

Thats it. Now hit F5 and you will see a command window pop up and all of your break points should be recognized. MyApp contains the meat of te logic. Main and OnStart are just dumb harnesses.

 

 

Debugging Managed Production Applications with WinDbg by Matt Wrock

Yesterday our issue tracking software was hanging and the vendor was not responding to our ticket requsts (They are nine hours ahead of us). The application is a .NET application so I decided to capture a crash dump and dive in with windbg. I have a love/hate relationship with windbg. I love it because it provides a vast wealth of informationvirtually telling me EVERYTHING thats going on with my process. It has saved my behind several times. I hate it because I don't use it frequently enough to have all of the cryptic commands memorized and often have to relearn and reresearch the commands I need to use in order to solve my problem. Windbg is not for the faint of heart. There is no drag and drop here. But if you have an app bugging out on a production server and don't want to attach a debuger to it, windbg is the tool for you.

This post is an adaptation of a document I created for my team and me a few years back. I use it like a cheat sheet to help me get started quickly.

When servers start crashing and/or hanging in production, often the only recourse you have is to capture a memory dump of the ailing process and analyze it using Microsoft’s Native debugger – WinDbg. Without this tool, you may just be shooting in the dark. These techniques can not only be applied to web applications but to any application – under managed or unmanaged code.

A memory dump will allow you to see everything going on in the captured process: executing threads and how long each have been running, stack traces of all threads and even the values of parameters passed to functions. They can also be used to troubleshoot memory leaks, allowing you to see what is in the heap.

A word of caution is in order: windbg is a pain to use. At least that has been my experience. There is almost no documentation included and the commands are very unintuitive, and this is compounded by the fact that you (hopefully) rarely use it.

There are three basic steps to this process:

  1. Preparing the debugging environment on the problem server.
  2. Actually capturing the dump while your process is crashing or crashed.
  3. Analyzing the dump in windbg

 

Preparing the Debugging Environment

There are a few steps to complete to get the server ready:

  1. Install Microsoft’s Debugging toolkit. Get the latest version at http://www.microsoft.com/whdc/DevTools/Debugging/default.mspx. Note that there is a 32 bit and 64 bit version. If you are running on a 64 bit server but you have a managed app that is compiled for 32 bit, you will need to use the 32 bit version of windbg to debug.
  2. Create an environment variable for path to symbol file (.pdb files that contain information that map native instructions tp function calls). Create a system environment variable called _NT_SYMBOL_PATH with the value: C:\symbols\debugginglabs*http://msdl.microsoft.com/download/symbols;C:\symbols\debugginglabs;C:\Program Files\Microsoft.Net\FrameworkSDK\symbols;C:\windows\system32
  3. Copy sos.dll from the Microsoft.net directory to the same directory where you installed the debugging toolkit. This file provides extensions to windbg for analyzing managed code.

 

Capturing a Memory Dump

This step can be a bit tricky depending on the circumstances of your crashing behavior. There are typically 3 ways to do this:

  1. Call up a test URL to see if the app has crashed or is locking
  2. Use Task Manager to see if the CPU is pinned
  3. Use Performance Monitor and look for queueing threads. If threads are queueing, that means that all available .net worker threads are busy which usually means something is wrong.

Once you have determined that the process has crashed, bring up a command prompt and navigate to the directory where you downloaded the debugging toolkit. Next type:

adplus.vbs –hang –pid [process ID of problem process]

If there are more than one worker process running and you are not sure which one is causing problems, repeat the above command for both processes.

This command will launch windbg in a separate window to load the process information. Just let it run and it will close when it completes.

Analyzing the Dump

  1. Open windbg.exe which is inside the directory that you extracted the debugging toolkit to.
  2. Go to File/Open Crash Dump and find the dump (.DMP) file you just captured. It will be in a subfolder of the debugging toolkit directory.
  3. type .load sos.dll to load the managed code extensions.

 

You are now ready to start troubleshooting. Below are some commands I commonly use to get useful information. At the end of this document are some links to some MS white papers with more detailed information on performance debugging.

Listing all threads and how long they have been running

!runaway

Note the thread IDs of any particularly long running threads. If you have several threads that have been running for minutes, that could point to a never ending loop that is eating CPU or just a long running background thread.

Listing Managed Threads

!threads

There are several noteworthy tidbits here:

Lock Count: If this is greater than 0, it means that the thread is waiting(blocking) for another thread. For instance it might be waiting for a DB query to come back or a response from a socket. If you have a bunch of these, it could be a tip that there is a bad query. See below on how to get the call stack of an individual thread to see exactly what it is doing.

Domain: This is the address of the app domain that the thread is running in. This is very helpful if you have several web sites running in the same worker process. Once you find the problem thread(s), you can use this to see which web app is causing the problem. Keep in mind that all asp.net workerprocess have a “default” app domain used for launching new app domains (there is one per web app) and handling GC.

Determine which Web Application a thread is running in

!dumpdomain [address]

This dumps a list of assemblies loaded into the domain which should tip you off as to which web app it is running in.

Get a summary information on the Threadpool

!threadpool

This tells you haw many threads are free/in use and what the CPU utilization was at the time of the capture.

Get the stack trace of a single thread including passed parameters

~[thread id]e !clrstack –p

Get the thread ID from !threads or use “*” to get a dump of ALL threads.

Get detailed information on an object

!dumpobj [address]

This gives info on all fields in the object.

More Resources

http://msdn.microsoft.com/en-us/library/ms954594.aspx
This is an old link but has good and thorough informatioin.

http://blogs.msdn.com/tess/ This is Tess Ferrandez's blog. She has tons of great posts on this subject and also on analyzing memory leaking problems.