Path: blob/master/src/java.base/share/classes/jdk/internal/misc/CDS.java
41159 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.misc;2627import java.io.BufferedReader;28import java.io.File;29import java.io.InputStreamReader;30import java.io.InputStream;31import java.io.IOException;32import java.io.PrintStream;33import java.util.Arrays;34import java.util.ArrayList;35import java.util.List;36import java.util.Map;37import java.util.Objects;38import java.util.stream.Stream;3940import jdk.internal.access.JavaLangInvokeAccess;41import jdk.internal.access.SharedSecrets;4243public class CDS {44private static final boolean isDumpingClassList;45private static final boolean isDumpingArchive;46private static final boolean isSharingEnabled;47static {48isDumpingClassList = isDumpingClassList0();49isDumpingArchive = isDumpingArchive0();50isSharingEnabled = isSharingEnabled0();51}5253/**54* indicator for dumping class list.55*/56public static boolean isDumpingClassList() {57return isDumpingClassList;58}5960/**61* Is the VM writing to a (static or dynamic) CDS archive.62*/63public static boolean isDumpingArchive() {64return isDumpingArchive;65}6667/**68* Is sharing enabled via the UseSharedSpaces flag.69*/70public static boolean isSharingEnabled() {71return isSharingEnabled;72}7374private static native boolean isDumpingClassList0();75private static native boolean isDumpingArchive0();76private static native boolean isSharingEnabled0();77private static native void logLambdaFormInvoker(String line);7879/**80* Initialize archived static fields in the given Class using archived81* values from CDS dump time. Also initialize the classes of objects in82* the archived graph referenced by those fields.83*84* Those static fields remain as uninitialized if there is no mapped CDS85* java heap data or there is any error during initialization of the86* object class in the archived graph.87*/88public static native void initializeFromArchive(Class<?> c);8990/**91* Ensure that the native representation of all archived java.lang.Module objects92* are properly restored.93*/94public static native void defineArchivedModules(ClassLoader platformLoader, ClassLoader systemLoader);9596/**97* Returns a predictable "random" seed derived from the VM's build ID and version,98* to be used by java.util.ImmutableCollections to ensure that archived99* ImmutableCollections are always sorted the same order for the same VM build.100*/101public static native long getRandomSeedForDumping();102103/**104* log lambda form invoker holder, name and method type105*/106public static void traceLambdaFormInvoker(String prefix, String holder, String name, String type) {107if (isDumpingClassList) {108logLambdaFormInvoker(prefix + " " + holder + " " + name + " " + type);109}110}111112/**113* log species114*/115public static void traceSpeciesType(String prefix, String cn) {116if (isDumpingClassList) {117logLambdaFormInvoker(prefix + " " + cn);118}119}120121static final String DIRECT_HOLDER_CLASS_NAME = "java.lang.invoke.DirectMethodHandle$Holder";122static final String DELEGATING_HOLDER_CLASS_NAME = "java.lang.invoke.DelegatingMethodHandle$Holder";123static final String BASIC_FORMS_HOLDER_CLASS_NAME = "java.lang.invoke.LambdaForm$Holder";124static final String INVOKERS_HOLDER_CLASS_NAME = "java.lang.invoke.Invokers$Holder";125126private static boolean isValidHolderName(String name) {127return name.equals(DIRECT_HOLDER_CLASS_NAME) ||128name.equals(DELEGATING_HOLDER_CLASS_NAME) ||129name.equals(BASIC_FORMS_HOLDER_CLASS_NAME) ||130name.equals(INVOKERS_HOLDER_CLASS_NAME);131}132133private static boolean isBasicTypeChar(char c) {134return "LIJFDV".indexOf(c) >= 0;135}136137private static boolean isValidMethodType(String type) {138String[] typeParts = type.split("_");139// check return type (second part)140if (typeParts.length != 2 || typeParts[1].length() != 1141|| !isBasicTypeChar(typeParts[1].charAt(0))) {142return false;143}144// first part145if (!isBasicTypeChar(typeParts[0].charAt(0))) {146return false;147}148for (int i = 1; i < typeParts[0].length(); i++) {149char c = typeParts[0].charAt(i);150if (!isBasicTypeChar(c)) {151if (!(c >= '0' && c <= '9')) {152return false;153}154}155}156return true;157}158159// Throw exception on invalid input160private static void validateInputLines(String[] lines) {161for (String s: lines) {162if (!s.startsWith("[LF_RESOLVE]") && !s.startsWith("[SPECIES_RESOLVE]")) {163throw new IllegalArgumentException("Wrong prefix: " + s);164}165166String[] parts = s.split(" ");167boolean isLF = s.startsWith("[LF_RESOLVE]");168169if (isLF) {170if (parts.length != 4) {171throw new IllegalArgumentException("Incorrect number of items in the line: " + parts.length);172}173if (!isValidHolderName(parts[1])) {174throw new IllegalArgumentException("Invalid holder class name: " + parts[1]);175}176if (!isValidMethodType(parts[3])) {177throw new IllegalArgumentException("Invalid method type: " + parts[3]);178}179} else {180if (parts.length != 2) {181throw new IllegalArgumentException("Incorrect number of items in the line: " + parts.length);182}183}184}185}186187/**188* called from vm to generate MethodHandle holder classes189* @return {@code Object[]} if holder classes can be generated.190* @param lines in format of LF_RESOLVE or SPECIES_RESOLVE output191*/192private static Object[] generateLambdaFormHolderClasses(String[] lines) {193Objects.requireNonNull(lines);194validateInputLines(lines);195Stream<String> lineStream = Arrays.stream(lines);196Map<String, byte[]> result = SharedSecrets.getJavaLangInvokeAccess().generateHolderClasses(lineStream);197int size = result.size();198Object[] retArray = new Object[size * 2];199int index = 0;200for (Map.Entry<String, byte[]> entry : result.entrySet()) {201retArray[index++] = entry.getKey();202retArray[index++] = entry.getValue();203};204return retArray;205}206207private static native void dumpClassList(String listFileName);208private static native void dumpDynamicArchive(String archiveFileName);209210private static String drainOutput(InputStream stream, long pid, String tail, List<String> cmds) {211String fileName = "java_pid" + pid + "_" + tail;212new Thread( ()-> {213try (InputStreamReader isr = new InputStreamReader(stream);214BufferedReader rdr = new BufferedReader(isr);215PrintStream prt = new PrintStream(fileName)) {216prt.println("Command:");217for (String s : cmds) {218prt.print(s + " ");219}220prt.println("");221String line;222while((line = rdr.readLine()) != null) {223prt.println(line);224}225} catch (IOException e) {226throw new RuntimeException("IOExeption happens during drain stream to file " +227fileName + ": " + e.getMessage());228}}).start();229return fileName;230}231232private static String[] excludeFlags = {233"-XX:DumpLoadedClassList=",234"-XX:+DumpSharedSpaces",235"-XX:+DynamicDumpSharedSpaces",236"-XX:+RecordDynamicDumpInfo",237"-Xshare:",238"-XX:SharedClassListFile=",239"-XX:SharedArchiveFile=",240"-XX:ArchiveClassesAtExit=",241"-XX:+UseSharedSpaces",242"-XX:+RequireSharedSpaces"};243private static boolean containsExcludedFlags(String testStr) {244for (String e : excludeFlags) {245if (testStr.contains(e)) {246return true;247}248}249return false;250}251252/**253* called from jcmd VM.cds to dump static or dynamic shared archive254* @param isStatic true for dump static archive or false for dynnamic archive.255* @param fileName user input archive name, can be null.256*/257private static void dumpSharedArchive(boolean isStatic, String fileName) throws Exception {258String currentPid = String.valueOf(ProcessHandle.current().pid());259String archiveFileName = fileName != null ? fileName :260"java_pid" + currentPid + (isStatic ? "_static.jsa" : "_dynamic.jsa");261262String tempArchiveFileName = archiveFileName + ".temp";263File tempArchiveFile = new File(tempArchiveFileName);264// The operation below may cause exception if the file or its dir is protected.265if (!tempArchiveFile.exists()) {266tempArchiveFile.createNewFile();267}268tempArchiveFile.delete();269270if (isStatic) {271String listFileName = archiveFileName + ".classlist";272File listFile = new File(listFileName);273if (listFile.exists()) {274listFile.delete();275}276dumpClassList(listFileName);277String jdkHome = System.getProperty("java.home");278String classPath = System.getProperty("java.class.path");279List<String> cmds = new ArrayList<String>();280cmds.add(jdkHome + File.separator + "bin" + File.separator + "java"); // java281cmds.add("-cp");282cmds.add(classPath);283cmds.add("-Xlog:cds");284cmds.add("-Xshare:dump");285cmds.add("-XX:SharedClassListFile=" + listFileName);286cmds.add("-XX:SharedArchiveFile=" + tempArchiveFileName);287288// All runtime args.289String[] vmArgs = VM.getRuntimeArguments();290if (vmArgs != null) {291for (String arg : vmArgs) {292if (arg != null && !containsExcludedFlags(arg)) {293cmds.add(arg);294}295}296}297298Process proc = Runtime.getRuntime().exec(cmds.toArray(new String[0]));299300// Drain stdout/stderr to files in new threads.301String stdOutFile = drainOutput(proc.getInputStream(), proc.pid(), "stdout", cmds);302String stdErrFile = drainOutput(proc.getErrorStream(), proc.pid(), "stderr", cmds);303304proc.waitFor();305// done, delete classlist file.306listFile.delete();307308// Check if archive has been successfully dumped. We won't reach here if exception happens.309// Throw exception if file is not created.310if (!tempArchiveFile.exists()) {311throw new RuntimeException("Archive file " + tempArchiveFileName +312" is not created, please check stdout file " +313stdOutFile + " or stderr file " +314stdErrFile + " for more detail");315}316} else {317dumpDynamicArchive(tempArchiveFileName);318if (!tempArchiveFile.exists()) {319throw new RuntimeException("Archive file " + tempArchiveFileName +320" is not created, please check process " +321currentPid + " output for more detail");322}323}324// Override the existing archive file325File archiveFile = new File(archiveFileName);326if (archiveFile.exists()) {327archiveFile.delete();328}329if (!tempArchiveFile.renameTo(archiveFile)) {330throw new RuntimeException("Cannot rename temp file " + tempArchiveFileName + " to archive file" + archiveFileName);331}332// Everyting goes well, print out the file name.333System.out.println((isStatic ? "Static" : " Dynamic") + " dump to file " + archiveFileName);334}335}336337338