| /*** |
| * 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; |
| } |
| }; |
| } |
| } |
| } |