jvm_dtrace.c revision 5776:de6a9e811145
1270114Sse/*
2270114Sse * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3270114Sse * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4270114Sse *
5270114Sse * This code is free software; you can redistribute it and/or modify it
6270114Sse * under the terms of the GNU General Public License version 2 only, as
7270114Sse * published by the Free Software Foundation.
8270114Sse *
9270114Sse * This code is distributed in the hope that it will be useful, but WITHOUT
10270114Sse * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11270114Sse * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12270114Sse * version 2 for more details (a copy is included in the LICENSE file that
13270114Sse * accompanied this code).
14270114Sse *
15270114Sse * You should have received a copy of the GNU General Public License version
16270114Sse * 2 along with this work; if not, write to the Free Software Foundation,
17270114Sse * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18322225Sse *
19270114Sse * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20270114Sse * or visit www.oracle.com if you need additional information or have any
21270114Sse * questions.
22270114Sse *
23283164Semaste */
24270114Sse
25270114Sse#include <door.h>
26283164Semaste#include <errno.h>
27270114Sse#include <fcntl.h>
28270114Sse#include <limits.h>
29283164Semaste#include <poll.h>
30283164Semaste#include <signal.h>
31283164Semaste#include <stdarg.h>
32270114Sse#include <stdio.h>
33270114Sse#include <stdlib.h>
34322225Sse#include <string.h>
35270114Sse#include <sys/types.h>
36270114Sse#include <sys/stat.h>
37270114Sse#include <thread.h>
38270114Sse#include <unistd.h>
39270114Sse#include "jvm_dtrace.h"
40270114Sse
41270114Sse// NOTE: These constants are used in JVM code as well.
42270114Sse// KEEP JVM CODE IN SYNC if you are going to change these...
43270114Sse
44270114Sse#define DTRACE_ALLOC_PROBES   0x1
45270114Sse#define DTRACE_METHOD_PROBES  0x2
46270114Sse#define DTRACE_MONITOR_PROBES 0x4
47270114Sse#define DTRACE_ALL_PROBES     -1
48270114Sse
49270114Sse// generic error messages
50270114Sse#define JVM_ERR_OUT_OF_MEMORY            "out of memory (native heap)"
51270114Sse#define JVM_ERR_INVALID_PARAM            "invalid input parameter(s)"
52270114Sse#define JVM_ERR_NULL_PARAM               "input paramater is NULL"
53270114Sse
54270114Sse// error messages for attach
55270114Sse#define JVM_ERR_CANT_OPEN_DOOR           "cannot open door file"
56270114Sse#define JVM_ERR_CANT_CREATE_ATTACH_FILE  "cannot create attach file"
57270114Sse#define JVM_ERR_DOOR_FILE_PERMISSION     "door file is not secure"
58270310Sse#define JVM_ERR_CANT_SIGNAL              "cannot send SIGQUIT to target"
59270310Sse
60270114Sse// error messages for enable probe
61270310Sse#define JVM_ERR_DOOR_CMD_SEND            "door command send failed"
62270310Sse#define JVM_ERR_DOOR_CANT_READ_STATUS    "cannot read door command status"
63270310Sse#define JVM_ERR_DOOR_CMD_STATUS          "door command error status"
64270310Sse
65270310Sse// error message for detach
66270114Sse#define JVM_ERR_CANT_CLOSE_DOOR          "cannot close door file"
67270310Sse
68270310Sse#define RESTARTABLE(_cmd, _result) do { \
69270310Sse    do { \
70270310Sse        _result = _cmd; \
71270310Sse    } while((_result == -1) && (errno == EINTR)); \
72270114Sse} while(0)
73270310Sse
74270310Ssestruct _jvm_t {
75270310Sse    pid_t pid;
76270114Sse    int door_fd;
77270114Sse};
78270114Sse
79270114Ssestatic int libjvm_dtrace_debug;
80270114Ssestatic void print_debug(const char* fmt,...) {
81270114Sse    if (libjvm_dtrace_debug) {
82270310Sse        va_list alist;
83270310Sse        va_start(alist, fmt);
84270310Sse        fputs("libjvm_dtrace DEBUG: ", stderr);
85270310Sse        vfprintf(stderr, fmt, alist);
86270114Sse        va_end(alist);
87270310Sse    }
88270310Sse}
89270114Sse
90270114Sse/* Key for thread local error message */
91270114Ssestatic thread_key_t jvm_error_key;
92270114Sse
93270114Sse/* init function for this library */
94270114Ssestatic void init_jvm_dtrace() {
95270114Sse    /* check for env. var for debug mode */
96270114Sse    libjvm_dtrace_debug = getenv("LIBJVM_DTRACE_DEBUG") != NULL;
97270114Sse    /* create key for thread local error message */
98270114Sse    if (thr_keycreate(&jvm_error_key, NULL) != 0) {
99270310Sse        print_debug("can't create thread_key_t for jvm error key\n");
100270310Sse        // exit(1); ?
101270310Sse    }
102270310Sse}
103270310Sse
104270114Sse#pragma init(init_jvm_dtrace)
105270114Sse
106270114Sse/* set thread local error message */
107270114Ssestatic void set_jvm_error(const char* msg) {
108270114Sse    thr_setspecific(jvm_error_key, (void*)msg);
109270114Sse}
110270114Sse
111270114Sse/* clear thread local error message */
112270114Ssestatic void clear_jvm_error() {
113270114Sse    thr_setspecific(jvm_error_key, NULL);
114270114Sse}
115270114Sse
116270114Sse/* file handling functions that can handle interrupt */
117270114Sse
118270114Ssestatic int file_open(const char* path, int flag) {
119270114Sse    int ret;
120270114Sse    RESTARTABLE(open(path, flag), ret);
121270114Sse    return ret;
122270114Sse}
123270114Sse
124270114Ssestatic int file_close(int fd) {
125283164Semaste    return close(fd);
126270114Sse}
127270114Sse
128270114Ssestatic int file_read(int fd, char* buf, int len) {
129270114Sse    int ret;
130270114Sse    RESTARTABLE(read(fd, buf, len), ret);
131270114Sse    return ret;
132270114Sse}
133270114Sse
134270114Sse/* send SIGQUIT signal to given process */
135270114Ssestatic int send_sigquit(pid_t pid) {
136270114Sse    int ret;
137270114Sse    RESTARTABLE(kill(pid, SIGQUIT), ret);
138270114Sse    return ret;
139270114Sse}
140270114Sse
141270114Sse/* called to check permissions on attach file */
142270114Ssestatic int check_permission(const char* path) {
143270114Sse    struct stat64 sb;
144270114Sse    uid_t uid, gid;
145270114Sse    int res;
146270114Sse
147270114Sse    /*
148270114Sse     * Check that the path is owned by the effective uid/gid of this
149270114Sse     * process. Also check that group/other access is not allowed.
150270114Sse     */
151270114Sse    uid = geteuid();
152270114Sse    gid = getegid();
153270114Sse
154270114Sse    res = stat64(path, &sb);
155270114Sse    if (res != 0) {
156270114Sse        print_debug("stat failed for %s\n", path);
157270114Sse        return -1;
158270114Sse    }
159270114Sse
160270114Sse    if ((sb.st_uid != uid) || (sb.st_gid != gid) ||
161270114Sse        ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)) {
162270114Sse        print_debug("well-known file %s is not secure\n", path);
163270114Sse        return -1;
164270114Sse    }
165270114Sse    return 0;
166270114Sse}
167270114Sse
168270114Sse#define ATTACH_FILE_PATTERN "/tmp/.attach_pid%d"
169270114Sse
170270114Sse/* fill-in the name of attach file name in given buffer */
171270114Ssestatic void fill_attach_file_name(char* path, int len, pid_t pid) {
172270114Sse    memset(path, 0, len);
173270114Sse    sprintf(path, ATTACH_FILE_PATTERN, pid);
174270114Sse}
175270114Sse
176270114Sse#define DOOR_FILE_PATTERN "/tmp/.java_pid%d"
177270114Sse
178270114Sse/* open door file for the given JVM */
179270114Ssestatic int open_door(pid_t pid) {
180270114Sse    char path[PATH_MAX + 1];
181270114Sse    int fd;
182270114Sse
183270114Sse    sprintf(path, DOOR_FILE_PATTERN, pid);
184270114Sse    fd = file_open(path, O_RDONLY);
185270114Sse    if (fd < 0) {
186270114Sse        set_jvm_error(JVM_ERR_CANT_OPEN_DOOR);
187270114Sse        print_debug("cannot open door file %s\n", path);
188270114Sse        return -1;
189270114Sse    }
190270114Sse    print_debug("opened door file %s\n", path);
191270114Sse    if (check_permission(path) != 0) {
192270114Sse        set_jvm_error(JVM_ERR_DOOR_FILE_PERMISSION);
193270114Sse        print_debug("check permission failed for %s\n", path);
194270114Sse        file_close(fd);
195270114Sse        fd = -1;
196270114Sse    }
197270114Sse    return fd;
198270114Sse}
199270114Sse
200270114Sse/* create attach file for given process */
201270310Ssestatic int create_attach_file(pid_t pid) {
202270114Sse    char path[PATH_MAX + 1];
203270310Sse    int fd;
204270310Sse    fill_attach_file_name(path, sizeof(path), pid);
205270310Sse    fd = file_open(path, O_CREAT | O_RDWR);
206270310Sse    if (fd < 0) {
207270310Sse        set_jvm_error(JVM_ERR_CANT_CREATE_ATTACH_FILE);
208270310Sse        print_debug("cannot create file %s\n", path);
209270114Sse    } else {
210270114Sse        print_debug("created attach file %s\n", path);
211270114Sse    }
212270114Sse    return fd;
213270114Sse}
214322225Sse
215270114Sse/* delete attach file for given process */
216270114Ssestatic void delete_attach_file(pid_t pid) {
217270114Sse    char path[PATH_MAX + 1];
218270114Sse    fill_attach_file_name(path, sizeof(path), pid);
219270114Sse    int res = unlink(path);
220270114Sse    if (res) {
221270114Sse        print_debug("cannot delete attach file %s\n", path);
222270114Sse    } else {
223270114Sse        print_debug("deleted attach file %s\n", path);
224270310Sse    }
225270310Sse}
226270310Sse
227270310Sse/* attach to given JVM */
228270310Ssejvm_t* jvm_attach(pid_t pid) {
229270310Sse    jvm_t* jvm;
230270310Sse    int door_fd, attach_fd, i;
231270114Sse
232270114Sse    jvm = (jvm_t*) calloc(1, sizeof(jvm_t));
233270114Sse    if (jvm == NULL) {
234270114Sse        set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
235270114Sse        print_debug("calloc failed in %s at %d\n", __FILE__, __LINE__);
236270114Sse        return NULL;
237270114Sse    }
238270114Sse    jvm->pid = pid;
239270114Sse    attach_fd = -1;
240270114Sse
241270114Sse    door_fd = open_door(pid);
242270114Sse    if (door_fd < 0) {
243270114Sse        print_debug("trying to create attach file\n");
244270114Sse        if ((attach_fd = create_attach_file(pid)) < 0) {
245270114Sse            goto quit;
246270114Sse        }
247270114Sse
248270114Sse        /* send QUIT signal to the target so that it will
249270114Sse         * check for the attach file.
250270114Sse         */
251270114Sse        if (send_sigquit(pid) != 0) {
252270114Sse            set_jvm_error(JVM_ERR_CANT_SIGNAL);
253270114Sse            print_debug("sending SIGQUIT failed\n");
254270114Sse            goto quit;
255270114Sse        }
256270114Sse
257270114Sse        /* give the target VM time to start the attach mechanism */
258270114Sse        do {
259270114Sse            int res;
260270114Sse            RESTARTABLE(poll(0, 0, 200), res);
261270114Sse            door_fd = open_door(pid);
262270114Sse            i++;
263270114Sse        } while (i <= 50 && door_fd == -1);
264270114Sse        if (door_fd < 0) {
265270114Sse            print_debug("Unable to open door to process %d\n", pid);
266270114Sse            goto quit;
267270114Sse        }
268270114Sse    }
269270114Sse
270270114Ssequit:
271270114Sse    if (attach_fd >= 0) {
272270114Sse        file_close(attach_fd);
273270114Sse        delete_attach_file(jvm->pid);
274270114Sse    }
275270114Sse    if (door_fd >= 0) {
276270114Sse        jvm->door_fd = door_fd;
277270114Sse        clear_jvm_error();
278270114Sse    } else {
279270114Sse        free(jvm);
280270114Sse        jvm = NULL;
281270114Sse    }
282270114Sse    return jvm;
283270114Sse}
284270114Sse
285270114Sse/* return the last thread local error message */
286270114Sseconst char* jvm_get_last_error() {
287270114Sse    const char* res = NULL;
288270114Sse    thr_getspecific(jvm_error_key, (void**)&res);
289270114Sse    return res;
290270114Sse}
291270114Sse
292270114Sse/* detach the givenb JVM */
293270114Sseint jvm_detach(jvm_t* jvm) {
294270114Sse    if (jvm) {
295270114Sse        int res;
296270114Sse        if (jvm->door_fd != -1) {
297270114Sse            if (file_close(jvm->door_fd) != 0) {
298270114Sse                set_jvm_error(JVM_ERR_CANT_CLOSE_DOOR);
299270114Sse                res = -1;
300270114Sse            } else {
301270114Sse                clear_jvm_error();
302270114Sse                res = 0;
303270114Sse            }
304270114Sse        }
305270114Sse        free(jvm);
306270114Sse        return res;
307270114Sse    } else {
308270114Sse        set_jvm_error(JVM_ERR_NULL_PARAM);
309270114Sse        print_debug("jvm_t* is NULL\n");
310270114Sse        return -1;
311270114Sse    }
312270114Sse}
313270114Sse
314270114Sse/*
315270114Sse * A simple table to translate some known errors into reasonable
316270114Sse * error messages
317270114Sse */
318270114Ssestatic struct {
319270114Sse    int err;
320270114Sse    const char* msg;
321270114Sse} const error_messages[] = {
322270114Sse    { 100,      "Bad request" },
323270114Sse    { 101,      "Protocol mismatch" },
324270114Sse    { 102,      "Resource failure" },
325270114Sse    { 103,      "Internal error" },
326270114Sse    { 104,      "Permission denied" },
327270114Sse};
328270114Sse
329270114Sse/*
330270114Sse * Lookup the given error code and return the appropriate
331270114Sse * message. If not found return NULL.
332270114Sse */
333270114Ssestatic const char* translate_error(int err) {
334270114Sse    int table_size = sizeof(error_messages) / sizeof(error_messages[0]);
335270114Sse    int i;
336270114Sse
337270114Sse    for (i=0; i<table_size; i++) {
338270114Sse        if (err == error_messages[i].err) {
339270114Sse            return error_messages[i].msg;
340270114Sse        }
341270114Sse    }
342270114Sse    return NULL;
343270114Sse}
344270114Sse
345270114Sse/*
346270114Sse * Current protocol version
347270114Sse */
348270114Ssestatic const char* PROTOCOL_VERSION = "1";
349270114Sse
350270114Sse#define RES_BUF_SIZE 128
351270114Sse
352270114Sse/*
353270114Sse * Enqueue attach-on-demand command to the given JVM
354270114Sse */
355270114Ssestatic
356270114Sseint enqueue_command(jvm_t* jvm, const char* cstr, int arg_count, const char** args) {
357270114Sse    size_t size;
358270114Sse    door_arg_t door_args;
359270114Sse    char res_buffer[RES_BUF_SIZE];
360270114Sse    int rc, i;
361270114Sse    char* buf = NULL;
362270114Sse    int result = -1;
363270114Sse
364270114Sse    /*
365270114Sse     * First we get the command string and create the start of the
366270114Sse     * argument string to send to the target VM:
367270114Sse     * <ver>\0<cmd>\0
368270114Sse     */
369270114Sse    if (cstr == NULL) {
370270114Sse        print_debug("command name is NULL\n");
371270114Sse        goto quit;
372270114Sse    }
373270114Sse    size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;
374270114Sse    buf = (char*)malloc(size);
375270114Sse    if (buf != NULL) {
376270114Sse        char* pos = buf;
377270114Sse        strcpy(buf, PROTOCOL_VERSION);
378270114Sse        pos += strlen(PROTOCOL_VERSION)+1;
379270114Sse        strcpy(pos, cstr);
380270114Sse    } else {
381270114Sse        set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
382270114Sse        print_debug("malloc failed at %d in %s\n", __LINE__, __FILE__);
383270114Sse        goto quit;
384270114Sse    }
385270114Sse
386270114Sse    /*
387270114Sse     * Next we iterate over the arguments and extend the buffer
388270114Sse     * to include them.
389270114Sse     */
390270114Sse    for (i=0; i<arg_count; i++) {
391270114Sse        cstr = args[i];
392270114Sse        if (cstr != NULL) {
393270114Sse            size_t len = strlen(cstr);
394270114Sse            char* newbuf = (char*)realloc(buf, size+len+1);
395270114Sse            if (newbuf == NULL) {
396270114Sse                set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
397270114Sse                print_debug("realloc failed in %s at %d\n", __FILE__, __LINE__);
398270114Sse                goto quit;
399270114Sse            }
400270114Sse            buf = newbuf;
401270114Sse            strcpy(buf+size, cstr);
402270114Sse            size += len+1;
403270114Sse        }
404270114Sse    }
405270114Sse
406270114Sse    /*
407270114Sse     * The arguments to the door function are in 'buf' so we now
408270114Sse     * do the door call
409270114Sse     */
410270114Sse    door_args.data_ptr = buf;
411270114Sse    door_args.data_size = size;
412270114Sse    door_args.desc_ptr = NULL;
413270114Sse    door_args.desc_num = 0;
414270114Sse    door_args.rbuf = (char*)&res_buffer;
415270114Sse    door_args.rsize = sizeof(res_buffer);
416270114Sse
417270114Sse    RESTARTABLE(door_call(jvm->door_fd, &door_args), rc);
418270114Sse
419270114Sse    /*
420270114Sse     * door_call failed
421270114Sse     */
422270114Sse    if (rc == -1) {
423270114Sse        print_debug("door_call failed\n");
424270114Sse    } else {
425270114Sse        /*
426270114Sse         * door_call succeeded but the call didn't return the the expected jint.
427270114Sse         */
428270114Sse        if (door_args.data_size < sizeof(int)) {
429270114Sse            print_debug("Enqueue error - reason unknown as result is truncated!");
430270114Sse        } else {
431270114Sse            int* res = (int*)(door_args.data_ptr);
432270114Sse            if (*res != 0) {
433270114Sse                const char* msg = translate_error(*res);
434270114Sse                if (msg == NULL) {
435270114Sse                    print_debug("Unable to enqueue command to target VM: %d\n", *res);
436270114Sse                } else {
437270114Sse                    print_debug("Unable to enqueue command to target VM: %s\n", msg);
438270114Sse                }
439270114Sse            } else {
440270114Sse                /*
441270114Sse                 * The door call should return a file descriptor to one end of
442270114Sse                 * a socket pair
443270114Sse                 */
444270114Sse                if ((door_args.desc_ptr != NULL) &&
445270114Sse                    (door_args.desc_num == 1) &&
446270114Sse                    (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {
447270114Sse                    result = door_args.desc_ptr->d_data.d_desc.d_descriptor;
448270114Sse                } else {
449270114Sse                    print_debug("Reply from enqueue missing descriptor!\n");
450270114Sse                }
451270114Sse            }
452270114Sse        }
453270114Sse    }
454270114Sse
455270114Ssequit:
456270114Sse    if (buf) free(buf);
457270114Sse    return result;
458270114Sse}
459270114Sse
460270114Sse/* read status code for a door command */
461270114Ssestatic int read_status(int fd) {
462270114Sse    char ch, buf[16];
463270114Sse    int index = 0;
464270114Sse
465270114Sse    while (1) {
466270114Sse        if (file_read(fd, &ch, sizeof(ch)) != sizeof(ch)) {
467270114Sse            set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);
468270114Sse            print_debug("door cmd status: read status failed\n");
469270114Sse            return -1;
470270114Sse        }
471270114Sse        buf[index++] = ch;
472270114Sse        if (ch == '\n') {
473270114Sse            buf[index - 1] = '\0';
474270114Sse            return atoi(buf);
475270114Sse        }
476270114Sse        if (index == sizeof(buf)) {
477270114Sse            set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);
478270114Sse            print_debug("door cmd status: read status overflow\n");
479270114Sse            return -1;
480270114Sse        }
481270114Sse    }
482270114Sse}
483270114Sse
484270114Ssestatic const char* ENABLE_DPROBES_CMD = "enabledprobes";
485270114Sse
486270114Sse/* enable one or more DTrace probes for a given JVM */
487270114Sseint jvm_enable_dtprobes(jvm_t* jvm, int num_probe_types, const char** probe_types) {
488270114Sse    int fd, status = 0;
489270114Sse    char ch;
490270114Sse    const char* args[1];
491270114Sse    char buf[16];
492270114Sse    int probe_type = 0, index;
493270114Sse    int count = 0;
494270114Sse
495270114Sse    if (jvm == NULL) {
496270114Sse        set_jvm_error(JVM_ERR_NULL_PARAM);
497270114Sse        print_debug("jvm_t* is NULL\n");
498270114Sse        return -1;
499270114Sse    }
500270114Sse
501270114Sse    if (num_probe_types == 0 || probe_types == NULL ||
502270114Sse        probe_types[0] == NULL) {
503270114Sse        set_jvm_error(JVM_ERR_INVALID_PARAM);
504270114Sse        print_debug("invalid probe type argument(s)\n");
505270114Sse        return -1;
506270114Sse    }
507322225Sse
508322225Sse    for (index = 0; index < num_probe_types; index++) {
509322225Sse        const char* p = probe_types[index];
510322225Sse        if (strcmp(p, JVM_DTPROBE_OBJECT_ALLOC) == 0) {
511322225Sse            probe_type |= DTRACE_ALLOC_PROBES;
512270114Sse            count++;
513322225Sse        } else if (strcmp(p, JVM_DTPROBE_METHOD_ENTRY) == 0 ||
514322225Sse                   strcmp(p, JVM_DTPROBE_METHOD_RETURN) == 0) {
515322225Sse            probe_type |= DTRACE_METHOD_PROBES;
516322225Sse            count++;
517322225Sse        } else if (strcmp(p, JVM_DTPROBE_MONITOR_ENTER) == 0   ||
518322225Sse                   strcmp(p, JVM_DTPROBE_MONITOR_ENTERED) == 0 ||
519270114Sse                   strcmp(p, JVM_DTPROBE_MONITOR_EXIT) == 0    ||
520270114Sse                   strcmp(p, JVM_DTPROBE_MONITOR_WAIT) == 0    ||
521270114Sse                   strcmp(p, JVM_DTPROBE_MONITOR_WAITED) == 0  ||
522270114Sse                   strcmp(p, JVM_DTPROBE_MONITOR_NOTIFY) == 0  ||
523270114Sse                   strcmp(p, JVM_DTPROBE_MONITOR_NOTIFYALL) == 0) {
524270114Sse            probe_type |= DTRACE_MONITOR_PROBES;
525270114Sse            count++;
526270114Sse        } else if (strcmp(p, JVM_DTPROBE_ALL) == 0) {
527322225Sse            probe_type |= DTRACE_ALL_PROBES;
528322225Sse            count++;
529322225Sse        }
530270114Sse    }
531270114Sse
532270114Sse    if (count == 0) {
533270114Sse        return count;
534270114Sse    }
535270114Sse    sprintf(buf, "%d", probe_type);
536270114Sse    args[0] = buf;
537270114Sse
538270114Sse    fd = enqueue_command(jvm, ENABLE_DPROBES_CMD, 1, args);
539270114Sse    if (fd < 0) {
540270114Sse        set_jvm_error(JVM_ERR_DOOR_CMD_SEND);
541270114Sse        return -1;
542270114Sse    }
543270114Sse
544270114Sse    status = read_status(fd);
545270114Sse    // non-zero status is error
546270114Sse    if (status) {
547270114Sse        set_jvm_error(JVM_ERR_DOOR_CMD_STATUS);
548270114Sse        print_debug("%s command failed (status: %d) in target JVM\n",
549270114Sse                    ENABLE_DPROBES_CMD, status);
550270114Sse        file_close(fd);
551270114Sse        return -1;
552270114Sse    }
553270114Sse    // read from stream until EOF
554270114Sse    while (file_read(fd, &ch, sizeof(ch)) == sizeof(ch)) {
555270114Sse        if (libjvm_dtrace_debug) {
556270114Sse            printf("%c", ch);
557270114Sse        }
558270114Sse    }
559270114Sse
560270114Sse    file_close(fd);
561270114Sse    clear_jvm_error();
562270114Sse    return count;
563270114Sse}
564270114Sse