Path: blob/master/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java
40948 views
/*1* Copyright (c) 2005, 2019, 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*/24package sun.tools.attach;2526import com.sun.tools.attach.AttachOperationFailedException;27import com.sun.tools.attach.AgentLoadException;28import com.sun.tools.attach.AttachNotSupportedException;29import com.sun.tools.attach.spi.AttachProvider;3031import java.io.InputStream;32import java.io.IOException;33import java.io.File;3435/*36* Bsd implementation of HotSpotVirtualMachine37*/38public class VirtualMachineImpl extends HotSpotVirtualMachine {39// "tmpdir" is used as a global well-known location for the files40// .java_pid<pid>. and .attach_pid<pid>. It is important that this41// location is the same for all processes, otherwise the tools42// will not be able to find all Hotspot processes.43// This is intentionally not the same as java.io.tmpdir, since44// the latter can be changed by the user.45// Any changes to this needs to be synchronized with HotSpot.46private static final String tmpdir;47String socket_path;4849/**50* Attaches to the target VM51*/52VirtualMachineImpl(AttachProvider provider, String vmid)53throws AttachNotSupportedException, IOException54{55super(provider, vmid);5657// This provider only understands pids58int pid;59try {60pid = Integer.parseInt(vmid);61if (pid < 1) {62throw new NumberFormatException();63}64} catch (NumberFormatException x) {65throw new AttachNotSupportedException("Invalid process identifier: " + vmid);66}6768// Find the socket file. If not found then we attempt to start the69// attach mechanism in the target VM by sending it a QUIT signal.70// Then we attempt to find the socket file again.71File socket_file = new File(tmpdir, ".java_pid" + pid);72socket_path = socket_file.getPath();73if (!socket_file.exists()) {74File f = createAttachFile(pid);75try {76sendQuitTo(pid);7778// give the target VM time to start the attach mechanism79final int delay_step = 100;80final long timeout = attachTimeout();81long time_spend = 0;82long delay = 0;83do {84// Increase timeout on each attempt to reduce polling85delay += delay_step;86try {87Thread.sleep(delay);88} catch (InterruptedException x) { }8990time_spend += delay;91if (time_spend > timeout/2 && !socket_file.exists()) {92// Send QUIT again to give target VM the last chance to react93sendQuitTo(pid);94}95} while (time_spend <= timeout && !socket_file.exists());96if (!socket_file.exists()) {97throw new AttachNotSupportedException(98String.format("Unable to open socket file %s: " +99"target process %d doesn't respond within %dms " +100"or HotSpot VM not loaded", socket_path,101pid, time_spend));102}103} finally {104f.delete();105}106}107108// Check that the file owner/permission to avoid attaching to109// bogus process110checkPermissions(socket_path);111112// Check that we can connect to the process113// - this ensures we throw the permission denied error now rather than114// later when we attempt to enqueue a command.115int s = socket();116try {117connect(s, socket_path);118} finally {119close(s);120}121}122123/**124* Detach from the target VM125*/126public void detach() throws IOException {127synchronized (this) {128if (socket_path != null) {129socket_path = null;130}131}132}133134// protocol version135private final static String PROTOCOL_VERSION = "1";136137// known errors138private final static int ATTACH_ERROR_BADVERSION = 101;139140/**141* Execute the given command in the target VM.142*/143InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {144assert args.length <= 3; // includes null145146// did we detach?147synchronized (this) {148if (socket_path == null) {149throw new IOException("Detached from target VM");150}151}152153// create UNIX socket154int s = socket();155156// connect to target VM157try {158connect(s, socket_path);159} catch (IOException x) {160close(s);161throw x;162}163164IOException ioe = null;165166// connected - write request167// <ver> <cmd> <args...>168try {169writeString(s, PROTOCOL_VERSION);170writeString(s, cmd);171172for (int i = 0; i < 3; i++) {173if (i < args.length && args[i] != null) {174writeString(s, (String)args[i]);175} else {176writeString(s, "");177}178}179} catch (IOException x) {180ioe = x;181}182183184// Create an input stream to read reply185SocketInputStream sis = new SocketInputStream(s);186187// Read the command completion status188int completionStatus;189try {190completionStatus = readInt(sis);191} catch (IOException x) {192sis.close();193if (ioe != null) {194throw ioe;195} else {196throw x;197}198}199200if (completionStatus != 0) {201// read from the stream and use that as the error message202String message = readErrorMessage(sis);203sis.close();204205// In the event of a protocol mismatch then the target VM206// returns a known error so that we can throw a reasonable207// error.208if (completionStatus == ATTACH_ERROR_BADVERSION) {209throw new IOException("Protocol mismatch with target VM");210}211212// Special-case the "load" command so that the right exception is213// thrown.214if (cmd.equals("load")) {215String msg = "Failed to load agent library";216if (!message.isEmpty())217msg += ": " + message;218throw new AgentLoadException(msg);219} else {220if (message.isEmpty())221message = "Command failed in target VM";222throw new AttachOperationFailedException(message);223}224}225226// Return the input stream so that the command output can be read227return sis;228}229230/*231* InputStream for the socket connection to get target VM232*/233private class SocketInputStream extends InputStream {234int s;235236public SocketInputStream(int s) {237this.s = s;238}239240public synchronized int read() throws IOException {241byte b[] = new byte[1];242int n = this.read(b, 0, 1);243if (n == 1) {244return b[0] & 0xff;245} else {246return -1;247}248}249250public synchronized int read(byte[] bs, int off, int len) throws IOException {251if ((off < 0) || (off > bs.length) || (len < 0) ||252((off + len) > bs.length) || ((off + len) < 0)) {253throw new IndexOutOfBoundsException();254} else if (len == 0) {255return 0;256}257258return VirtualMachineImpl.read(s, bs, off, len);259}260261public synchronized void close() throws IOException {262if (s != -1) {263int toClose = s;264s = -1;265VirtualMachineImpl.close(toClose);266}267}268}269270/*271* Write/sends the given to the target VM. String is transmitted in272* UTF-8 encoding.273*/274private void writeString(int fd, String s) throws IOException {275if (s.length() > 0) {276byte b[];277try {278b = s.getBytes("UTF-8");279} catch (java.io.UnsupportedEncodingException x) {280throw new InternalError(x);281}282VirtualMachineImpl.write(fd, b, 0, b.length);283}284byte b[] = new byte[1];285b[0] = 0;286write(fd, b, 0, 1);287}288289private File createAttachFile(int pid) throws IOException {290File f = new File(tmpdir, ".attach_pid" + pid);291createAttachFile0(f.getPath());292return f;293}294295//-- native methods296297static native void sendQuitTo(int pid) throws IOException;298299static native void checkPermissions(String path) throws IOException;300301static native int socket() throws IOException;302303static native void connect(int fd, String path) throws IOException;304305static native void close(int fd) throws IOException;306307static native int read(int fd, byte buf[], int off, int bufLen) throws IOException;308309static native void write(int fd, byte buf[], int off, int bufLen) throws IOException;310311static native void createAttachFile0(String path);312313static native String getTempDir();314315static {316System.loadLibrary("attach");317tmpdir = getTempDir();318}319}320321322