make/tools/classanalyzer/src/com/sun/classanalyzer/BootAnalyzer.java

Print this page




   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 package com.sun.classanalyzer;
  25 

  26 import java.io.BufferedReader;
  27 import java.io.FileInputStream;
  28 import java.io.IOException;
  29 import java.io.InputStreamReader;
  30 import java.io.PrintWriter;
  31 import java.io.File;
  32 import java.util.ArrayDeque;
  33 import java.util.Deque;
  34 import java.util.HashMap;
  35 import java.util.LinkedList;
  36 import java.util.List;
  37 import java.util.Map;
  38 import java.util.Set;
  39 import java.util.TreeSet;
  40 
  41 import com.sun.tools.classfile.*;
  42 import com.sun.tools.classfile.ConstantPool.*;
  43 import static com.sun.tools.classfile.ConstantPool.*;
  44 import com.sun.tools.classfile.Instruction.TypeKind;
  45 import com.sun.tools.classfile.Type.*;


  46 
  47 /**
  48  * Generate the module config for the boot module with
  49  * a given set of roots (classes or methods) and exclude list.
  50  *
  51  * This tool does method-level dependency analysis starting
  52  * from the root set and follows references transitively as follows:
  53  * <ul>
  54  * <li>For a given class, it will parse the ClassFile to
  55  *     find its superclass and superinterfaces and also
  56  *     its static initializer &lt;clinit&gt;.</li>
  57  * <li>For each method, it will parse its Code attribute
  58  *     to look for a Methodref, Fieldref, and InterfaceMethodref.
  59  *     </li>
  60  * <li>For each Fieldref, it will include the type of
  61  *     the field in the dependency.</li>
  62  * <li>For each MethodRef, it will follow all references in
  63  *     that method.</li>
  64  * <li>For each InterfaceMethodref, it will follow all references in
  65  *     that method defined its implementation classes in
  66  *     the resulting dependency list.</li>
  67  * </ul>
  68  *
  69  * Limitation:
  70  * <ul>
  71  * <li>For each Methodref, it only parses the method of
  72  *     the specified type.  It doesn't analyze the class hierarchy
  73  *     and follow references of its subclasses since it ends up
  74  *     pulls in many unnecessary dependencies.  For now,
  75  *     the list of subclasses and methods need to be listed in
  76  *     the root set.</li>
  77  * </ul>
  78  *
  79  * @author Mandy Chung
  80  */
  81 public class BootAnalyzer {
  82 
  83     public static void main(String[] args) throws Exception {
  84         String jdkhome = null;
  85         String config = null;
  86         String output = ".";

  87         boolean printClassList = false;
  88 
  89         // process arguments
  90         int i = 0;
  91         while (i < args.length) {
  92             String arg = args[i++];
  93             if (arg.equals("-jdkhome")) {
  94                 if (i < args.length) {
  95                     jdkhome = args[i++];
  96                 } else {
  97                     usage();
  98                 }


  99             } else if (arg.equals("-config")) {
 100                 config = args[i++];
 101             } else if (arg.equals("-output")) {
 102                 output = args[i++];
 103             } else if (arg.equals("-classlist")) {
 104                 printClassList = true;
 105             } else {
 106                 usage();
 107             }
 108         }
 109 
 110 
 111 
 112         if (jdkhome == null || config == null) {
 113             usage();
 114         }
 115 
 116         File jre = new File(jdkhome, "jre");
 117         if (jre.exists()) {
 118             ClassPath.setJDKHome(jdkhome);
 119         } else {
 120             File classes = new File(jdkhome, "classes");
 121             if (classes.exists()) {
 122                 ClassPath.setClassPath(classes.getCanonicalPath());
 123             } else {
 124                 throw new RuntimeException("Invalid jdkhome: " + jdkhome);
 125             }
 126         }
 127 
 128         parseConfigFile(config);
 129         followRoots();
 130 
 131         // create output directory if it doesn't exist
 132         File dir = new File(output);
 133         if (!dir.isDirectory()) {
 134             if (!dir.exists()) {
 135                 boolean created = dir.mkdir();
 136                 if (!created) {
 137                     throw new RuntimeException("Unable to create `" + dir + "'");
 138                 }
 139             }
 140         }
 141 
 142         String bootmodule = "boot";
 143         String bootconfig = resolve(dir, bootmodule, "config");
 144         printBootConfig(bootconfig, bootmodule);
 145 
 146         List<ModuleConfig> list = ModuleConfig.readConfigurationFile(bootconfig);
 147         Module module = Module.addModule(list.get(0));







 148         for (Klass k : Klass.getAllClasses()) {
 149             module.addKlass(k);
 150         }
 151         module.fixupDependencies();
 152 
 153         if (printClassList) {
 154             module.printClassListTo(resolve(dir, bootmodule, "classlist"));
 155             module.printSummaryTo(resolve(dir, bootmodule, "summary"));


 156         }
 157     }
 158 






















 159     // print boot.config file as an input to the ClassAnalyzer
 160     private static void printBootConfig(String output, String bootmodule) throws IOException {
 161 
 162         File f = new File(output);
 163         PrintWriter writer = new PrintWriter(f);
 164         try {
 165             int count = 0;
 166             writer.format("module %s {%n", bootmodule);
 167             for (Klass k : Klass.getAllClasses()) {
 168                 if (count++ == 0) {
 169                     writer.format("%4s%7s %s", "", "include", k);
 170                 } else {
 171                     writer.format(",%n");
 172                     writer.format("%4s%7s %s", "", "", k);
 173                 }
 174             }
 175             writer.format(";%n}%n");
 176         } finally {
 177             writer.close();
 178         }


 321         KlassInfo getSuperclass() {
 322             ensureParse();
 323             return superclass;
 324         }
 325 
 326         List<KlassInfo> getInterfaces() {
 327             ensureParse();
 328             return java.util.Collections.unmodifiableList(interfaces);
 329         }
 330 
 331         void ensureParse() {
 332             try {
 333                 getClassFileParser();
 334             } catch (IOException e) {
 335                 throw new RuntimeException(e);
 336             }
 337         }
 338 
 339         synchronized ClassFileParser getClassFileParser() throws IOException {
 340             if (parser == null) {
 341                 parser = ClassPath.parserForClass(classname);
 342                 if (parser != null) {
 343                     parseClassFile();
 344                     List<String> descriptors = parse(new MethodDescriptor(classname + ".<clinit>", "()V", false));
 345                 }
 346             }
 347             return parser;
 348         }
 349 
 350         List<String> parse(MethodDescriptor md) {
 351             ensureParse();
 352             try {
 353                 List<String> descriptors = new LinkedList<String>();
 354                 for (Method m : parser.classfile.methods) {
 355                     String name = m.getName(parser.classfile.constant_pool);
 356                     String desc = parser.constantPoolParser.getDescriptor(m.descriptor.index);
 357                     if (name.equals(md.methodname)) {
 358                         if (md.descriptor.equals("*") || md.descriptor.equals(desc)) {
 359                             parseMethod(parser, m);
 360                             descriptors.add(desc);
 361                         }


 787 
 788             public String visitMethodref(CONSTANT_Methodref_info info, Void p) {
 789                 return addMethodDescriptor(info, p);
 790             }
 791 
 792             public String visitModuleId(CONSTANT_ModuleId_info info, Void p) {
 793                 // skip
 794                 return null;
 795             }
 796 
 797             public String visitString(CONSTANT_String_info info, Void p) {
 798                 // skip
 799                 return null;
 800             }
 801 
 802             public String visitUtf8(CONSTANT_Utf8_info info, Void p) {
 803                 return null;
 804             }
 805         };
 806     }
 807     static boolean traceOn = System.getProperty("classanalyzer.debug") != null;
 808 
 809     private static void trace(String format, Object... args) {
 810         if (traceOn) {
 811             System.out.format(format, args);
 812         }
 813     }
 814 
 815     private static void usage() {
 816         System.out.println("Usage: BootAnalyzer <options>");
 817         System.out.println("Options: ");
 818         System.out.println("\t-jdkhome <JDK home> where all jars will be parsed");
 819         System.out.println("\t-config  <roots for the boot module>");
 820         System.out.println("\t-output  <output dir>");
 821         System.out.println("\t-classlist print class list and summary");
 822         System.exit(-1);
 823     }
 824 }


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 package com.sun.classanalyzer;
  25 
  26 import java.util.Collections;
  27 import java.io.BufferedReader;
  28 import java.io.FileInputStream;
  29 import java.io.IOException;
  30 import java.io.InputStreamReader;
  31 import java.io.PrintWriter;
  32 import java.io.File;
  33 import java.util.ArrayDeque;
  34 import java.util.Deque;
  35 import java.util.HashMap;
  36 import java.util.LinkedList;
  37 import java.util.List;
  38 import java.util.Map;
  39 import java.util.Set;
  40 import java.util.TreeSet;
  41 
  42 import com.sun.tools.classfile.*;
  43 import com.sun.tools.classfile.ConstantPool.*;
  44 import static com.sun.tools.classfile.ConstantPool.*;
  45 import com.sun.tools.classfile.Instruction.TypeKind;
  46 import com.sun.tools.classfile.Type.*;
  47 import com.sun.classanalyzer.ModuleInfo.PackageInfo;
  48 import static com.sun.classanalyzer.Trace.*;
  49 
  50 /**
  51  * Generate the module config for the boot module with
  52  * a given set of roots (classes or methods) and exclude list.
  53  *
  54  * This tool does method-level dependency analysis starting
  55  * from the root set and follows references transitively as follows:
  56  * <ul>
  57  * <li>For a given class, it will parse the ClassFile to
  58  *     find its superclass and superinterfaces and also
  59  *     its static initializer &lt;clinit&gt;.</li>
  60  * <li>For each method, it will parse its Code attribute
  61  *     to look for a Methodref, Fieldref, and InterfaceMethodref.
  62  *     </li>
  63  * <li>For each Fieldref, it will include the type of
  64  *     the field in the dependency.</li>
  65  * <li>For each MethodRef, it will follow all references in
  66  *     that method.</li>
  67  * <li>For each InterfaceMethodref, it will follow all references in
  68  *     that method defined its implementation classes in
  69  *     the resulting dependency list.</li>
  70  * </ul>
  71  *
  72  * Limitation:
  73  * <ul>
  74  * <li>For each Methodref, it only parses the method of
  75  *     the specified type.  It doesn't analyze the class hierarchy
  76  *     and follow references of its subclasses since it ends up
  77  *     pulls in many unnecessary dependencies.  For now,
  78  *     the list of subclasses and methods need to be listed in
  79  *     the root set.</li>
  80  * </ul>
  81  *
  82  * @author Mandy Chung
  83  */
  84 public class BootAnalyzer {
  85     private static ClassPaths cpaths;
  86     public static void main(String[] args) throws Exception {
  87         String jdkhome = null;
  88         String config = null;
  89         String output = ".";
  90         String version = "7-ea";
  91         boolean printClassList = false;
  92 
  93         // process arguments
  94         int i = 0;
  95         while (i < args.length) {
  96             String arg = args[i++];
  97             if (arg.equals("-jdkhome")) {
  98                 if (i < args.length) {
  99                     jdkhome = args[i++];
 100                 } else {
 101                     usage();
 102                 }
 103             } else if (arg.equals("-version")) {
 104                 version = args[i++];
 105             } else if (arg.equals("-config")) {
 106                 config = args[i++];
 107             } else if (arg.equals("-output")) {
 108                 output = args[i++];
 109             } else if (arg.equals("-classlist")) {
 110                 printClassList = true;
 111             } else {
 112                 usage();
 113             }
 114         }
 115 


 116         if (jdkhome == null || config == null) {
 117             usage();
 118         }
 119 
 120         File jre = new File(jdkhome, "jre");
 121         if (jre.exists()) {
 122             cpaths = ClassPaths.newJDKClassPaths(jdkhome);
 123         } else {
 124             File classes = new File(jdkhome, "classes");
 125             if (classes.exists()) {
 126                 cpaths = ClassPaths.newInstance(classes.getCanonicalPath());
 127             } else {
 128                 throw new RuntimeException("Invalid jdkhome: " + jdkhome);
 129             }
 130         }
 131 
 132         parseConfigFile(config);
 133         followRoots();
 134 
 135         // create output directory if it doesn't exist
 136         File dir = new File(output);
 137         if (!dir.isDirectory()) {
 138             if (!dir.exists()) {
 139                 boolean created = dir.mkdir();
 140                 if (!created) {
 141                     throw new RuntimeException("Unable to create `" + dir + "'");
 142                 }
 143             }
 144         }
 145 
 146         String bootmodule = "boot";
 147         String bootconfig = resolve(dir, bootmodule, "config");
 148         printBootConfig(bootconfig, bootmodule);
 149 
 150         ModuleBuilder builder =
 151                 new ModuleBuilder(Collections.singletonList(bootconfig), version);
 152 
 153         assert Module.getAllModules().size() == 1;
 154         Module module = null;
 155         for (Module m : Module.getAllModules()) {
 156             module = m;
 157             break;
 158         }
 159         for (Klass k : Klass.getAllClasses()) {
 160             module.addKlass(k);
 161         }
 162         builder.run();
 163 
 164         if (printClassList) {
 165             ClassListWriter writer = new ClassListWriter(dir, module);
 166             writer.printClassList();
 167             writer.printResourceList();
 168             printModuleSummary(dir, module);
 169         }
 170     }
 171 
 172     private static void printModuleSummary(File dir, Module m) throws IOException {
 173         PrintWriter summary =
 174                 new PrintWriter(Files.resolve(dir, m.name(), "summary"));
 175         try {
 176             long total = 0L;
 177             int count = 0;
 178             summary.format("%10s\t%10s\t%s%n", "Bytes", "Classes", "Package name");
 179             for (PackageInfo info : m.getModuleInfo().packages()) {
 180                 if (info.count > 0) {
 181                     summary.format("%10d\t%10d\t%s%n",
 182                                   info.filesize, info.count, info.pkgName);
 183                     total += info.filesize;
 184                     count += info.count;
 185                 }
 186             }
 187             summary.format("%nTotal: %d bytes (uncompressed) %d classes%n",
 188                     total, count);
 189         } finally {
 190             summary.close();
 191         }
 192     }
 193 
 194     // print boot.config file as an input to the ClassAnalyzer
 195     private static void printBootConfig(String output, String bootmodule) throws IOException {
 196 
 197         File f = new File(output);
 198         PrintWriter writer = new PrintWriter(f);
 199         try {
 200             int count = 0;
 201             writer.format("module %s {%n", bootmodule);
 202             for (Klass k : Klass.getAllClasses()) {
 203                 if (count++ == 0) {
 204                     writer.format("%4s%7s %s", "", "include", k);
 205                 } else {
 206                     writer.format(",%n");
 207                     writer.format("%4s%7s %s", "", "", k);
 208                 }
 209             }
 210             writer.format(";%n}%n");
 211         } finally {
 212             writer.close();
 213         }


 356         KlassInfo getSuperclass() {
 357             ensureParse();
 358             return superclass;
 359         }
 360 
 361         List<KlassInfo> getInterfaces() {
 362             ensureParse();
 363             return java.util.Collections.unmodifiableList(interfaces);
 364         }
 365 
 366         void ensureParse() {
 367             try {
 368                 getClassFileParser();
 369             } catch (IOException e) {
 370                 throw new RuntimeException(e);
 371             }
 372         }
 373 
 374         synchronized ClassFileParser getClassFileParser() throws IOException {
 375             if (parser == null) {
 376                 parser = cpaths.parserForClass(classname);
 377                 if (parser != null) {
 378                     parseClassFile();
 379                     List<String> descriptors = parse(new MethodDescriptor(classname + ".<clinit>", "()V", false));
 380                 }
 381             }
 382             return parser;
 383         }
 384 
 385         List<String> parse(MethodDescriptor md) {
 386             ensureParse();
 387             try {
 388                 List<String> descriptors = new LinkedList<String>();
 389                 for (Method m : parser.classfile.methods) {
 390                     String name = m.getName(parser.classfile.constant_pool);
 391                     String desc = parser.constantPoolParser.getDescriptor(m.descriptor.index);
 392                     if (name.equals(md.methodname)) {
 393                         if (md.descriptor.equals("*") || md.descriptor.equals(desc)) {
 394                             parseMethod(parser, m);
 395                             descriptors.add(desc);
 396                         }


 822 
 823             public String visitMethodref(CONSTANT_Methodref_info info, Void p) {
 824                 return addMethodDescriptor(info, p);
 825             }
 826 
 827             public String visitModuleId(CONSTANT_ModuleId_info info, Void p) {
 828                 // skip
 829                 return null;
 830             }
 831 
 832             public String visitString(CONSTANT_String_info info, Void p) {
 833                 // skip
 834                 return null;
 835             }
 836 
 837             public String visitUtf8(CONSTANT_Utf8_info info, Void p) {
 838                 return null;
 839             }
 840         };
 841     }

 842 






 843     private static void usage() {
 844         System.out.println("Usage: BootAnalyzer <options>");
 845         System.out.println("Options: ");
 846         System.out.println("\t-jdkhome <JDK home> where all jars will be parsed");
 847         System.out.println("\t-config  <roots for the boot module>");
 848         System.out.println("\t-output  <output dir>");
 849         System.out.println("\t-classlist print class list and summary");
 850         System.exit(-1);
 851     }
 852 }