Mercurial > hg > truffle
annotate src/os/solaris/dtrace/jvm_dtrace.c @ 4582:b24386206122
Made all vm builds go into subdirectories, even product builds to simplify building the various types of VMs (server, client and graal).
Made HotSpot build jobs use the number of CPUs on the host machine.
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Mon, 13 Feb 2012 23:13:37 +0100 |
parents | f95d63e2154a |
children | e95fc50106cf |
rev | line source |
---|---|
0 | 1 /* |
1972 | 2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. |
0 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
21 * questions. |
0 | 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 } |