/** * Performs the actual editing of a class. Does a whole mess of stuff including reading in the * classfile, building data structures to represent the class file, converting the CFG for each * method in the class into SSA form, perform some anlayses and optimizations on the method, and * finally committing it back to the class file. Phew. */ private static void editClass(final String className) { ClassFile classFile; // Holds info about a class (implements // ClassInfo) // Get information about the class className try { classFile = (ClassFile) Main.context.loadClass(className); } catch (final ClassNotFoundException ex) { System.err.println("** Couldn't find class: " + ex.getMessage()); return; } if (!Main.FORCE) { // Check to see if the file is up-to-date (i.e. has been // recompiled since it was last optimized). If so, do nothing // because the FORCE flag is false. final File source = classFile.file(); final File target = classFile.outputFile(); if ((source != null) && (target != null) && source.exists() && target.exists() && (source.lastModified() < target.lastModified())) { if (Main.VERBOSE) { System.out.println(classFile.name() + " is up to date"); } return; } } if (Main.DEBUG) { // Print the contents of the class file to System.out classFile.print(System.out); } final ClassEditor c = Main.context.editClass(classFile); boolean skip = false; final String name = c.type().className(); final String qual = c.type().qualifier() + "/*"; // Edit only classes explicitly mentioned. if (Main.ONLY.size() > 0) { skip = true; // Only edit classes we explicitly don't name. for (int i = 0; i < Main.ONLY.size(); i++) { final String pkg = (String) Main.ONLY.get(i); if (name.equals(pkg) || qual.equals(pkg)) { skip = false; break; } } } // Don't edit classes we explicitly skip. if (!skip) { for (int i = 0; i < Main.SKIP.size(); i++) { final String pkg = (String) Main.SKIP.get(i); if (name.equals(pkg) || qual.equals(pkg)) { skip = true; break; } } } if (skip) { if (Main.VERBOSE) { System.out.println("Skipping " + c.type().className()); } // We're done with this class file, decrement its reference count Main.context.release(classFile); return; } // Touch the output file first. That is, create the file, but make // it empty, just to make sure we can create it. try { final File f = classFile.outputFile(); if (f.exists()) { f.delete(); } final File dir = new File(f.getParent()); dir.mkdirs(); if (!dir.exists()) { throw new RuntimeException("Couldn't create directory: " + dir); } final DataOutputStream out = new DataOutputStream(new FileOutputStream(f)); new PrintStream(out).println(); out.close(); } catch (final IOException e) { e.printStackTrace(); System.exit(1); } if (Main.VERBOSE) { System.out.println("Optimizing " + c.type().className()); } // Finally, we can start playing with the methods... final MethodInfo[] methods = c.methods(); final int numMethods = methods.length + 1; ; int whichMethod = 0; for (int j = 0; j < methods.length; j++) { final MethodEditor m; try { m = Main.context.editMethod(methods[j]); } catch (final ClassFormatException ex) { System.err.println(ex.getMessage()); continue; } if (Main.TRACE) { whichMethod++; System.out.println( "Optimizing " + name + "." + m.name() + " (method " + whichMethod + " of " + numMethods + ")"); } if (Main.METHOD != null) { // A method name has been specified on the command line using // -only-method. boolean pass = true; String t = m.name() + m.type(); if (t.equals(Main.METHOD)) { pass = false; } t = m.name(); if (t.equals(Main.METHOD)) { pass = false; } if (pass) { // This isn't the method we're looking for. // Decrement its reference count. Main.context.release(methods[j]); continue; } } if (Main.DEBUG) { m.print(System.out); } if (m.isNative() || m.isAbstract()) { // We can't edit native or abstract methods Main.context.release(methods[j]); continue; } Main.bloatMethod(m, Main.context); } if (Main.ANNO) { String s = "Optimized with: EDU.purdue.cs.bloat.optimize.Main"; for (int i = 0; i < Main.ARGS.length; i++) { if ((Main.ARGS[i].indexOf(' ') >= 0) || (Main.ARGS[i].indexOf('\t') >= 0) || (Main.ARGS[i].indexOf('\r') >= 0) || (Main.ARGS[i].indexOf('\n') >= 0)) { s += " '" + Main.ARGS[i] + "'"; } else { s += " " + Main.ARGS[i]; } } System.out.println(s); // c.constants().addConstant(Constant.UTF8, s); } Main.context.commit(classFile); Main.context.release(classFile); if (Main.TRACE) { System.out.println(Main.context.toString()); } }
/** * Performs intraprocedural BLOAT on a program's live methods. * * @param liveMethods Should be alphabetized. This way we can commit a class once we've BLOATed * all of its methods. */ private static void intraBloat(final Collection liveMethods, final BloatContext context) { ClassEditor prevClass = null; final Iterator iter = liveMethods.iterator(); for (int count = 0; iter.hasNext(); count++) { MethodEditor live = null; ClassEditor ce = null; // Hack to make sure commit happens try { live = context.editMethod((MemberRef) iter.next()); ce = context.editClass(live.declaringClass().classInfo()); } catch (final NoSuchMethodException ex3) { BloatBenchmark.err.println("** Could not find method " + ex3.getMessage()); System.exit(1); } /* So we can skip classes or packages */ final String name = ce.type().className(); final String qual = ce.type().qualifier() + "/*"; boolean skip = false; for (int i = 0; i < BloatBenchmark.SKIP.size(); i++) { final String pkg = (String) BloatBenchmark.SKIP.get(i); if (name.equals(pkg) || qual.equals(pkg)) { skip = true; break; } } if (context.ignoreMethod(live.memberRef()) || skip) { // Don't display ignored methods, it's misleading. context.release(live.methodInfo()); continue; } final Runtime runtime = Runtime.getRuntime(); runtime.gc(); final Date start = new Date(); BloatBenchmark.tr( " " + count + ") " + live.declaringClass().name() + "." + live.name() + live.type()); BloatBenchmark.tr(" Start: " + start); try { EDU.purdue.cs.bloat.optimize.Main.TRACE = BloatBenchmark.TRACE; if (!BloatBenchmark.VERIFY) { EDU.purdue.cs.bloat.optimize.Main.VERIFY = false; } EDU.purdue.cs.bloat.optimize.Main.bloatMethod(live, context); } catch (final Exception oops) { BloatBenchmark.err.println("******************************************"); BloatBenchmark.err.println( "Exception while BLOATing " + live.declaringClass().name() + "." + live.name() + live.type()); BloatBenchmark.err.println(oops.getMessage()); oops.printStackTrace(System.err); BloatBenchmark.err.println("******************************************"); } // Commit here in an attempt to conserve memory context.commit(live.methodInfo()); context.release(live.methodInfo()); if (prevClass == null) { prevClass = ce; } else if (!prevClass.equals(ce)) { // We've finished BLOATed the methods for prevClass, commit // prevClass and move on BloatBenchmark.tr(prevClass.type() + " != " + ce.type()); context.commit(prevClass.classInfo()); context.release(prevClass.classInfo()); // context.commitDirty(); // tr(context.toString()); prevClass = ce; } else { context.release(ce.classInfo()); } final Date end = new Date(); BloatBenchmark.tr(" Ellapsed time: " + (end.getTime() - start.getTime()) + " ms"); } context.commitDirty(); }