/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.hotspot; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; import org.graalvm.compiler.debug.CSVUtil; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.spi.Canonicalizable; import org.graalvm.compiler.nodes.memory.MemoryCheckpoint; import org.graalvm.compiler.nodes.spi.Virtualizable; public class NodeCostDumpUtil { private static final String prefix1 = "com.oracle."; private static final String prefix2 = "org.graalvm."; private static final String FMT = CSVUtil.buildFormatString("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s"); private static String getArgumentRegex(String arg) { if (arg.length() == 0) { return null; } try { Pattern.compile(arg); return arg; } catch (PatternSyntaxException e) { // silently ignore System.err.println("Invalid regex given, defaulting to \".*\" regex.."); return null; } } public static void main(String[] args) { if (args.length != 1) { System.err.println("NodeCostDumpUtil expects exactly one argument, the node name regex to match against."); System.exit(-1); } final String pattern = getArgumentRegex(args[0]); String version = System.getProperty("java.specification.version"); if (version.compareTo("1.9") >= 0) { System.err.printf("NodeCostDumpUtil does not support JDK versions greater than 1.8, current version is %s.\n", version); System.exit(-1); } String[] jvmciCP = System.getProperty("jvmci.class.path.append").split(File.pathSeparator); String[] primarySuiteCP = System.getProperty("primary.suite.cp").split(File.pathSeparator); ClassLoader applicationClassLoader = Thread.currentThread().getContextClassLoader(); HashSet<Class<?>> classes = new HashSet<>(); try { Set<String> uniquePaths = new HashSet<>(Arrays.asList(primarySuiteCP)); uniquePaths.addAll(Arrays.asList(jvmciCP)); for (String path : uniquePaths) { if (new File(path).exists()) { if (path.endsWith(".jar")) { try (FileSystem jarFileSystem = FileSystems.newFileSystem(URI.create("jar:file:" + path), Collections.emptyMap())) { initAllClasses(jarFileSystem.getPath("/"), applicationClassLoader, classes); } } else { initAllClasses(FileSystems.getDefault().getPath(path), applicationClassLoader, classes); } } } } catch (IOException ex) { GraalError.shouldNotReachHere(); } System.err.printf("Loaded %d classes...\n", classes.size()); List<Class<?>> nodeClasses = new ArrayList<>(); for (Class<?> loaded : classes) { if (Node.class.isAssignableFrom(loaded) && !loaded.isArray()) { nodeClasses.add(loaded); } } System.err.printf("Loaded %s node classes...\n", nodeClasses.size()); List<NodeClass<?>> nc = new ArrayList<>(); for (Class<?> nodeClass : nodeClasses) { Field f; try { f = nodeClass.getField("TYPE"); f.setAccessible(true); Object val = f.get(null); NodeClass<?> nodeType = (NodeClass<?>) val; nc.add(nodeType); } catch (Throwable t) { // Silently ignore problems here } } System.err.printf("Read TYPE field from %s node classes...\n", nc.size()); nc = nc.stream().filter(x -> x != null).collect(Collectors.toList()); nc.sort((x, y) -> { String a = x.getJavaClass().getName(); String b = y.getJavaClass().getName(); return a.compareTo(b); }); CSVUtil.Escape.println(System.out, FMT, "NodeName", "Size", "Overrides Size Method", "Cycles", "Overrides Cycles Method", "Canonicalizable", "MemoryCheckPoint", "Virtualizable"); for (NodeClass<?> nodeclass : nc) { String packageStrippedName = null; try { packageStrippedName = nodeclass.getJavaClass().getCanonicalName().replace(prefix1, "").replace(prefix2, ""); } catch (Throwable t) { // do nothing continue; } if (pattern != null && !packageStrippedName.matches(pattern)) { continue; } boolean overridesSizeMethod = false; boolean overridesCyclesMethod = false; Class<?> c = nodeclass.getJavaClass(); try { c.getDeclaredMethod("estimatedNodeSize"); overridesSizeMethod = true; } catch (Throwable t) { // do nothing } try { c.getDeclaredMethod("estimatedNodeCycles"); overridesCyclesMethod = true; } catch (Throwable t) { // do nothing } CSVUtil.Escape.println(System.out, FMT, packageStrippedName, nodeclass.size(), overridesSizeMethod, nodeclass.cycles(), overridesCyclesMethod, canonicalizable(c), memoryCheckPoint(c), virtualizable(c)); } } private static boolean canonicalizable(Class<?> c) { return Canonicalizable.class.isAssignableFrom(c); } private static boolean virtualizable(Class<?> c) { return Virtualizable.class.isAssignableFrom(c); } private static boolean memoryCheckPoint(Class<?> c) { return MemoryCheckpoint.class.isAssignableFrom(c); } private static void initAllClasses(final Path root, ClassLoader classLoader, HashSet<Class<?>> classes) { try { Files.walkFileTree(root, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String className = root.relativize(file).toString(); ClassLoader c = classLoader; if (className.endsWith(".class")) { String prefix = prefixed(className); if (prefix != null) { String stripped = stripClassName(className); c = new URLClassLoader(new URL[]{new File(constructURLPart(stripped, className, prefix)).toURI().toURL()}, classLoader); className = constructClazzPart(stripped, prefix); } else { String clazzPart = className.replace('/', '.'); className = clazzPart.substring(0, clazzPart.length() - ".class".length()); } try { Class<?> systemClass = Class.forName(className, false, c); if (systemClass.getEnclosingClass() != null) { try { classes.add(systemClass.getEnclosingClass()); } catch (Throwable t) { // do nothing } } classes.add(systemClass); } catch (Throwable ignored) { } } return FileVisitResult.CONTINUE; } }); } catch (IOException ex) { GraalError.shouldNotReachHere(); } } private static String prefixed(String className) { if (className.contains(prefix1) && className.indexOf(prefix1) > 0) { return prefix1; } else if (className.contains(prefix2) && className.indexOf(prefix2) > 0) { return prefix2; } return null; } private static String stripClassName(String className) { return className.replace('/', '.'); } private static String constructClazzPart(String stripped, String prefix) { String clazzPart = stripped.substring(stripped.lastIndexOf(prefix), stripped.length()); return clazzPart.substring(0, clazzPart.length() - ".class".length()); } private static String constructURLPart(String stripped, String className, String prefix) { return className.substring(0, stripped.lastIndexOf(prefix)); } }