1 /* 2 * Copyright (c) 2015, 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 static org.graalvm.compiler.hotspot.HotSpotGraalOptionValues.GRAAL_OPTION_PROPERTY_PREFIX; 28 import static org.graalvm.compiler.hotspot.HotSpotGraalOptionValues.HOTSPOT_OPTIONS; 29 30 import java.io.FileNotFoundException; 31 import java.io.FileOutputStream; 32 import java.io.IOException; 33 import java.io.OutputStream; 34 import java.io.PrintStream; 35 import java.util.List; 36 37 import org.graalvm.compiler.core.common.SuppressFBWarnings; 38 import org.graalvm.compiler.debug.TTYStreamProvider; 39 import org.graalvm.compiler.options.Option; 40 import org.graalvm.compiler.options.OptionKey; 41 import org.graalvm.compiler.options.OptionType; 42 import org.graalvm.compiler.options.OptionValues; 43 import org.graalvm.compiler.serviceprovider.GraalServices; 44 import org.graalvm.compiler.serviceprovider.ServiceProvider; 45 46 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; 47 48 @ServiceProvider(TTYStreamProvider.class) 49 public class HotSpotTTYStreamProvider implements TTYStreamProvider { 50 51 public static class Options { 52 53 // @formatter:off 54 @Option(help = "File to which logging is sent. A %p in the name will be replaced with a string identifying " + 55 "the process, usually the process id and %t will be replaced by System.currentTimeMillis().", type = OptionType.Expert) 56 public static final LogStreamOptionKey LogFile = new LogStreamOptionKey(); 57 // @formatter:on 58 } 59 60 @Override 61 public PrintStream getStream() { 62 return Options.LogFile.getStream(HOTSPOT_OPTIONS); 63 } 64 65 /** 66 * An option for a configurable file name that can also open a {@link PrintStream} on the file. 67 * If no value is given for the option, the stream will output to HotSpot's 68 * {@link HotSpotJVMCIRuntime#getLogStream() log} stream 69 */ 70 private static class LogStreamOptionKey extends OptionKey<String> { 71 72 LogStreamOptionKey() { 73 super(null); 74 } 75 76 /** 77 * @return {@code nameTemplate} with all instances of %p replaced by 78 * {@link GraalServices#getExecutionID()} and %t by 79 * {@link System#currentTimeMillis()} 80 */ 81 private static String makeFilename(String nameTemplate) { 82 String name = nameTemplate; 83 if (name.contains("%p")) { 84 name = name.replaceAll("%p", GraalServices.getExecutionID()); 85 } 86 if (name.contains("%t")) { 87 name = name.replaceAll("%t", String.valueOf(System.currentTimeMillis())); 88 } 89 return name; 90 } 91 92 /** 93 * An output stream that redirects to {@link HotSpotJVMCIRuntime#getLogStream()}. The 94 * {@link HotSpotJVMCIRuntime#getLogStream()} value is only accessed the first time an IO 95 * operation is performed on the stream. This is required to break a deadlock in early JVMCI 96 * initialization. 97 */ 98 static class DelayedOutputStream extends OutputStream { 99 private volatile OutputStream lazy; 100 101 private OutputStream lazy() { 102 if (lazy == null) { 103 synchronized (this) { 104 if (lazy == null) { 105 lazy = HotSpotJVMCIRuntime.runtime().getLogStream(); 106 PrintStream ps = new PrintStream(lazy); 107 ps.printf("[Use -D%sLogFile=<path> to redirect Graal log output to a file.]%n", GRAAL_OPTION_PROPERTY_PREFIX); 108 } 109 } 110 } 111 return lazy; 112 } 113 114 @Override 115 public void write(byte[] b, int off, int len) throws IOException { 116 lazy().write(b, off, len); 117 } 118 119 @Override 120 public void write(int b) throws IOException { 121 lazy().write(b); 122 } 123 124 @Override 125 public void flush() throws IOException { 126 lazy().flush(); 127 } 128 129 @Override 130 public void close() throws IOException { 131 lazy().close(); 132 } 133 } 134 135 /** 136 * Gets the print stream configured by this option. If no file is configured, the print 137 * stream will output to HotSpot's {@link HotSpotJVMCIRuntime#getLogStream() log} stream. 138 */ 139 @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "false positive on dead store to `ps`") 140 public PrintStream getStream(OptionValues options) { 141 String nameTemplate = getValue(options); 142 if (nameTemplate != null) { 143 String name = makeFilename(nameTemplate); 144 try { 145 final boolean enableAutoflush = true; 146 PrintStream ps = new PrintStream(new FileOutputStream(name), enableAutoflush); 147 /* 148 * Add the JVM and Java arguments to the log file to help identity it. 149 */ 150 List<String> inputArguments = GraalServices.getInputArguments(); 151 if (inputArguments != null) { 152 ps.println("VM Arguments: " + String.join(" ", inputArguments)); 153 } 154 String cmd = System.getProperty("sun.java.command"); 155 if (cmd != null) { 156 ps.println("sun.java.command=" + cmd); 157 } 158 return ps; 159 } catch (FileNotFoundException e) { 160 throw new RuntimeException("couldn't open file: " + name, e); 161 } 162 } else { 163 return new PrintStream(new DelayedOutputStream()); 164 } 165 } 166 } 167 168 }