How to break on the ‘Main’ function with the .NET CLR 4.0 and WinDbg
If you ever tried to write a post on a technical blog, maybe you already experienced a strange thing: start with the idea of an article, and ends up with a totally different one. This is definitely the case of this one, as I just tried to have a decent local variable value in a call to the famous “!clrstack” function of the Windbg extension SOS, and I had a lot of trouble trying to break on the main function.
Most internet resources (even the help documentation of SOS itself !) advise the user to subscribe to the CLR load notification, and then load SOS and setup a deferred breakpoint. Well I don’t have any CLR load notification on my computer (Windows 7, and I tried x86/x64 applications targeting 4.0, 3.5 and 2.0), and I remembered experiencing the same behavior on my computer at work. As I’m not the only one having this issue (check this), I decided to write a short post about it.
So how can I break in the ‘main’ function of my application with .NET 4.0 and WinDbg?
Maybe this is your main question if you are reading this, so I’m going to start with the answer. Here is the advice given by SOS:
If it does not work, try this:
1) Start the debugger and type ‘sxe ld:clrjit.dll‘ : it asks to the debugging engine to stop when the clrjit.dll file is loaded.
2) Type ‘g’ to continue; once you reached the previous event, you can now load the SOS extension by typing ‘.loadby sos clr‘ and use any ‘!bpmd’ function you like, including ‘!bpmd myapp.exe MyApp.Main‘ (to be honest, you can load the SOS extension as soon as clr.dll is loaded, but the !bpmd function does not work before the loading of clrjit.dll, on my computer at least).
You should now have a right breakpoint set up!
Why the CLR notification events don’t work?
Well it’s a very good question, and nobody on the net seems to have an answer (me neither). I spent a few minutes to track down what’s going on (thanks to this page, I have the callstack of a classic program triggering the CLR load event) and although I changed some functions behavior right in memory (clr!JITNotifications::IsActive among other ones…) nothing worked. But it is quite clear that the runtime is not aware that it has to throw an exception for CLR loading. More investigation would require more time, and as I found an easy workaround…maybe someday I will take the time to find an answer about that!