Command Line Debugging Revisited - Part 6: IL Debugging Commands

Dan Elliott recently posted about the IL OpCodes supported by the .NET Compact Framework.  This got me inspired to talk about the IL debugging commands in the .NET Compact Framework MDbg extension (mdbgnetcf.dll).

Commands
dis[assemble]
in[ext]
is[tep]
o[ut]

As with the previous installments of this series, I will be using the Visual Studio 2005 WebCrawler sample application as the debuggee.  You can download the sample from here.  To debug at the IL level, we first need to get connected to the debuggee (Device Emulator users should refer to this addendum).

Once connected, we can set a breakpoint on an interesting method, I will use Crawler.Crawl().

[p#:0, t#:0] mdbg> x webcrawler!*Crawl*~0. Microsoft.Samples.NetCF.Crawler.add_CurrentPageEvent(value)~1. Microsoft.Samples.NetCF.Crawler.remove_CurrentPageEvent(value)~2. Microsoft.Samples.NetCF.Crawler.add_PageFoundEvent(value)~3. Microsoft.Samples.NetCF.Crawler.remove_PageFoundEvent(value)~4. Microsoft.Samples.NetCF.Crawler.add_CrawlFinishedEvent(value)~5. Microsoft.Samples.NetCF.Crawler.remove_CrawlFinishedEvent(value)~6. Microsoft.Samples.NetCF.Crawler..ctor(startingPage,noProxy)~7. Microsoft.Samples.NetCF.Crawler.Start()~8. Microsoft.Samples.NetCF.Crawler.Stop()~9. Microsoft.Samples.NetCF.Crawler.PageIsHtml(pageAddress,status)~10. Microsoft.Samples.NetCF.Crawler.GetPageData(pageUri,pageData)~11. Microsoft.Samples.NetCF.Crawler.GetPageLinks(pageUri,pageBody,tag,attribute,links)~12. Microsoft.Samples.NetCF.Crawler.Crawl()~13. Microsoft.Samples.NetCF.Crawler.CurrentPageEventHandler..ctor(object,method)~14. Microsoft.Samples.NetCF.Crawler.CurrentPageEventHandler.Invoke(sender,e)~15. Microsoft.Samples.NetCF.Crawler.CurrentPageEventHandler.BeginInvoke(sender,e,callback,object)~16. Microsoft.Samples.NetCF.Crawler.CurrentPageEventHandler.EndInvoke(result)~17. Microsoft.Samples.NetCF.MainForm.HandleCrawlFinishedEvent(sender,e)[p#:0, t#:0] mdbg> b ~12Breakpoint #1 bound (:1!Microsoft.Samples.NetCF.Crawler::Crawl(+0))[p#:0, t#:0] mdbg> g

After hitting the breakpoint, we can look at the disassembly using the dis[assemble] command.

STOP: Breakpoint 1 Hitlocated at line 380 in Crawler.cs[p#:0, t#:1] mdbg> dis 5IL at 0x49e0172c*[IL:0000] 00: nop [IL:0001] 14: ldnull [IL:0002] 0a: stloc.0 [IL:0003] 00: nop [IL:0004] 28:4100000a call System.StringComparer::get_InvariantCultureIgnoreCase
Stepping through IL code is as easy as stepping through high level languages (C#, VB.Net) using the in[ext] command.

p#:0, t#:1] mdbg> inL at 0x49e0172c [IL:0000] 00: nop*[IL:0001] 14: ldnull [IL:0002] 0a: stloc.0 [IL:0003] 00: nop [IL:0004] 28:4100000a call System.StringComparer::get_InvariantCultureIgnoreCase
This is a pretty long method and single stepping can be time consuming.  To save me some time, I am going to set an additional breakpoint on the call to Crawler.GetPageData().

[p#:0, t#:1] mdbg> sh 100< additional lines removed for space / clarity >425 LinkInfo li = (LinkInfo)links[page];426 // check that the page contains html text427 try428 {429 // get the currently stored HttpStatusCode430 // for the target page431 HttpStatusCode currentStatus = li.StatusCode;432433 // check to see if we have tried to connect434 // and if the page is HTML435 if (((HttpStatusCode)0 == currentStatus) &&436 PageIsHtml(page, out currentStatus))437 {438 // read the page439 Uri pageUri = new Uri(page);440 string pageData = "";441 currentStatus = GetPageData(ref pageUri,442 out pageData);< additional lines removed for space / clarity >[p#:0, t#:1] mdbg> b 441Breakpoint #2 bound (line 441 in Crawler.cs)[p#:0, t#:1] mdbg> g
Now that I am at the second breakpoint, I would like to step into the call to Crawler.GetPageData().  As with in[ext], there is a corresponding step into command for IL: is[tep].

STOP: Breakpoint 2 Hit441: currentStatus = GetPageData(ref pageUri,[p#:0, t#:1] mdbg> isIL at 0x49e01388*[IL:0000] 00: nop [IL:0001] 16: ldc.i4.0 [IL:0002] 0a: stloc.0 [IL:0003] 14: ldnull [IL:0004] 0b: stloc.1
The stack shows that we are at the start of the Crawler.GetPageData method.

[p#:0, t#:1] mdbg> wThread [#:1]*0. Microsoft.Samples.NetCF.Crawler.GetPageData (Crawler.cs:216) 1. Microsoft.Samples.NetCF.Crawler.Crawl (Crawler.cs:441)
I can now step through Crawler.GetPageData in IL as desired. 

[p#:0, t#:1] mdbg> inIL at 0x49e01388 [IL:0000] 00: nop*[IL:0001] 16: ldc.i4.0 [IL:0002] 0a: stloc.0 [IL:0003] 14: ldnull [IL:0004] 0b: stloc.1
When I am no longer interested in debugging the method, I can return to the caller using the o[ut] command.  This returns me to where I left off in the Crawler.Crawl method.

[p#:0, t#:1] mdbg> o441: currentStatus = GetPageData(ref pageUri,
The stack and source confirm that we have returned to the Crawler.Crawl() method at the call to Crawler.GetPageData().

[p#:0, t#:1] mdbg> wThread [#:1]*0. Microsoft.Samples.NetCF.Crawler.Crawl (Crawler.cs:441)[p#:0, t#:1] mdbg> sh 5436 PageIsHtml(page, out currentStatus))437 {438 // read the page439 Uri pageUri = new Uri(page);440 string pageData = "";441:* currentStatus = GetPageData(ref pageUri,442 out pageData);443444 // if we successfully retrieved the page data445 if (HttpStatusCode.OK == currentStatus)
For more information on the commands I have used here, please refer to the MDbg h[elp] command.

Take care,
-- DK

Disclaimers:
This posting is provided "AS IS" with no warranties, and confers no rights.