1 /* 2 * Copyright (c) 2017, 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.debug; 26 27 import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost; 28 import static org.graalvm.compiler.debug.DebugOptions.PrintGraphPort; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.io.InterruptedIOException; 33 import java.net.InetSocketAddress; 34 import java.nio.ByteBuffer; 35 import java.nio.channels.ClosedByInterruptException; 36 import java.nio.channels.FileChannel; 37 import java.nio.channels.SocketChannel; 38 import java.nio.channels.WritableByteChannel; 39 import java.nio.file.Path; 40 import java.nio.file.StandardOpenOption; 41 import java.util.function.Supplier; 42 43 import org.graalvm.compiler.debug.DebugOptions.PrintGraphTarget; 44 import org.graalvm.compiler.options.OptionValues; 45 46 import jdk.vm.ci.common.NativeImageReinitialize; 47 48 final class IgvDumpChannel implements WritableByteChannel { 49 private final Supplier<Path> pathProvider; 50 private final OptionValues options; 51 private WritableByteChannel sharedChannel; 52 private boolean closed; 53 54 IgvDumpChannel(Supplier<Path> pathProvider, OptionValues options) { 55 this.pathProvider = pathProvider; 56 this.options = options; 57 } 58 59 @Override 60 public int write(ByteBuffer src) throws IOException { 61 WritableByteChannel channel = channel(); 62 return channel == null ? 0 : channel.write(src); 63 } 64 65 @Override 66 public boolean isOpen() { 67 return !closed; 68 } 69 70 @Override 71 public void close() throws IOException { 72 } 73 74 void realClose() throws IOException { 75 closed = true; 76 if (sharedChannel != null) { 77 sharedChannel.close(); 78 sharedChannel = null; 79 } 80 } 81 82 WritableByteChannel channel() throws IOException { 83 if (closed) { 84 throw new IOException(); 85 } 86 if (sharedChannel == null) { 87 PrintGraphTarget target = DebugOptions.PrintGraph.getValue(options); 88 if (target == PrintGraphTarget.File) { 89 sharedChannel = createFileChannel(pathProvider, null); 90 } else if (target == PrintGraphTarget.Network) { 91 sharedChannel = createNetworkChannel(pathProvider, options); 92 } else { 93 TTY.println("WARNING: Graph dumping requested but value of %s option is %s", DebugOptions.PrintGraph.getName(), PrintGraphTarget.Disable); 94 } 95 } 96 return sharedChannel; 97 } 98 99 private static WritableByteChannel createNetworkChannel(Supplier<Path> pathProvider, OptionValues options) throws IOException { 100 String host = PrintGraphHost.getValue(options); 101 int port = PrintGraphPort.getValue(options); 102 try { 103 WritableByteChannel channel = SocketChannel.open(new InetSocketAddress(host, port)); 104 String targetAnnouncement = String.format("Connected to the IGV on %s:%d", host, port); 105 maybeAnnounceTarget(targetAnnouncement); 106 return channel; 107 } catch (ClosedByInterruptException | InterruptedIOException e) { 108 /* 109 * Interrupts should not count as errors because they may be caused by a cancelled Graal 110 * compilation. ClosedByInterruptException occurs if the SocketChannel could not be 111 * opened. InterruptedIOException occurs if new Socket(..) was interrupted. 112 */ 113 return null; 114 } catch (IOException e) { 115 String networkFailure = String.format("Could not connect to the IGV on %s:%d", host, port); 116 if (pathProvider != null) { 117 return createFileChannel(pathProvider, networkFailure); 118 } else { 119 throw new IOException(networkFailure, e); 120 } 121 } 122 } 123 124 @NativeImageReinitialize private static String lastTargetAnnouncement; 125 126 private static void maybeAnnounceTarget(String targetAnnouncement) { 127 if (!targetAnnouncement.equals(lastTargetAnnouncement)) { 128 // Ignore races - an extra announcement is ok 129 lastTargetAnnouncement = targetAnnouncement; 130 TTY.println(targetAnnouncement); 131 } 132 } 133 134 private static WritableByteChannel createFileChannel(Supplier<Path> pathProvider, String networkFailure) throws IOException { 135 Path path = pathProvider.get(); 136 try { 137 FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE); 138 File dir = path.toFile(); 139 if (!dir.isDirectory()) { 140 dir = dir.getParentFile(); 141 } 142 if (networkFailure == null) { 143 maybeAnnounceTarget("Dumping IGV graphs in " + dir); 144 } else { 145 maybeAnnounceTarget(networkFailure + ". Dumping IGV graphs in " + dir); 146 } 147 return channel; 148 } catch (IOException e) { 149 throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e); 150 } 151 } 152 153 }