Once again about remote debugging
This is a follow-up article for Jacob Lauzier’s Cross-Domain remote debugging. Here I will discuss one particular scenario, which, in my mind, should be extremely popular in virtualized environments but, for some reason, is least straight forward to setup.
My development desktop is a Windows XP with Visual Studio 2008 on it. I debug against several virtual machines, my typical setup is Windows 2003 R2 server with WSS on it. Virtual machine obviously is NOT on the domain, because I want to manage my own VMs myself; my desktop obviously IS on the domain, because I use TFS and all other cool things available at Atalasoft. I think this configuration makes sense, since development VMs are normally insecure and disposable, and should not be exposed to company’s production infrastructure. And it feels like I am not the only one adhering to this path, since lots of people run into numerous problems with setting up a remote debugger in similar configuration. Typical issues one will encounter here:
1. You cannot attach to the remote process due to authentication issues (“The Microsoft Visual Studio Remote Debugging Monitor (MSVSMON.EXE) does not appear to be running on the remote computer”, here);
2. Remote debugger cannot connect back due to firewall issues or different authentication issues (“The Visual Studio Remote Debugger service on the target computer cannot connect back to this computer”, here);
3. Publishing your solution (we do SharePoint development) on the target server is not trivial task;
4. Remote debugging does not pick up debugger databases, so you cannot step through your code (“The breakpoint will not currently be hit. No symbols have been loaded for this document.”, here, here and here);
5. …or will find them but will refuse to load them (“The symbol file XXX.pdb does not match the module”, here);
6. …or will skip loading because of “Just My Code” debugging settings;
7. …or different debugger errors.
Here is some information on how to get this thing to work…
You run debugger on remote server and local desktop. Remote server, as I mentioned above, is not on domain (in my case it is a virtual machine in “Debug” workgroup), and local desktop belongs to Atalasoft domain. According to MSDN, CLR does not provide explicit remote debugging capabilities, so the debugging is done through a proxy application called “Visual Studio Remote Debugging Monitor”. Your IDE (Visual Studio) talks to that remote debugging monitor, which interacts with the process you want to debug. The way communication is happening is controlled by transport. Default transport is DCOM-based two-way protocol with Windows authentication on top of it. For CLR it is pretty much the only way of remote debugging (the other way is called “Remote” and it is suitable for native code only). Debugging happens in the following way:
1. First, you deploy assemblies on the target machine;
2. Then you start the process you want to debug on the remote machine. For SharePoint the process is already running (w3wp.exe, WWW worker process), for other applications you may either use WMI to start it remotely or use Sysinternals’ psexec;
3. Attach to the remote process from Visual studio (this is done in two directions: first your visual studio connects to visual studio remote debugging monitor, then visual studio remote debugger monitor connects back to your visual studio, and this is where 50% of troubles are);
4. Have your debugging symbols database (PDB) files loaded (this is where remainder 50% of troubles are);
5. Enjoy your remote debugging
Setting up
MSDN has number of articles on this subject. For this particular scenario you will need:
1. On remote machine:
a. Install remote debugger (it can be downloaded here). In my case running installer from desktop did not work, so I had do copy it to the temp folder and bounce the server.
b. Make sure the local security setting is set to “Classic” (look for Local Security Settings under Administrative tools):
c. Create an account on the remote machine. Visual Studio will use this account to connect to the remote machine. It must have the same user name and password as the user you running the Visual Studio as (i.e. your domain user name). In my case, my domain login is “dbarvitsky@atalasoft.lcl” and the user name I am creating on the target server is “dbarvitsky”. This must be the user local account, since Visual Studio will be logging in as DBARVITSKYLAB\dbarvitsky (where DBARVITSKYLAB is my virtual machine I use for debugging). User must have the same password as well. It also must be an administrator on the virtual machine.
d. Create second account on the remote machine. This account will be running the remote debugging monitor. I use “Debugger” name on remote machine.
e. If you are planning to run debugger as a service (so that you do not have to start it manually) make sure “Debugger” user has a permission to run as a service (look for Local Security Settings under Administrative tools or just run secpol.msc):
2. On local machine:
a. Create account on the local machine (where you have Visual Studio installed). It must be local user account with the same as the account you created on previous step (“Debugger”). The password must be identical to the “Debugger” account on the remote machine. Remote debugger will use this account to connect back to Visual Studio.
b. Alter firewall settings to allow inbound TCP 135 port (Remote Debugging DCOM) and grant Visual Studio permissions to create outbound connections:
It normally makes sense to limit scope to “my network/subnet” only (assuming that your VMs are in the same subnet as your machine):
Hints on deploying assemblies
The first step, deploying assemblies (and content) to the remote server implies that you have them built locally and the target platform matches the virtual machine you deploy to. Getting the content on the target machine is not a problem and can easily be done with NAnt:
<copy todir="${destination}" verbose="true"> <fileset basedir="${source}"> <include name="pattern\*.*"/>
</fileset>
</copy>
For assemblies you have two options:
1. Simply copy them to the C:\Inetpub\wwwroot\wss\VirtualDirectories\<port>\bin on the target server with their PDBs (debugger database files, where your debugging symbols come from; Visual Studio needs them to relate point in compiled assembly back to the source code, variables, etc);
2. Install assemblies with PDBs into GAC. You can use AtalaGAC.exe tool for it.
Choose the one that is closer to your production deployment. If you just drop the application in virtual folder and will have all assemblies in bin directory, choose the first option. If you are going to have some fancy installer and will require assemblies to be registered in GAC, go with the second one.
Either way, you will need to have your new assemblies reloaded. Again, there are two ways to do it:
1. If you are running in share environment (i.e. many people use the same server for debugging), recycling entire IIS is not nice. Instead you can reload only application pool you are working with. Running “iisapp /a ‘SharePoint - 80’ /r” remotely will do the trick (“SharePoint - 80” is the application pool name);
2. If you can afford recycling entire IIS just run “iisreset” on remote system.
Example NAnt script:
<exec program="C:\Program Files\Sysinternals\PsUtils\psexec.exe">
<arg value="\\${target-server}"/> <arg value="-u"/>
<arg value="${target-server.login}"/> <arg value="-p"/>
<arg value="${target-server.password}"/> <arg value="iisreset"/>
</exec>
Attaching to the remote process from Visual Studio
This is a bit tricky part. In order to do this, you need to start remote debugger monitor. Again, two options:
1. Use “Run As…” and runs as Debugger:
2. Use remote debugger’s configuration wizard to run it as a service:
After you started remote debugger, run your Visual Studio and run “Attach to process”.
If you are running as a service, use DBARVITSKYLAB for qualifier and remember to check “Show process from all users”:
If you are running remote debugger as application, use Debugger@DBARVITSKYLAB as qualifier:
Then select a process (w3wp.exe in my case) and click “Attach”. If you connected successfully, you should be able to see the list of remote modules (Debug, Windows then Modules or Ctrl+D, then M). You should be able to find your assemblies; assemblies must be loaded with symbols. If it is not the case, check your Visual Studio’s Tools\Debugging\General and disable “Just My Code” and make sure you have deployed your PDB files properly. Technically you do not have to have the PDB files on remote machine, but Visual Studio must be able to find them.
Normally you will not have all PDBs loaded immediately after starting application pool, because assemblies did not have a chance to load as yet. All it takes is a single GET request, which I normally include in my Nant deployment script.
References:
1. http://blogs.msdn.com/jmstall/
2. http://blog.ray1.net/2008/04/debugging-sharepoint-gac-dlls.html
3. http://www.belshe.com/2004/03/29/gacutil-gac-install/
4. http://www.wictorwilen.se/Post/How-to-get-Remote-Debugging-work-properly.aspx
5. http://www.eggheadcafe.com/software/aspnet/30490153/the-symbol-mymodulepdb-d.aspx
6. http://forums.asp.net/t/1349247.aspx