BUG: The debugger appears to step incorrectly through the code when you use the debugger to step through Visual C# source code (316834)



The information in this article applies to:

  • Microsoft Visual C# .NET (2002)
  • Microsoft .NET Framework 1.0
  • Microsoft .NET Framework 1.0 SP1

This article was previously published under Q316834

SYMPTOMS

When you use the debugger to step through Visual C# .NET source code, the debugger appears to step incorrectly through the code.

CAUSE

The compiler generates incorrect program database (PDB) debug information, and emits Microsoft intermediate language (IL) instructions that have no associated lines of source code.

RESOLUTION

To resolve this problem, obtain the latest service pack for Microsoft .NET Framework. For additional information, click the following article number to view the article in the Microsoft Knowledge Base:

318836 INFO: How to Obtain the Latest .NET Framework Service Pack



NOTE: to see the proper stepping behavior, you must recompile your code after you have installed the service pack.

STATUS

Microsoft has confirmed that this is a problem in Visual C# .NET. This problem was first corrected in Microsoft .NET Framework Service Pack 2 (SP2).

MORE INFORMATION

Example 1

The following code sample is a try-catch-finally construct:

try
{
  Console.WriteLine("In try");        
}
catch(Exception e)
{
  Console.WriteLine("In catch");
}
finally
{
  Console.WriteLine("In finally");
}      
				
Assume that the debugger is stopped at the following line:
Console.WriteLine("In try");
				
When the next step occurs, it appears that the debugger has stepped into the catch block. However, the following output of the construct shows that this is not actually the case:
In try
In finally
				
In this example, the instruction that causes the error is a CEE_LEAVE instruction in the try block. If you use Ildasm.exe to view the IL that is generated for the instruction, the instruction appears as the following:
leave.s <some line number>
				
This instruction has no associated source line; however, when the debugger steps into this instruction, the debugger shows the source information of the lexically previous instruction that had associated source code. In this example, this is the following source line:
Console.WriteLine("In catch");
				

Example 2

The following example is a nested if construct:

bool testCond = true;
	  
if (testCond)
{
  if (!testCond)
  {
    Console.WriteLine("Appears to step, but doesn't execute");
  }
}
else
  Console.WriteLine("Doesn't execute, and doesn't stop here");
				
When the debugger steps through this code, the debugger appears to run to the inner if statement. As in example 1, however, the debugger is not actually running the code, and no output is generated.

In example 2, the instruction that causes the error is a CEE_BR instruction. When you use Ildasm.exe, the instruction appears at the end of the true clause of the if (testCond) statement as follows:
br.s <some line number>
				
This instruction has no associated source line; therefore, when the debugger steps into this instruction, the following line appears to be run, even though it is not:
Console.WriteLine("Appears to step, but doesn't execute");
				
This occurs only if the instructions that precede the CEE_BR instruction (that is, instructions that are generated by compiling the true clause) are not run because the true clause in which the instructions are found is not run.

WORKAROUND

To work around this problem, use any of the following three methods:
  • Set breakpoints around the block of code that you believe is causing the problem to determine whether the instructions are actually being run.

    That is, set a breakpoint on the first line of the erroneous construct (the line of code that you believe is not running) and on the next line that should be run. When the debugger reaches the first breakpoint, either press F5, or click Continue on the Debug menu. If the code is truly not being run, the next breakpoint that the debugger reaches is the next line that should be run. In example 1, set a breakpoint at each of the following lines:
    Console.WriteLine("In try");
    					
    Console.WriteLine("In catch");
    					
    Console.WriteLine("In finally");
    					

    When the debugger reaches the breakpoint that is associated with the following line, either press F5 or select Continue from the Debug menu:
    Console.WriteLine("In try");
    					
    Note that the next breakpoint that the debugger reaches is the breakpoint that is associated with the following line:
    Console.WriteLine("In finally");
    					
    With this workaround, you can test a suspect block of code to determine whether it is really running.

  • Examine data in the Disassembly window. When the source level stepping appears to be incorrect, you can consult the assembly level stepping information. In example 2, switch to the Disassembly window when the currently stopped source line appears to be the following:
    Console.WriteLine("Appears to step, but doesn't execute");
    					
    Note that the current run point is actually a jmp address instruction, and that this instruction occurs after the following line:
    Console.WriteLine("Appears to step, but doesn't execute");
    					
  • In most cases, you can mitigate the instructions that cause the problem by rewriting that particular code construct. To do this, you must usually either add a dummy line of code, or rewrite the code in an unnatural way. For example, you can rewrite the code for example 1 as follows:
    try
    {
      try
     {
       Console.WriteLine("In try");        
     } catch(Exception e)
     {
       Console.WriteLine("In catch");
     }
      // A dummy line of code.
      int i = 5;
    }
    finally
    {
      Console.WriteLine("In finally");
    }      
    					
    Note that the stepping behavior now performs as expected. In general, in a try-catch-finally case, you must split the construct into a try-finally construct that contains a try-catch construct, and add a dummy line of code after the catch block and before the erroneous leave instruction.

    You can rewrite the code for example 2 as follows:
    bool testCond = true;
    
    if (testCond)
    {
      if (!testCond)
      {
        Console.WriteLine("Appears to step, but doesn't execute");
      }
      // A dummy line of code.
      int i = 5;
    }
    else
      Console.WriteLine("Doesn't execute, and doesn't stop here");
    					
    Note that the stepping behavior now performs as expected. In general, in a nested if situation, you must insert a dummy line of code after the inner if and before the erroneous branch instruction.

Modification Type:MajorLast Reviewed:1/5/2006
Keywords:kbprb kbtshoot kbBug kbDebug KB316834 kbAudDeveloper