1 /*
2  * Copyright (c) 2016, 2018, 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;
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 
getArgumentRegex(String arg)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 
main(String[] args)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 
canonicalizable(Class<?> c)164     private static boolean canonicalizable(Class<?> c) {
165         return Canonicalizable.class.isAssignableFrom(c);
166     }
167 
virtualizable(Class<?> c)168     private static boolean virtualizable(Class<?> c) {
169         return Virtualizable.class.isAssignableFrom(c);
170     }
171 
memoryCheckPoint(Class<?> c)172     private static boolean memoryCheckPoint(Class<?> c) {
173         return MemoryCheckpoint.class.isAssignableFrom(c);
174     }
175 
initAllClasses(final Path root, ClassLoader classLoader, HashSet<Class<?>> classes)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 
prefixed(String className)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 
stripClassName(String className)223     private static String stripClassName(String className) {
224         return className.replace('/', '.');
225     }
226 
constructClazzPart(String stripped, String prefix)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 
constructURLPart(String stripped, String className, String prefix)232     private static String constructURLPart(String stripped, String className, String prefix) {
233         return className.substring(0, stripped.lastIndexOf(prefix));
234     }
235 
236 }
237