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