In this post I am going to be discussing about debugging .Net Framework 4.0 using windbg . I am going to demonstrating how to have a break-point within a method, but without the framework source code. This would help in debugging .NET framework when you don’t have VS in a production environment and the same technique can be used to debug other third party assemblies where you don’t have the source code. This is kind of like .NET Reflector where you can step through third party assemblies, but without any cost. It is not going to be as convenient as the professional version of Reflector.
I am going to be using the same example that I used to debug .NET Framework source 3.5 using windbg.
FYI the .NET framework 4.0 has private symbols available on MS symbol server, but the source code is still not available. To debug .NET framework source code it is important to have correct symbol path and here is my symbol path in the _NT_SYMBOL_PATH environment variable.
SRV*d:\dev\symbols*http://referencesource.microsoft.com/symbols; SRV*d:\dev\symbols*http://msdl.microsoft.com/download/symbols
Here is the sample source code that I am going to be using to demonstrate framework debugging
using System; using System.Net; namespace Test { class Program { static void Main(string[] args) { Console.WriteLine("Hello World of debugging"); var wr = WebRequest.Create("http://www.google.com"); Console.WriteLine("Web request created"); var req = wr.GetRequestStream(); Console.WriteLine("Hello World Debugging"); Console.Read(); } } }
Launched the exe within the debugger
Then issued the command to notify when the clrjit is loaded,
sxe ld:clrjit
This is because, to load sos and sosex after the framework is loaded.Then issued the following commands to load sos, sosex and to set break-point on WebRequest.Create
.loadby sos mscorwks .load sosex !mbm System.Net.WebRequest.Create
And when the break-point hits the first time, let it continue by using the g command. It would break into the debugger for other overloaded method for WebRequest.Create and here is the call-stack
0:000> !mk
Thread 0:
ESP EIP
00:M 000000000023ece0 000007fef6ea5a44 System.Net.WebRequest.Create(System.Uri, Boolean)(+0x0 IL)(+0x14 Native) [f:\dd\ndp\fx\src\Net\System\Net\WebRequest.cs, @ 93,13]
01:M 000000000023ed60 000007ff00140176 Test.Program.Main(System.String[])(+0xc IL)(+0x56 Native)
02:U 000000000023edc0 000007fef8b210b4 clr!CallDescrWorker+0x84
03:U 000000000023ee10 000007fef8b211c9 clr!CallDescrWorkerWithHandler+0xa9
04:U 000000000023ee90 000007fef8b21245 clr!MethodDesc::CallDescr+0x2a1
05:U 000000000023f0c0 000007fef8c21675 clr!ClassLoader::RunMain+0x228
06:U 000000000023f310 000007fef8c217ac clr!Assembly::ExecuteMainMethod+0xac
07:U 000000000023f5c0 000007fef8c21562 clr!SystemDomain::ExecuteMainMethod+0x452
08:U 000000000023fb70 000007fef8c23dd6 clr!ExecuteEXE+0x43
09:U 000000000023fbd0 000007fef8c23cf3 clr!CorExeMainInternal+0xc4
0a:U 000000000023fc40 000007fef8ca7365 clr!CorExeMain+0x15
0b:U 000000000023fc80 000007fef9493309 mscoreei!CorExeMain+0x41
0c:U 000000000023fcb0 000007fef9525b21 MSCOREE!CorExeMain_Exported+0x57
0d:U 000000000023fce0 00000000776cf56d KERNEL32!BaseThreadInitThunk+0xd
0e:U 000000000023fd10 0000000077903281 ntdll!RtlUserThreadStart+0x1d
And here is the source code for this method using reflector
private static WebRequest Create(Uri requestUri, bool useUriBase) { string absoluteUri; if (Logging.On) { Logging.Enter(Logging.Web, "WebRequest", "Create", requestUri.ToString()); } WebRequestPrefixElement element = null; bool flag = false; if (!useUriBase) { absoluteUri = requestUri.AbsoluteUri; } else { absoluteUri = requestUri.Scheme + ':'; } int length = absoluteUri.Length; ArrayList prefixList = PrefixList; for (int i = 0; i < prefixList.Count; i++) { element = (WebRequestPrefixElement) prefixList[i]; if ((length >= element.Prefix.Length) && (string.Compare(element.Prefix, 0, absoluteUri, 0, element.Prefix.Length, StringComparison.OrdinalIgnoreCase) == 0)) { flag = true; break; } } WebRequest retObject = null; if (flag) { retObject = element.Creator.Create(requestUri); if (Logging.On) { Logging.Exit(Logging.Web, "WebRequest", "Create", retObject); } return retObject; } if (Logging.On) { Logging.Exit(Logging.Web, "WebRequest", "Create", (string) null); } throw new NotSupportedException(SR.GetString("net_unknown_prefix")); }
Let’s try and have a break-point on line “ ArrayList prefixList = PrefixList;” so that we can check the local variables value. Just because I have the private symbols ,I could have counted the line numbers manually and then set a break-point using !mbp command, but that is no fun. Here is another way of doing this.
.shell -ci "!u 000007fef6ea5a44 " findstr get_PrefixList
In the above command I am disassembling the ip 000007fef6ea5a44 (which is there in the above callstack )to look for get_PrefixList Instruction pointer . Here is the outcome
0:000> .shell -ci “!u 000007fef6ea5a44 ” findstr get_PrefixList
000007fe`f6ea5a7a e8f1000000 call System_ni+0x275b70 (000007fe`f6ea5b70) (System.Net.WebRequest.get_PrefixList(), mdToken: 00000000060019bc)
.shell: Process exited
I use the .shell command to manually avoid searching for an instruction. Now that I have the instruction pointer ,I am going to set a break-point on that using
bp 000007fe`f6ea5a7a "!mdv"
and here is the result of the break-point
0:000> g
(1758.167c): CLR notification exception – code e0444143 (first chance)
(1758.167c): CLR notification exception – code e0444143 (first chance)
Frame 0x0: (System.Net.WebRequest.Create(System.Uri, Boolean)):
[A0]:requestUri:<?>
[A1]:useUriBase:<?>
[L0]:LookupUri:0x00000000022c1a98 (System.String) STRVAL=http://www.google.com/
[L1]:Current:<?>
[L2]:Found:0x0000000000000000 (System.Boolean)
[L3]:LookupLength:0x0000000000000016 (System.Int32)
[L4]:prefixList:<?>
[L5]:i:<?>
[L6]:webRequest:<?>System_ni+0x275a7a:
000007fe`f6ea5a7a e8f1000000 call System_ni+0x275b70 (000007fe`f6ea5b70)
Voila! now I am able to have a break-point within the framework method and also see locals and parameters like Visual Studio. The same technique can be used to debug third party assemblies where you don’t have source code or symbols.
Image may be NSFW.
Clik here to view.
Clik here to view.
