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 } |