1 /*
   2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   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 
  25 package org.graalvm.compiler.hotspot.test;
  26 
  27 import java.io.File;
  28 import java.io.IOException;
  29 import java.net.URI;
  30 import java.net.URL;
  31 import java.net.URLClassLoader;
  32 import java.nio.file.FileSystem;
  33 import java.nio.file.FileSystems;
  34 import java.nio.file.FileVisitResult;
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.nio.file.SimpleFileVisitor;
  38 import java.nio.file.attribute.BasicFileAttributes;
  39 import java.util.ArrayList;
  40 import java.util.Arrays;
  41 import java.util.Collections;
  42 import java.util.HashSet;
  43 import java.util.List;
  44 import java.util.Set;
  45 import java.util.regex.Pattern;
  46 import java.util.regex.PatternSyntaxException;
  47 import java.util.stream.Collectors;
  48 
  49 import org.graalvm.compiler.debug.CSVUtil;
  50 import org.graalvm.compiler.debug.GraalError;
  51 import org.graalvm.compiler.graph.Node;
  52 import org.graalvm.compiler.graph.NodeClass;
  53 import org.graalvm.compiler.graph.spi.Canonicalizable;
  54 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
  55 import org.graalvm.compiler.nodes.spi.Virtualizable;
  56 
  57 public class NodeCostDumpUtil {
  58 
  59     private static final String prefix1 = "com.oracle.";
  60     private static final String prefix2 = "org.graalvm.";
  61     private static final String FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s");
  62 
  63     private static String getArgumentRegex(String arg) {
  64         if (arg.length() == 0) {
  65             return null;
  66         }
  67         try {
  68             Pattern.compile(arg);
  69             return arg;
  70         } catch (PatternSyntaxException e) {
  71             // silently ignore
  72             System.err.println("Invalid regex given, defaulting to \".*\" regex..");
  73             return null;
  74         }
  75     }
  76 
  77     public static void main(String[] args) {
  78         if (args.length != 1) {
  79             System.err.println("NodeCostDumpUtil expects exactly one argument, the node name regex to match against.");
  80             System.exit(-1);
  81         }
  82         final String pattern = getArgumentRegex(args[0]);
  83         String version = System.getProperty("java.specification.version");
  84         if (version.compareTo("1.9") >= 0) {
  85             System.err.printf("NodeCostDumpUtil does not support JDK versions greater than 1.8, current version is %s.\n", version);
  86             System.exit(-1);
  87         }
  88         String[] jvmciCP = System.getProperty("jvmci.class.path.append").split(File.pathSeparator);
  89         String[] primarySuiteCP = System.getProperty("primary.suite.cp").split(File.pathSeparator);
  90         ClassLoader applicationClassLoader = Thread.currentThread().getContextClassLoader();
  91         HashSet<Class<?>> classes = new HashSet<>();
  92         try {
  93             Set<String> uniquePaths = new HashSet<>(Arrays.asList(primarySuiteCP));
  94             uniquePaths.addAll(Arrays.asList(jvmciCP));
  95             for (String path : uniquePaths) {
  96                 if (new File(path).exists()) {
  97                     if (path.endsWith(".jar")) {
  98                         try (FileSystem jarFileSystem = FileSystems.newFileSystem(URI.create("jar:file:" + path), Collections.emptyMap())) {
  99                             initAllClasses(jarFileSystem.getPath("/"), applicationClassLoader, classes);
 100                         }
 101                     } else {
 102                         initAllClasses(FileSystems.getDefault().getPath(path), applicationClassLoader, classes);
 103                     }
 104                 }
 105             }
 106         } catch (IOException ex) {
 107             GraalError.shouldNotReachHere();
 108         }
 109         System.err.printf("Loaded %d classes...\n", classes.size());
 110         List<Class<?>> nodeClasses = new ArrayList<>();
 111         for (Class<?> loaded : classes) {
 112             if (Node.class.isAssignableFrom(loaded) && !loaded.isArray()) {
 113                 nodeClasses.add(loaded);
 114             }
 115         }
 116         System.err.printf("Loaded %s node classes...\n", nodeClasses.size());
 117         List<NodeClass<?>> nc = new ArrayList<>();
 118         for (Class<?> c : nodeClasses) {
 119             try {
 120                 nc.add(NodeClass.get(c));
 121             } catch (Throwable t) {
 122                 // Silently ignore problems here
 123             }
 124         }
 125         System.err.printf("Read TYPE field from %s node classes...\n", nc.size());
 126         nc = nc.stream().filter(x -> x != null).collect(Collectors.toList());
 127         nc.sort((x, y) -> {
 128             String a = x.getJavaClass().getName();
 129             String b = y.getJavaClass().getName();
 130             return a.compareTo(b);
 131         });
 132         CSVUtil.Escape.println(System.out, FMT, "NodeName", "Size", "Overrides Size Method", "Cycles", "Overrides Cycles Method", "Canonicalizable", "MemoryCheckPoint", "Virtualizable");
 133         for (NodeClass<?> nodeclass : nc) {
 134             String packageStrippedName = null;
 135             try {
 136                 packageStrippedName = nodeclass.getJavaClass().getCanonicalName().replace(prefix1, "").replace(prefix2, "");
 137             } catch (Throwable t) {
 138                 // do nothing
 139                 continue;
 140             }
 141             if (pattern != null && !packageStrippedName.matches(pattern)) {
 142                 continue;
 143             }
 144             boolean overridesSizeMethod = false;
 145             boolean overridesCyclesMethod = false;
 146             Class<?> c = nodeclass.getJavaClass();
 147             try {
 148                 c.getDeclaredMethod("estimatedNodeSize");
 149                 overridesSizeMethod = true;
 150             } catch (Throwable t) {
 151                 // do nothing
 152             }
 153             try {
 154                 c.getDeclaredMethod("estimatedNodeCycles");
 155                 overridesCyclesMethod = true;
 156             } catch (Throwable t) {
 157                 // do nothing
 158             }
 159             CSVUtil.Escape.println(System.out, FMT, packageStrippedName, nodeclass.size(), overridesSizeMethod, nodeclass.cycles(), overridesCyclesMethod, canonicalizable(c), memoryCheckPoint(c),
 160                             virtualizable(c));
 161         }
 162     }
 163 
 164     private static boolean canonicalizable(Class<?> c) {
 165         return Canonicalizable.class.isAssignableFrom(c);
 166     }
 167 
 168     private static boolean virtualizable(Class<?> c) {
 169         return Virtualizable.class.isAssignableFrom(c);
 170     }
 171 
 172     private static boolean memoryCheckPoint(Class<?> c) {
 173         return MemoryCheckpoint.class.isAssignableFrom(c);
 174     }
 175 
 176     private static void initAllClasses(final Path root, ClassLoader classLoader, HashSet<Class<?>> classes) {
 177         try {
 178             Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
 179                 @Override
 180                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
 181                     String className = root.relativize(file).toString();
 182                     ClassLoader c = classLoader;
 183                     if (className.endsWith(".class")) {
 184                         String prefix = prefixed(className);
 185                         if (prefix != null) {
 186                             String stripped = stripClassName(className);
 187                             c = new URLClassLoader(new URL[]{new File(constructURLPart(stripped, className, prefix)).toURI().toURL()}, classLoader);
 188                             className = constructClazzPart(stripped, prefix);
 189                         } else {
 190                             String clazzPart = className.replace('/', '.');
 191                             className = clazzPart.substring(0, clazzPart.length() - ".class".length());
 192                         }
 193                         try {
 194                             Class<?> systemClass = Class.forName(className, false, c);
 195                             if (systemClass.getEnclosingClass() != null) {
 196                                 try {
 197                                     classes.add(systemClass.getEnclosingClass());
 198                                 } catch (Throwable t) {
 199                                     // do nothing
 200                                 }
 201                             }
 202                             classes.add(systemClass);
 203                         } catch (Throwable ignored) {
 204                         }
 205                     }
 206                     return FileVisitResult.CONTINUE;
 207                 }
 208             });
 209         } catch (IOException ex) {
 210             GraalError.shouldNotReachHere();
 211         }
 212     }
 213 
 214     private static String prefixed(String className) {
 215         if (className.contains(prefix1) && className.indexOf(prefix1) > 0) {
 216             return prefix1;
 217         } else if (className.contains(prefix2) && className.indexOf(prefix2) > 0) {
 218             return prefix2;
 219         }
 220         return null;
 221     }
 222 
 223     private static String stripClassName(String className) {
 224         return className.replace('/', '.');
 225     }
 226 
 227     private static String constructClazzPart(String stripped, String prefix) {
 228         String clazzPart = stripped.substring(stripped.lastIndexOf(prefix), stripped.length());
 229         return clazzPart.substring(0, clazzPart.length() - ".class".length());
 230     }
 231 
 232     private static String constructURLPart(String stripped, String className, String prefix) {
 233         return className.substring(0, stripped.lastIndexOf(prefix));
 234     }
 235 
 236 }
 237