VirtualMachineImpl.java revision 10551:521ee1e45c45
111695Sache/*
211695Sache * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
311695Sache * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
411695Sache *
511695Sache * This code is free software; you can redistribute it and/or modify it
611695Sache * under the terms of the GNU General Public License version 2 only, as
711695Sache * published by the Free Software Foundation.  Oracle designates this
8227753Stheraven * particular file as subject to the "Classpath" exception as provided
9227753Stheraven * by Oracle in the LICENSE file that accompanied this code.
10227753Stheraven *
11227753Stheraven * This code is distributed in the hope that it will be useful, but WITHOUT
12227753Stheraven * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1311695Sache * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1411695Sache * version 2 for more details (a copy is included in the LICENSE file that
1511695Sache * accompanied this code).
1611695Sache *
1711695Sache * You should have received a copy of the GNU General Public License version
1811695Sache * 2 along with this work; if not, write to the Free Software Foundation,
1911695Sache * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2011695Sache *
2111695Sache * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2211695Sache * or visit www.oracle.com if you need additional information or have any
2311695Sache * questions.
2411695Sache */
2511695Sachepackage sun.tools.attach;
2611695Sache
2711695Sacheimport com.sun.tools.attach.AttachOperationFailedException;
2811695Sacheimport com.sun.tools.attach.AgentLoadException;
2911695Sacheimport com.sun.tools.attach.AttachNotSupportedException;
3011695Sacheimport com.sun.tools.attach.spi.AttachProvider;
3111695Sache
3211695Sacheimport java.io.InputStream;
3311695Sacheimport java.io.IOException;
3411695Sacheimport java.io.File;
3511695Sacheimport java.io.FileNotFoundException;
3611695Sache
3711695Sache/*
3892986Sobrien * Solaris implementation of HotSpotVirtualMachine.
3992986Sobrien */
4092986Sobrienpublic class VirtualMachineImpl extends HotSpotVirtualMachine {
41150065Sstefanf    // "/tmp" is used as a global well-known location for the files
423050Sache    // .java_pid<pid>. and .attach_pid<pid>. It is important that this
43132820Stjr    // location is the same for all processes, otherwise the tools
44227753Stheraven    // will not be able to find all Hotspot processes.
45227753Stheraven    // Any changes to this needs to be synchronized with HotSpot.
463050Sache    private static final String tmpdir = "/tmp";
47102227Smike
48227753Stheraven    // door descriptor;
49102227Smike    private int fd = -1;
50227753Stheraven
513050Sache    /**
52129065Stjr     * Attaches to the target VM
53227753Stheraven     */
54235239Sdim    VirtualMachineImpl(AttachProvider provider, String vmid)
55129065Stjr        throws AttachNotSupportedException, IOException
563050Sache    {
5714812Sache        super(provider, vmid);
5814812Sache        // This provider only understands process-ids (pids).
5914812Sache        int pid;
60129065Stjr        try {
61130961Stjr            pid = Integer.parseInt(vmid);
62130961Stjr        } catch (NumberFormatException x) {
63129065Stjr            throw new AttachNotSupportedException("invalid process identifier");
64130961Stjr        }
65227753Stheraven
66130961Stjr        // Opens the door file to the target VM. If the file is not
67227753Stheraven        // found it might mean that the attach mechanism isn't started in the
68130961Stjr        // target VM so we attempt to start it and retry.
69129065Stjr        try {
70129065Stjr            fd = openDoor(pid);
71129065Stjr        } catch (FileNotFoundException fnf1) {
723050Sache            File f = createAttachFile(pid);
7361218Sache            try {
743050Sache                // kill -QUIT will tickle target VM to check for the
753050Sache                // attach file.
76227753Stheraven                sigquit(pid);
77227753Stheraven
78227753Stheraven                // give the target VM time to start the attach mechanism
79227753Stheraven                int i = 0;
80227753Stheraven                long delay = 200;
81227753Stheraven                int retries = (int)(attachTimeout() / delay);
82                do {
83                    try {
84                        Thread.sleep(delay);
85                    } catch (InterruptedException x) { }
86                    try {
87                        fd = openDoor(pid);
88                    } catch (FileNotFoundException fnf2) { }
89                    i++;
90                } while (i <= retries && fd == -1);
91                if (fd == -1) {
92                    throw new AttachNotSupportedException(
93                        "Unable to open door: target process not responding or " +
94                        "HotSpot VM not loaded");
95                }
96            } finally {
97                f.delete();
98            }
99        }
100        assert fd >= 0;
101    }
102
103    /**
104     * Detach from the target VM
105     */
106    public void detach() throws IOException {
107        synchronized (this) {
108            if (fd != -1) {
109                close(fd);
110                fd = -1;
111            }
112        }
113    }
114
115    /**
116     * Execute the given command in the target VM.
117     */
118    InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {
119        assert args.length <= 3;                // includes null
120
121        // first check that we are still attached
122        int door;
123        synchronized (this) {
124            if (fd == -1) {
125                throw new IOException("Detached from target VM");
126            }
127            door = fd;
128        }
129
130        // enqueue the command via a door call
131        int s = enqueue(door, cmd, args);
132        assert s >= 0;                          // valid file descriptor
133
134        // The door call returns a file descriptor (one end of a socket pair).
135        // Create an input stream around it.
136        SocketInputStream sis = new SocketInputStream(s);
137
138        // Read the command completion status
139        int completionStatus;
140        try {
141            completionStatus = readInt(sis);
142        } catch (IOException ioe) {
143            sis.close();
144            throw ioe;
145        }
146
147        // If non-0 it means an error but we need to special-case the
148        // "load" command to ensure that the right exception is thrown.
149        if (completionStatus != 0) {
150            // read from the stream and use that as the error message
151            String message = readErrorMessage(sis);
152            sis.close();
153            if (cmd.equals("load")) {
154                throw new AgentLoadException("Failed to load agent library");
155            } else {
156                if (message == null) {
157                    throw new AttachOperationFailedException("Command failed in target VM");
158                } else {
159                    throw new AttachOperationFailedException(message);
160                }
161            }
162        }
163
164        // Return the input stream so that the command output can be read
165        return sis;
166    }
167
168    // InputStream over a socket
169    private class SocketInputStream extends InputStream {
170        int s;
171
172        public SocketInputStream(int s) {
173            this.s = s;
174        }
175
176        public synchronized int read() throws IOException {
177            byte b[] = new byte[1];
178            int n = this.read(b, 0, 1);
179            if (n == 1) {
180                return b[0] & 0xff;
181            } else {
182                return -1;
183            }
184        }
185
186        public synchronized int read(byte[] bs, int off, int len) throws IOException {
187            if ((off < 0) || (off > bs.length) || (len < 0) ||
188                ((off + len) > bs.length) || ((off + len) < 0)) {
189                throw new IndexOutOfBoundsException();
190            } else if (len == 0)
191                return 0;
192
193            return VirtualMachineImpl.read(s, bs, off, len);
194        }
195
196        public void close() throws IOException {
197            VirtualMachineImpl.close(s);
198        }
199    }
200
201    // The door is attached to .java_pid<pid> in the temporary directory.
202    private int openDoor(int pid) throws IOException {
203        String path = tmpdir + "/.java_pid" + pid;;
204        fd = open(path);
205
206        // Check that the file owner/permission to avoid attaching to
207        // bogus process
208        try {
209            checkPermissions(path);
210        } catch (IOException ioe) {
211            close(fd);
212            throw ioe;
213        }
214        return fd;
215    }
216
217    // On Solaris/Linux a simple handshake is used to start the attach mechanism
218    // if not already started. The client creates a .attach_pid<pid> file in the
219    // target VM's working directory (or temporary directory), and the SIGQUIT
220    // handler checks for the file.
221    private File createAttachFile(int pid) throws IOException {
222        String fn = ".attach_pid" + pid;
223        String path = "/proc/" + pid + "/cwd/" + fn;
224        File f = new File(path);
225        try {
226            f.createNewFile();
227        } catch (IOException x) {
228            f = new File(tmpdir, fn);
229            f.createNewFile();
230        }
231        return f;
232    }
233
234    //-- native methods
235
236    static native int open(String path) throws IOException;
237
238    static native void close(int fd) throws IOException;
239
240    static native int read(int fd, byte buf[], int off, int buflen) throws IOException;
241
242    static native void checkPermissions(String path) throws IOException;
243
244    static native void sigquit(int pid) throws IOException;
245
246    // enqueue a command (and arguments) to the given door
247    static native int enqueue(int fd, String cmd, Object ... args)
248        throws IOException;
249
250    static {
251        System.loadLibrary("attach");
252    }
253}
254