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 ~12
Breakpoint #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 Hit
located at line 380 in Crawler.cs
[p#:0, t#:1] mdbg> dis 5
IL 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> in
L 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 text
427 try
428 {
429 // get the currently stored HttpStatusCode
430 // for the target page
431 HttpStatusCode currentStatus = li.StatusCode;
432
433 // check to see if we have tried to connect
434 // and if the page is HTML
435 if (((HttpStatusCode)0 == currentStatus) &&
436 PageIsHtml(page, out currentStatus))
437 {
438 // read the page
439 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 441
Breakpoint #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 Hit
441: currentStatus = GetPageData(ref pageUri,
[p#:0, t#:1] mdbg> is
IL 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> w
Thread [#: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> in
IL 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> o
441: 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> w
Thread [#:1]
*0. Microsoft.Samples.NetCF.Crawler.Crawl (Crawler.cs:441)
[p#:0, t#:1] mdbg> sh 5
436 PageIsHtml(page, out currentStatus))
437 {
438 // read the page
439 Uri pageUri = new Uri(page);
440 string pageData = "";
441:* currentStatus = GetPageData(ref pageUri,
442 out pageData);
443
444 // if we successfully retrieved the page data
445 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.