Fri, 15 Feb 2013 08:54:12 +0100
8008102: SA on OS X does not stop the attached process
Reviewed-by: dholmes, rbackman
1 /*
2 * Copyright (c) 2002, 2012, 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 */
25 #include <objc/objc-runtime.h>
26 #import <Foundation/Foundation.h>
27 #import <JavaNativeFoundation/JavaNativeFoundation.h>
29 #include <JavaVM/jni.h>
31 #import <mach/mach.h>
32 #import <mach/mach_types.h>
33 #import <sys/sysctl.h>
34 #import <stdio.h>
35 #import <stdarg.h>
36 #import <stdlib.h>
37 #import <strings.h>
38 #import <dlfcn.h>
39 #import <limits.h>
40 #import <errno.h>
41 #import <sys/types.h>
42 #import <sys/ptrace.h>
44 jboolean debug = JNI_FALSE;
46 static jfieldID symbolicatorID = 0; // set in _init0
47 static jfieldID taskID = 0; // set in _init0
49 static void putSymbolicator(JNIEnv *env, jobject this_obj, id symbolicator) {
50 (*env)->SetLongField(env, this_obj, symbolicatorID, (jlong)(intptr_t)symbolicator);
51 }
53 static id getSymbolicator(JNIEnv *env, jobject this_obj) {
54 jlong ptr = (*env)->GetLongField(env, this_obj, symbolicatorID);
55 return (id)(intptr_t)ptr;
56 }
58 static void putTask(JNIEnv *env, jobject this_obj, task_t task) {
59 (*env)->SetLongField(env, this_obj, taskID, (jlong)task);
60 }
62 static task_t getTask(JNIEnv *env, jobject this_obj) {
63 jlong ptr = (*env)->GetLongField(env, this_obj, taskID);
64 return (task_t)ptr;
65 }
67 #define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; }
68 #define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;}
69 #define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; }
70 #define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;}
71 #define CHECK_EXCEPTION_CLEAR if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); }
72 #define CHECK_EXCEPTION_CLEAR_VOID if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); return; }
73 #define CHECK_EXCEPTION_CLEAR_(value) if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); return value; }
75 static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) {
76 (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg);
77 }
79 #if defined(__i386__)
80 #define hsdb_thread_state_t x86_thread_state32_t
81 #define hsdb_float_state_t x86_float_state32_t
82 #define HSDB_THREAD_STATE x86_THREAD_STATE32
83 #define HSDB_FLOAT_STATE x86_FLOAT_STATE32
84 #define HSDB_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
85 #define HSDB_FLOAT_STATE_COUNT x86_FLOAT_STATE32_COUNT
86 #elif defined(__x86_64__)
87 #define hsdb_thread_state_t x86_thread_state64_t
88 #define hsdb_float_state_t x86_float_state64_t
89 #define HSDB_THREAD_STATE x86_THREAD_STATE64
90 #define HSDB_FLOAT_STATE x86_FLOAT_STATE64
91 #define HSDB_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
92 #define HSDB_FLOAT_STATE_COUNT x86_FLOAT_STATE64_COUNT
93 #else
94 #error "Unsupported architecture"
95 #endif
97 /*
98 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
99 * Method: init0
100 * Signature: ()V
101 */
102 JNIEXPORT void JNICALL
103 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) {
104 symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J");
105 taskID = (*env)->GetFieldID(env, cls, "task", "J");
106 CHECK_EXCEPTION;
107 }
109 /*
110 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
111 * Method: lookupByName0
112 * Signature: (Ljava/lang/String;Ljava/lang/String;)J
113 */
114 JNIEXPORT jlong JNICALL
115 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0(
116 JNIEnv *env, jobject this_obj,
117 jstring objectName, jstring symbolName)
118 {
119 jlong address = 0;
121 JNF_COCOA_ENTER(env);
122 NSString *symbolNameString = JNFJavaToNSString(env, symbolName);
124 if (debug) {
125 printf("lookupInProcess called for %s\n", [symbolNameString UTF8String]);
126 }
128 id symbolicator = getSymbolicator(env, this_obj);
129 if (symbolicator != nil) {
130 uint64_t (*dynamicCall)(id, SEL, NSString *) = (uint64_t (*)(id, SEL, NSString *))&objc_msgSend;
131 address = (jlong) dynamicCall(symbolicator, @selector(addressForSymbol:), symbolNameString);
132 }
134 if (debug) {
135 printf("address of symbol %s = %llx\n", [symbolNameString UTF8String], address);
136 }
137 JNF_COCOA_EXIT(env);
139 return address;
140 }
142 /*
143 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
144 * Method: readBytesFromProcess0
145 * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult;
146 */
147 JNIEXPORT jbyteArray JNICALL
148 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0(
149 JNIEnv *env, jobject this_obj,
150 jlong addr, jlong numBytes)
151 {
152 if (debug) printf("readBytesFromProcess called. addr = %llx numBytes = %lld\n", addr, numBytes);
154 // must allocate storage instead of using former parameter buf
155 jboolean isCopy;
156 jbyteArray array;
157 jbyte *bufPtr;
159 array = (*env)->NewByteArray(env, numBytes);
160 CHECK_EXCEPTION_(0);
162 unsigned long alignedAddress;
163 unsigned long alignedLength;
164 kern_return_t result;
165 vm_offset_t *pages;
166 int *mapped;
167 long pageCount;
168 uint byteCount;
169 int i;
170 unsigned long remaining;
172 alignedAddress = trunc_page(addr);
173 if (addr != alignedAddress) {
174 alignedLength += addr - alignedAddress;
175 }
176 alignedLength = round_page(numBytes);
177 pageCount = alignedLength/vm_page_size;
179 // Allocate storage for pages and flags.
180 pages = malloc(pageCount * sizeof(vm_offset_t));
181 mapped = calloc(pageCount, sizeof(int));
183 task_t gTask = getTask(env, this_obj);
184 // Try to read each of the pages.
185 for (i = 0; i < pageCount; i++) {
186 result = vm_read(gTask, alignedAddress + i*vm_page_size, vm_page_size,
187 &pages[i], &byteCount);
188 mapped[i] = (result == KERN_SUCCESS);
189 // assume all failures are unmapped pages
190 }
192 if (debug) fprintf(stderr, "%ld pages\n", pageCount);
194 remaining = numBytes;
196 for (i = 0; i < pageCount; i++) {
197 unsigned long len = vm_page_size;
198 unsigned long start = 0;
200 if (i == 0) {
201 start = addr - alignedAddress;
202 len = vm_page_size - start;
203 }
205 if (i == (pageCount - 1)) {
206 len = remaining;
207 }
209 if (mapped[i]) {
210 if (debug) fprintf(stderr, "page %d mapped (len %ld start %ld)\n", i, len, start);
211 (*env)->SetByteArrayRegion(env, array, 0, len, ((jbyte *) pages[i] + start));
212 vm_deallocate(mach_task_self(), pages[i], vm_page_size);
213 }
215 remaining -= len;
216 }
218 free (pages);
219 free (mapped);
220 return array;
221 }
224 /*
225 * Lookup the thread_t that corresponds to the given thread_id.
226 * The thread_id should be the result from calling thread_info() with THREAD_IDENTIFIER_INFO
227 * and reading the m_ident_info.thread_id returned.
228 * The returned thread_t is the mach send right to the kernel port for the corresponding thread.
229 *
230 * We cannot simply use the OSThread._thread_id field in the JVM. This is set to ::mach_thread_self()
231 * in the VM, but that thread port is not valid for a remote debugger to access the thread.
232 */
233 thread_t
234 lookupThreadFromThreadId(task_t task, jlong thread_id) {
235 if (debug) {
236 printf("lookupThreadFromThreadId thread_id=0x%llx\n", thread_id);
237 }
239 thread_array_t thread_list = NULL;
240 mach_msg_type_number_t thread_list_count = 0;
241 thread_t result_thread = 0;
242 int i;
244 // get the list of all the send rights
245 kern_return_t result = task_threads(task, &thread_list, &thread_list_count);
246 if (result != KERN_SUCCESS) {
247 if (debug) {
248 printf("task_threads returned 0x%x\n", result);
249 }
250 return 0;
251 }
253 for(i = 0 ; i < thread_list_count; i++) {
254 thread_identifier_info_data_t m_ident_info;
255 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
257 // get the THREAD_IDENTIFIER_INFO for the send right
258 result = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count);
259 if (result != KERN_SUCCESS) {
260 if (debug) {
261 printf("thread_info returned 0x%x\n", result);
262 }
263 break;
264 }
266 // if this is the one we're looking for, return the send right
267 if (thread_id == m_ident_info.thread_id)
268 {
269 result_thread = thread_list[i];
270 break;
271 }
272 }
274 vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t));
275 vm_deallocate(mach_task_self(), (vm_address_t) thread_list, thread_list_count);
277 return result_thread;
278 }
281 /*
282 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
283 * Method: getThreadIntegerRegisterSet0
284 * Signature: (J)[J
285 */
286 JNIEXPORT jlongArray JNICALL
287 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0(
288 JNIEnv *env, jobject this_obj,
289 jlong thread_id)
290 {
291 if (debug)
292 printf("getThreadRegisterSet0 called\n");
294 kern_return_t result;
295 thread_t tid;
296 mach_msg_type_number_t count = HSDB_THREAD_STATE_COUNT;
297 hsdb_thread_state_t state;
298 unsigned int *r;
299 int i;
300 jlongArray registerArray;
301 jlong *primitiveArray;
302 task_t gTask = getTask(env, this_obj);
304 tid = lookupThreadFromThreadId(gTask, thread_id);
306 result = thread_get_state(tid, HSDB_THREAD_STATE, (thread_state_t)&state, &count);
308 if (result != KERN_SUCCESS) {
309 if (debug)
310 printf("getregs: thread_get_state(%d) failed (%d)\n", tid, result);
311 return NULL;
312 }
314 // 40 32-bit registers on ppc, 16 on x86.
315 // Output order is the same as the order in the ppc_thread_state/i386_thread_state struct.
316 #if defined(__i386__)
317 r = (unsigned int *)&state;
318 registerArray = (*env)->NewLongArray(env, 8);
319 primitiveArray = (*env)->GetLongArrayElements(env, registerArray, NULL);
320 primitiveArray[0] = r[0]; // eax
321 primitiveArray[1] = r[2]; // ecx
322 primitiveArray[2] = r[3]; // edx
323 primitiveArray[3] = r[1]; // ebx
324 primitiveArray[4] = r[7]; // esp
325 primitiveArray[5] = r[6]; // ebp
326 primitiveArray[6] = r[5]; // esi
327 primitiveArray[7] = r[4]; // edi
328 (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0);
329 #elif defined(__x86_64__)
330 /* From AMD64ThreadContext.java
331 public static final int R15 = 0;
332 public static final int R14 = 1;
333 public static final int R13 = 2;
334 public static final int R12 = 3;
335 public static final int R11 = 4;
336 public static final int R10 = 5;
337 public static final int R9 = 6;
338 public static final int R8 = 7;
339 public static final int RDI = 8;
340 public static final int RSI = 9;
341 public static final int RBP = 10;
342 public static final int RBX = 11;
343 public static final int RDX = 12;
344 public static final int RCX = 13;
345 public static final int RAX = 14;
346 public static final int TRAPNO = 15;
347 public static final int ERR = 16;
348 public static final int RIP = 17;
349 public static final int CS = 18;
350 public static final int RFL = 19;
351 public static final int RSP = 20;
352 public static final int SS = 21;
353 public static final int FS = 22;
354 public static final int GS = 23;
355 public static final int ES = 24;
356 public static final int DS = 25;
357 public static final int FSBASE = 26;
358 public static final int GSBASE = 27;
359 */
360 // 64 bit
361 if (debug) printf("Getting threads for a 64-bit process\n");
362 registerArray = (*env)->NewLongArray(env, 28);
363 primitiveArray = (*env)->GetLongArrayElements(env, registerArray, NULL);
365 primitiveArray[0] = state.__r15;
366 primitiveArray[1] = state.__r14;
367 primitiveArray[2] = state.__r13;
368 primitiveArray[3] = state.__r12;
369 primitiveArray[4] = state.__r11;
370 primitiveArray[5] = state.__r10;
371 primitiveArray[6] = state.__r9;
372 primitiveArray[7] = state.__r8;
373 primitiveArray[8] = state.__rdi;
374 primitiveArray[9] = state.__rsi;
375 primitiveArray[10] = state.__rbp;
376 primitiveArray[11] = state.__rbx;
377 primitiveArray[12] = state.__rdx;
378 primitiveArray[13] = state.__rcx;
379 primitiveArray[14] = state.__rax;
380 primitiveArray[15] = 0; // trapno ?
381 primitiveArray[16] = 0; // err ?
382 primitiveArray[17] = state.__rip;
383 primitiveArray[18] = state.__cs;
384 primitiveArray[19] = state.__rflags;
385 primitiveArray[20] = state.__rsp;
386 primitiveArray[21] = 0; // We don't have SS
387 primitiveArray[22] = state.__fs;
388 primitiveArray[23] = state.__gs;
389 primitiveArray[24] = 0;
390 primitiveArray[25] = 0;
391 primitiveArray[26] = 0;
392 primitiveArray[27] = 0;
394 if (debug) printf("set registers\n");
396 (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0);
397 #else
398 #error Unsupported architecture
399 #endif
401 return registerArray;
402 }
404 /*
405 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
406 * Method: translateTID0
407 * Signature: (I)I
408 */
409 JNIEXPORT jint JNICALL
410 Java_sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal_translateTID0(
411 JNIEnv *env, jobject this_obj, jint tid)
412 {
413 if (debug)
414 printf("translateTID0 called on tid = 0x%x\n", (int)tid);
416 kern_return_t result;
417 thread_t foreign_tid, usable_tid;
418 mach_msg_type_name_t type;
420 foreign_tid = tid;
422 task_t gTask = getTask(env, this_obj);
423 result = mach_port_extract_right(gTask, foreign_tid,
424 MACH_MSG_TYPE_COPY_SEND,
425 &usable_tid, &type);
426 if (result != KERN_SUCCESS)
427 return -1;
429 if (debug)
430 printf("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid);
432 return (jint) usable_tid;
433 }
436 static bool ptrace_continue(pid_t pid, int signal) {
437 // pass the signal to the process so we don't swallow it
438 int res;
439 if ((res = ptrace(PT_CONTINUE, pid, (caddr_t)1, signal)) < 0) {
440 fprintf(stderr, "attach: ptrace(PT_CONTINUE, %d) failed with %d\n", pid, res);
441 return false;
442 }
443 return true;
444 }
446 // waits until the ATTACH has stopped the process
447 // by signal SIGSTOP
448 static bool ptrace_waitpid(pid_t pid) {
449 int ret;
450 int status;
451 while (true) {
452 // Wait for debuggee to stop.
453 ret = waitpid(pid, &status, 0);
454 if (ret >= 0) {
455 if (WIFSTOPPED(status)) {
456 // Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP
457 // will still be pending and delivered when the process is DETACHED and the process
458 // will go to sleep.
459 if (WSTOPSIG(status) == SIGSTOP) {
460 // Debuggee stopped by SIGSTOP.
461 return true;
462 }
463 if (!ptrace_continue(pid, WSTOPSIG(status))) {
464 fprintf(stderr, "attach: Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status));
465 return false;
466 }
467 } else {
468 fprintf(stderr, "attach: waitpid(): Child process exited/terminated (status = 0x%x)\n", status);
469 return false;
470 }
471 } else {
472 switch (errno) {
473 case EINTR:
474 continue;
475 break;
476 case ECHILD:
477 fprintf(stderr, "attach: waitpid() failed. Child process pid (%d) does not exist \n", pid);
478 break;
479 case EINVAL:
480 fprintf(stderr, "attach: waitpid() failed. Invalid options argument.\n");
481 break;
482 default:
483 fprintf(stderr, "attach: waitpid() failed. Unexpected error %d\n",errno);
484 break;
485 }
486 return false;
487 }
488 }
489 }
491 // attach to a process/thread specified by "pid"
492 static bool ptrace_attach(pid_t pid) {
493 int res;
494 if ((res = ptrace(PT_ATTACH, pid, 0, 0)) < 0) {
495 fprintf(stderr, "ptrace(PT_ATTACH, %d) failed with %d\n", pid, res);
496 return false;
497 } else {
498 return ptrace_waitpid(pid);
499 }
500 }
502 /*
503 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
504 * Method: attach0
505 * Signature: (I)V
506 */
507 JNIEXPORT void JNICALL
508 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I(
509 JNIEnv *env, jobject this_obj, jint jpid)
510 {
511 JNF_COCOA_ENTER(env);
512 if (getenv("JAVA_SAPROC_DEBUG") != NULL)
513 debug = JNI_TRUE;
514 else
515 debug = JNI_FALSE;
516 if (debug) printf("attach0 called for jpid=%d\n", (int)jpid);
518 // get the task from the pid
519 kern_return_t result;
520 task_t gTask = 0;
521 result = task_for_pid(mach_task_self(), jpid, &gTask);
522 if (result != KERN_SUCCESS) {
523 fprintf(stderr, "attach: task_for_pid(%d) failed (%d)\n", (int)jpid, result);
524 THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process");
525 }
526 putTask(env, this_obj, gTask);
528 // use ptrace to stop the process
529 // on os x, ptrace only needs to be called on the process, not the individual threads
530 if (ptrace_attach(jpid) != true) {
531 mach_port_deallocate(mach_task_self(), gTask);
532 THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process");
533 }
535 id symbolicator = nil;
536 id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator");
537 if (jrsSymbolicator != nil) {
538 id (*dynamicCall)(id, SEL, pid_t) = (id (*)(id, SEL, pid_t))&objc_msgSend;
539 symbolicator = dynamicCall(jrsSymbolicator, @selector(symbolicatorForPid:), (pid_t)jpid);
540 }
541 if (symbolicator != nil) {
542 CFRetain(symbolicator); // pin symbolicator while in java heap
543 }
545 putSymbolicator(env, this_obj, symbolicator);
546 if (symbolicator == nil) {
547 THROW_NEW_DEBUGGER_EXCEPTION("Can't attach symbolicator to the process");
548 }
550 JNF_COCOA_EXIT(env);
551 }
553 /*
554 * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal
555 * Method: detach0
556 * Signature: ()V
557 */
558 JNIEXPORT void JNICALL
559 Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0(
560 JNIEnv *env, jobject this_obj)
561 {
562 JNF_COCOA_ENTER(env);
563 if (debug) printf("detach0 called\n");
565 task_t gTask = getTask(env, this_obj);
567 // detach from the ptraced process causing it to resume execution
568 int pid;
569 kern_return_t k_res;
570 k_res = pid_for_task(gTask, &pid);
571 if (k_res != KERN_SUCCESS) {
572 fprintf(stderr, "detach: pid_for_task(%d) failed (%d)\n", pid, k_res);
573 }
574 else {
575 int res = ptrace(PT_DETACH, pid, 0, 0);
576 if (res < 0) {
577 fprintf(stderr, "detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res);
578 }
579 }
581 mach_port_deallocate(mach_task_self(), gTask);
582 id symbolicator = getSymbolicator(env, this_obj);
583 if (symbolicator != nil) {
584 CFRelease(symbolicator);
585 }
586 JNF_COCOA_EXIT(env);
587 }
589 /*
590 * Class: sun_jvm_hotspot_asm_Disassembler
591 * Method: load_library
592 * Signature: (Ljava/lang/String;)L
593 */
594 JNIEXPORT jlong JNICALL
595 Java_sun_jvm_hotspot_asm_Disassembler_load_1library(
596 JNIEnv * env,
597 jclass disclass,
598 jstring jrepath_s,
599 jstring libname_s)
600 {
601 uintptr_t func = 0;
602 const char* error_message = NULL;
603 const char* java_home;
604 jboolean isCopy;
605 uintptr_t *handle = NULL;
607 const char * jrepath = (*env)->GetStringUTFChars(env, jrepath_s, &isCopy); // like $JAVA_HOME/jre/lib/sparc/
608 const char * libname = (*env)->GetStringUTFChars(env, libname_s, &isCopy);
609 char buffer[128];
611 /* Load the hsdis library */
612 void* hsdis_handle;
613 hsdis_handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
614 if (hsdis_handle == NULL) {
615 snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname);
616 hsdis_handle = dlopen(buffer, RTLD_LAZY | RTLD_GLOBAL);
617 }
618 if (hsdis_handle != NULL) {
619 func = (uintptr_t)dlsym(hsdis_handle, "decode_instructions_virtual");
620 }
621 if (func == 0) {
622 error_message = dlerror();
623 fprintf(stderr, "%s\n", error_message);
624 }
626 (*env)->ReleaseStringUTFChars(env, libname_s, libname);
627 (*env)->ReleaseStringUTFChars(env, jrepath_s, jrepath);
629 if (func == 0) {
630 /* Couldn't find entry point. error_message should contain some
631 * platform dependent error message.
632 */
633 THROW_NEW_DEBUGGER_EXCEPTION(error_message);
634 }
635 return (jlong)func;
636 }
638 /* signature of decode_instructions_virtual from hsdis.h */
639 typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va,
640 unsigned char* start, uintptr_t length,
641 void* (*event_callback)(void*, const char*, void*),
642 void* event_stream,
643 int (*printf_callback)(void*, const char*, ...),
644 void* printf_stream,
645 const char* options);
647 /* container for call back state when decoding instructions */
648 typedef struct {
649 JNIEnv* env;
650 jobject dis;
651 jobject visitor;
652 jmethodID handle_event;
653 jmethodID raw_print;
654 char buffer[4096];
655 } decode_env;
658 /* event callback binding to Disassembler.handleEvent */
659 static void* event_to_env(void* env_pv, const char* event, void* arg) {
660 decode_env* denv = (decode_env*)env_pv;
661 JNIEnv* env = denv->env;
662 jstring event_string = (*env)->NewStringUTF(env, event);
663 jlong result = (*env)->CallLongMethod(env, denv->dis, denv->handle_event, denv->visitor,
664 event_string, (jlong) (uintptr_t)arg);
665 /* ignore exceptions for now */
666 CHECK_EXCEPTION_CLEAR_((void *)0);
667 return (void*)(uintptr_t)result;
668 }
670 /* printing callback binding to Disassembler.rawPrint */
671 static int printf_to_env(void* env_pv, const char* format, ...) {
672 jstring output;
673 va_list ap;
674 int cnt;
675 decode_env* denv = (decode_env*)env_pv;
676 JNIEnv* env = denv->env;
677 size_t flen = strlen(format);
678 const char* raw = NULL;
680 if (flen == 0) return 0;
681 if (flen < 2 ||
682 strchr(format, '%') == NULL) {
683 raw = format;
684 } else if (format[0] == '%' && format[1] == '%' &&
685 strchr(format+2, '%') == NULL) {
686 // happens a lot on machines with names like %foo
687 flen--;
688 raw = format+1;
689 }
690 if (raw != NULL) {
691 jstring output = (*env)->NewStringUTF(env, raw);
692 (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
693 CHECK_EXCEPTION_CLEAR;
694 return (int) flen;
695 }
696 va_start(ap, format);
697 cnt = vsnprintf(denv->buffer, sizeof(denv->buffer), format, ap);
698 va_end(ap);
700 output = (*env)->NewStringUTF(env, denv->buffer);
701 (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output);
702 CHECK_EXCEPTION_CLEAR;
703 return cnt;
704 }
706 /*
707 * Class: sun_jvm_hotspot_asm_Disassembler
708 * Method: decode
709 * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V
710 */
711 JNIEXPORT void JNICALL
712 Java_sun_jvm_hotspot_asm_Disassembler_decode(
713 JNIEnv * env,
714 jobject dis,
715 jobject visitor,
716 jlong startPc,
717 jbyteArray code,
718 jstring options_s,
719 jlong decode_instructions_virtual)
720 {
721 jboolean isCopy;
722 jbyte* start = (*env)->GetByteArrayElements(env, code, &isCopy);
723 jbyte* end = start + (*env)->GetArrayLength(env, code);
724 const char * options = (*env)->GetStringUTFChars(env, options_s, &isCopy);
725 jclass disclass = (*env)->GetObjectClass(env, dis);
727 decode_env denv;
728 denv.env = env;
729 denv.dis = dis;
730 denv.visitor = visitor;
732 /* find Disassembler.handleEvent callback */
733 denv.handle_event = (*env)->GetMethodID(env, disclass, "handleEvent",
734 "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;J)J");
735 CHECK_EXCEPTION_CLEAR_VOID
737 /* find Disassembler.rawPrint callback */
738 denv.raw_print = (*env)->GetMethodID(env, disclass, "rawPrint",
739 "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;)V");
740 CHECK_EXCEPTION_CLEAR_VOID
742 /* decode the buffer */
743 (*(decode_func)(uintptr_t)decode_instructions_virtual)(startPc,
744 startPc + end - start,
745 (unsigned char*)start,
746 end - start,
747 &event_to_env, (void*) &denv,
748 &printf_to_env, (void*) &denv,
749 options);
751 /* cleanup */
752 (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT);
753 (*env)->ReleaseStringUTFChars(env, options_s, options);
754 }