comparison src/os/bsd/dtrace/jvm_dtrace.c @ 4006:436b4a3231bf

7098194: integrate macosx-port changes Summary: Integrate bsd-port/hotspot and macosx-port/hotspot changes as of 2011.09.29. Reviewed-by: kvn, dholmes, never, phh Contributed-by: Christos Zoulas <christos@zoulas.com>, Greg Lewis <glewis@eyesbeyond.com>, Kurt Miller <kurt@intricatesoftware.com>, Alexander Strange <astrange@apple.com>, Mike Swingler <swingler@apple.com>, Roger Hoover <rhoover@apple.com>, Victor Hernandez <vhernandez@apple.com>, Pratik Solanki <psolanki@apple.com>
author dcubed
date Thu, 13 Oct 2011 09:35:42 -0700
parents
children e95fc50106cf
comparison
equal deleted inserted replaced
4005:2ef3386478e6 4006:436b4a3231bf
1 /*
2 * Copyright (c) 2006, 2010, 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 #include <door.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <poll.h>
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <thread.h>
38 #include <unistd.h>
39 #include "jvm_dtrace.h"
40
41 // NOTE: These constants are used in JVM code as well.
42 // KEEP JVM CODE IN SYNC if you are going to change these...
43
44 #define DTRACE_ALLOC_PROBES 0x1
45 #define DTRACE_METHOD_PROBES 0x2
46 #define DTRACE_MONITOR_PROBES 0x4
47 #define DTRACE_ALL_PROBES -1
48
49 // generic error messages
50 #define JVM_ERR_OUT_OF_MEMORY "out of memory (native heap)"
51 #define JVM_ERR_INVALID_PARAM "invalid input parameter(s)"
52 #define JVM_ERR_NULL_PARAM "input paramater is NULL"
53
54 // error messages for attach
55 #define JVM_ERR_CANT_OPEN_DOOR "cannot open door file"
56 #define JVM_ERR_CANT_CREATE_ATTACH_FILE "cannot create attach file"
57 #define JVM_ERR_DOOR_FILE_PERMISSION "door file is not secure"
58 #define JVM_ERR_CANT_SIGNAL "cannot send SIGQUIT to target"
59
60 // error messages for enable probe
61 #define JVM_ERR_DOOR_CMD_SEND "door command send failed"
62 #define JVM_ERR_DOOR_CANT_READ_STATUS "cannot read door command status"
63 #define JVM_ERR_DOOR_CMD_STATUS "door command error status"
64
65 // error message for detach
66 #define JVM_ERR_CANT_CLOSE_DOOR "cannot close door file"
67
68 #define RESTARTABLE(_cmd, _result) do { \
69 do { \
70 _result = _cmd; \
71 } while((_result == -1) && (errno == EINTR)); \
72 } while(0)
73
74 struct _jvm_t {
75 pid_t pid;
76 int door_fd;
77 };
78
79 static int libjvm_dtrace_debug;
80 static void print_debug(const char* fmt,...) {
81 if (libjvm_dtrace_debug) {
82 va_list alist;
83 va_start(alist, fmt);
84 fputs("libjvm_dtrace DEBUG: ", stderr);
85 vfprintf(stderr, fmt, alist);
86 va_end(alist);
87 }
88 }
89
90 /* Key for thread local error message */
91 static thread_key_t jvm_error_key;
92
93 /* init function for this library */
94 static void init_jvm_dtrace() {
95 /* check for env. var for debug mode */
96 libjvm_dtrace_debug = getenv("LIBJVM_DTRACE_DEBUG") != NULL;
97 /* create key for thread local error message */
98 if (thr_keycreate(&jvm_error_key, NULL) != 0) {
99 print_debug("can't create thread_key_t for jvm error key\n");
100 // exit(1); ?
101 }
102 }
103
104 #pragma init(init_jvm_dtrace)
105
106 /* set thread local error message */
107 static void set_jvm_error(const char* msg) {
108 thr_setspecific(jvm_error_key, (void*)msg);
109 }
110
111 /* clear thread local error message */
112 static void clear_jvm_error() {
113 thr_setspecific(jvm_error_key, NULL);
114 }
115
116 /* file handling functions that can handle interrupt */
117
118 static int file_open(const char* path, int flag) {
119 int ret;
120 RESTARTABLE(open(path, flag), ret);
121 return ret;
122 }
123
124 static int file_close(int fd) {
125 int ret;
126 RESTARTABLE(close(fd), ret);
127 return ret;
128 }
129
130 static int file_read(int fd, char* buf, int len) {
131 int ret;
132 RESTARTABLE(read(fd, buf, len), ret);
133 return ret;
134 }
135
136 /* send SIGQUIT signal to given process */
137 static int send_sigquit(pid_t pid) {
138 int ret;
139 RESTARTABLE(kill(pid, SIGQUIT), ret);
140 return ret;
141 }
142
143 /* called to check permissions on attach file */
144 static int check_permission(const char* path) {
145 struct stat64 sb;
146 uid_t uid, gid;
147 int res;
148
149 /*
150 * Check that the path is owned by the effective uid/gid of this
151 * process. Also check that group/other access is not allowed.
152 */
153 uid = geteuid();
154 gid = getegid();
155
156 res = stat64(path, &sb);
157 if (res != 0) {
158 print_debug("stat failed for %s\n", path);
159 return -1;
160 }
161
162 if ((sb.st_uid != uid) || (sb.st_gid != gid) ||
163 ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)) {
164 print_debug("well-known file %s is not secure\n", path);
165 return -1;
166 }
167 return 0;
168 }
169
170 #define ATTACH_FILE_PATTERN "/tmp/.attach_pid%d"
171
172 /* fill-in the name of attach file name in given buffer */
173 static void fill_attach_file_name(char* path, int len, pid_t pid) {
174 memset(path, 0, len);
175 sprintf(path, ATTACH_FILE_PATTERN, pid);
176 }
177
178 #define DOOR_FILE_PATTERN "/tmp/.java_pid%d"
179
180 /* open door file for the given JVM */
181 static int open_door(pid_t pid) {
182 char path[PATH_MAX + 1];
183 int fd;
184
185 sprintf(path, DOOR_FILE_PATTERN, pid);
186 fd = file_open(path, O_RDONLY);
187 if (fd < 0) {
188 set_jvm_error(JVM_ERR_CANT_OPEN_DOOR);
189 print_debug("cannot open door file %s\n", path);
190 return -1;
191 }
192 print_debug("opened door file %s\n", path);
193 if (check_permission(path) != 0) {
194 set_jvm_error(JVM_ERR_DOOR_FILE_PERMISSION);
195 print_debug("check permission failed for %s\n", path);
196 file_close(fd);
197 fd = -1;
198 }
199 return fd;
200 }
201
202 /* create attach file for given process */
203 static int create_attach_file(pid_t pid) {
204 char path[PATH_MAX + 1];
205 int fd;
206 fill_attach_file_name(path, sizeof(path), pid);
207 fd = file_open(path, O_CREAT | O_RDWR);
208 if (fd < 0) {
209 set_jvm_error(JVM_ERR_CANT_CREATE_ATTACH_FILE);
210 print_debug("cannot create file %s\n", path);
211 } else {
212 print_debug("created attach file %s\n", path);
213 }
214 return fd;
215 }
216
217 /* delete attach file for given process */
218 static void delete_attach_file(pid_t pid) {
219 char path[PATH_MAX + 1];
220 fill_attach_file_name(path, sizeof(path), pid);
221 int res = unlink(path);
222 if (res) {
223 print_debug("cannot delete attach file %s\n", path);
224 } else {
225 print_debug("deleted attach file %s\n", path);
226 }
227 }
228
229 /* attach to given JVM */
230 jvm_t* jvm_attach(pid_t pid) {
231 jvm_t* jvm;
232 int door_fd, attach_fd, i;
233
234 jvm = (jvm_t*) calloc(1, sizeof(jvm_t));
235 if (jvm == NULL) {
236 set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
237 print_debug("calloc failed in %s at %d\n", __FILE__, __LINE__);
238 return NULL;
239 }
240 jvm->pid = pid;
241 attach_fd = -1;
242
243 door_fd = open_door(pid);
244 if (door_fd < 0) {
245 print_debug("trying to create attach file\n");
246 if ((attach_fd = create_attach_file(pid)) < 0) {
247 goto quit;
248 }
249
250 /* send QUIT signal to the target so that it will
251 * check for the attach file.
252 */
253 if (send_sigquit(pid) != 0) {
254 set_jvm_error(JVM_ERR_CANT_SIGNAL);
255 print_debug("sending SIGQUIT failed\n");
256 goto quit;
257 }
258
259 /* give the target VM time to start the attach mechanism */
260 do {
261 int res;
262 RESTARTABLE(poll(0, 0, 200), res);
263 door_fd = open_door(pid);
264 i++;
265 } while (i <= 50 && door_fd == -1);
266 if (door_fd < 0) {
267 print_debug("Unable to open door to process %d\n", pid);
268 goto quit;
269 }
270 }
271
272 quit:
273 if (attach_fd >= 0) {
274 file_close(attach_fd);
275 delete_attach_file(jvm->pid);
276 }
277 if (door_fd >= 0) {
278 jvm->door_fd = door_fd;
279 clear_jvm_error();
280 } else {
281 free(jvm);
282 jvm = NULL;
283 }
284 return jvm;
285 }
286
287 /* return the last thread local error message */
288 const char* jvm_get_last_error() {
289 const char* res = NULL;
290 thr_getspecific(jvm_error_key, (void**)&res);
291 return res;
292 }
293
294 /* detach the givenb JVM */
295 int jvm_detach(jvm_t* jvm) {
296 if (jvm) {
297 int res;
298 if (jvm->door_fd != -1) {
299 if (file_close(jvm->door_fd) != 0) {
300 set_jvm_error(JVM_ERR_CANT_CLOSE_DOOR);
301 res = -1;
302 } else {
303 clear_jvm_error();
304 res = 0;
305 }
306 }
307 free(jvm);
308 return res;
309 } else {
310 set_jvm_error(JVM_ERR_NULL_PARAM);
311 print_debug("jvm_t* is NULL\n");
312 return -1;
313 }
314 }
315
316 /*
317 * A simple table to translate some known errors into reasonable
318 * error messages
319 */
320 static struct {
321 int err;
322 const char* msg;
323 } const error_messages[] = {
324 { 100, "Bad request" },
325 { 101, "Protocol mismatch" },
326 { 102, "Resource failure" },
327 { 103, "Internal error" },
328 { 104, "Permission denied" },
329 };
330
331 /*
332 * Lookup the given error code and return the appropriate
333 * message. If not found return NULL.
334 */
335 static const char* translate_error(int err) {
336 int table_size = sizeof(error_messages) / sizeof(error_messages[0]);
337 int i;
338
339 for (i=0; i<table_size; i++) {
340 if (err == error_messages[i].err) {
341 return error_messages[i].msg;
342 }
343 }
344 return NULL;
345 }
346
347 /*
348 * Current protocol version
349 */
350 static const char* PROTOCOL_VERSION = "1";
351
352 #define RES_BUF_SIZE 128
353
354 /*
355 * Enqueue attach-on-demand command to the given JVM
356 */
357 static
358 int enqueue_command(jvm_t* jvm, const char* cstr, int arg_count, const char** args) {
359 size_t size;
360 door_arg_t door_args;
361 char res_buffer[RES_BUF_SIZE];
362 int rc, i;
363 char* buf = NULL;
364 int result = -1;
365
366 /*
367 * First we get the command string and create the start of the
368 * argument string to send to the target VM:
369 * <ver>\0<cmd>\0
370 */
371 if (cstr == NULL) {
372 print_debug("command name is NULL\n");
373 goto quit;
374 }
375 size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;
376 buf = (char*)malloc(size);
377 if (buf != NULL) {
378 char* pos = buf;
379 strcpy(buf, PROTOCOL_VERSION);
380 pos += strlen(PROTOCOL_VERSION)+1;
381 strcpy(pos, cstr);
382 } else {
383 set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
384 print_debug("malloc failed at %d in %s\n", __LINE__, __FILE__);
385 goto quit;
386 }
387
388 /*
389 * Next we iterate over the arguments and extend the buffer
390 * to include them.
391 */
392 for (i=0; i<arg_count; i++) {
393 cstr = args[i];
394 if (cstr != NULL) {
395 size_t len = strlen(cstr);
396 char* newbuf = (char*)realloc(buf, size+len+1);
397 if (newbuf == NULL) {
398 set_jvm_error(JVM_ERR_OUT_OF_MEMORY);
399 print_debug("realloc failed in %s at %d\n", __FILE__, __LINE__);
400 goto quit;
401 }
402 buf = newbuf;
403 strcpy(buf+size, cstr);
404 size += len+1;
405 }
406 }
407
408 /*
409 * The arguments to the door function are in 'buf' so we now
410 * do the door call
411 */
412 door_args.data_ptr = buf;
413 door_args.data_size = size;
414 door_args.desc_ptr = NULL;
415 door_args.desc_num = 0;
416 door_args.rbuf = (char*)&res_buffer;
417 door_args.rsize = sizeof(res_buffer);
418
419 RESTARTABLE(door_call(jvm->door_fd, &door_args), rc);
420
421 /*
422 * door_call failed
423 */
424 if (rc == -1) {
425 print_debug("door_call failed\n");
426 } else {
427 /*
428 * door_call succeeded but the call didn't return the the expected jint.
429 */
430 if (door_args.data_size < sizeof(int)) {
431 print_debug("Enqueue error - reason unknown as result is truncated!");
432 } else {
433 int* res = (int*)(door_args.data_ptr);
434 if (*res != 0) {
435 const char* msg = translate_error(*res);
436 if (msg == NULL) {
437 print_debug("Unable to enqueue command to target VM: %d\n", *res);
438 } else {
439 print_debug("Unable to enqueue command to target VM: %s\n", msg);
440 }
441 } else {
442 /*
443 * The door call should return a file descriptor to one end of
444 * a socket pair
445 */
446 if ((door_args.desc_ptr != NULL) &&
447 (door_args.desc_num == 1) &&
448 (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {
449 result = door_args.desc_ptr->d_data.d_desc.d_descriptor;
450 } else {
451 print_debug("Reply from enqueue missing descriptor!\n");
452 }
453 }
454 }
455 }
456
457 quit:
458 if (buf) free(buf);
459 return result;
460 }
461
462 /* read status code for a door command */
463 static int read_status(int fd) {
464 char ch, buf[16];
465 int index = 0;
466
467 while (1) {
468 if (file_read(fd, &ch, sizeof(ch)) != sizeof(ch)) {
469 set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);
470 print_debug("door cmd status: read status failed\n");
471 return -1;
472 }
473 buf[index++] = ch;
474 if (ch == '\n') {
475 buf[index - 1] = '\0';
476 return atoi(buf);
477 }
478 if (index == sizeof(buf)) {
479 set_jvm_error(JVM_ERR_DOOR_CANT_READ_STATUS);
480 print_debug("door cmd status: read status overflow\n");
481 return -1;
482 }
483 }
484 }
485
486 static const char* ENABLE_DPROBES_CMD = "enabledprobes";
487
488 /* enable one or more DTrace probes for a given JVM */
489 int jvm_enable_dtprobes(jvm_t* jvm, int num_probe_types, const char** probe_types) {
490 int fd, status = 0;
491 char ch;
492 const char* args[1];
493 char buf[16];
494 int probe_type = 0, index;
495 int count = 0;
496
497 if (jvm == NULL) {
498 set_jvm_error(JVM_ERR_NULL_PARAM);
499 print_debug("jvm_t* is NULL\n");
500 return -1;
501 }
502
503 if (num_probe_types == 0 || probe_types == NULL ||
504 probe_types[0] == NULL) {
505 set_jvm_error(JVM_ERR_INVALID_PARAM);
506 print_debug("invalid probe type argument(s)\n");
507 return -1;
508 }
509
510 for (index = 0; index < num_probe_types; index++) {
511 const char* p = probe_types[index];
512 if (strcmp(p, JVM_DTPROBE_OBJECT_ALLOC) == 0) {
513 probe_type |= DTRACE_ALLOC_PROBES;
514 count++;
515 } else if (strcmp(p, JVM_DTPROBE_METHOD_ENTRY) == 0 ||
516 strcmp(p, JVM_DTPROBE_METHOD_RETURN) == 0) {
517 probe_type |= DTRACE_METHOD_PROBES;
518 count++;
519 } else if (strcmp(p, JVM_DTPROBE_MONITOR_ENTER) == 0 ||
520 strcmp(p, JVM_DTPROBE_MONITOR_ENTERED) == 0 ||
521 strcmp(p, JVM_DTPROBE_MONITOR_EXIT) == 0 ||
522 strcmp(p, JVM_DTPROBE_MONITOR_WAIT) == 0 ||
523 strcmp(p, JVM_DTPROBE_MONITOR_WAITED) == 0 ||
524 strcmp(p, JVM_DTPROBE_MONITOR_NOTIFY) == 0 ||
525 strcmp(p, JVM_DTPROBE_MONITOR_NOTIFYALL) == 0) {
526 probe_type |= DTRACE_MONITOR_PROBES;
527 count++;
528 } else if (strcmp(p, JVM_DTPROBE_ALL) == 0) {
529 probe_type |= DTRACE_ALL_PROBES;
530 count++;
531 }
532 }
533
534 if (count == 0) {
535 return count;
536 }
537 sprintf(buf, "%d", probe_type);
538 args[0] = buf;
539
540 fd = enqueue_command(jvm, ENABLE_DPROBES_CMD, 1, args);
541 if (fd < 0) {
542 set_jvm_error(JVM_ERR_DOOR_CMD_SEND);
543 return -1;
544 }
545
546 status = read_status(fd);
547 // non-zero status is error
548 if (status) {
549 set_jvm_error(JVM_ERR_DOOR_CMD_STATUS);
550 print_debug("%s command failed (status: %d) in target JVM\n",
551 ENABLE_DPROBES_CMD, status);
552 file_close(fd);
553 return -1;
554 }
555 // read from stream until EOF
556 while (file_read(fd, &ch, sizeof(ch)) == sizeof(ch)) {
557 if (libjvm_dtrace_debug) {
558 printf("%c", ch);
559 }
560 }
561
562 file_close(fd);
563 clear_jvm_error();
564 return count;
565 }