We just resolved some issues that caused JVM crashes. Apparently JVM 
is not always able to recover from the invalid bytecode and that is only 
discoverable when JVM option -Xverify:all is specified.

  To detect those kind of issues at early stages we probably should 
enable this option on some of the monkeys, or better add unit tests for 
all custom transformers that would use ASM's CheckClassAdapter to verify 
if transformed bytecode is sane. That may look something like this 
(though we may need to generalize it into declaratively defined test suite)

    String res = 
BootJar.classNameToFileName("java.util.concurrent.LinkedBlockingQueue$Itr");
    ClassReader cr = new 
ClassReader(getClass().getClassLoader().getResourceAsStream(res));
    ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
    ClassVisitor cv = new 
JavaUtilConcurrentLinkedBlockingQueueIteratorClassAdapter(new 
CheckClassAdapter(cw));
    cr.accept(cv, ClassReader.SKIP_FRAMES);

    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
    assertTrue(sw.toString(), sw.toString().length()==0);

  Above code loads class from Java 5, then applies corresponding 
transformation and then run result bytecode trough CheckClassAdapter. If 
method bytecode has errors assertion stack will have erroneous 
instruction number and some neat output that would show info about 
failed method and locals and stack slot for each instruction. For 
example (format is - insnNumber locals : stack):

junit.framework.AssertionFailedError: 
com.tc.asm.tree.analysis.AnalyzerException: Error at instruction 71: 
Expected I, but found .
    at com.tc.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
    at com.tc.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
....
remove()V
00000 LinkedBlockingQueue$Itr . . . . . . . .  :
      ICONST_0
00001 LinkedBlockingQueue$Itr . . . . . . . .  : I
      ISTORE 2
....
00071 LinkedBlockingQueue$Itr . ReentrantLock ReentrantLock 
LinkedBlockingQueue$Node LinkedBlockingQueue$Node 
LinkedBlockingQueue$Node . .  : LinkedBlockingQueue String Object Object 
I Integer Integer
      ILOAD 1
00072 ?                
      INVOKESPECIAL java/lang/Integer.<init> (I)V
....

  So, in above output you can see that variable 1 that being loaded by 
ILOAD 1 at position 00071 is not actually initialized. You can also see 
that at the beginning of the method (code inserted by the 
transformation) variable 2 is initialized.
 
  We can probably expand such verification to the boot jar tool and 
maybe even to classes transformed at the runtime, i.e. when special flad 
is turned on.

  Note that when used like that, CheckClassAdapter can trigger some 
additional classloading. There is a way around that, of course. Let me 
know if you'd like to know more about that. ;-)

  regards,
  Eugene

PS: that particular failure happens due to a common mistake in a 
subclass of LocalVariablesSorter class, so newly added code been added 
using super.visit*Insn(..) calls, instead of mv.visit*Insn(..) calls, 
and as a result there was erroneous variable remapping happening for the 
newly added code...


_______________________________________________
tc-dev mailing list
[email protected]
http://lists.terracotta.org/mailman/listinfo/tc-dev

Reply via email to