JDK-6742507 : Problem with stepping after class redefine
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: hs11,5.0u14,6u2
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2008-08-28
  • Updated: 2011-04-28
  • Resolved: 2011-04-28
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7Resolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
We have found a problem with ignored step over requests submitted after a class redefine. It was encountered after "Compile on Save" feature was implemented in NetBeans, which is followed by redefinition of the changed classes.

Basically the behavior is: after a method is redefined, step over such redefined method behaves as plain resume, the step event is not received.

A sample application that demonstrates the problem is attached.
Brief description:

1) When "Compile on Save" is active, it allows to run or debug application that have compile errors. The parts of the application that can not be compiled throw RuntimeException in the bytecode.

2) When the app is being debugged and such RuntimeException is triggered (we submit ExceptionRequest for that), we suspend the execution of the appropriate thread and wait if the user fix the compilation error in the source code.

3) If the user fixes the compile error, we redefine the class and pop from the top frame so that we re-execute the (now fixed) method.

4) If step over is submitted now, no step event is received. The strange thing is, that step into works fine. But even after step into is performed several times, or some line breakpoints are hit, next step over will just continue the thread and no event comes. It looks like it's not possible to step over the redefined method.

Description of the sample application testcase:
The testcase should be started via "run" script. It debugs an application that has a method which throws RuntimeException. (App.run())
The debugger receives the exception and redefines the App class with changed run() method. Then it pops, submits a step over and resumes the thread (EventThread.doFixAndStep()). However, the app is finished and no step event is received.
This test case is interesting. Here is the App.java file:

% cat -n src/stepafterredefineclasses/App.java
     1  /*
     2   * To change this template, choose Tools | Templates
     3   * and open the template in the editor.
     4   */
     5
     6  package stepafterredefineclasses;
     7
     8  /**
     9   *
    10   * @author martin
    11   */
    12  public class App {
    13
    14      /**
    15       * @param args the command line arguments
    16       */
    17      public static void main(String[] args) {
    18          for (int i = 0; i < 10; i++) {
    19              run(i);
    20          }
    21          System.err.println("Something after the loop.");
    22      }
    23
    24      private static void run(int i) {
    25          XXX//System.err.println(i);
    26      }
    27  }

Line 25 in App.java will keep the code from compiling.


Here is the App.class file:

% javap -c -private -classpath build/classes stepafterredefineclasses/App
Compiled from "App.java"
public class stepafterredefineclasses.App extends java.lang.Object{
public stepafterredefineclasses.App();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   bipush  10
   5:   if_icmpge       18
   8:   iload_1
   9:   invokestatic    #2; //Method run:(I)V
   12:  iinc    1, 1
   15:  goto    2
   18:  getstatic       #3; //Field java/lang/System.err:Ljava/io/PrintStream;
   21:  ldc     #4; //String Something after the loop.
   23:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   26:  return

private static void run(int);
  Code:
   0:   new     #6; //class java/lang/RuntimeException
   3:   dup
   4:   ldc     #7; //String Uncompilable source code - not a statement
   6:   invokespecial   #8; //Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
   9:   athrow

}


The above App.class files blows up (as expected)
when we try to run it:

% java -classpath build/classes stepafterredefineclasses/App
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - not a statement
        at stepafterredefineclasses.App.run(App.java:25)
        at stepafterredefineclasses.App.main(App.java:19)



This App.class failure is meant to simulate the following:

% javac src/stepafterredefineclasses/App.java
src/stepafterredefineclasses/App.java:25: not a statement
        XXX//System.err.println(i);
        ^
src/stepafterredefineclasses/App.java:25: ';' expected
        XXX//System.err.println(i);
           ^
2 errors


What the JDI test does is catch a RuntimeException in the
running App code and simulate a fix-and-recompile by
redefining the "broken" class with a new class that isn't
broken.

The user thread is in App.run() when the exception is caught
by the debugger. The debugger redefines App.class with the
right bits, pops the current frame which takes the thread
back to App.main(line 19) and then tries to do a "step over"
to get over the call to run().

The "step over" event never happens. However, the new version
of run() is executed.

If the "step over" is replaced by a "step in", then the "step
in" event happens and the new version of run is also executed.

Comments
PUBLIC COMMENTS Ran the test with -XX:TraceRedefineClasses=257 and some System.err.println() debugging: Waiting for events... XXX - about to call redefineClasses() XXX - frame 0 is stepafterredefineclasses.App:25 in thread instance of java.lang.Thread(name='main', id=2) XXX - frame 1 is stepafterredefineclasses.App:19 in thread instance of java.lang.Thread(name='main', id=2) RedefineClasses-0x1: loading name=stepafterredefineclasses.App (avail_mem=112580K) VM option 'TraceRedefineClasses=257' RedefineClasses-0x1: loaded name=stepafterredefineclasses.App (avail_mem=112472K) RedefineClasses-0x100: mark run((I)V) as obsolete RedefineClasses-0x100: EMCP_cnt=2, obsolete_cnt=1 RedefineClasses-0x100: adding previous version ref for stepafterredefineclasses.App @0, EMCP_cnt=2 RedefineClasses-0x1: redefined name=stepafterredefineclasses.App, count=1 (avail_mem=112388K) XXX - after call to redefineClasses() XXX - frame 0 is stepafterredefineclasses.App.<obsolete>()+9 in thread instance of java.lang.Thread(name='main', id=2) XXX - frame 1 is stepafterredefineclasses.App:19 in thread instance of java.lang.Thread(name='main', id=2) XXX - after pop - frame 0 is stepafterredefineclasses.App:19 in thread instance of java.lang.Thread(name='main', id=2) XXX - req=step request instance of java.lang.Thread(name='main', id=2) (enabled)0 1 2 stepafterredefineclasses.App.run() is marked obsolete according to the tracing output. JDI also detects that method as "obsolete" in the "frame 0" XXX debug output so we have agreement between the JDI and JVM/TI layers. Two other methods in stepafterredefineclasses.App were marked as EMCP. That would be the constructor and the main() method. The main() method is not shown as obsolete in the XXX debug output and that also matches the JVM/TI layer. However, we still have two copies of the main() method. There is the copy still be executed by our thread that is EMCP and the new copy from the RedefineClasses() call. I wonder if the EMCP'ness of main() is affecting how the "step over" works. Update: I changed the test case so that main() is also obsolete instead of EMCP and that didn't make the "step over" event happen. So "step over" isn't happy with either obsolete or EMCP methods and that's where we need to go next...
25-02-2009

EVALUATION <moved note to description entry #2>
24-02-2009