src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotTTYStreamProvider.java
branchdatagramsocketimpl-branch
changeset 58678 9cf78a70fa4f
parent 54328 37648a9c4a6a
child 58679 9c3209ff7550
equal deleted inserted replaced
58677:13588c901957 58678:9cf78a70fa4f
    37 import org.graalvm.compiler.core.common.SuppressFBWarnings;
    37 import org.graalvm.compiler.core.common.SuppressFBWarnings;
    38 import org.graalvm.compiler.debug.TTYStreamProvider;
    38 import org.graalvm.compiler.debug.TTYStreamProvider;
    39 import org.graalvm.compiler.options.Option;
    39 import org.graalvm.compiler.options.Option;
    40 import org.graalvm.compiler.options.OptionKey;
    40 import org.graalvm.compiler.options.OptionKey;
    41 import org.graalvm.compiler.options.OptionType;
    41 import org.graalvm.compiler.options.OptionType;
    42 import org.graalvm.compiler.options.OptionValues;
       
    43 import org.graalvm.compiler.serviceprovider.GraalServices;
    42 import org.graalvm.compiler.serviceprovider.GraalServices;
    44 import org.graalvm.compiler.serviceprovider.ServiceProvider;
    43 import org.graalvm.compiler.serviceprovider.ServiceProvider;
    45 
    44 
       
    45 import jdk.vm.ci.common.NativeImageReinitialize;
    46 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
    46 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
    47 import jdk.vm.ci.services.Services;
    47 import jdk.vm.ci.services.Services;
    48 
    48 
    49 @ServiceProvider(TTYStreamProvider.class)
    49 @ServiceProvider(TTYStreamProvider.class)
    50 public class HotSpotTTYStreamProvider implements TTYStreamProvider {
    50 public class HotSpotTTYStreamProvider implements TTYStreamProvider {
    51 
    51 
    52     public static class Options {
    52     public static class Options {
    53 
    53 
    54         // @formatter:off
    54         // @formatter:off
    55         @Option(help = "File to which logging is sent.  A %p in the name will be replaced with a string identifying " +
    55         @Option(help = "File to which logging is sent.  A %p in the name will be replaced with a string identifying " +
    56                        "the process, usually the process id and %t will be replaced by System.currentTimeMillis().", type = OptionType.Expert)
    56                        "the process, usually the process id and %t will be replaced by System.currentTimeMillis().  " +
       
    57                        "Using %o as filename sends logging to System.out whereas %e sends logging to System.err.", type = OptionType.Expert)
    57         public static final LogStreamOptionKey LogFile = new LogStreamOptionKey();
    58         public static final LogStreamOptionKey LogFile = new LogStreamOptionKey();
    58         // @formatter:on
    59         // @formatter:on
    59     }
    60     }
    60 
    61 
    61     @Override
    62     @Override
    62     public PrintStream getStream() {
    63     public PrintStream getStream() {
    63         return Options.LogFile.getStream(defaultOptions());
    64         return Options.LogFile.getStream();
    64     }
    65     }
    65 
    66 
    66     /**
    67     /**
    67      * An option for a configurable file name that can also open a {@link PrintStream} on the file.
    68      * An option for a configurable file name that can also open a {@link PrintStream} on the file.
    68      * If no value is given for the option, the stream will output to HotSpot's
    69      * If no value is given for the option, the stream will output to HotSpot's
    75         }
    76         }
    76 
    77 
    77         /**
    78         /**
    78          * @return {@code nameTemplate} with all instances of %p replaced by
    79          * @return {@code nameTemplate} with all instances of %p replaced by
    79          *         {@link GraalServices#getExecutionID()} and %t by
    80          *         {@link GraalServices#getExecutionID()} and %t by
    80          *         {@link System#currentTimeMillis()}
    81          *         {@link System#currentTimeMillis()}. Checks %o and %e are not combined with any
       
    82          *         other characters.
    81          */
    83          */
    82         private static String makeFilename(String nameTemplate) {
    84         private static String makeFilename(String nameTemplate) {
    83             String name = nameTemplate;
    85             String name = nameTemplate;
    84             if (name.contains("%p")) {
    86             if (name.contains("%p")) {
    85                 name = name.replaceAll("%p", GraalServices.getExecutionID());
    87                 name = name.replaceAll("%p", GraalServices.getExecutionID());
    86             }
    88             }
    87             if (name.contains("%t")) {
    89             if (name.contains("%t")) {
    88                 name = name.replaceAll("%t", String.valueOf(System.currentTimeMillis()));
    90                 name = name.replaceAll("%t", String.valueOf(System.currentTimeMillis()));
    89             }
    91             }
       
    92 
       
    93             for (String subst : new String[]{"%o", "%e"}) {
       
    94                 if (name.contains(subst) && !name.equals(subst)) {
       
    95                     throw new IllegalArgumentException("LogFile substitution " + subst + " cannot be combined with any other characters");
       
    96                 }
       
    97             }
       
    98 
    90             return name;
    99             return name;
    91         }
   100         }
    92 
   101 
    93         /**
   102         /**
    94          * An output stream that redirects to {@link HotSpotJVMCIRuntime#getLogStream()}. The
   103          * An output stream that redirects to {@link HotSpotJVMCIRuntime#getLogStream()}. The
    95          * {@link HotSpotJVMCIRuntime#getLogStream()} value is only accessed the first time an IO
   104          * {@link HotSpotJVMCIRuntime#getLogStream()} value is only accessed the first time an IO
    96          * operation is performed on the stream. This is required to break a deadlock in early JVMCI
   105          * operation is performed on the stream. This is required to break a deadlock in early JVMCI
    97          * initialization.
   106          * initialization.
    98          */
   107          */
    99         static class DelayedOutputStream extends OutputStream {
   108         class DelayedOutputStream extends OutputStream {
   100             private volatile OutputStream lazy;
   109             @NativeImageReinitialize private volatile OutputStream lazy;
   101 
   110 
   102             private OutputStream lazy() {
   111             private OutputStream lazy() {
   103                 if (lazy == null) {
   112                 if (lazy == null) {
   104                     synchronized (this) {
   113                     synchronized (this) {
   105                         if (lazy == null) {
   114                         if (lazy == null) {
       
   115                             String nameTemplate = LogStreamOptionKey.this.getValue(defaultOptions());
       
   116                             if (nameTemplate != null) {
       
   117                                 String name = makeFilename(nameTemplate);
       
   118                                 switch (name) {
       
   119                                     case "%o":
       
   120                                         lazy = System.out;
       
   121                                         break;
       
   122                                     case "%e":
       
   123                                         lazy = System.err;
       
   124                                         break;
       
   125                                     default:
       
   126                                         try {
       
   127                                             final boolean enableAutoflush = true;
       
   128                                             FileOutputStream result = new FileOutputStream(name);
       
   129                                             if (!Services.IS_IN_NATIVE_IMAGE) {
       
   130                                                 printVMConfig(enableAutoflush, result);
       
   131                                             } else {
       
   132                                                 // There are no VM arguments for the libgraal
       
   133                                                 // library.
       
   134                                             }
       
   135                                             lazy = result;
       
   136                                         } catch (FileNotFoundException e) {
       
   137                                             throw new RuntimeException("couldn't open file: " + name, e);
       
   138                                         }
       
   139                                 }
       
   140                                 return lazy;
       
   141                             }
       
   142 
   106                             lazy = HotSpotJVMCIRuntime.runtime().getLogStream();
   143                             lazy = HotSpotJVMCIRuntime.runtime().getLogStream();
   107                             PrintStream ps = new PrintStream(lazy);
   144                             PrintStream ps = new PrintStream(lazy);
   108                             ps.printf("[Use -D%sLogFile=<path> to redirect Graal log output to a file.]%n", GRAAL_OPTION_PROPERTY_PREFIX);
   145                             ps.printf("[Use -D%sLogFile=<path> to redirect Graal log output to a file.]%n", GRAAL_OPTION_PROPERTY_PREFIX);
       
   146                             ps.flush();
   109                         }
   147                         }
   110                     }
   148                     }
   111                 }
   149                 }
   112                 return lazy;
   150                 return lazy;
       
   151             }
       
   152 
       
   153             @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "false positive on dead store to `ps`")
       
   154             private void printVMConfig(final boolean enableAutoflush, FileOutputStream result) {
       
   155                 /*
       
   156                  * Add the JVM and Java arguments to the log file to help identity it.
       
   157                  */
       
   158                 PrintStream ps = new PrintStream(result, enableAutoflush);
       
   159                 List<String> inputArguments = GraalServices.getInputArguments();
       
   160                 if (inputArguments != null) {
       
   161                     ps.println("VM Arguments: " + String.join(" ", inputArguments));
       
   162                 }
       
   163                 String cmd = Services.getSavedProperties().get("sun.java.command");
       
   164                 if (cmd != null) {
       
   165                     ps.println("sun.java.command=" + cmd);
       
   166                 }
   113             }
   167             }
   114 
   168 
   115             @Override
   169             @Override
   116             public void write(byte[] b, int off, int len) throws IOException {
   170             public void write(byte[] b, int off, int len) throws IOException {
   117                 lazy().write(b, off, len);
   171                 lazy().write(b, off, len);
   135 
   189 
   136         /**
   190         /**
   137          * Gets the print stream configured by this option. If no file is configured, the print
   191          * Gets the print stream configured by this option. If no file is configured, the print
   138          * stream will output to HotSpot's {@link HotSpotJVMCIRuntime#getLogStream() log} stream.
   192          * stream will output to HotSpot's {@link HotSpotJVMCIRuntime#getLogStream() log} stream.
   139          */
   193          */
   140         @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "false positive on dead store to `ps`")
   194         public PrintStream getStream() {
   141         public PrintStream getStream(OptionValues options) {
   195             return new PrintStream(new DelayedOutputStream());
   142             String nameTemplate = getValue(options);
       
   143             if (nameTemplate != null) {
       
   144                 String name = makeFilename(nameTemplate);
       
   145                 try {
       
   146                     final boolean enableAutoflush = true;
       
   147                     PrintStream ps = new PrintStream(new FileOutputStream(name), enableAutoflush);
       
   148                     /*
       
   149                      * Add the JVM and Java arguments to the log file to help identity it.
       
   150                      */
       
   151                     List<String> inputArguments = GraalServices.getInputArguments();
       
   152                     if (inputArguments != null) {
       
   153                         ps.println("VM Arguments: " + String.join(" ", inputArguments));
       
   154                     }
       
   155                     String cmd = Services.getSavedProperties().get("sun.java.command");
       
   156                     if (cmd != null) {
       
   157                         ps.println("sun.java.command=" + cmd);
       
   158                     }
       
   159                     return ps;
       
   160                 } catch (FileNotFoundException e) {
       
   161                     throw new RuntimeException("couldn't open file: " + name, e);
       
   162                 }
       
   163             } else {
       
   164                 return new PrintStream(new DelayedOutputStream());
       
   165             }
       
   166         }
   196         }
   167     }
   197     }
   168 
   198 
   169 }
   199 }