blob: fe5247477606aad13ddf6717353f2eba4d1cdd43 [file] [log] [blame]
/***
* ASM performance test: measures the performances of asm package
* Copyright (c) 2000-2011 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.objectweb.asm;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.verifier.structurals.ModifiedPass3bVerifier;
import org.objectweb.asm.commons.LocalVariablesSorter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
import serp.bytecode.BCClass;
import serp.bytecode.BCMethod;
import serp.bytecode.Code;
import serp.bytecode.Project;
/**
* @author Eric Bruneton
* @author Peter Lawrey
*/
public abstract class ALLPerfTest {
static boolean compute;
static boolean computeFrames;
static boolean skipDebug;
static int repeats;
static List<byte[]> classes = new ArrayList<byte[]>();
static List<String> classNames = new ArrayList<String>();
private static final int MAX_ITERATION_SEC = Integer.getInteger(
"max.iteration.sec", 10).intValue();
public static void main(final String[] args) throws IOException,
InterruptedException {
String clazz = System.getProperty("asm.test.class");
List<String> jars = findFiles(System.getProperty("java.home"), ".jar");
jars.addAll(findJars(File.pathSeparatorChar,
System.getProperty("java.class.path")));
repeats = Integer.getInteger("repeats", 3).intValue() + 1;
Set<String> classesFound = new HashSet<String>();
for (int i = 0; i < jars.size(); i++) {
ZipFile zip;
try {
zip = new ZipFile(jars.get(i));
} catch (IOException e) {
System.err.println("Error openning " + jars.get(i));
e.printStackTrace();
continue;
}
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
String s = e.getName();
if (s.endsWith(".class")) {
s = s.substring(0, s.length() - 6).replace('/', '.');
if (!classesFound.add(s)) {
continue;
}
if (clazz == null || s.indexOf(clazz) != -1) {
InputStream is = zip.getInputStream(e);
byte[] bytes = new ClassReader(is).b;
classes.add(bytes);
classNames.add(s);
is.close();
if (classes.size() % 2500 == 0) {
System.out.println("... searching, found "
+ classes.size() + " classes.");
}
}
}
}
zip.close();
}
System.out.println("Found " + classes.size() + " classes.");
RunTest nullBCELAdapt = new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) throws IOException {
nullBCELAdapt(bytes);
}
};
RunTest nullAspectjBCELAdapt = new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) throws IOException {
nullAspectjBCELAdapt(bytes);
}
};
RunTest nullJavassistAdapt = new RunTest() {
ClassPool pool;
@Override
public void init() {
pool = new ClassPool(null);
}
@Override
public void test(byte[] bytes, int[] errors) throws Exception {
nullJavassistAdapt(pool, bytes);
}
};
RunTest nullSERPAdapt = new RunTest() {
Project p;
BCClass c;
@Override
public void init() {
p = new Project();
c = null;
}
@Override
public void test(byte[] bytes, int[] errors) throws Exception {
c = nullSERPAdapt(p, c, bytes);
}
};
// get class info and deserialize tests
runTestAll("get class info", "", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassReader cr = new ClassReader(bytes);
cr.getAccess();
cr.getClassName();
cr.getSuperName();
cr.getInterfaces();
}
});
runTestAll("deserialize", "", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
new ClassReader(bytes).accept(new EmptyVisitor(), 0);
}
});
runTest("deserialize", "tree package", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
new ClassReader(bytes).accept(new ClassNode(), 0);
}
});
System.out.println();
// deserialize and reserialize tests
runTestAll("deserialize and reserialize", "", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(0);
cr.accept(cw, 0);
cw.toByteArray();
}
});
runTestAll("deserialize and reserialize", "copyPool", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(cw, 0);
cw.toByteArray();
}
});
runTest("deserialize and reserialize", "tree package", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassWriter cw = new ClassWriter(0);
ClassNode cn = new ClassNode();
new ClassReader(bytes).accept(cn, 0);
cn.accept(cw);
cw.toByteArray();
}
});
compute = false;
computeFrames = false;
runTest("deserialize and reserialize", "BCEL", nullBCELAdapt);
runTest("deserialize and reserialize", "Aspectj BCEL",
nullAspectjBCELAdapt);
runTest("deserialize and reserialize", "Javassist", nullJavassistAdapt);
runTest("deserialize and reserialize", "SERP", nullSERPAdapt);
System.out.println();
// deserialize and reserialize tests with computeMaxs
runTest("deserialize and reserialize", "computeMaxs", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
new ClassReader(bytes).accept(cw, 0);
cw.toByteArray();
}
});
compute = true;
computeFrames = false;
runTest("deserialize and reserialize", "BCEL and computeMaxs",
nullBCELAdapt);
runTest("deserialize and reserialize", "Aspectj BCEL and computeMaxs",
nullAspectjBCELAdapt);
// misc. tests
runTest("deserialize and reserialize", "LocalVariablesSorter",
new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassWriter cw = new ClassWriter(0);
new ClassReader(bytes).accept(new ClassVisitor(
Opcodes.ASM4, cw) {
@Override
public MethodVisitor visitMethod(final int access,
final String name, final String desc,
final String signature,
final String[] exceptions) {
return new LocalVariablesSorter(access, desc,
cv.visitMethod(access, name, desc,
signature, exceptions));
}
}, ClassReader.EXPAND_FRAMES);
cw.toByteArray();
}
});
// This test repeatedly tests the same classes as SimpleVerifier
// actually calls Class.forName() on the class which fills the PermGen
runTestSome("analyze", "SimpleVerifier", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassReader cr = new ClassReader(bytes);
ClassNode cn = new ClassNode();
cr.accept(cn, ClassReader.SKIP_DEBUG);
List<MethodNode> methods = cn.methods;
for (int k = 0; k < methods.size(); ++k) {
MethodNode method = methods.get(k);
Analyzer<?> a = new Analyzer<BasicValue>(
new SimpleVerifier());
try {
a.analyze(cn.name, method);
} catch (Throwable th) {
// System.err.println(th);
++errors[0];
}
}
}
});
System.out.println();
// deserialize and reserialize tests with computeFrames
runTest("deserialize and reserialize", "computeFrames", new RunTest() {
@Override
public void test(byte[] bytes, int[] errors) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
new ClassReader(bytes).accept(cw, 0);
cw.toByteArray();
}
});
// the BCEL+computeFrames tests must be done only at the end, because
// after them other tests run very slowly, for some unknown reason
// (memory usage?)
compute = false;
computeFrames = true;
runTest("deserialize and reserialize", "BCEL and computeFrames",
nullBCELAdapt);
runTest("deserialize and reserialize",
"Aspectj BCEL and computeFrames", nullAspectjBCELAdapt);
}
public static List<String> findFiles(String directory, String suffix) {
List<String> matches = new ArrayList<String>();
findFiles(matches, new File(directory), suffix);
return matches;
}
static void findFiles(List<String> matches, File directory, String suffix) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.isDirectory()) {
findFiles(matches, file, suffix);
} else if (file.getName().endsWith(suffix)) {
matches.add(file.getAbsolutePath());
}
}
}
static abstract class RunTest {
public void init() {
}
public abstract void test(byte[] bytes, int[] errors) throws Exception;
}
static void runTestAll(String testName, String with, RunTest runTest)
throws InterruptedException {
runTest0(1, true, testName, with, runTest);
}
static void runTest(String testName, String with, RunTest runTest)
throws InterruptedException {
runTest0(repeats - 1, false, testName, with, runTest);
}
static void runTestSome(String testName, String with, RunTest runTest)
throws InterruptedException {
runTest0(repeats - 1, true, testName, with, runTest);
}
private static void runTest0(int testSkip, boolean startAtZero,
String testName, String with, RunTest runTest)
throws InterruptedException {
if (with.length() > 0) {
with = " with " + with;
}
boolean skipBigClasses = with.contains("BCEL and computeFrames");
int totalCount = 0;
long totalSize = 0;
long totalTime = 0;
System.out.println("\nStarting " + testName + with + " test.");
for (int i = 0; i < repeats; ++i) {
runTest.init();
long t = System.currentTimeMillis();
int count = 0;
long size = 0;
int[] errors = { 0 };
long longest = 0;
int longestSize = 0;
int skipped = 0;
for (int j = startAtZero ? 0 : i; j < classes.size(); j += testSkip) {
count++;
byte[] b = classes.get(j);
if (skipBigClasses && b.length > 16 * 1024) {
skipped++;
continue;
}
size += b.length;
try {
long start = System.currentTimeMillis();
runTest.test(b, errors);
long end = System.currentTimeMillis();
long time = end - start;
if (longest < time) {
longest = time;
longestSize = b.length;
}
if (time > MAX_ITERATION_SEC * 1000 / 10) {
System.out.println("--- time to " + testName
+ " the class " + classNames.get(j) + with
+ " took " + time + " ms. bytes=" + b.length);
}
if (end - t > MAX_ITERATION_SEC * 1000) {
// System.out.println("Stopping iteration due to a
// longer run time.");
break;
}
} catch (Exception ignored) {
errors[0]++;
} catch (Throwable e) {
System.err.println(classNames.get(j) + ": " + e);
errors[0]++;
}
}
t = System.currentTimeMillis() - t;
String errorStr = errors[0] > 0 ? " (" + errors[0] + " errors)"
: "";
String skippedStr = skipped == 0 ? ""
: " ("
+ skipped
+ " skipped as BCEL/computeFrames on >16K classes is very slow)";
String longestStr = "";
if (longest > 50) {
longestStr = " the longest took " + longest + " ms ("
+ longestSize + " bytes)";
}
if (i > 0) {
System.out.println("- to " + testName + ' ' + count
+ " classes" + with + " = " + t + " ms" + errorStr
+ longestStr + skippedStr + '.');
totalCount += count;
totalSize += size;
totalTime += t;
}
}
System.out.println("Time to " + testName + ' ' + totalCount
+ " classes" + with + " = " + totalTime + " ms.\n"
+ "Processing rate = " + totalCount * 1000 / totalTime
+ " classes per sec (" + totalSize * 1000 / totalTime / 1024
+ " kB per sec).");
System.gc();
Thread.sleep(2500);
}
private static List<String> findJars(char pathSeparatorChar, String s) {
List<String> ret = new ArrayList<String>();
int start = 0;
int pos = s.indexOf(pathSeparatorChar);
while (pos >= 0) {
String name = s.substring(start, pos);
if (name.endsWith(".jar")) {
ret.add(name);
}
start = pos + 1;
pos = s.indexOf(pathSeparatorChar, start);
}
return ret;
}
static void nullBCELAdapt(final byte[] b) throws IOException {
JavaClass jc = new ClassParser(new ByteArrayInputStream(b),
"class-name").parse();
ClassGen cg = new ClassGen(jc);
ConstantPoolGen cp = cg.getConstantPool();
Method[] ms = cg.getMethods();
for (int k = 0; k < ms.length; ++k) {
MethodGen mg = new MethodGen(ms[k], cg.getClassName(), cp);
boolean lv = ms[k].getLocalVariableTable() == null;
boolean ln = ms[k].getLineNumberTable() == null;
if (lv) {
mg.removeLocalVariables();
}
if (ln) {
mg.removeLineNumbers();
}
mg.stripAttributes(skipDebug);
InstructionList il = mg.getInstructionList();
if (il != null) {
InstructionHandle ih = il.getStart();
while (ih != null) {
ih = ih.getNext();
}
if (compute) {
mg.setMaxStack();
mg.setMaxLocals();
}
if (computeFrames) {
ModifiedPass3bVerifier verif;
verif = new ModifiedPass3bVerifier(jc, k);
verif.do_verify();
}
}
cg.replaceMethod(ms[k], mg.getMethod());
}
cg.getJavaClass().getBytes();
}
static void nullAspectjBCELAdapt(final byte[] b) throws IOException {
org.aspectj.apache.bcel.classfile.JavaClass jc = new org.aspectj.apache.bcel.classfile.ClassParser(
new ByteArrayInputStream(b), "class-name").parse();
org.aspectj.apache.bcel.generic.ClassGen cg = new org.aspectj.apache.bcel.generic.ClassGen(
jc);
org.aspectj.apache.bcel.generic.ConstantPoolGen cp = cg
.getConstantPool();
org.aspectj.apache.bcel.classfile.Method[] ms = cg.getMethods();
for (int k = 0; k < ms.length; ++k) {
org.aspectj.apache.bcel.generic.MethodGen mg = new org.aspectj.apache.bcel.generic.MethodGen(
ms[k], cg.getClassName(), cp);
boolean lv = ms[k].getLocalVariableTable() == null;
boolean ln = ms[k].getLineNumberTable() == null;
if (lv) {
mg.removeLocalVariables();
}
if (ln) {
mg.removeLineNumbers();
}
mg.stripAttributes(skipDebug);
org.aspectj.apache.bcel.generic.InstructionList il = mg
.getInstructionList();
if (il != null) {
org.aspectj.apache.bcel.generic.InstructionHandle ih = il
.getStart();
while (ih != null) {
ih = ih.getNext();
}
if (compute) {
mg.setMaxStack();
mg.setMaxLocals();
}
if (computeFrames) {
org.aspectj.apache.bcel.verifier.structurals.ModifiedPass3bVerifier verif = new org.aspectj.apache.bcel.verifier.structurals.ModifiedPass3bVerifier(
jc, k);
verif.do_verify();
}
}
cg.replaceMethod(ms[k], mg.getMethod());
}
cg.getJavaClass().getBytes();
}
static void nullJavassistAdapt(ClassPool pool, final byte[] b)
throws Exception {
CtClass cc = pool.makeClass(new ByteArrayInputStream(b));
CtMethod[] ms = cc.getDeclaredMethods();
for (int j = 0; j < ms.length; ++j) {
if (skipDebug) {
// is there a mean to remove the debug attributes?
}
if (compute) {
// how to force recomputation of maxStack and maxLocals?
}
}
cc.toBytecode();
}
static BCClass nullSERPAdapt(Project p, BCClass c, final byte[] b)
throws Exception {
if (c != null) {
p.removeClass(c);
}
c = p.loadClass(new ByteArrayInputStream(b));
c.getDeclaredFields();
BCMethod[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
Code code = methods[i].getCode(false);
if (code != null) {
while (code.hasNext()) {
code.next();
}
if (compute) {
code.calculateMaxStack();
code.calculateMaxLocals();
}
}
}
c.toByteArray();
return c;
}
static class EmptyVisitor extends ClassVisitor {
AnnotationVisitor av = new AnnotationVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
return this;
}
@Override
public AnnotationVisitor visitArray(String name) {
return this;
}
};
public EmptyVisitor() {
super(Opcodes.ASM4);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return av;
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
return new FieldVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotation(String desc,
boolean visible) {
return av;
}
};
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotationDefault() {
return av;
}
@Override
public AnnotationVisitor visitAnnotation(String desc,
boolean visible) {
return av;
}
@Override
public AnnotationVisitor visitParameterAnnotation(
int parameter, String desc, boolean visible) {
return av;
}
};
}
}
}