Mercurial > hg > graal-compiler
changeset 8865:818a1ac7da7a
Merge
author | morris |
---|---|
date | Sun, 24 Mar 2013 12:43:13 -0700 |
parents | 59a41e1357ab (diff) 3c786355ffb4 (current diff) |
children | 16885e702c88 |
files | |
diffstat | 37 files changed, 2476 insertions(+), 1211 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Sat Mar 23 06:22:07 2013 -0700 +++ b/.hgtags Sun Mar 24 12:43:13 2013 -0700 @@ -324,3 +324,5 @@ dd6350b4abc4a6c19c89dd982cc0e4f3d119885c hs25-b22 65b797426a3bec6e91b64085a0cfb94adadb634a jdk8-b81 0631ebcc45f05c73b09a56c2586685af1f781c1d hs25-b23 +3db4ab0e12f437fe374817de346b2b0c6b4a5b31 jdk8-b82 +e3a41fc0234895eba4f272b984f7dacff495f8eb hs25-b24
--- a/agent/src/os/bsd/MacosxDebuggerLocal.m Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/MacosxDebuggerLocal.m Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,12 +40,34 @@ #import <errno.h> #import <sys/types.h> #import <sys/ptrace.h> +#include "libproc_impl.h" -jboolean debug = JNI_FALSE; +#define UNSUPPORTED_ARCH "Unsupported architecture!" + +#if defined(x86_64) && !defined(amd64) +#define amd64 1 +#endif + +#if amd64 +#include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h" +#else +#error UNSUPPORTED_ARCH +#endif static jfieldID symbolicatorID = 0; // set in _init0 static jfieldID taskID = 0; // set in _init0 +static jfieldID p_ps_prochandle_ID = 0; +static jfieldID loadObjectList_ID = 0; +static jmethodID listAdd_ID = 0; + +static jmethodID createClosestSymbol_ID = 0; +static jmethodID createLoadObject_ID = 0; +static jmethodID getJavaThreadsInfo_ID = 0; + +// indicator if thread id (lwpid_t) was set +static bool _threads_filled = false; + static void putSymbolicator(JNIEnv *env, jobject this_obj, id symbolicator) { (*env)->SetLongField(env, this_obj, symbolicatorID, (jlong)(intptr_t)symbolicator); } @@ -76,6 +98,11 @@ (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); } +static struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj) { + jlong ptr = (*env)->GetLongField(env, this_obj, p_ps_prochandle_ID); + return (struct ps_prochandle*)(intptr_t)ptr; +} + #if defined(__i386__) #define hsdb_thread_state_t x86_thread_state32_t #define hsdb_float_state_t x86_float_state32_t @@ -91,7 +118,7 @@ #define HSDB_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT #define HSDB_FLOAT_STATE_COUNT x86_FLOAT_STATE64_COUNT #else - #error "Unsupported architecture" + #error UNSUPPORTED_ARCH #endif /* @@ -104,6 +131,66 @@ symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J"); taskID = (*env)->GetFieldID(env, cls, "task", "J"); CHECK_EXCEPTION; + + // for core file + p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J"); + CHECK_EXCEPTION; + loadObjectList_ID = (*env)->GetFieldID(env, cls, "loadObjectList", "Ljava/util/List;"); + CHECK_EXCEPTION; + + // methods we use + createClosestSymbol_ID = (*env)->GetMethodID(env, cls, "createClosestSymbol", + "(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;"); + CHECK_EXCEPTION; + createLoadObject_ID = (*env)->GetMethodID(env, cls, "createLoadObject", + "(Ljava/lang/String;JJ)Lsun/jvm/hotspot/debugger/cdbg/LoadObject;"); + CHECK_EXCEPTION; + + // java.util.List method we call + jclass listClass = (*env)->FindClass(env, "java/util/List"); + CHECK_EXCEPTION; + listAdd_ID = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_EXCEPTION; + getJavaThreadsInfo_ID = (*env)->GetMethodID(env, cls, "getJavaThreadsInfo", + "()[J"); + CHECK_EXCEPTION; + + init_libproc(getenv("LIBSAPROC_DEBUG") != NULL); +} + +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize + (JNIEnv *env, jclass cls) +{ +#ifdef _LP64 + return 8; +#else + #error UNSUPPORTED_ARCH +#endif +} + +/** called by Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0 */ +jlong lookupByNameIncore( + JNIEnv *env, struct ps_prochandle *ph, jobject this_obj, jstring objectName, jstring symbolName) +{ + const char *objectName_cstr, *symbolName_cstr; + jlong addr; + jboolean isCopy; + objectName_cstr = NULL; + if (objectName != NULL) { + objectName_cstr = (*env)->GetStringUTFChars(env, objectName, &isCopy); + CHECK_EXCEPTION_(0); + } + symbolName_cstr = (*env)->GetStringUTFChars(env, symbolName, &isCopy); + CHECK_EXCEPTION_(0); + + print_debug("look for %s \n", symbolName_cstr); + addr = (jlong) lookup_symbol(ph, objectName_cstr, symbolName_cstr); + + if (objectName_cstr != NULL) { + (*env)->ReleaseStringUTFChars(env, objectName, objectName_cstr); + } + (*env)->ReleaseStringUTFChars(env, symbolName, symbolName_cstr); + return addr; } /* @@ -116,14 +203,17 @@ JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph->core != NULL) { + return lookupByNameIncore(env, ph, this_obj, objectName, symbolName); + } + jlong address = 0; JNF_COCOA_ENTER(env); NSString *symbolNameString = JNFJavaToNSString(env, symbolName); - if (debug) { - printf("lookupInProcess called for %s\n", [symbolNameString UTF8String]); - } + print_debug("lookupInProcess called for %s\n", [symbolNameString UTF8String]); id symbolicator = getSymbolicator(env, this_obj); if (symbolicator != nil) { @@ -131,9 +221,7 @@ address = (jlong) dynamicCall(symbolicator, @selector(addressForSymbol:), symbolNameString); } - if (debug) { - printf("address of symbol %s = %llx\n", [symbolNameString UTF8String], address); - } + print_debug("address of symbol %s = %llx\n", [symbolNameString UTF8String], address); JNF_COCOA_EXIT(env); return address; @@ -141,6 +229,42 @@ /* * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: lookupByAddress0 + * Signature: (J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol; + */ +JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByAddress0 + (JNIEnv *env, jobject this_obj, jlong addr) { + uintptr_t offset; + const char* sym = NULL; + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); + if (sym == NULL) return 0; + return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, + (*env)->NewStringUTF(env, sym), (jlong)offset); +} + +/** called from Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0 */ +jbyteArray readBytesFromCore( + JNIEnv *env, struct ps_prochandle *ph, jobject this_obj, jlong addr, jlong numBytes) +{ + jboolean isCopy; + jbyteArray array; + jbyte *bufPtr; + ps_err_e err; + + array = (*env)->NewByteArray(env, numBytes); + CHECK_EXCEPTION_(0); + bufPtr = (*env)->GetByteArrayElements(env, array, &isCopy); + CHECK_EXCEPTION_(0); + + err = ps_pread(ph, (psaddr_t) (uintptr_t)addr, bufPtr, numBytes); + (*env)->ReleaseByteArrayElements(env, array, bufPtr, 0); + return (err == PS_OK)? array : 0; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal * Method: readBytesFromProcess0 * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; */ @@ -149,12 +273,15 @@ JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) { - if (debug) printf("readBytesFromProcess called. addr = %llx numBytes = %lld\n", addr, numBytes); + print_debug("readBytesFromProcess called. addr = %llx numBytes = %lld\n", addr, numBytes); // must allocate storage instead of using former parameter buf - jboolean isCopy; jbyteArray array; - jbyte *bufPtr; + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph->core != NULL) { + return readBytesFromCore(env, ph, this_obj, addr, numBytes); + } array = (*env)->NewByteArray(env, numBytes); CHECK_EXCEPTION_(0); @@ -189,7 +316,7 @@ // assume all failures are unmapped pages } - if (debug) fprintf(stderr, "%ld pages\n", pageCount); + print_debug("%ld pages\n", pageCount); remaining = numBytes; @@ -207,7 +334,7 @@ } if (mapped[i]) { - if (debug) fprintf(stderr, "page %d mapped (len %ld start %ld)\n", i, len, start); + print_debug("page %d mapped (len %ld start %ld)\n", i, len, start); (*env)->SetByteArrayRegion(env, array, 0, len, ((jbyte *) pages[i] + start)); vm_deallocate(mach_task_self(), pages[i], vm_page_size); } @@ -220,6 +347,115 @@ return array; } +/** Only used for core file reading, set thread_id for threads which is got after core file parsed. + * Thread context is available in Mach-O core file but thread id is not. We can get thread id + * from Threads which store all java threads information when they are created. Here we can identify + * them as java threads by checking if a thread's rsp or rbp within a java thread's stack. + * Note Macosx uses unique_thread_id which is different from other platforms though printed ids + * are still pthread id. Function BsdDebuggerLocal.getJavaThreadsInfo returns an array of long + * integers to host all java threads' id, stack_start, stack_end as: + * [uid0, stack_start0, stack_end0, uid1, stack_start1, stack_end1, ...] + * + * The work cannot be done at init0 since Threads is not available yet(VM not initialized yet). + * This function should be called only once if succeeded + */ +bool fill_java_threads(JNIEnv* env, jobject this_obj, struct ps_prochandle* ph) { + int n = 0, i = 0, j; + struct reg regs; + + jlongArray thrinfos = (*env)->CallObjectMethod(env, this_obj, getJavaThreadsInfo_ID); + CHECK_EXCEPTION_(false); + int len = (int)(*env)->GetArrayLength(env, thrinfos); + uint64_t* cinfos = (uint64_t *)(*env)->GetLongArrayElements(env, thrinfos, NULL); + CHECK_EXCEPTION_(false); + n = get_num_threads(ph); + print_debug("fill_java_threads called, num_of_thread = %d\n", n); + for (i = 0; i < n; i++) { + if (!get_nth_lwp_regs(ph, i, ®s)) { + print_debug("Could not get regs of thread %d, already set!\n", i); + return false; + } + for (j = 0; j < len; j += 3) { + lwpid_t uid = cinfos[j]; + uint64_t beg = cinfos[j + 1]; + uint64_t end = cinfos[j + 2]; + if ((regs.r_rsp < end && regs.r_rsp >= beg) || + (regs.r_rbp < end && regs.r_rbp >= beg)) { + set_lwp_id(ph, i, uid); + break; + } + } + } + (*env)->ReleaseLongArrayElements(env, thrinfos, (jlong*)cinfos, 0); + CHECK_EXCEPTION_(false); + return true; +} + +/* For core file only, called from + * Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0 + */ +jlongArray getThreadIntegerRegisterSetFromCore(JNIEnv *env, jobject this_obj, long lwp_id) { + if (!_threads_filled) { + if (!fill_java_threads(env, this_obj, get_proc_handle(env, this_obj))) { + throw_new_debugger_exception(env, "Failed to fill in threads"); + return 0; + } else { + _threads_filled = true; + } + } + + struct reg gregs; + jboolean isCopy; + jlongArray array; + jlong *regs; + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (get_lwp_regs(ph, lwp_id, &gregs) != true) { + THROW_NEW_DEBUGGER_EXCEPTION_("get_thread_regs failed for a lwp", 0); + } + +#undef NPRGREG +#undef REG_INDEX +#if amd64 +#define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg + + array = (*env)->NewLongArray(env, NPRGREG); + CHECK_EXCEPTION_(0); + regs = (*env)->GetLongArrayElements(env, array, &isCopy); + + regs[REG_INDEX(R15)] = gregs.r_r15; + regs[REG_INDEX(R14)] = gregs.r_r14; + regs[REG_INDEX(R13)] = gregs.r_r13; + regs[REG_INDEX(R12)] = gregs.r_r12; + regs[REG_INDEX(RBP)] = gregs.r_rbp; + regs[REG_INDEX(RBX)] = gregs.r_rbx; + regs[REG_INDEX(R11)] = gregs.r_r11; + regs[REG_INDEX(R10)] = gregs.r_r10; + regs[REG_INDEX(R9)] = gregs.r_r9; + regs[REG_INDEX(R8)] = gregs.r_r8; + regs[REG_INDEX(RAX)] = gregs.r_rax; + regs[REG_INDEX(RCX)] = gregs.r_rcx; + regs[REG_INDEX(RDX)] = gregs.r_rdx; + regs[REG_INDEX(RSI)] = gregs.r_rsi; + regs[REG_INDEX(RDI)] = gregs.r_rdi; + regs[REG_INDEX(RIP)] = gregs.r_rip; + regs[REG_INDEX(CS)] = gregs.r_cs; + regs[REG_INDEX(RSP)] = gregs.r_rsp; + regs[REG_INDEX(SS)] = gregs.r_ss; + regs[REG_INDEX(FSBASE)] = 0; + regs[REG_INDEX(GSBASE)] = 0; + regs[REG_INDEX(DS)] = gregs.r_ds; + regs[REG_INDEX(ES)] = gregs.r_es; + regs[REG_INDEX(FS)] = gregs.r_fs; + regs[REG_INDEX(GS)] = gregs.r_gs; + regs[REG_INDEX(TRAPNO)] = gregs.r_trapno; + regs[REG_INDEX(RFL)] = gregs.r_rflags; + +#endif /* amd64 */ + (*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT); + return array; +} /* * Lookup the thread_t that corresponds to the given thread_id. @@ -232,9 +468,7 @@ */ thread_t lookupThreadFromThreadId(task_t task, jlong thread_id) { - if (debug) { - printf("lookupThreadFromThreadId thread_id=0x%llx\n", thread_id); - } + print_debug("lookupThreadFromThreadId thread_id=0x%llx\n", thread_id); thread_array_t thread_list = NULL; mach_msg_type_number_t thread_list_count = 0; @@ -244,9 +478,7 @@ // get the list of all the send rights kern_return_t result = task_threads(task, &thread_list, &thread_list_count); if (result != KERN_SUCCESS) { - if (debug) { - printf("task_threads returned 0x%x\n", result); - } + print_debug("task_threads returned 0x%x\n", result); return 0; } @@ -257,9 +489,7 @@ // get the THREAD_IDENTIFIER_INFO for the send right result = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count); if (result != KERN_SUCCESS) { - if (debug) { - printf("thread_info returned 0x%x\n", result); - } + print_debug("thread_info returned 0x%x\n", result); break; } @@ -288,15 +518,17 @@ JNIEnv *env, jobject this_obj, jlong thread_id) { - if (debug) - printf("getThreadRegisterSet0 called\n"); + print_debug("getThreadRegisterSet0 called\n"); + + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph->core != NULL) { + return getThreadIntegerRegisterSetFromCore(env, this_obj, thread_id); + } kern_return_t result; thread_t tid; mach_msg_type_number_t count = HSDB_THREAD_STATE_COUNT; hsdb_thread_state_t state; - unsigned int *r; - int i; jlongArray registerArray; jlong *primitiveArray; task_t gTask = getTask(env, this_obj); @@ -306,97 +538,56 @@ result = thread_get_state(tid, HSDB_THREAD_STATE, (thread_state_t)&state, &count); if (result != KERN_SUCCESS) { - if (debug) - printf("getregs: thread_get_state(%d) failed (%d)\n", tid, result); + print_error("getregs: thread_get_state(%d) failed (%d)\n", tid, result); return NULL; } - // 40 32-bit registers on ppc, 16 on x86. - // Output order is the same as the order in the ppc_thread_state/i386_thread_state struct. -#if defined(__i386__) - r = (unsigned int *)&state; - registerArray = (*env)->NewLongArray(env, 8); - primitiveArray = (*env)->GetLongArrayElements(env, registerArray, NULL); - primitiveArray[0] = r[0]; // eax - primitiveArray[1] = r[2]; // ecx - primitiveArray[2] = r[3]; // edx - primitiveArray[3] = r[1]; // ebx - primitiveArray[4] = r[7]; // esp - primitiveArray[5] = r[6]; // ebp - primitiveArray[6] = r[5]; // esi - primitiveArray[7] = r[4]; // edi - (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0); -#elif defined(__x86_64__) - /* From AMD64ThreadContext.java - public static final int R15 = 0; - public static final int R14 = 1; - public static final int R13 = 2; - public static final int R12 = 3; - public static final int R11 = 4; - public static final int R10 = 5; - public static final int R9 = 6; - public static final int R8 = 7; - public static final int RDI = 8; - public static final int RSI = 9; - public static final int RBP = 10; - public static final int RBX = 11; - public static final int RDX = 12; - public static final int RCX = 13; - public static final int RAX = 14; - public static final int TRAPNO = 15; - public static final int ERR = 16; - public static final int RIP = 17; - public static final int CS = 18; - public static final int RFL = 19; - public static final int RSP = 20; - public static final int SS = 21; - public static final int FS = 22; - public static final int GS = 23; - public static final int ES = 24; - public static final int DS = 25; - public static final int FSBASE = 26; - public static final int GSBASE = 27; - */ - // 64 bit - if (debug) printf("Getting threads for a 64-bit process\n"); - registerArray = (*env)->NewLongArray(env, 28); - primitiveArray = (*env)->GetLongArrayElements(env, registerArray, NULL); +#if amd64 +#define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG +#undef REG_INDEX +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg + + // 64 bit + print_debug("Getting threads for a 64-bit process\n"); + registerArray = (*env)->NewLongArray(env, NPRGREG); + CHECK_EXCEPTION_(0); + primitiveArray = (*env)->GetLongArrayElements(env, registerArray, NULL); - primitiveArray[0] = state.__r15; - primitiveArray[1] = state.__r14; - primitiveArray[2] = state.__r13; - primitiveArray[3] = state.__r12; - primitiveArray[4] = state.__r11; - primitiveArray[5] = state.__r10; - primitiveArray[6] = state.__r9; - primitiveArray[7] = state.__r8; - primitiveArray[8] = state.__rdi; - primitiveArray[9] = state.__rsi; - primitiveArray[10] = state.__rbp; - primitiveArray[11] = state.__rbx; - primitiveArray[12] = state.__rdx; - primitiveArray[13] = state.__rcx; - primitiveArray[14] = state.__rax; - primitiveArray[15] = 0; // trapno ? - primitiveArray[16] = 0; // err ? - primitiveArray[17] = state.__rip; - primitiveArray[18] = state.__cs; - primitiveArray[19] = state.__rflags; - primitiveArray[20] = state.__rsp; - primitiveArray[21] = 0; // We don't have SS - primitiveArray[22] = state.__fs; - primitiveArray[23] = state.__gs; - primitiveArray[24] = 0; - primitiveArray[25] = 0; - primitiveArray[26] = 0; - primitiveArray[27] = 0; + primitiveArray[REG_INDEX(R15)] = state.__r15; + primitiveArray[REG_INDEX(R14)] = state.__r14; + primitiveArray[REG_INDEX(R13)] = state.__r13; + primitiveArray[REG_INDEX(R12)] = state.__r12; + primitiveArray[REG_INDEX(R11)] = state.__r11; + primitiveArray[REG_INDEX(R10)] = state.__r10; + primitiveArray[REG_INDEX(R9)] = state.__r9; + primitiveArray[REG_INDEX(R8)] = state.__r8; + primitiveArray[REG_INDEX(RDI)] = state.__rdi; + primitiveArray[REG_INDEX(RSI)] = state.__rsi; + primitiveArray[REG_INDEX(RBP)] = state.__rbp; + primitiveArray[REG_INDEX(RBX)] = state.__rbx; + primitiveArray[REG_INDEX(RDX)] = state.__rdx; + primitiveArray[REG_INDEX(RCX)] = state.__rcx; + primitiveArray[REG_INDEX(RAX)] = state.__rax; + primitiveArray[REG_INDEX(TRAPNO)] = 0; // trapno, not used + primitiveArray[REG_INDEX(ERR)] = 0; // err, not used + primitiveArray[REG_INDEX(RIP)] = state.__rip; + primitiveArray[REG_INDEX(CS)] = state.__cs; + primitiveArray[REG_INDEX(RFL)] = state.__rflags; + primitiveArray[REG_INDEX(RSP)] = state.__rsp; + primitiveArray[REG_INDEX(SS)] = 0; // We don't have SS + primitiveArray[REG_INDEX(FS)] = state.__fs; + primitiveArray[REG_INDEX(GS)] = state.__gs; + primitiveArray[REG_INDEX(ES)] = 0; + primitiveArray[REG_INDEX(DS)] = 0; + primitiveArray[REG_INDEX(FSBASE)] = 0; + primitiveArray[REG_INDEX(GSBASE)] = 0; + print_debug("set registers\n"); - if (debug) printf("set registers\n"); + (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0); - (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0); #else -#error Unsupported architecture -#endif +#error UNSUPPORTED_ARCH +#endif /* amd64 */ return registerArray; } @@ -410,8 +601,7 @@ Java_sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal_translateTID0( JNIEnv *env, jobject this_obj, jint tid) { - if (debug) - printf("translateTID0 called on tid = 0x%x\n", (int)tid); + print_debug("translateTID0 called on tid = 0x%x\n", (int)tid); kern_return_t result; thread_t foreign_tid, usable_tid; @@ -426,8 +616,7 @@ if (result != KERN_SUCCESS) return -1; - if (debug) - printf("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid); + print_debug("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid); return (jint) usable_tid; } @@ -437,7 +626,7 @@ // pass the signal to the process so we don't swallow it int res; if ((res = ptrace(PT_CONTINUE, pid, (caddr_t)1, signal)) < 0) { - fprintf(stderr, "attach: ptrace(PT_CONTINUE, %d) failed with %d\n", pid, res); + print_error("attach: ptrace(PT_CONTINUE, %d) failed with %d\n", pid, res); return false; } return true; @@ -461,11 +650,11 @@ return true; } if (!ptrace_continue(pid, WSTOPSIG(status))) { - fprintf(stderr, "attach: Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status)); + print_error("attach: Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status)); return false; } } else { - fprintf(stderr, "attach: waitpid(): Child process exited/terminated (status = 0x%x)\n", status); + print_error("attach: waitpid(): Child process exited/terminated (status = 0x%x)\n", status); return false; } } else { @@ -474,13 +663,13 @@ continue; break; case ECHILD: - fprintf(stderr, "attach: waitpid() failed. Child process pid (%d) does not exist \n", pid); + print_error("attach: waitpid() failed. Child process pid (%d) does not exist \n", pid); break; case EINVAL: - fprintf(stderr, "attach: waitpid() failed. Invalid options argument.\n"); + print_error("attach: waitpid() failed. Invalid options argument.\n"); break; default: - fprintf(stderr, "attach: waitpid() failed. Unexpected error %d\n",errno); + print_error("attach: waitpid() failed. Unexpected error %d\n",errno); break; } return false; @@ -492,7 +681,7 @@ static bool ptrace_attach(pid_t pid) { int res; if ((res = ptrace(PT_ATTACH, pid, 0, 0)) < 0) { - fprintf(stderr, "ptrace(PT_ATTACH, %d) failed with %d\n", pid, res); + print_error("ptrace(PT_ATTACH, %d) failed with %d\n", pid, res); return false; } else { return ptrace_waitpid(pid); @@ -504,23 +693,19 @@ * Method: attach0 * Signature: (I)V */ -JNIEXPORT void JNICALL +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I( - JNIEnv *env, jobject this_obj, jint jpid) + JNIEnv *env, jobject this_obj, jint jpid) { + print_debug("attach0 called for jpid=%d\n", (int)jpid); + JNF_COCOA_ENTER(env); - if (getenv("JAVA_SAPROC_DEBUG") != NULL) - debug = JNI_TRUE; - else - debug = JNI_FALSE; - if (debug) printf("attach0 called for jpid=%d\n", (int)jpid); - - // get the task from the pid + kern_return_t result; task_t gTask = 0; result = task_for_pid(mach_task_self(), jpid, &gTask); if (result != KERN_SUCCESS) { - fprintf(stderr, "attach: task_for_pid(%d) failed (%d)\n", (int)jpid, result); + print_error("attach: task_for_pid(%d) failed (%d)\n", (int)jpid, result); THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); } putTask(env, this_obj, gTask); @@ -550,18 +735,79 @@ JNF_COCOA_EXIT(env); } +/** For core file, + called from Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2 */ +static void fillLoadObjects(JNIEnv* env, jobject this_obj, struct ps_prochandle* ph) { + int n = 0, i = 0; + + // add load objects + n = get_num_libs(ph); + for (i = 0; i < n; i++) { + uintptr_t base; + const char* name; + jobject loadObject; + jobject loadObjectList; + + base = get_lib_base(ph, i); + name = get_lib_name(ph, i); + loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID, + (*env)->NewStringUTF(env, name), (jlong)0, (jlong)base); + CHECK_EXCEPTION; + loadObjectList = (*env)->GetObjectField(env, this_obj, loadObjectList_ID); + CHECK_EXCEPTION; + (*env)->CallBooleanMethod(env, loadObjectList, listAdd_ID, loadObject); + CHECK_EXCEPTION; + } +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: attach0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2( + JNIEnv *env, jobject this_obj, jstring execName, jstring coreName) +{ + const char *execName_cstr; + const char *coreName_cstr; + jboolean isCopy; + struct ps_prochandle* ph; + + execName_cstr = (*env)->GetStringUTFChars(env, execName, &isCopy); + CHECK_EXCEPTION; + coreName_cstr = (*env)->GetStringUTFChars(env, coreName, &isCopy); + CHECK_EXCEPTION; + + print_debug("attach: %s %s\n", execName_cstr, coreName_cstr); + + if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) { + (*env)->ReleaseStringUTFChars(env, execName, execName_cstr); + (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr); + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file"); + } + (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); + (*env)->ReleaseStringUTFChars(env, execName, execName_cstr); + (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr); + fillLoadObjects(env, this_obj, ph); +} + /* * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal * Method: detach0 * Signature: ()V */ -JNIEXPORT void JNICALL +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0( - JNIEnv *env, jobject this_obj) + JNIEnv *env, jobject this_obj) { + print_debug("detach0 called\n"); + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph != NULL && ph->core != NULL) { + Prelease(ph); + return; + } JNF_COCOA_ENTER(env); - if (debug) printf("detach0 called\n"); - task_t gTask = getTask(env, this_obj); // detach from the ptraced process causing it to resume execution @@ -569,15 +815,15 @@ kern_return_t k_res; k_res = pid_for_task(gTask, &pid); if (k_res != KERN_SUCCESS) { - fprintf(stderr, "detach: pid_for_task(%d) failed (%d)\n", pid, k_res); + print_error("detach: pid_for_task(%d) failed (%d)\n", pid, k_res); } else { int res = ptrace(PT_DETACH, pid, 0, 0); if (res < 0) { - fprintf(stderr, "detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res); + print_error("detach: ptrace(PT_DETACH, %d) failed (%d)\n", pid, res); } } - + mach_port_deallocate(mach_task_self(), gTask); id symbolicator = getSymbolicator(env, this_obj); if (symbolicator != nil) { @@ -585,170 +831,3 @@ } JNF_COCOA_EXIT(env); } - -/* - * Class: sun_jvm_hotspot_asm_Disassembler - * Method: load_library - * Signature: (Ljava/lang/String;)L - */ -JNIEXPORT jlong JNICALL -Java_sun_jvm_hotspot_asm_Disassembler_load_1library( - JNIEnv * env, - jclass disclass, - jstring jrepath_s, - jstring libname_s) -{ - uintptr_t func = 0; - const char* error_message = NULL; - const char* java_home; - jboolean isCopy; - uintptr_t *handle = NULL; - - const char * jrepath = (*env)->GetStringUTFChars(env, jrepath_s, &isCopy); // like $JAVA_HOME/jre/lib/sparc/ - const char * libname = (*env)->GetStringUTFChars(env, libname_s, &isCopy); - char buffer[128]; - - /* Load the hsdis library */ - void* hsdis_handle; - hsdis_handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL); - if (hsdis_handle == NULL) { - snprintf(buffer, sizeof(buffer), "%s%s", jrepath, libname); - hsdis_handle = dlopen(buffer, RTLD_LAZY | RTLD_GLOBAL); - } - if (hsdis_handle != NULL) { - func = (uintptr_t)dlsym(hsdis_handle, "decode_instructions_virtual"); - } - if (func == 0) { - error_message = dlerror(); - fprintf(stderr, "%s\n", error_message); - } - - (*env)->ReleaseStringUTFChars(env, libname_s, libname); - (*env)->ReleaseStringUTFChars(env, jrepath_s, jrepath); - - if (func == 0) { - /* Couldn't find entry point. error_message should contain some - * platform dependent error message. - */ - THROW_NEW_DEBUGGER_EXCEPTION_(error_message, (jlong)func); - } - return (jlong)func; -} - -/* signature of decode_instructions_virtual from hsdis.h */ -typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va, - unsigned char* start, uintptr_t length, - void* (*event_callback)(void*, const char*, void*), - void* event_stream, - int (*printf_callback)(void*, const char*, ...), - void* printf_stream, - const char* options); - -/* container for call back state when decoding instructions */ -typedef struct { - JNIEnv* env; - jobject dis; - jobject visitor; - jmethodID handle_event; - jmethodID raw_print; - char buffer[4096]; -} decode_env; - - -/* event callback binding to Disassembler.handleEvent */ -static void* event_to_env(void* env_pv, const char* event, void* arg) { - decode_env* denv = (decode_env*)env_pv; - JNIEnv* env = denv->env; - jstring event_string = (*env)->NewStringUTF(env, event); - jlong result = (*env)->CallLongMethod(env, denv->dis, denv->handle_event, denv->visitor, - event_string, (jlong) (uintptr_t)arg); - /* ignore exceptions for now */ - CHECK_EXCEPTION_CLEAR_((void *)0); - return (void*)(uintptr_t)result; -} - -/* printing callback binding to Disassembler.rawPrint */ -static int printf_to_env(void* env_pv, const char* format, ...) { - jstring output; - va_list ap; - int cnt; - decode_env* denv = (decode_env*)env_pv; - JNIEnv* env = denv->env; - size_t flen = strlen(format); - const char* raw = NULL; - - if (flen == 0) return 0; - if (flen < 2 || - strchr(format, '%') == NULL) { - raw = format; - } else if (format[0] == '%' && format[1] == '%' && - strchr(format+2, '%') == NULL) { - // happens a lot on machines with names like %foo - flen--; - raw = format+1; - } - if (raw != NULL) { - jstring output = (*env)->NewStringUTF(env, raw); - (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output); - CHECK_EXCEPTION_CLEAR; - return (int) flen; - } - va_start(ap, format); - cnt = vsnprintf(denv->buffer, sizeof(denv->buffer), format, ap); - va_end(ap); - - output = (*env)->NewStringUTF(env, denv->buffer); - (*env)->CallVoidMethod(env, denv->dis, denv->raw_print, denv->visitor, output); - CHECK_EXCEPTION_CLEAR; - return cnt; -} - -/* - * Class: sun_jvm_hotspot_asm_Disassembler - * Method: decode - * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V - */ -JNIEXPORT void JNICALL -Java_sun_jvm_hotspot_asm_Disassembler_decode( - JNIEnv * env, - jobject dis, - jobject visitor, - jlong startPc, - jbyteArray code, - jstring options_s, - jlong decode_instructions_virtual) -{ - jboolean isCopy; - jbyte* start = (*env)->GetByteArrayElements(env, code, &isCopy); - jbyte* end = start + (*env)->GetArrayLength(env, code); - const char * options = (*env)->GetStringUTFChars(env, options_s, &isCopy); - jclass disclass = (*env)->GetObjectClass(env, dis); - - decode_env denv; - denv.env = env; - denv.dis = dis; - denv.visitor = visitor; - - /* find Disassembler.handleEvent callback */ - denv.handle_event = (*env)->GetMethodID(env, disclass, "handleEvent", - "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;J)J"); - CHECK_EXCEPTION_CLEAR_VOID - - /* find Disassembler.rawPrint callback */ - denv.raw_print = (*env)->GetMethodID(env, disclass, "rawPrint", - "(Lsun/jvm/hotspot/asm/InstructionVisitor;Ljava/lang/String;)V"); - CHECK_EXCEPTION_CLEAR_VOID - - /* decode the buffer */ - (*(decode_func)(uintptr_t)decode_instructions_virtual)(startPc, - startPc + end - start, - (unsigned char*)start, - end - start, - &event_to_env, (void*) &denv, - &printf_to_env, (void*) &denv, - options); - - /* cleanup */ - (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT); - (*env)->ReleaseStringUTFChars(env, options_s, options); -}
--- a/agent/src/os/bsd/Makefile Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/Makefile Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -22,34 +22,60 @@ # # -ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "amd64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi ) +ARCH := $(shell if ([ `uname -m` = "ia64" ]) ; then echo ia64 ; elif ([ `uname -m` = "amd64" ]) ; then echo amd64; elif ([ `uname -m` = "x86_64" ]) ; then echo amd64; elif ([ `uname -m` = "sparc64" ]) ; then echo sparc; else echo i386 ; fi ) + +OS := $(shell uname -s) + GCC = gcc JAVAH = ${JAVA_HOME}/bin/javah +ifneq ($(OS), Darwin) SOURCES = salibelf.c \ symtab.c \ libproc_impl.c \ ps_proc.c \ ps_core.c \ BsdDebuggerLocal.c - -INCLUDES = -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(shell uname -s | tr "[:upper:]" "[:lower:]") - -OBJS = $(SOURCES:.c=.o) +OBJS = $(SOURCES:.c=.o) +OBJSPLUS = $(OBJS) sadis.o +LIBSA = $(ARCH)/libsaproc.so LIBS = -lutil -lthread_db -CFLAGS = -c -fPIC -g -Wall -D_ALLBSD_SOURCE -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) +else -LIBSA = $(ARCH)/libsaproc.so +SOURCES = symtab.c \ + libproc_impl.c \ + ps_core.c +OBJS = $(SOURCES:.c=.o) +OBJSPLUS = MacosxDebuggerLocal.o sadis.o $(OBJS) +EXTINCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers -I. +EXTCFLAGS = -m64 -D__APPLE__ -framework JavaNativeFoundation +FOUNDATIONFLAGS = -framework Foundation -F/System/Library/Frameworks/JavaVM.framework/Frameworks -framework JavaNativeFoundation -framework Security -framework CoreFoundation +LIBSA = $(ARCH)/libsaproc.dylib +endif # Darwin + +INCLUDES = -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(shell uname -s | tr "[:upper:]" "[:lower:]") $(EXTINCLUDE) + + + +CFLAGS = -c -fPIC -g -Wall -D_ALLBSD_SOURCE -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) $(EXTCFLAGS) + + all: $(LIBSA) -BsdDebuggerLocal.o: BsdDebuggerLocal.c - $(JAVAH) -jni -classpath ../../../../../build/bsd-i586/hotspot/outputdir/bsd_i486_compiler2/generated/saclasses \ +MacosxDebuggerLocal.o: MacosxDebuggerLocal.m + echo "OS="$(OS) + $(JAVAH) -jni -classpath ../../../build/classes \ sun.jvm.hotspot.debugger.x86.X86ThreadContext \ sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(GCC) $(CFLAGS) $(FOUNDATIONFLAGS) $< + +sadis.o: ../../share/native/sadis.c + $(JAVAH) -jni -classpath ../../../build/classes \ + sun.jvm.hotspot.asm.Disassembler $(GCC) $(CFLAGS) $< .c.obj: @@ -59,9 +85,9 @@ LFLAGS_LIBSA = -Xlinker --version-script=mapfile endif -$(LIBSA): $(OBJS) mapfile +$(LIBSA): $(OBJSPLUS) mapfile if [ ! -d $(ARCH) ] ; then mkdir $(ARCH) ; fi - $(GCC) -shared $(LFLAGS_LIBSA) -o $(LIBSA) $(OBJS) $(LIBS) + $(GCC) -shared $(LFLAGS_LIBSA) -o $(LIBSA) $(FOUNDATIONFLAGS) $(OBJSPLUS) $(LIBS) $(SALIBS) test.o: $(LIBSA) test.c $(GCC) -c -o test.o -g -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) test.c @@ -71,7 +97,6 @@ clean: rm -f $(LIBSA) - rm -f $(OBJS) + rm -f *.o rm -f test.o -rmdir $(ARCH) -
--- a/agent/src/os/bsd/libproc.h Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/libproc.h Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,9 +27,38 @@ #include <unistd.h> #include <stdint.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#ifdef __APPLE__ +typedef enum ps_err_e { + PS_OK, PS_ERR, PS_BADPID, PS_BADLID, + PS_BADADDR, PS_NOSYM, PS_NOFREGS +} ps_err_e; + +#ifndef psaddr_t +#define psaddr_t uintptr_t +#endif + +#ifndef bool +typedef int bool; +#define true 1 +#define false 0 +#endif // bool + +#ifndef lwpid_t +#define lwpid_t uintptr_t +#endif + +#include <mach/thread_status.h> +#else // __APPLE__ +#include <elf.h> +#include <link.h> #include <machine/reg.h> #include <proc_service.h> - #if defined(sparc) || defined(sparcv9) /* If _LP64 is defined ptrace.h should be taken from /usr/include/asm-sparc64 @@ -44,6 +73,14 @@ #endif //sparc or sparcv9 +// This C bool type must be int for compatibility with BSD calls and +// it would be a mistake to equivalence it to C++ bool on many platforms +typedef int bool; +#define true 1 +#define false 0 + +#endif // __APPLE__ + /************************************************************************************ 0. This is very minimal subset of Solaris libproc just enough for current application. @@ -72,13 +109,7 @@ *************************************************************************************/ -// This C bool type must be int for compatibility with BSD calls and -// it would be a mistake to equivalence it to C++ bool on many platforms - -typedef int bool; -#define true 1 -#define false 0 - +struct reg; struct ps_prochandle; // attach to a process
--- a/agent/src/os/bsd/libproc_impl.c Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/libproc_impl.c Sun Mar 24 12:43:13 2013 -0700 @@ -21,12 +21,6 @@ * questions. * */ -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> -#include <thread_db.h> #include "libproc_impl.h" static const char* alt_root = NULL; @@ -34,61 +28,65 @@ #define SA_ALTROOT "SA_ALTROOT" +off_t ltell(int fd) { + return lseek(fd, 0, SEEK_CUR); +} + static void init_alt_root() { - if (alt_root_len == -1) { - alt_root = getenv(SA_ALTROOT); - if (alt_root) { - alt_root_len = strlen(alt_root); - } else { - alt_root_len = 0; - } - } + if (alt_root_len == -1) { + alt_root = getenv(SA_ALTROOT); + if (alt_root) { + alt_root_len = strlen(alt_root); + } else { + alt_root_len = 0; + } + } } int pathmap_open(const char* name) { - int fd; - char alt_path[PATH_MAX + 1]; + int fd; + char alt_path[PATH_MAX + 1]; + + init_alt_root(); - init_alt_root(); - fd = open(name, O_RDONLY); - if (fd >= 0) { + if (alt_root_len > 0) { + strcpy(alt_path, alt_root); + strcat(alt_path, name); + fd = open(alt_path, O_RDONLY); + if (fd >= 0) { + print_debug("path %s substituted for %s\n", alt_path, name); return fd; - } + } - if (alt_root_len > 0) { + if (strrchr(name, '/')) { strcpy(alt_path, alt_root); - strcat(alt_path, name); + strcat(alt_path, strrchr(name, '/')); fd = open(alt_path, O_RDONLY); if (fd >= 0) { - print_debug("path %s substituted for %s\n", alt_path, name); - return fd; + print_debug("path %s substituted for %s\n", alt_path, name); + return fd; } - - if (strrchr(name, '/')) { - strcpy(alt_path, alt_root); - strcat(alt_path, strrchr(name, '/')); - fd = open(alt_path, O_RDONLY); - if (fd >= 0) { - print_debug("path %s substituted for %s\n", alt_path, name); - return fd; - } - } - } - - return -1; + } + } else { + fd = open(name, O_RDONLY); + if (fd >= 0) { + return fd; + } + } + return -1; } static bool _libsaproc_debug; void print_debug(const char* format,...) { - if (_libsaproc_debug) { - va_list alist; + if (_libsaproc_debug) { + va_list alist; - va_start(alist, format); - fputs("libsaproc DEBUG: ", stderr); - vfprintf(stderr, format, alist); - va_end(alist); - } + va_start(alist, format); + fputs("libsaproc DEBUG: ", stderr); + vfprintf(stderr, format, alist); + va_end(alist); + } } void print_error(const char* format,...) { @@ -100,172 +98,235 @@ } bool is_debug() { - return _libsaproc_debug; + return _libsaproc_debug; } +#ifdef __APPLE__ +// get arch offset in file +bool get_arch_off(int fd, cpu_type_t cputype, off_t *offset) { + struct fat_header fatheader; + struct fat_arch fatarch; + off_t img_start = 0; + + off_t pos = ltell(fd); + if (read(fd, (void *)&fatheader, sizeof(struct fat_header)) != sizeof(struct fat_header)) { + return false; + } + if (fatheader.magic == FAT_CIGAM) { + int i; + for (i = 0; i < ntohl(fatheader.nfat_arch); i++) { + if (read(fd, (void *)&fatarch, sizeof(struct fat_arch)) != sizeof(struct fat_arch)) { + return false; + } + if (ntohl(fatarch.cputype) == cputype) { + print_debug("fat offset=%x\n", ntohl(fatarch.offset)); + img_start = ntohl(fatarch.offset); + break; + } + } + if (img_start == 0) { + return false; + } + } + lseek(fd, pos, SEEK_SET); + *offset = img_start; + return true; +} + +bool is_macho_file(int fd) { + mach_header_64 fhdr; + off_t x86_64_off; + + if (fd < 0) { + print_debug("Invalid file handle passed to is_macho_file\n"); + return false; + } + + off_t pos = ltell(fd); + // check fat header + if (!get_arch_off(fd, CPU_TYPE_X86_64, &x86_64_off)) { + print_debug("failed to get fat header\n"); + return false; + } + lseek(fd, x86_64_off, SEEK_SET); + if (read(fd, (void *)&fhdr, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + return false; + } + lseek(fd, pos, SEEK_SET); // restore + print_debug("fhdr.magic %x\n", fhdr.magic); + return (fhdr.magic == MH_MAGIC_64 || fhdr.magic == MH_CIGAM_64); +} + +#endif //__APPLE__ + // initialize libproc bool init_libproc(bool debug) { - // init debug mode _libsaproc_debug = debug; - +#ifndef __APPLE__ // initialize the thread_db library if (td_init() != TD_OK) { print_debug("libthread_db's td_init failed\n"); return false; } - +#endif // __APPLE__ return true; } -static void destroy_lib_info(struct ps_prochandle* ph) { - lib_info* lib = ph->libs; - while (lib) { - lib_info *next = lib->next; - if (lib->symtab) { - destroy_symtab(lib->symtab); - } - free(lib); - lib = next; - } +void destroy_lib_info(struct ps_prochandle* ph) { + lib_info* lib = ph->libs; + while (lib) { + lib_info* next = lib->next; + if (lib->symtab) { + destroy_symtab(lib->symtab); + } + free(lib); + lib = next; + } } -static void destroy_thread_info(struct ps_prochandle* ph) { - thread_info* thr = ph->threads; - while (thr) { - thread_info *next = thr->next; - free(thr); - thr = next; - } +void destroy_thread_info(struct ps_prochandle* ph) { + sa_thread_info* thr = ph->threads; + while (thr) { + sa_thread_info* n = thr->next; + free(thr); + thr = n; + } } // ps_prochandle cleanup - -// ps_prochandle cleanup void Prelease(struct ps_prochandle* ph) { - // do the "derived class" clean-up first - ph->ops->release(ph); - destroy_lib_info(ph); - destroy_thread_info(ph); - free(ph); + // do the "derived class" clean-up first + ph->ops->release(ph); + destroy_lib_info(ph); + destroy_thread_info(ph); + free(ph); } lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base) { - return add_lib_info_fd(ph, libname, -1, base); + return add_lib_info_fd(ph, libname, -1, base); } lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base) { lib_info* newlib; + print_debug("add_lib_info_fd %s\n", libname); - if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) { - print_debug("can't allocate memory for lib_info\n"); - return NULL; - } - - strncpy(newlib->name, libname, sizeof(newlib->name)); - newlib->base = base; + if ( (newlib = (lib_info*) calloc(1, sizeof(struct lib_info))) == NULL) { + print_debug("can't allocate memory for lib_info\n"); + return NULL; + } - if (fd == -1) { - if ( (newlib->fd = pathmap_open(newlib->name)) < 0) { - print_debug("can't open shared object %s\n", newlib->name); - free(newlib); - return NULL; - } - } else { - newlib->fd = fd; - } + strncpy(newlib->name, libname, sizeof(newlib->name)); + newlib->base = base; - // check whether we have got an ELF file. /proc/<pid>/map - // gives out all file mappings and not just shared objects - if (is_elf_file(newlib->fd) == false) { - close(newlib->fd); + if (fd == -1) { + if ( (newlib->fd = pathmap_open(newlib->name)) < 0) { + print_debug("can't open shared object %s\n", newlib->name); free(newlib); return NULL; - } - - newlib->symtab = build_symtab(newlib->fd); - if (newlib->symtab == NULL) { - print_debug("symbol table build failed for %s\n", newlib->name); - } - else { - print_debug("built symbol table for %s\n", newlib->name); - } + } + } else { + newlib->fd = fd; + } - // even if symbol table building fails, we add the lib_info. - // This is because we may need to read from the ELF file for core file - // address read functionality. lookup_symbol checks for NULL symtab. - if (ph->libs) { - ph->lib_tail->next = newlib; - ph->lib_tail = newlib; - } else { - ph->libs = ph->lib_tail = newlib; - } - ph->num_libs++; +#ifdef __APPLE__ + // check whether we have got an Macho file. + if (is_macho_file(newlib->fd) == false) { + close(newlib->fd); + free(newlib); + print_debug("not a mach-o file\n"); + return NULL; + } +#else + // check whether we have got an ELF file. /proc/<pid>/map + // gives out all file mappings and not just shared objects + if (is_elf_file(newlib->fd) == false) { + close(newlib->fd); + free(newlib); + return NULL; + } +#endif // __APPLE__ - return newlib; + newlib->symtab = build_symtab(newlib->fd); + if (newlib->symtab == NULL) { + print_debug("symbol table build failed for %s\n", newlib->name); + } else { + print_debug("built symbol table for %s\n", newlib->name); + } + + // even if symbol table building fails, we add the lib_info. + // This is because we may need to read from the ELF file or MachO file for core file + // address read functionality. lookup_symbol checks for NULL symtab. + if (ph->libs) { + ph->lib_tail->next = newlib; + ph->lib_tail = newlib; + } else { + ph->libs = ph->lib_tail = newlib; + } + ph->num_libs++; + return newlib; } // lookup for a specific symbol uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, const char* sym_name) { - // ignore object_name. search in all libraries - // FIXME: what should we do with object_name?? The library names are obtained - // by parsing /proc/<pid>/maps, which may not be the same as object_name. - // What we need is a utility to map object_name to real file name, something - // dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For - // now, we just ignore object_name and do a global search for the symbol. + // ignore object_name. search in all libraries + // FIXME: what should we do with object_name?? The library names are obtained + // by parsing /proc/<pid>/maps, which may not be the same as object_name. + // What we need is a utility to map object_name to real file name, something + // dlopen() does by looking at LD_LIBRARY_PATH and /etc/ld.so.cache. For + // now, we just ignore object_name and do a global search for the symbol. - lib_info* lib = ph->libs; - while (lib) { - if (lib->symtab) { - uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL); - if (res) return res; - } - lib = lib->next; - } + lib_info* lib = ph->libs; + while (lib) { + if (lib->symtab) { + uintptr_t res = search_symbol(lib->symtab, lib->base, sym_name, NULL); + if (res) return res; + } + lib = lib->next; + } - print_debug("lookup failed for symbol '%s' in obj '%s'\n", + print_debug("lookup failed for symbol '%s' in obj '%s'\n", sym_name, object_name); - return (uintptr_t) NULL; + return (uintptr_t) NULL; } - const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset) { - const char* res = NULL; - lib_info* lib = ph->libs; - while (lib) { - if (lib->symtab && addr >= lib->base) { - res = nearest_symbol(lib->symtab, addr - lib->base, poffset); - if (res) return res; - } - lib = lib->next; - } - return NULL; + const char* res = NULL; + lib_info* lib = ph->libs; + while (lib) { + if (lib->symtab && addr >= lib->base) { + res = nearest_symbol(lib->symtab, addr - lib->base, poffset); + if (res) return res; + } + lib = lib->next; + } + return NULL; } // add a thread to ps_prochandle -thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) { - thread_info* newthr; - if ( (newthr = (thread_info*) calloc(1, sizeof(thread_info))) == NULL) { - print_debug("can't allocate memory for thread_info\n"); - return NULL; - } +sa_thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) { + sa_thread_info* newthr; + if ( (newthr = (sa_thread_info*) calloc(1, sizeof(sa_thread_info))) == NULL) { + print_debug("can't allocate memory for thread_info\n"); + return NULL; + } - // initialize thread info - newthr->pthread_id = pthread_id; - newthr->lwp_id = lwp_id; + // initialize thread info + newthr->pthread_id = pthread_id; + newthr->lwp_id = lwp_id; - // add new thread to the list - newthr->next = ph->threads; - ph->threads = newthr; - ph->num_threads++; - return newthr; + // add new thread to the list + newthr->next = ph->threads; + ph->threads = newthr; + ph->num_threads++; + return newthr; } - +#ifndef __APPLE__ // struct used for client data from thread_db callback struct thread_db_client_data { - struct ps_prochandle* ph; - thread_info_callback callback; + struct ps_prochandle* ph; + thread_info_callback callback; }; // callback function for libthread_db @@ -314,6 +375,7 @@ return true; } +#endif // __APPLE__ // get number of threads int get_num_threads(struct ps_prochandle* ph) { @@ -322,18 +384,54 @@ // get lwp_id of n'th thread lwpid_t get_lwp_id(struct ps_prochandle* ph, int index) { - int count = 0; - thread_info* thr = ph->threads; - while (thr) { - if (count == index) { - return thr->lwp_id; - } - count++; - thr = thr->next; - } - return -1; + int count = 0; + sa_thread_info* thr = ph->threads; + while (thr) { + if (count == index) { + return thr->lwp_id; + } + count++; + thr = thr->next; + } + return 0; } +#ifdef __APPLE__ +// set lwp_id of n'th thread +bool set_lwp_id(struct ps_prochandle* ph, int index, lwpid_t lwpid) { + int count = 0; + sa_thread_info* thr = ph->threads; + while (thr) { + if (count == index) { + thr->lwp_id = lwpid; + return true; + } + count++; + thr = thr->next; + } + return false; +} + +// get regs of n-th thread, only used in fillThreads the first time called +bool get_nth_lwp_regs(struct ps_prochandle* ph, int index, struct reg* regs) { + int count = 0; + sa_thread_info* thr = ph->threads; + while (thr) { + if (count == index) { + break; + } + count++; + thr = thr->next; + } + if (thr != NULL) { + memcpy(regs, &thr->regs, sizeof(struct reg)); + return true; + } + return false; +} + +#endif // __APPLE__ + // get regs for a given lwp bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct reg* regs) { return ph->ops->get_lwp_regs(ph, lwp_id, regs); @@ -341,35 +439,35 @@ // get number of shared objects int get_num_libs(struct ps_prochandle* ph) { - return ph->num_libs; + return ph->num_libs; } // get name of n'th solib const char* get_lib_name(struct ps_prochandle* ph, int index) { - int count = 0; - lib_info* lib = ph->libs; - while (lib) { - if (count == index) { - return lib->name; - } - count++; - lib = lib->next; - } - return NULL; + int count = 0; + lib_info* lib = ph->libs; + while (lib) { + if (count == index) { + return lib->name; + } + count++; + lib = lib->next; + } + return NULL; } // get base address of a lib uintptr_t get_lib_base(struct ps_prochandle* ph, int index) { - int count = 0; - lib_info* lib = ph->libs; - while (lib) { - if (count == index) { - return lib->base; - } - count++; - lib = lib->next; - } - return (uintptr_t)NULL; + int count = 0; + lib_info* lib = ph->libs; + while (lib) { + if (count == index) { + return lib->base; + } + count++; + lib = lib->next; + } + return (uintptr_t)NULL; } bool find_lib(struct ps_prochandle* ph, const char *lib_name) { @@ -425,6 +523,7 @@ va_end(alist); } +#ifndef __APPLE__ // ------------------------------------------------------------------------ // Functions below this point are not yet implemented. They are here only // to make the linker happy. @@ -458,3 +557,4 @@ print_debug("ps_pcontinue not implemented\n"); return PS_OK; } +#endif // __APPLE__
--- a/agent/src/os/bsd/libproc_impl.h Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/libproc_impl.h Sun Mar 24 12:43:13 2013 -0700 @@ -30,6 +30,60 @@ #include "libproc.h" #include "symtab.h" +#ifdef __APPLE__ +#include <inttypes.h> // for PRIx64, 32, ... +#include <pthread.h> +#include <mach-o/loader.h> +#include <mach-o/nlist.h> +#include <mach-o/fat.h> + +#ifndef register_t +#define register_t uint64_t +#endif + +/*** registers copied from bsd/amd64 */ +typedef struct reg { + register_t r_r15; + register_t r_r14; + register_t r_r13; + register_t r_r12; + register_t r_r11; + register_t r_r10; + register_t r_r9; + register_t r_r8; + register_t r_rdi; + register_t r_rsi; + register_t r_rbp; + register_t r_rbx; + register_t r_rdx; + register_t r_rcx; + register_t r_rax; + uint32_t r_trapno; // not used + uint16_t r_fs; + uint16_t r_gs; + uint32_t r_err; // not used + uint16_t r_es; // not used + uint16_t r_ds; // not used + register_t r_rip; + register_t r_cs; + register_t r_rflags; + register_t r_rsp; + register_t r_ss; // not used +} reg; + +// convenient defs +typedef struct mach_header_64 mach_header_64; +typedef struct load_command load_command; +typedef struct segment_command_64 segment_command_64; +typedef struct thread_command thread_command; +typedef struct dylib_command dylib_command; +typedef struct symtab_command symtab_command; +typedef struct nlist_64 nlist_64; +#else +#include <thread_db.h> +#include "salibelf.h" +#endif // __APPLE__ + // data structures in this file mimic those of Solaris 8.0 - libproc's Pcontrol.h #define BUF_SIZE (PATH_MAX + NAME_MAX + 1) @@ -44,12 +98,12 @@ } lib_info; // list of threads -typedef struct thread_info { - lwpid_t lwp_id; - pthread_t pthread_id; // not used cores, always -1 +typedef struct sa_thread_info { + lwpid_t lwp_id; // same as pthread_t + pthread_t pthread_id; // struct reg regs; // not for process, core uses for caching regset - struct thread_info* next; -} thread_info; + struct sa_thread_info* next; +} sa_thread_info; // list of virtual memory maps typedef struct map_info { @@ -91,6 +145,7 @@ // part of the class sharing workaround map_info* class_share_maps;// class share maps in a linked list map_info** map_array; // sorted (by vaddr) array of map_info pointers + char exec_path[4096]; // file name java }; struct ps_prochandle { @@ -100,12 +155,11 @@ lib_info* libs; // head of lib list lib_info* lib_tail; // tail of lib list - to append at the end int num_threads; - thread_info* threads; // head of thread list + sa_thread_info* threads; // head of thread list struct core_data* core; // data only used for core dumps, NULL for process }; int pathmap_open(const char* name); - void print_debug(const char* format,...); void print_error(const char* format,...); bool is_debug(); @@ -122,10 +176,45 @@ lib_info* add_lib_info_fd(struct ps_prochandle* ph, const char* libname, int fd, uintptr_t base); -// adds a new thread to threads list, returns NULL on failure -thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id); - +sa_thread_info* add_thread_info(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id); // a test for ELF signature without using libelf + +#ifdef __APPLE__ +// a test for Mach-O signature +bool is_macho_file(int fd); +// skip fat head to get image start offset of cpu_type_t +// return false if any error happens, else value in offset. +bool get_arch_off(int fd, cpu_type_t cputype, off_t *offset); +#else bool is_elf_file(int fd); +#endif // __APPLE__ + +lwpid_t get_lwp_id(struct ps_prochandle* ph, int index); +bool set_lwp_id(struct ps_prochandle* ph, int index, lwpid_t lwpid); +bool get_nth_lwp_regs(struct ps_prochandle* ph, int index, struct reg* regs); + +// ps_pglobal_lookup() looks up the symbol sym_name in the symbol table +// of the load object object_name in the target process identified by ph. +// It returns the symbol's value as an address in the target process in +// *sym_addr. +ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name, + const char *sym_name, psaddr_t *sym_addr); + +// read "size" bytes info "buf" from address "addr" +ps_err_e ps_pread(struct ps_prochandle *ph, psaddr_t addr, + void *buf, size_t size); + +// write "size" bytes of data to debuggee at address "addr" +ps_err_e ps_pwrite(struct ps_prochandle *ph, psaddr_t addr, + const void *buf, size_t size); + +// fill in ptrace_lwpinfo for lid +ps_err_e ps_linfo(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo); + +// needed for when libthread_db is compiled with TD_DEBUG defined +void ps_plog (const char *format, ...); + +// untility, tells the position in file +off_t ltell(int fd); #endif //_LIBPROC_IMPL_H_
--- a/agent/src/os/bsd/ps_core.c Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/ps_core.c Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,10 +28,11 @@ #include <string.h> #include <stdlib.h> #include <stddef.h> -#include <elf.h> -#include <link.h> #include "libproc_impl.h" -#include "salibelf.h" + +#ifdef __APPLE__ +#include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h" +#endif // This file has the libproc implementation to read core files. // For live processes, refer to ps_proc.c. Portions of this is adapted @@ -41,156 +42,158 @@ // ps_prochandle cleanup helper functions // close all file descriptors -static void close_elf_files(struct ps_prochandle* ph) { - lib_info* lib = NULL; +static void close_files(struct ps_prochandle* ph) { + lib_info* lib = NULL; + // close core file descriptor + if (ph->core->core_fd >= 0) + close(ph->core->core_fd); - // close core file descriptor - if (ph->core->core_fd >= 0) - close(ph->core->core_fd); + // close exec file descriptor + if (ph->core->exec_fd >= 0) + close(ph->core->exec_fd); - // close exec file descriptor - if (ph->core->exec_fd >= 0) - close(ph->core->exec_fd); + // close interp file descriptor + if (ph->core->interp_fd >= 0) + close(ph->core->interp_fd); - // close interp file descriptor - if (ph->core->interp_fd >= 0) - close(ph->core->interp_fd); - - // close class share archive file - if (ph->core->classes_jsa_fd >= 0) - close(ph->core->classes_jsa_fd); + // close class share archive file + if (ph->core->classes_jsa_fd >= 0) + close(ph->core->classes_jsa_fd); - // close all library file descriptors - lib = ph->libs; - while (lib) { - int fd = lib->fd; - if (fd >= 0 && fd != ph->core->exec_fd) close(fd); - lib = lib->next; - } + // close all library file descriptors + lib = ph->libs; + while (lib) { + int fd = lib->fd; + if (fd >= 0 && fd != ph->core->exec_fd) { + close(fd); + } + lib = lib->next; + } } // clean all map_info stuff static void destroy_map_info(struct ps_prochandle* ph) { map_info* map = ph->core->maps; while (map) { - map_info* next = map->next; - free(map); - map = next; + map_info* next = map->next; + free(map); + map = next; } if (ph->core->map_array) { - free(ph->core->map_array); + free(ph->core->map_array); } // Part of the class sharing workaround map = ph->core->class_share_maps; while (map) { - map_info* next = map->next; - free(map); - map = next; + map_info* next = map->next; + free(map); + map = next; } } // ps_prochandle operations static void core_release(struct ps_prochandle* ph) { - if (ph->core) { - close_elf_files(ph); - destroy_map_info(ph); - free(ph->core); - } + if (ph->core) { + close_files(ph); + destroy_map_info(ph); + free(ph->core); + } } static map_info* allocate_init_map(int fd, off_t offset, uintptr_t vaddr, size_t memsz) { - map_info* map; - if ( (map = (map_info*) calloc(1, sizeof(map_info))) == NULL) { - print_debug("can't allocate memory for map_info\n"); - return NULL; - } + map_info* map; + if ( (map = (map_info*) calloc(1, sizeof(map_info))) == NULL) { + print_debug("can't allocate memory for map_info\n"); + return NULL; + } - // initialize map - map->fd = fd; - map->offset = offset; - map->vaddr = vaddr; - map->memsz = memsz; - return map; + // initialize map + map->fd = fd; + map->offset = offset; + map->vaddr = vaddr; + map->memsz = memsz; + return map; } // add map info with given fd, offset, vaddr and memsz static map_info* add_map_info(struct ps_prochandle* ph, int fd, off_t offset, uintptr_t vaddr, size_t memsz) { - map_info* map; - if ((map = allocate_init_map(fd, offset, vaddr, memsz)) == NULL) { - return NULL; - } + map_info* map; + if ((map = allocate_init_map(fd, offset, vaddr, memsz)) == NULL) { + return NULL; + } - // add this to map list - map->next = ph->core->maps; - ph->core->maps = map; - ph->core->num_maps++; + // add this to map list + map->next = ph->core->maps; + ph->core->maps = map; + ph->core->num_maps++; - return map; + return map; } // Part of the class sharing workaround static map_info* add_class_share_map_info(struct ps_prochandle* ph, off_t offset, uintptr_t vaddr, size_t memsz) { - map_info* map; - if ((map = allocate_init_map(ph->core->classes_jsa_fd, - offset, vaddr, memsz)) == NULL) { - return NULL; - } + map_info* map; + if ((map = allocate_init_map(ph->core->classes_jsa_fd, + offset, vaddr, memsz)) == NULL) { + return NULL; + } - map->next = ph->core->class_share_maps; - ph->core->class_share_maps = map; - return map; + map->next = ph->core->class_share_maps; + ph->core->class_share_maps = map; + return map; } // Return the map_info for the given virtual address. We keep a sorted // array of pointers in ph->map_array, so we can binary search. static map_info* core_lookup(struct ps_prochandle *ph, uintptr_t addr) { - int mid, lo = 0, hi = ph->core->num_maps - 1; - map_info *mp; + int mid, lo = 0, hi = ph->core->num_maps - 1; + map_info *mp; - while (hi - lo > 1) { - mid = (lo + hi) / 2; - if (addr >= ph->core->map_array[mid]->vaddr) - lo = mid; - else - hi = mid; - } + while (hi - lo > 1) { + mid = (lo + hi) / 2; + if (addr >= ph->core->map_array[mid]->vaddr) { + lo = mid; + } else { + hi = mid; + } + } - if (addr < ph->core->map_array[hi]->vaddr) - mp = ph->core->map_array[lo]; - else - mp = ph->core->map_array[hi]; + if (addr < ph->core->map_array[hi]->vaddr) { + mp = ph->core->map_array[lo]; + } else { + mp = ph->core->map_array[hi]; + } - if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) - return (mp); + if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) { + return (mp); + } - // Part of the class sharing workaround - // Unfortunately, we have no way of detecting -Xshare state. - // Check out the share maps atlast, if we don't find anywhere. - // This is done this way so to avoid reading share pages - // ahead of other normal maps. For eg. with -Xshare:off we don't - // want to prefer class sharing data to data from core. - mp = ph->core->class_share_maps; - if (mp) { - print_debug("can't locate map_info at 0x%lx, trying class share maps\n", - addr); - } - while (mp) { - if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) { - print_debug("located map_info at 0x%lx from class share maps\n", - addr); - return (mp); - } - mp = mp->next; - } + // Part of the class sharing workaround + // Unfortunately, we have no way of detecting -Xshare state. + // Check out the share maps atlast, if we don't find anywhere. + // This is done this way so to avoid reading share pages + // ahead of other normal maps. For eg. with -Xshare:off we don't + // want to prefer class sharing data to data from core. + mp = ph->core->class_share_maps; + if (mp) { + print_debug("can't locate map_info at 0x%lx, trying class share maps\n", addr); + } + while (mp) { + if (addr >= mp->vaddr && addr < mp->vaddr + mp->memsz) { + print_debug("located map_info at 0x%lx from class share maps\n", addr); + return (mp); + } + mp = mp->next; + } - print_debug("can't locate map_info at 0x%lx\n", addr); - return (NULL); + print_debug("can't locate map_info at 0x%lx\n", addr); + return (NULL); } //--------------------------------------------------------------- @@ -239,157 +242,171 @@ }; static bool read_jboolean(struct ps_prochandle* ph, uintptr_t addr, jboolean* pvalue) { - jboolean i; - if (ps_pread(ph, (psaddr_t) addr, &i, sizeof(i)) == PS_OK) { - *pvalue = i; - return true; - } else { - return false; - } + jboolean i; + if (ps_pread(ph, (psaddr_t) addr, &i, sizeof(i)) == PS_OK) { + *pvalue = i; + return true; + } else { + return false; + } } static bool read_pointer(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* pvalue) { - uintptr_t uip; - if (ps_pread(ph, (psaddr_t) addr, &uip, sizeof(uip)) == PS_OK) { - *pvalue = uip; - return true; - } else { - return false; - } + uintptr_t uip; + if (ps_pread(ph, (psaddr_t) addr, (char *)&uip, sizeof(uip)) == PS_OK) { + *pvalue = uip; + return true; + } else { + return false; + } } // used to read strings from debuggee static bool read_string(struct ps_prochandle* ph, uintptr_t addr, char* buf, size_t size) { - size_t i = 0; - char c = ' '; + size_t i = 0; + char c = ' '; - while (c != '\0') { - if (ps_pread(ph, (psaddr_t) addr, &c, sizeof(char)) != PS_OK) - return false; - if (i < size - 1) - buf[i] = c; - else // smaller buffer - return false; - i++; addr++; - } - - buf[i] = '\0'; - return true; + while (c != '\0') { + if (ps_pread(ph, (psaddr_t) addr, &c, sizeof(char)) != PS_OK) { + return false; + } + if (i < size - 1) { + buf[i] = c; + } else { + // smaller buffer + return false; + } + i++; addr++; + } + buf[i] = '\0'; + return true; } +#ifdef __APPLE__ +#define USE_SHARED_SPACES_SYM "_UseSharedSpaces" +// mangled name of Arguments::SharedArchivePath +#define SHARED_ARCHIVE_PATH_SYM "_ZN9Arguments17SharedArchivePathE" +#else #define USE_SHARED_SPACES_SYM "UseSharedSpaces" // mangled name of Arguments::SharedArchivePath -#define SHARED_ARCHIVE_PATH_SYM "_ZN9Arguments17SharedArchivePathE" +#define SHARED_ARCHIVE_PATH_SYM "__ZN9Arguments17SharedArchivePathE" +#endif // __APPLE_ static bool init_classsharing_workaround(struct ps_prochandle* ph) { - lib_info* lib = ph->libs; - while (lib != NULL) { - // we are iterating over shared objects from the core dump. look for - // libjvm[_g].so. - const char *jvm_name = 0; - if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0 || - (jvm_name = strstr(lib->name, "/libjvm_g.so")) != 0) { - char classes_jsa[PATH_MAX]; - struct FileMapHeader header; - size_t n = 0; - int fd = -1, m = 0; - uintptr_t base = 0, useSharedSpacesAddr = 0; - uintptr_t sharedArchivePathAddrAddr = 0, sharedArchivePathAddr = 0; - jboolean useSharedSpaces = 0; - - memset(classes_jsa, 0, sizeof(classes_jsa)); - jvm_name = lib->name; - useSharedSpacesAddr = lookup_symbol(ph, jvm_name, USE_SHARED_SPACES_SYM); - if (useSharedSpacesAddr == 0) { - print_debug("can't lookup 'UseSharedSpaces' flag\n"); - return false; - } + int m; + size_t n; + lib_info* lib = ph->libs; + while (lib != NULL) { + // we are iterating over shared objects from the core dump. look for + // libjvm[_g].so. + const char *jvm_name = 0; +#ifdef __APPLE__ + if ((jvm_name = strstr(lib->name, "/libjvm.dylib")) != 0 || + (jvm_name = strstr(lib->name, "/libjvm_g.dylib")) != 0) +#else + if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0 || + (jvm_name = strstr(lib->name, "/libjvm_g.so")) != 0) +#endif // __APPLE__ + { + char classes_jsa[PATH_MAX]; + struct FileMapHeader header; + int fd = -1; + uintptr_t base = 0, useSharedSpacesAddr = 0; + uintptr_t sharedArchivePathAddrAddr = 0, sharedArchivePathAddr = 0; + jboolean useSharedSpaces = 0; - // Hotspot vm types are not exported to build this library. So - // using equivalent type jboolean to read the value of - // UseSharedSpaces which is same as hotspot type "bool". - if (read_jboolean(ph, useSharedSpacesAddr, &useSharedSpaces) != true) { - print_debug("can't read the value of 'UseSharedSpaces' flag\n"); - return false; - } + memset(classes_jsa, 0, sizeof(classes_jsa)); + jvm_name = lib->name; + useSharedSpacesAddr = lookup_symbol(ph, jvm_name, USE_SHARED_SPACES_SYM); + if (useSharedSpacesAddr == 0) { + print_debug("can't lookup 'UseSharedSpaces' flag\n"); + return false; + } - if ((int)useSharedSpaces == 0) { - print_debug("UseSharedSpaces is false, assuming -Xshare:off!\n"); - return true; - } + // Hotspot vm types are not exported to build this library. So + // using equivalent type jboolean to read the value of + // UseSharedSpaces which is same as hotspot type "bool". + if (read_jboolean(ph, useSharedSpacesAddr, &useSharedSpaces) != true) { + print_debug("can't read the value of 'UseSharedSpaces' flag\n"); + return false; + } - sharedArchivePathAddrAddr = lookup_symbol(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM); - if (sharedArchivePathAddrAddr == 0) { - print_debug("can't lookup shared archive path symbol\n"); - return false; - } + if ((int)useSharedSpaces == 0) { + print_debug("UseSharedSpaces is false, assuming -Xshare:off!\n"); + return true; + } - if (read_pointer(ph, sharedArchivePathAddrAddr, &sharedArchivePathAddr) != true) { - print_debug("can't read shared archive path pointer\n"); - return false; - } + sharedArchivePathAddrAddr = lookup_symbol(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM); + if (sharedArchivePathAddrAddr == 0) { + print_debug("can't lookup shared archive path symbol\n"); + return false; + } - if (read_string(ph, sharedArchivePathAddr, classes_jsa, sizeof(classes_jsa)) != true) { - print_debug("can't read shared archive path value\n"); - return false; - } + if (read_pointer(ph, sharedArchivePathAddrAddr, &sharedArchivePathAddr) != true) { + print_debug("can't read shared archive path pointer\n"); + return false; + } - print_debug("looking for %s\n", classes_jsa); - // open the class sharing archive file - fd = pathmap_open(classes_jsa); - if (fd < 0) { - print_debug("can't open %s!\n", classes_jsa); - ph->core->classes_jsa_fd = -1; - return false; - } else { - print_debug("opened %s\n", classes_jsa); - } + if (read_string(ph, sharedArchivePathAddr, classes_jsa, sizeof(classes_jsa)) != true) { + print_debug("can't read shared archive path value\n"); + return false; + } - // read FileMapHeader from the file - memset(&header, 0, sizeof(struct FileMapHeader)); - if ((n = read(fd, &header, sizeof(struct FileMapHeader))) - != sizeof(struct FileMapHeader)) { - print_debug("can't read shared archive file map header from %s\n", classes_jsa); - close(fd); - return false; - } + print_debug("looking for %s\n", classes_jsa); + // open the class sharing archive file + fd = pathmap_open(classes_jsa); + if (fd < 0) { + print_debug("can't open %s!\n", classes_jsa); + ph->core->classes_jsa_fd = -1; + return false; + } else { + print_debug("opened %s\n", classes_jsa); + } - // check file magic - if (header._magic != 0xf00baba2) { - print_debug("%s has bad shared archive file magic number 0x%x, expecing 0xf00baba2\n", - classes_jsa, header._magic); - close(fd); - return false; - } + // read FileMapHeader from the file + memset(&header, 0, sizeof(struct FileMapHeader)); + if ((n = read(fd, &header, sizeof(struct FileMapHeader))) + != sizeof(struct FileMapHeader)) { + print_debug("can't read shared archive file map header from %s\n", classes_jsa); + close(fd); + return false; + } - // check version - if (header._version != CURRENT_ARCHIVE_VERSION) { - print_debug("%s has wrong shared archive file version %d, expecting %d\n", - classes_jsa, header._version, CURRENT_ARCHIVE_VERSION); - close(fd); - return false; - } + // check file magic + if (header._magic != 0xf00baba2) { + print_debug("%s has bad shared archive file magic number 0x%x, expecing 0xf00baba2\n", + classes_jsa, header._magic); + close(fd); + return false; + } + + // check version + if (header._version != CURRENT_ARCHIVE_VERSION) { + print_debug("%s has wrong shared archive file version %d, expecting %d\n", + classes_jsa, header._version, CURRENT_ARCHIVE_VERSION); + close(fd); + return false; + } - ph->core->classes_jsa_fd = fd; - // add read-only maps from classes[_g].jsa to the list of maps - for (m = 0; m < NUM_SHARED_MAPS; m++) { - if (header._space[m]._read_only) { - base = (uintptr_t) header._space[m]._base; - // no need to worry about the fractional pages at-the-end. - // possible fractional pages are handled by core_read_data. - add_class_share_map_info(ph, (off_t) header._space[m]._file_offset, - base, (size_t) header._space[m]._used); - print_debug("added a share archive map at 0x%lx\n", base); - } - } - return true; + ph->core->classes_jsa_fd = fd; + // add read-only maps from classes[_g].jsa to the list of maps + for (m = 0; m < NUM_SHARED_MAPS; m++) { + if (header._space[m]._read_only) { + base = (uintptr_t) header._space[m]._base; + // no need to worry about the fractional pages at-the-end. + // possible fractional pages are handled by core_read_data. + add_class_share_map_info(ph, (off_t) header._space[m]._file_offset, + base, (size_t) header._space[m]._used); + print_debug("added a share archive map at 0x%lx\n", base); + } } - lib = lib->next; - } - return true; + return true; + } + lib = lib->next; + } + return true; } - //--------------------------------------------------------------------------- // functions to handle map_info @@ -397,54 +414,57 @@ // callback for sorting the array of map_info pointers. static int core_cmp_mapping(const void *lhsp, const void *rhsp) { - const map_info *lhs = *((const map_info **)lhsp); - const map_info *rhs = *((const map_info **)rhsp); + const map_info *lhs = *((const map_info **)lhsp); + const map_info *rhs = *((const map_info **)rhsp); - if (lhs->vaddr == rhs->vaddr) - return (0); + if (lhs->vaddr == rhs->vaddr) { + return (0); + } - return (lhs->vaddr < rhs->vaddr ? -1 : 1); + return (lhs->vaddr < rhs->vaddr ? -1 : 1); } // we sort map_info by starting virtual address so that we can do // binary search to read from an address. static bool sort_map_array(struct ps_prochandle* ph) { - size_t num_maps = ph->core->num_maps; - map_info* map = ph->core->maps; - int i = 0; + size_t num_maps = ph->core->num_maps; + map_info* map = ph->core->maps; + int i = 0; - // allocate map_array - map_info** array; - if ( (array = (map_info**) malloc(sizeof(map_info*) * num_maps)) == NULL) { - print_debug("can't allocate memory for map array\n"); - return false; - } + // allocate map_array + map_info** array; + if ( (array = (map_info**) malloc(sizeof(map_info*) * num_maps)) == NULL) { + print_debug("can't allocate memory for map array\n"); + return false; + } - // add maps to array - while (map) { - array[i] = map; - i++; - map = map->next; - } + // add maps to array + while (map) { + array[i] = map; + i++; + map = map->next; + } - // sort is called twice. If this is second time, clear map array - if (ph->core->map_array) free(ph->core->map_array); - ph->core->map_array = array; - // sort the map_info array by base virtual address. - qsort(ph->core->map_array, ph->core->num_maps, sizeof (map_info*), - core_cmp_mapping); + // sort is called twice. If this is second time, clear map array + if (ph->core->map_array) { + free(ph->core->map_array); + } + ph->core->map_array = array; + // sort the map_info array by base virtual address. + qsort(ph->core->map_array, ph->core->num_maps, sizeof (map_info*), + core_cmp_mapping); - // print map - if (is_debug()) { - int j = 0; - print_debug("---- sorted virtual address map ----\n"); - for (j = 0; j < ph->core->num_maps; j++) { - print_debug("base = 0x%lx\tsize = %d\n", ph->core->map_array[j]->vaddr, - ph->core->map_array[j]->memsz); - } - } + // print map + if (is_debug()) { + int j = 0; + print_debug("---- sorted virtual address map ----\n"); + for (j = 0; j < ph->core->num_maps; j++) { + print_debug("base = 0x%lx\tsize = %d\n", ph->core->map_array[j]->vaddr, + ph->core->map_array[j]->memsz); + } + } - return true; + return true; } #ifndef MIN @@ -461,16 +481,18 @@ off_t off; int fd; - if (mp == NULL) + if (mp == NULL) { break; /* No mapping for this address */ + } fd = mp->fd; mapoff = addr - mp->vaddr; len = MIN(resid, mp->memsz - mapoff); off = mp->offset + mapoff; - if ((len = pread(fd, buf, len, off)) <= 0) + if ((len = pread(fd, buf, len, off)) <= 0) { break; + } resid -= len; addr += len; @@ -507,8 +529,8 @@ static bool core_get_lwp_regs(struct ps_prochandle* ph, lwpid_t lwp_id, struct reg* regs) { - // for core we have cached the lwp regs from NOTE section - thread_info* thr = ph->threads; + // for core we have cached the lwp regs after segment parsed + sa_thread_info* thr = ph->threads; while (thr) { if (thr->lwp_id == lwp_id) { memcpy(regs, &thr->regs, sizeof(struct reg)); @@ -519,7 +541,7 @@ return false; } -static bool core_get_lwp_info(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) { +static bool core_get_lwp_info(struct ps_prochandle *ph, lwpid_t id, void *info) { print_debug("core_get_lwp_info not implemented\n"); return false; } @@ -532,12 +554,451 @@ .get_lwp_info= core_get_lwp_info }; -// read regs and create thread from NT_PRSTATUS entries from core file +// from this point, mainly two blocks divided by def __APPLE__ +// one for Macosx, the other for regular Bsd + +#ifdef __APPLE__ + +void print_thread(sa_thread_info *threadinfo) { + print_debug("thread added: %d\n", threadinfo->lwp_id); + print_debug("registers:\n"); + print_debug(" r_r15: 0x%" PRIx64 "\n", threadinfo->regs.r_r15); + print_debug(" r_r14: 0x%" PRIx64 "\n", threadinfo->regs.r_r14); + print_debug(" r_r13: 0x%" PRIx64 "\n", threadinfo->regs.r_r13); + print_debug(" r_r12: 0x%" PRIx64 "\n", threadinfo->regs.r_r12); + print_debug(" r_r11: 0x%" PRIx64 "\n", threadinfo->regs.r_r11); + print_debug(" r_r10: 0x%" PRIx64 "\n", threadinfo->regs.r_r10); + print_debug(" r_r9: 0x%" PRIx64 "\n", threadinfo->regs.r_r9); + print_debug(" r_r8: 0x%" PRIx64 "\n", threadinfo->regs.r_r8); + print_debug(" r_rdi: 0x%" PRIx64 "\n", threadinfo->regs.r_rdi); + print_debug(" r_rsi: 0x%" PRIx64 "\n", threadinfo->regs.r_rsi); + print_debug(" r_rbp: 0x%" PRIx64 "\n", threadinfo->regs.r_rbp); + print_debug(" r_rbx: 0x%" PRIx64 "\n", threadinfo->regs.r_rbx); + print_debug(" r_rdx: 0x%" PRIx64 "\n", threadinfo->regs.r_rdx); + print_debug(" r_rcx: 0x%" PRIx64 "\n", threadinfo->regs.r_rcx); + print_debug(" r_rax: 0x%" PRIx64 "\n", threadinfo->regs.r_rax); + print_debug(" r_fs: 0x%" PRIx32 "\n", threadinfo->regs.r_fs); + print_debug(" r_gs: 0x%" PRIx32 "\n", threadinfo->regs.r_gs); + print_debug(" r_rip 0x%" PRIx64 "\n", threadinfo->regs.r_rip); + print_debug(" r_cs: 0x%" PRIx64 "\n", threadinfo->regs.r_cs); + print_debug(" r_rsp: 0x%" PRIx64 "\n", threadinfo->regs.r_rsp); + print_debug(" r_rflags: 0x%" PRIx64 "\n", threadinfo->regs.r_rflags); +} + +// read all segments64 commands from core file +// read all thread commands from core file +static bool read_core_segments(struct ps_prochandle* ph) { + int i = 0; + int num_threads = 0; + int fd = ph->core->core_fd; + off_t offset = 0; + mach_header_64 fhead; + load_command lcmd; + segment_command_64 segcmd; + // thread_command thrcmd; + + lseek(fd, offset, SEEK_SET); + if(read(fd, (void *)&fhead, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + goto err; + } + print_debug("total commands: %d\n", fhead.ncmds); + offset += sizeof(mach_header_64); + for (i = 0; i < fhead.ncmds; i++) { + lseek(fd, offset, SEEK_SET); + if (read(fd, (void *)&lcmd, sizeof(load_command)) != sizeof(load_command)) { + goto err; + } + offset += lcmd.cmdsize; // next command position + if (lcmd.cmd == LC_SEGMENT_64) { + lseek(fd, -sizeof(load_command), SEEK_CUR); + if (read(fd, (void *)&segcmd, sizeof(segment_command_64)) != sizeof(segment_command_64)) { + print_debug("failed to read LC_SEGMENT_64 i = %d!\n", i); + goto err; + } + if (add_map_info(ph, fd, segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize) == NULL) { + print_debug("Failed to add map_info at i = %d\n", i); + goto err; + } + print_debug("segment added: %" PRIu64 " 0x%" PRIx64 " %d\n", + segcmd.fileoff, segcmd.vmaddr, segcmd.vmsize); + } else if (lcmd.cmd == LC_THREAD || lcmd.cmd == LC_UNIXTHREAD) { + typedef struct thread_fc { + uint32_t flavor; + uint32_t count; + } thread_fc; + thread_fc fc; + uint32_t size = sizeof(load_command); + while (size < lcmd.cmdsize) { + if (read(fd, (void *)&fc, sizeof(thread_fc)) != sizeof(thread_fc)) { + printf("Reading flavor, count failed.\n"); + goto err; + } + size += sizeof(thread_fc); + if (fc.flavor == x86_THREAD_STATE) { + x86_thread_state_t thrstate; + if (read(fd, (void *)&thrstate, sizeof(x86_thread_state_t)) != sizeof(x86_thread_state_t)) { + printf("Reading flavor, count failed.\n"); + goto err; + } + size += sizeof(x86_thread_state_t); + // create thread info list, update lwp_id later + sa_thread_info* newthr = add_thread_info(ph, (pthread_t) -1, (lwpid_t) num_threads++); + if (newthr == NULL) { + printf("create thread_info failed\n"); + goto err; + } + + // note __DARWIN_UNIX03 depengs on other definitions +#if __DARWIN_UNIX03 +#define get_register_v(regst, regname) \ + regst.uts.ts64.__##regname +#else +#define get_register_v(regst, regname) \ + regst.uts.ts64.##regname +#endif // __DARWIN_UNIX03 + newthr->regs.r_rax = get_register_v(thrstate, rax); + newthr->regs.r_rbx = get_register_v(thrstate, rbx); + newthr->regs.r_rcx = get_register_v(thrstate, rcx); + newthr->regs.r_rdx = get_register_v(thrstate, rdx); + newthr->regs.r_rdi = get_register_v(thrstate, rdi); + newthr->regs.r_rsi = get_register_v(thrstate, rsi); + newthr->regs.r_rbp = get_register_v(thrstate, rbp); + newthr->regs.r_rsp = get_register_v(thrstate, rsp); + newthr->regs.r_r8 = get_register_v(thrstate, r8); + newthr->regs.r_r9 = get_register_v(thrstate, r9); + newthr->regs.r_r10 = get_register_v(thrstate, r10); + newthr->regs.r_r11 = get_register_v(thrstate, r11); + newthr->regs.r_r12 = get_register_v(thrstate, r12); + newthr->regs.r_r13 = get_register_v(thrstate, r13); + newthr->regs.r_r14 = get_register_v(thrstate, r14); + newthr->regs.r_r15 = get_register_v(thrstate, r15); + newthr->regs.r_rip = get_register_v(thrstate, rip); + newthr->regs.r_rflags = get_register_v(thrstate, rflags); + newthr->regs.r_cs = get_register_v(thrstate, cs); + newthr->regs.r_fs = get_register_v(thrstate, fs); + newthr->regs.r_gs = get_register_v(thrstate, gs); + print_thread(newthr); + } else if (fc.flavor == x86_FLOAT_STATE) { + x86_float_state_t flstate; + if (read(fd, (void *)&flstate, sizeof(x86_float_state_t)) != sizeof(x86_float_state_t)) { + print_debug("Reading flavor, count failed.\n"); + goto err; + } + size += sizeof(x86_float_state_t); + } else if (fc.flavor == x86_EXCEPTION_STATE) { + x86_exception_state_t excpstate; + if (read(fd, (void *)&excpstate, sizeof(x86_exception_state_t)) != sizeof(x86_exception_state_t)) { + printf("Reading flavor, count failed.\n"); + goto err; + } + size += sizeof(x86_exception_state_t); + } + } + } + } + return true; +err: + return false; +} + +/**local function **/ +bool exists(const char *fname) +{ + int fd; + if ((fd = open(fname, O_RDONLY)) > 0) { + close(fd); + return true; + } + return false; +} + +// we check: 1. lib +// 2. lib/server +// 3. jre/lib +// 4. jre/lib/server +// from: 1. exe path +// 2. JAVA_HOME +// 3. DYLD_LIBRARY_PATH +static bool get_real_path(struct ps_prochandle* ph, char *rpath) { + /** check if they exist in JAVA ***/ + char* execname = ph->core->exec_path; + char filepath[4096]; + char* filename = strrchr(rpath, '/'); // like /libjvm.dylib + if (filename == NULL) { + return false; + } + + char* posbin = strstr(execname, "/bin/java"); + if (posbin != NULL) { + memcpy(filepath, execname, posbin - execname); // not include trailing '/' + filepath[posbin - execname] = '\0'; + } else { + char* java_home = getenv("JAVA_HOME"); + if (java_home != NULL) { + strcpy(filepath, java_home); + } else { + char* dyldpath = getenv("DYLD_LIBRARY_PATH"); + char* dypath = strtok(dyldpath, ":"); + while (dypath != NULL) { + strcpy(filepath, dypath); + strcat(filepath, filename); + if (exists(filepath)) { + strcpy(rpath, filepath); + return true; + } + dypath = strtok(dyldpath, ":"); + } + // not found + return false; + } + } + // for exec and java_home, jdkpath now is filepath + size_t filepath_base_size = strlen(filepath); + + // first try /lib/ and /lib/server + strcat(filepath, "/lib"); + strcat(filepath, filename); + if (exists(filepath)) { + strcpy(rpath, filepath); + return true; + } + char* pos = strstr(filepath, filename); // like /libjvm.dylib + *pos = '\0'; + strcat(filepath, "/server"); + strcat(filepath, filename); + if (exists(filepath)) { + strcpy(rpath, filepath); + return true; + } + + // then try /jre/lib/ and /jre/lib/server + filepath[filepath_base_size] = '\0'; + strcat(filepath, "/jre/lib"); + strcat(filepath, filename); + if (exists(filepath)) { + strcpy(rpath, filepath); + return true; + } + pos = strstr(filepath, filename); + *pos = '\0'; + strcat(filepath, "/server"); + strcat(filepath, filename); + if (exists(filepath)) { + strcpy(rpath, filepath); + return true; + } + + return false; +} + +static bool read_shared_lib_info(struct ps_prochandle* ph) { + static int pagesize = 0; + int fd = ph->core->core_fd; + int i = 0, j; + uint32_t v; + mach_header_64 header; // used to check if a file header in segment + load_command lcmd; + dylib_command dylibcmd; + + char name[BUF_SIZE]; // use to store name + + if (pagesize == 0) { + pagesize = getpagesize(); + print_debug("page size is %d\n", pagesize); + } + for (j = 0; j < ph->core->num_maps; j++) { + map_info *iter = ph->core->map_array[j]; // head + off_t fpos = iter->offset; + if (iter->fd != fd) { + // only search core file! + continue; + } + print_debug("map_info %d: vmaddr = 0x%016" PRIx64 " fileoff = %" PRIu64 " vmsize = %" PRIu64 "\n", + j, iter->vaddr, iter->offset, iter->memsz); + lseek(fd, fpos, SEEK_SET); + // we assume .dylib loaded at segment address --- which is true for JVM libraries + // multiple files may be loaded in one segment. + // if first word is not a magic word, means this segment does not contain lib file. + if (read(fd, (void *)&v, sizeof(uint32_t)) == sizeof(uint32_t)) { + if (v != MH_MAGIC_64) { + continue; + } + } else { + // may be encountered last map, which is not readable + continue; + } + while (ltell(fd) - iter->offset < iter->memsz) { + lseek(fd, fpos, SEEK_SET); + if (read(fd, (void *)&v, sizeof(uint32_t)) != sizeof(uint32_t)) { + break; + } + if (v != MH_MAGIC_64) { + fpos = (ltell(fd) + pagesize -1)/pagesize * pagesize; + continue; + } + lseek(fd, -sizeof(uint32_t), SEEK_CUR); + // this is the file begining to core file. + if (read(fd, (void *)&header, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + goto err; + } + fpos = ltell(fd); + + // found a mach-o file in this segment + for (i = 0; i < header.ncmds; i++) { + // read commands in this "file" + // LC_ID_DYLIB is the file itself for a .dylib + lseek(fd, fpos, SEEK_SET); + if (read(fd, (void *)&lcmd, sizeof(load_command)) != sizeof(load_command)) { + return false; // error + } + fpos += lcmd.cmdsize; // next command position + // make sure still within seg size. + if (fpos - lcmd.cmdsize - iter->offset > iter->memsz) { + print_debug("Warning: out of segement limit: %ld \n", fpos - lcmd.cmdsize - iter->offset); + break; // no need to iterate all commands + } + if (lcmd.cmd == LC_ID_DYLIB) { + lseek(fd, -sizeof(load_command), SEEK_CUR); + if (read(fd, (void *)&dylibcmd, sizeof(dylib_command)) != sizeof(dylib_command)) { + return false; + } + /**** name stored at dylib_command.dylib.name.offset, is a C string */ + lseek(fd, dylibcmd.dylib.name.offset - sizeof(dylib_command), SEEK_CUR); + int j = 0; + while (j < BUF_SIZE) { + read(fd, (void *)(name + j), sizeof(char)); + if (name[j] == '\0') break; + j++; + } + print_debug("%s\n", name); + // changed name from @rpath/xxxx.dylib to real path + if (strrchr(name, '@')) { + get_real_path(ph, name); + print_debug("get_real_path returned: %s\n", name); + } + add_lib_info(ph, name, iter->vaddr); + break; + } + } + // done with the file, advanced to next page to search more files + fpos = (ltell(fd) + pagesize - 1) / pagesize * pagesize; + } + } + return true; +err: + return false; +} + +bool read_macho64_header(int fd, mach_header_64* core_header) { + bool is_macho = false; + if (fd < 0) return false; + off_t pos = ltell(fd); + lseek(fd, 0, SEEK_SET); + if (read(fd, (void *)core_header, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + is_macho = false; + } else { + is_macho = (core_header->magic == MH_MAGIC_64 || core_header->magic == MH_CIGAM_64); + } + lseek(fd, pos, SEEK_SET); + return is_macho; +} + +// the one and only one exposed stuff from this file +struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { + mach_header_64 core_header; + mach_header_64 exec_header; + + struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); + if (ph == NULL) { + print_debug("cant allocate ps_prochandle\n"); + return NULL; + } + + if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { + free(ph); + print_debug("can't allocate ps_prochandle\n"); + return NULL; + } + + // initialize ph + ph->ops = &core_ops; + ph->core->core_fd = -1; + ph->core->exec_fd = -1; + ph->core->interp_fd = -1; + + print_debug("exec: %s core: %s", exec_file, core_file); + + strncpy(ph->core->exec_path, exec_file, sizeof(ph->core->exec_path)); + + // open the core file + if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { + print_error("can't open core file\n"); + goto err; + } + + // read core file header + if (read_macho64_header(ph->core->core_fd, &core_header) != true || core_header.filetype != MH_CORE) { + print_debug("core file is not a valid Mach-O file\n"); + goto err; + } + + if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { + print_error("can't open executable file\n"); + goto err; + } + + if (read_macho64_header(ph->core->exec_fd, &exec_header) != true || + exec_header.filetype != MH_EXECUTE) { + print_error("executable file is not a valid Mach-O file\n"); + goto err; + } + + // process core file segments + if (read_core_segments(ph) != true) { + print_error("failed to read core segments\n"); + goto err; + } + + // allocate and sort maps into map_array, we need to do this + // here because read_shared_lib_info needs to read from debuggee + // address space + if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); + goto err; + } + + if (read_shared_lib_info(ph) != true) { + print_error("failed to read libraries\n"); + goto err; + } + + // sort again because we have added more mappings from shared objects + if (sort_map_array(ph) != true) { + print_error("failed to sort segment map array\n"); + goto err; + } + + if (init_classsharing_workaround(ph) != true) { + print_error("failed to workaround classshareing\n"); + goto err; + } + + print_debug("Leave Pgrab_core\n"); + return ph; + +err: + Prelease(ph); + return NULL; +} + +#else // __APPLE__ (none macosx) + +// read regs and create thread from core file static bool core_handle_prstatus(struct ps_prochandle* ph, const char* buf, size_t nbytes) { // we have to read prstatus_t from buf // assert(nbytes == sizeof(prstaus_t), "size mismatch on prstatus_t"); prstatus_t* prstat = (prstatus_t*) buf; - thread_info* newthr; + sa_thread_info* newthr; print_debug("got integer regset for lwp %d\n", prstat->pr_pid); // we set pthread_t to -1 for core dump if((newthr = add_thread_info(ph, (pthread_t) -1, prstat->pr_pid)) == NULL) @@ -632,8 +1093,9 @@ notep->n_type, notep->n_descsz); if (notep->n_type == NT_PRSTATUS) { - if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) + if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) { return false; + } } p = descdata + ROUNDUP(notep->n_descsz, 4); } @@ -681,7 +1143,9 @@ for (core_php = phbuf, i = 0; i < core_ehdr->e_phnum; i++) { switch (core_php->p_type) { case PT_NOTE: - if (core_handle_note(ph, core_php) != true) goto err; + if (core_handle_note(ph, core_php) != true) { + goto err; + } break; case PT_LOAD: { @@ -800,7 +1264,6 @@ return false; } - #define FIRST_LINK_MAP_OFFSET offsetof(struct r_debug, r_map) #define LD_BASE_OFFSET offsetof(struct r_debug, r_ldbase) #define LINK_MAP_ADDR_OFFSET offsetof(struct link_map, l_addr) @@ -810,213 +1273,218 @@ // read shared library info from runtime linker's data structures. // This work is done by librtlb_db in Solaris static bool read_shared_lib_info(struct ps_prochandle* ph) { - uintptr_t addr = ph->core->dynamic_addr; - uintptr_t debug_base; - uintptr_t first_link_map_addr; - uintptr_t ld_base_addr; - uintptr_t link_map_addr; - uintptr_t lib_base_diff; - uintptr_t lib_base; - uintptr_t lib_name_addr; - char lib_name[BUF_SIZE]; - ELF_DYN dyn; - ELF_EHDR elf_ehdr; - int lib_fd; + uintptr_t addr = ph->core->dynamic_addr; + uintptr_t debug_base; + uintptr_t first_link_map_addr; + uintptr_t ld_base_addr; + uintptr_t link_map_addr; + uintptr_t lib_base_diff; + uintptr_t lib_base; + uintptr_t lib_name_addr; + char lib_name[BUF_SIZE]; + ELF_DYN dyn; + ELF_EHDR elf_ehdr; + int lib_fd; - // _DYNAMIC has information of the form - // [tag] [data] [tag] [data] ..... - // Both tag and data are pointer sized. - // We look for dynamic info with DT_DEBUG. This has shared object info. - // refer to struct r_debug in link.h + // _DYNAMIC has information of the form + // [tag] [data] [tag] [data] ..... + // Both tag and data are pointer sized. + // We look for dynamic info with DT_DEBUG. This has shared object info. + // refer to struct r_debug in link.h - dyn.d_tag = DT_NULL; - while (dyn.d_tag != DT_DEBUG) { - if (ps_pread(ph, (psaddr_t) addr, &dyn, sizeof(ELF_DYN)) != PS_OK) { - print_debug("can't read debug info from _DYNAMIC\n"); - return false; - } - addr += sizeof(ELF_DYN); - } + dyn.d_tag = DT_NULL; + while (dyn.d_tag != DT_DEBUG) { + if (ps_pread(ph, (psaddr_t) addr, &dyn, sizeof(ELF_DYN)) != PS_OK) { + print_debug("can't read debug info from _DYNAMIC\n"); + return false; + } + addr += sizeof(ELF_DYN); + } - // we have got Dyn entry with DT_DEBUG - debug_base = dyn.d_un.d_ptr; - // at debug_base we have struct r_debug. This has first link map in r_map field - if (ps_pread(ph, (psaddr_t) debug_base + FIRST_LINK_MAP_OFFSET, - &first_link_map_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read first link map address\n"); - return false; - } + // we have got Dyn entry with DT_DEBUG + debug_base = dyn.d_un.d_ptr; + // at debug_base we have struct r_debug. This has first link map in r_map field + if (ps_pread(ph, (psaddr_t) debug_base + FIRST_LINK_MAP_OFFSET, + &first_link_map_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read first link map address\n"); + return false; + } - // read ld_base address from struct r_debug - // XXX: There is no r_ldbase member on BSD -/* - if (ps_pread(ph, (psaddr_t) debug_base + LD_BASE_OFFSET, &ld_base_addr, - sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read ld base address\n"); - return false; - } - ph->core->ld_base_addr = ld_base_addr; -*/ - ph->core->ld_base_addr = 0; + // read ld_base address from struct r_debug + // XXX: There is no r_ldbase member on BSD + /* + if (ps_pread(ph, (psaddr_t) debug_base + LD_BASE_OFFSET, &ld_base_addr, + sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read ld base address\n"); + return false; + } + ph->core->ld_base_addr = ld_base_addr; + */ + ph->core->ld_base_addr = 0; - print_debug("interpreter base address is 0x%lx\n", ld_base_addr); + print_debug("interpreter base address is 0x%lx\n", ld_base_addr); - // now read segments from interp (i.e ld-elf.so.1) - if (read_interp_segments(ph) != true) - return false; + // now read segments from interp (i.e ld-elf.so.1) + if (read_interp_segments(ph) != true) + return false; - // after adding interpreter (ld.so) mappings sort again - if (sort_map_array(ph) != true) - return false; + // after adding interpreter (ld.so) mappings sort again + if (sort_map_array(ph) != true) + return false; - print_debug("first link map is at 0x%lx\n", first_link_map_addr); + print_debug("first link map is at 0x%lx\n", first_link_map_addr); - link_map_addr = first_link_map_addr; - while (link_map_addr != 0) { - // read library base address of the .so. Note that even though <sys/link.h> calls - // link_map->l_addr as "base address", this is * not * really base virtual - // address of the shared object. This is actually the difference b/w the virtual - // address mentioned in shared object and the actual virtual base where runtime - // linker loaded it. We use "base diff" in read_lib_segments call below. + link_map_addr = first_link_map_addr; + while (link_map_addr != 0) { + // read library base address of the .so. Note that even though <sys/link.h> calls + // link_map->l_addr as "base address", this is * not * really base virtual + // address of the shared object. This is actually the difference b/w the virtual + // address mentioned in shared object and the actual virtual base where runtime + // linker loaded it. We use "base diff" in read_lib_segments call below. - if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_ADDR_OFFSET, - &lib_base_diff, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read shared object base address diff\n"); - return false; - } + if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_ADDR_OFFSET, + &lib_base_diff, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read shared object base address diff\n"); + return false; + } - // read address of the name - if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_NAME_OFFSET, - &lib_name_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read address of shared object name\n"); - return false; - } + // read address of the name + if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_NAME_OFFSET, + &lib_name_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read address of shared object name\n"); + return false; + } - // read name of the shared object - if (read_string(ph, (uintptr_t) lib_name_addr, lib_name, sizeof(lib_name)) != true) { - print_debug("can't read shared object name\n"); - return false; - } + // read name of the shared object + if (read_string(ph, (uintptr_t) lib_name_addr, lib_name, sizeof(lib_name)) != true) { + print_debug("can't read shared object name\n"); + return false; + } - if (lib_name[0] != '\0') { - // ignore empty lib names - lib_fd = pathmap_open(lib_name); + if (lib_name[0] != '\0') { + // ignore empty lib names + lib_fd = pathmap_open(lib_name); - if (lib_fd < 0) { - print_debug("can't open shared object %s\n", lib_name); - // continue with other libraries... - } else { - if (read_elf_header(lib_fd, &elf_ehdr)) { - lib_base = lib_base_diff + find_base_address(lib_fd, &elf_ehdr); - print_debug("reading library %s @ 0x%lx [ 0x%lx ]\n", - lib_name, lib_base, lib_base_diff); - // while adding library mappings we need to use "base difference". - if (! read_lib_segments(ph, lib_fd, &elf_ehdr, lib_base_diff)) { - print_debug("can't read shared object's segments\n"); - close(lib_fd); - return false; - } - add_lib_info_fd(ph, lib_name, lib_fd, lib_base); - // Map info is added for the library (lib_name) so - // we need to re-sort it before calling the p_pdread. - if (sort_map_array(ph) != true) - return false; - } else { - print_debug("can't read ELF header for shared object %s\n", lib_name); - close(lib_fd); - // continue with other libraries... - } - } + if (lib_fd < 0) { + print_debug("can't open shared object %s\n", lib_name); + // continue with other libraries... + } else { + if (read_elf_header(lib_fd, &elf_ehdr)) { + lib_base = lib_base_diff + find_base_address(lib_fd, &elf_ehdr); + print_debug("reading library %s @ 0x%lx [ 0x%lx ]\n", + lib_name, lib_base, lib_base_diff); + // while adding library mappings we need to use "base difference". + if (! read_lib_segments(ph, lib_fd, &elf_ehdr, lib_base_diff)) { + print_debug("can't read shared object's segments\n"); + close(lib_fd); + return false; + } + add_lib_info_fd(ph, lib_name, lib_fd, lib_base); + // Map info is added for the library (lib_name) so + // we need to re-sort it before calling the p_pdread. + if (sort_map_array(ph) != true) + return false; + } else { + print_debug("can't read ELF header for shared object %s\n", lib_name); + close(lib_fd); + // continue with other libraries... + } } + } - // read next link_map address - if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_NEXT_OFFSET, - &link_map_addr, sizeof(uintptr_t)) != PS_OK) { - print_debug("can't read next link in link_map\n"); - return false; - } - } + // read next link_map address + if (ps_pread(ph, (psaddr_t) link_map_addr + LINK_MAP_NEXT_OFFSET, + &link_map_addr, sizeof(uintptr_t)) != PS_OK) { + print_debug("can't read next link in link_map\n"); + return false; + } + } - return true; + return true; } // the one and only one exposed stuff from this file struct ps_prochandle* Pgrab_core(const char* exec_file, const char* core_file) { - ELF_EHDR core_ehdr; - ELF_EHDR exec_ehdr; + ELF_EHDR core_ehdr; + ELF_EHDR exec_ehdr; - struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); - if (ph == NULL) { - print_debug("can't allocate ps_prochandle\n"); - return NULL; - } + struct ps_prochandle* ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle)); + if (ph == NULL) { + print_debug("cant allocate ps_prochandle\n"); + return NULL; + } - if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { - free(ph); - print_debug("can't allocate ps_prochandle\n"); - return NULL; - } + if ((ph->core = (struct core_data*) calloc(1, sizeof(struct core_data))) == NULL) { + free(ph); + print_debug("can't allocate ps_prochandle\n"); + return NULL; + } - // initialize ph - ph->ops = &core_ops; - ph->core->core_fd = -1; - ph->core->exec_fd = -1; - ph->core->interp_fd = -1; + // initialize ph + ph->ops = &core_ops; + ph->core->core_fd = -1; + ph->core->exec_fd = -1; + ph->core->interp_fd = -1; + + print_debug("exec: %s core: %s", exec_file, core_file); - // open the core file - if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { - print_debug("can't open core file\n"); - goto err; - } + // open the core file + if ((ph->core->core_fd = open(core_file, O_RDONLY)) < 0) { + print_debug("can't open core file\n"); + goto err; + } - // read core file ELF header - if (read_elf_header(ph->core->core_fd, &core_ehdr) != true || core_ehdr.e_type != ET_CORE) { - print_debug("core file is not a valid ELF ET_CORE file\n"); - goto err; - } + // read core file ELF header + if (read_elf_header(ph->core->core_fd, &core_ehdr) != true || core_ehdr.e_type != ET_CORE) { + print_debug("core file is not a valid ELF ET_CORE file\n"); + goto err; + } - if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { - print_debug("can't open executable file\n"); - goto err; - } + if ((ph->core->exec_fd = open(exec_file, O_RDONLY)) < 0) { + print_debug("can't open executable file\n"); + goto err; + } - if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || exec_ehdr.e_type != ET_EXEC) { - print_debug("executable file is not a valid ELF ET_EXEC file\n"); - goto err; - } + if (read_elf_header(ph->core->exec_fd, &exec_ehdr) != true || exec_ehdr.e_type != ET_EXEC) { + print_debug("executable file is not a valid ELF ET_EXEC file\n"); + goto err; + } - // process core file segments - if (read_core_segments(ph, &core_ehdr) != true) - goto err; + // process core file segments + if (read_core_segments(ph, &core_ehdr) != true) + goto err; - // process exec file segments - if (read_exec_segments(ph, &exec_ehdr) != true) - goto err; + // process exec file segments + if (read_exec_segments(ph, &exec_ehdr) != true) + goto err; - // exec file is also treated like a shared object for symbol search - if (add_lib_info_fd(ph, exec_file, ph->core->exec_fd, - (uintptr_t)0 + find_base_address(ph->core->exec_fd, &exec_ehdr)) == NULL) - goto err; + // exec file is also treated like a shared object for symbol search + if (add_lib_info_fd(ph, exec_file, ph->core->exec_fd, + (uintptr_t)0 + find_base_address(ph->core->exec_fd, &exec_ehdr)) == NULL) + goto err; - // allocate and sort maps into map_array, we need to do this - // here because read_shared_lib_info needs to read from debuggee - // address space - if (sort_map_array(ph) != true) - goto err; + // allocate and sort maps into map_array, we need to do this + // here because read_shared_lib_info needs to read from debuggee + // address space + if (sort_map_array(ph) != true) + goto err; - if (read_shared_lib_info(ph) != true) - goto err; + if (read_shared_lib_info(ph) != true) + goto err; - // sort again because we have added more mappings from shared objects - if (sort_map_array(ph) != true) - goto err; + // sort again because we have added more mappings from shared objects + if (sort_map_array(ph) != true) + goto err; - if (init_classsharing_workaround(ph) != true) - goto err; + if (init_classsharing_workaround(ph) != true) + goto err; - return ph; + print_debug("Leave Pgrab_core\n"); + return ph; err: - Prelease(ph); - return NULL; + Prelease(ph); + return NULL; } + +#endif // __APPLE__
--- a/agent/src/os/bsd/symtab.c Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/symtab.c Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,32 +28,182 @@ #include <string.h> #include <db.h> #include <fcntl.h> + +#include "libproc_impl.h" #include "symtab.h" +#ifndef __APPLE__ #include "salibelf.h" +#endif // __APPLE__ // ---------------------------------------------------- // functions for symbol lookups // ---------------------------------------------------- +typedef struct symtab_symbol { + char *name; // name like __ZThread_... + uintptr_t offset; // to loaded address + uintptr_t size; // size strlen +} symtab_symbol; + +typedef struct symtab { + char *strs; // all symbols "__symbol1__'\0'__symbol2__...." + size_t num_symbols; + DB* hash_table; + symtab_symbol* symbols; +} symtab_t; + +#ifdef __APPLE__ + +void build_search_table(symtab_t *symtab) { + int i; + for (i = 0; i < symtab->num_symbols; i++) { + DBT key, value; + key.data = symtab->symbols[i].name; + key.size = strlen(key.data) + 1; + value.data = &(symtab->symbols[i]); + value.size = sizeof(symtab_symbol); + (*symtab->hash_table->put)(symtab->hash_table, &key, &value, 0); + + // check result + if (is_debug()) { + DBT rkey, rvalue; + char* tmp = (char *)malloc(strlen(symtab->symbols[i].name) + 1); + strcpy(tmp, symtab->symbols[i].name); + rkey.data = tmp; + rkey.size = strlen(tmp) + 1; + (*symtab->hash_table->get)(symtab->hash_table, &rkey, &rvalue, 0); + // we may get a copy back so compare contents + symtab_symbol *res = (symtab_symbol *)rvalue.data; + if (strcmp(res->name, symtab->symbols[i].name) || + res->offset != symtab->symbols[i].offset || + res->size != symtab->symbols[i].size) { + print_debug("error to get hash_table value!\n"); + } + free(tmp); + } + } +} + +// read symbol table from given fd. +struct symtab* build_symtab(int fd) { + symtab_t* symtab = NULL; + int i; + mach_header_64 header; + off_t image_start; + + if (!get_arch_off(fd, CPU_TYPE_X86_64, &image_start)) { + print_debug("failed in get fat header\n"); + return NULL; + } + lseek(fd, image_start, SEEK_SET); + if (read(fd, (void *)&header, sizeof(mach_header_64)) != sizeof(mach_header_64)) { + print_debug("reading header failed!\n"); + return NULL; + } + // header + if (header.magic != MH_MAGIC_64) { + print_debug("not a valid .dylib file\n"); + return NULL; + } + + load_command lcmd; + symtab_command symtabcmd; + nlist_64 lentry; + + bool lcsymtab_exist = false; + + long filepos = ltell(fd); + for (i = 0; i < header.ncmds; i++) { + lseek(fd, filepos, SEEK_SET); + if (read(fd, (void *)&lcmd, sizeof(load_command)) != sizeof(load_command)) { + print_debug("read load_command failed for file\n"); + return NULL; + } + filepos += lcmd.cmdsize; // next command position + if (lcmd.cmd == LC_SYMTAB) { + lseek(fd, -sizeof(load_command), SEEK_CUR); + lcsymtab_exist = true; + break; + } + } + if (!lcsymtab_exist) { + print_debug("No symtab command found!\n"); + return NULL; + } + if (read(fd, (void *)&symtabcmd, sizeof(symtab_command)) != sizeof(symtab_command)) { + print_debug("read symtab_command failed for file"); + return NULL; + } + symtab = (symtab_t *)malloc(sizeof(symtab_t)); + if (symtab == NULL) { + print_debug("out of memory: allocating symtab\n"); + return NULL; + } + + // create hash table, we use berkeley db to + // manipulate the hash table. + symtab->hash_table = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL); + if (symtab->hash_table == NULL) + goto quit; + + symtab->num_symbols = symtabcmd.nsyms; + symtab->symbols = (symtab_symbol *)malloc(sizeof(symtab_symbol) * symtab->num_symbols); + symtab->strs = (char *)malloc(sizeof(char) * symtabcmd.strsize); + if (symtab->symbols == NULL || symtab->strs == NULL) { + print_debug("out of memory: allocating symtab.symbol or symtab.strs\n"); + goto quit; + } + lseek(fd, image_start + symtabcmd.symoff, SEEK_SET); + for (i = 0; i < symtab->num_symbols; i++) { + if (read(fd, (void *)&lentry, sizeof(nlist_64)) != sizeof(nlist_64)) { + print_debug("read nlist_64 failed at %i\n", i); + goto quit; + } + symtab->symbols[i].offset = lentry.n_value; + symtab->symbols[i].size = lentry.n_un.n_strx; // index + } + + // string table + lseek(fd, image_start + symtabcmd.stroff, SEEK_SET); + int size = read(fd, (void *)(symtab->strs), symtabcmd.strsize * sizeof(char)); + if (size != symtabcmd.strsize * sizeof(char)) { + print_debug("reading string table failed\n"); + goto quit; + } + + for (i = 0; i < symtab->num_symbols; i++) { + symtab->symbols[i].name = symtab->strs + symtab->symbols[i].size; + if (i > 0) { + // fix size + symtab->symbols[i - 1].size = symtab->symbols[i].size - symtab->symbols[i - 1].size; + print_debug("%s size = %d\n", symtab->symbols[i - 1].name, symtab->symbols[i - 1].size); + + } + + if (i == symtab->num_symbols - 1) { + // last index + symtab->symbols[i].size = + symtabcmd.strsize - symtab->symbols[i].size; + print_debug("%s size = %d\n", symtab->symbols[i].name, symtab->symbols[i].size); + } + } + + // build a hashtable for fast query + build_search_table(symtab); + return symtab; +quit: + if (symtab) destroy_symtab(symtab); + return NULL; +} + +#else // __APPLE__ + struct elf_section { ELF_SHDR *c_shdr; void *c_data; }; -struct elf_symbol { - char *name; - uintptr_t offset; - uintptr_t size; -}; - -typedef struct symtab { - char *strs; - size_t num_symbols; - struct elf_symbol *symbols; - DB* hash_table; -} symtab_t; - // read symbol table from given fd. struct symtab* build_symtab(int fd) { ELF_EHDR ehdr; @@ -176,7 +326,7 @@ key.data = sym_name; key.size = strlen(sym_name) + 1; value.data = &(symtab->symbols[j]); - value.size = sizeof(void *); + value.size = sizeof(symtab_symbol); (*symtab->hash_table->put)(symtab->hash_table, &key, &value, 0); } } @@ -201,30 +351,29 @@ return symtab; } -void destroy_symtab(struct symtab* symtab) { +#endif // __APPLE__ + +void destroy_symtab(symtab_t* symtab) { if (!symtab) return; - if (symtab->strs) free(symtab->strs); - if (symtab->symbols) free(symtab->symbols); - if (symtab->hash_table) { - (*symtab->hash_table->close)(symtab->hash_table); - } + free(symtab->strs); + free(symtab->symbols); free(symtab); } -uintptr_t search_symbol(struct symtab* symtab, uintptr_t base, - const char *sym_name, int *sym_size) { +uintptr_t search_symbol(struct symtab* symtab, uintptr_t base, const char *sym_name, int *sym_size) { DBT key, value; int ret; // library does not have symbol table - if (!symtab || !symtab->hash_table) + if (!symtab || !symtab->hash_table) { return 0; + } key.data = (char*)(uintptr_t)sym_name; key.size = strlen(sym_name) + 1; ret = (*symtab->hash_table->get)(symtab->hash_table, &key, &value, 0); if (ret == 0) { - struct elf_symbol *sym = value.data; + symtab_symbol *sym = value.data; uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset); if (sym_size) *sym_size = sym->size; return rslt; @@ -238,7 +387,7 @@ int n = 0; if (!symtab) return NULL; for (; n < symtab->num_symbols; n++) { - struct elf_symbol* sym = &(symtab->symbols[n]); + symtab_symbol* sym = &(symtab->symbols[n]); if (sym->name != NULL && offset >= sym->offset && offset < sym->offset + sym->size) { if (poffset) *poffset = (offset - sym->offset);
--- a/agent/src/os/bsd/symtab.h Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/os/bsd/symtab.h Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,11 @@ #include <stdint.h> -// interface to manage ELF symbol tables +// interface to manage ELF or MachO symbol tables struct symtab; -// build symbol table for a given ELF file descriptor +// build symbol table for a given ELF or MachO file escriptor struct symtab* build_symtab(int fd); // destroy the symbol table
--- a/agent/src/share/classes/sun/jvm/hotspot/BsdVtblAccess.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/BsdVtblAccess.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,11 +34,18 @@ public BsdVtblAccess(SymbolLookup symbolLookup, String[] dllNames) { super(symbolLookup, dllNames); - - if (symbolLookup.lookup("libjvm.so", "__vt_10JavaThread") != null || - symbolLookup.lookup("libjvm_g.so", "__vt_10JavaThread") != null) { + boolean oldVT = false; + boolean isDarwin = dllNames[0].lastIndexOf(".dylib") != -1; + String vtJavaThread = isDarwin ? "_vt_10JavaThread" : "__vt_10JavaThread"; + for (String dllName : dllNames) { + if (symbolLookup.lookup(dllName, vtJavaThread) != null) { + oldVT = true; + break; + } + } + if (oldVT) { // old C++ ABI - vt = "__vt_"; + vt = isDarwin ? "_vt_" : "__vt_"; } else { // new C++ ABI vt = "_ZTV";
--- a/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java Sun Mar 24 12:43:13 2013 -0700 @@ -24,36 +24,81 @@ package sun.jvm.hotspot; -import java.io.*; -import java.math.*; -import java.util.*; -import java.util.regex.*; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -import sun.jvm.hotspot.types.Type; -import sun.jvm.hotspot.types.Field; -import sun.jvm.hotspot.HotSpotTypeDataBase; -import sun.jvm.hotspot.types.basic.BasicType; -import sun.jvm.hotspot.types.basic.BasicTypeDataBase; -import sun.jvm.hotspot.types.CIntegerType; -import sun.jvm.hotspot.code.*; -import sun.jvm.hotspot.compiler.*; -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.interpreter.*; -import sun.jvm.hotspot.memory.*; -import sun.jvm.hotspot.oops.*; -import sun.jvm.hotspot.opto.*; -import sun.jvm.hotspot.ci.*; -import sun.jvm.hotspot.asm.*; -import sun.jvm.hotspot.runtime.*; -import sun.jvm.hotspot.utilities.*; -import sun.jvm.hotspot.utilities.soql.*; -import sun.jvm.hotspot.ui.classbrowser.*; -import sun.jvm.hotspot.ui.tree.*; -import sun.jvm.hotspot.tools.*; +import sun.jvm.hotspot.ci.ciEnv; +import sun.jvm.hotspot.code.CodeBlob; +import sun.jvm.hotspot.code.CodeCacheVisitor; +import sun.jvm.hotspot.code.NMethod; +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.debugger.OopHandle; +import sun.jvm.hotspot.memory.SymbolTable; +import sun.jvm.hotspot.memory.SystemDictionary; +import sun.jvm.hotspot.memory.Universe; +import sun.jvm.hotspot.oops.DefaultHeapVisitor; +import sun.jvm.hotspot.oops.HeapVisitor; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.Metadata; +import sun.jvm.hotspot.oops.Method; +import sun.jvm.hotspot.oops.MethodData; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.RawHeapVisitor; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.UnknownOopException; +import sun.jvm.hotspot.opto.Compile; +import sun.jvm.hotspot.opto.InlineTree; +import sun.jvm.hotspot.runtime.CompiledVFrame; +import sun.jvm.hotspot.runtime.CompilerThread; +import sun.jvm.hotspot.runtime.JavaThread; +import sun.jvm.hotspot.runtime.JavaVFrame; +import sun.jvm.hotspot.runtime.Threads; +import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.tools.ObjectHistogram; +import sun.jvm.hotspot.tools.PMap; +import sun.jvm.hotspot.tools.PStack; import sun.jvm.hotspot.tools.StackTrace; import sun.jvm.hotspot.tools.jcore.ClassDump; import sun.jvm.hotspot.tools.jcore.ClassFilter; +import sun.jvm.hotspot.types.CIntegerType; +import sun.jvm.hotspot.types.Field; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.basic.BasicType; +import sun.jvm.hotspot.ui.classbrowser.HTMLGenerator; +import sun.jvm.hotspot.ui.tree.CTypeTreeNodeAdapter; +import sun.jvm.hotspot.ui.tree.OopTreeNodeAdapter; +import sun.jvm.hotspot.ui.tree.SimpleTreeNode; +import sun.jvm.hotspot.utilities.AddressOps; +import sun.jvm.hotspot.utilities.Assert; +import sun.jvm.hotspot.utilities.HeapProgressThunk; +import sun.jvm.hotspot.utilities.LivenessPathElement; +import sun.jvm.hotspot.utilities.MethodArray; +import sun.jvm.hotspot.utilities.ObjectReader; +import sun.jvm.hotspot.utilities.PointerFinder; +import sun.jvm.hotspot.utilities.PointerLocation; +import sun.jvm.hotspot.utilities.ReversePtrs; +import sun.jvm.hotspot.utilities.ReversePtrsAnalysis; +import sun.jvm.hotspot.utilities.RobustOopDeterminator; +import sun.jvm.hotspot.utilities.SystemDictionaryHelper; +import sun.jvm.hotspot.utilities.soql.JSJavaFactory; +import sun.jvm.hotspot.utilities.soql.JSJavaFactoryImpl; +import sun.jvm.hotspot.utilities.soql.JSJavaScriptEngine; public class CommandProcessor { public abstract static class DebuggerInterface { @@ -1132,6 +1177,10 @@ Klass klass = null; if (t.countTokens() == 1) { klass = SystemDictionaryHelper.findInstanceKlass(t.nextToken()); + if (klass == null) { + out.println("No such type."); + return; + } } while (base != null && base.lessThan(end)) { long step = stride; @@ -1517,7 +1566,7 @@ ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { - out.println(bos.toString() + " = " + thread.getAddress()); + out.println("Thread " + bos.toString() + " Address: " + thread.getAddress()); HTMLGenerator gen = new HTMLGenerator(false); try { out.println(gen.genHTMLForJavaStackTrace(thread)); @@ -1546,7 +1595,7 @@ ByteArrayOutputStream bos = new ByteArrayOutputStream(); thread.printThreadIDOn(new PrintStream(bos)); if (all || bos.toString().equals(name)) { - out.println(bos.toString() + " = " + thread.getAddress()); + out.println("Thread " + bos.toString() + " Address " + thread.getAddress()); if (!all) return; } }
--- a/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -311,6 +311,8 @@ setupDebuggerLinux(); } else if (os.equals("bsd")) { setupDebuggerBsd(); + } else if (os.equals("darwin")) { + setupDebuggerDarwin(); } else { // Add support for more operating systems here throw new DebuggerException("Operating system " + os + " not yet supported"); @@ -370,6 +372,10 @@ db = new HotSpotTypeDataBase(machDesc, new BsdVtblAccess(debugger, jvmLibNames), debugger, jvmLibNames); + } else if (os.equals("darwin")) { + db = new HotSpotTypeDataBase(machDesc, + new BsdVtblAccess(debugger, jvmLibNames), + debugger, jvmLibNames); } else { throw new DebuggerException("OS \"" + os + "\" not yet supported (no VtblAccess yet)"); } @@ -459,6 +465,8 @@ setupJVMLibNamesLinux(); } else if (os.equals("bsd")) { setupJVMLibNamesBsd(); + } else if (os.equals("darwin")) { + setupJVMLibNamesDarwin(); } else { throw new RuntimeException("Unknown OS type"); } @@ -567,6 +575,29 @@ jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" }; } + // + // Darwin + // + + private void setupDebuggerDarwin() { + setupJVMLibNamesDarwin(); + + if (cpu.equals("amd64") || cpu.equals("x86_64")) { + machDesc = new MachineDescriptionAMD64(); + } else { + throw new DebuggerException("Darwin only supported on x86_64. Current arch: " + cpu); + } + + BsdDebuggerLocal dbg = new BsdDebuggerLocal(machDesc, !isServer); + debugger = dbg; + + attachDebugger(); + } + + private void setupJVMLibNamesDarwin() { + jvmLibNames = new String[] { "libjvm.dylib", "libjvm_g.dylib" }; + } + /** Convenience routine which should be called by per-platform debugger setup. Should not be called when startupMode is REMOTE_MODE. */
--- a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,9 @@ import sun.jvm.hotspot.debugger.x86.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.utilities.*; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.Threads; +import sun.jvm.hotspot.runtime.JavaThread; import java.lang.reflect.*; /** <P> An implementation of the JVMDebugger interface. The basic debug @@ -51,10 +54,11 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { private boolean useGCC32ABI; private boolean attached; - private long p_ps_prochandle; // native debugger handle - private long symbolicator; // macosx symbolicator handle - private long task; // macosx task handle + private long p_ps_prochandle; // native debugger handle + private long symbolicator; // macosx symbolicator handle + private long task; // macosx task handle private boolean isCore; + private boolean isDarwin; // variant for bsd // CDebugger support private BsdCDebugger cdbg; @@ -208,6 +212,7 @@ } } + isDarwin = getOS().equals("darwin"); workerThread = new BsdDebuggerLocalWorkerThread(this); workerThread.start(); } @@ -240,8 +245,11 @@ /* called from attach methods */ private void findABIVersion() throws DebuggerException { - if (lookupByName0("libjvm.so", "__vt_10JavaThread") != 0 || - lookupByName0("libjvm_g.so", "__vt_10JavaThread") != 0) { + String libjvmName = isDarwin ? "libjvm.dylib" : "libjvm.so"; + String libjvm_gName = isDarwin? "libjvm_g.dylib" : "libjvm_g.so"; + String javaThreadVt = isDarwin ? "_vt_10JavaThread" : "__vt_10JavaThread"; + if (lookupByName0(libjvmName, javaThreadVt) != 0 || + lookupByName0(libjvm_gName, javaThreadVt) != 0) { // old C++ ABI useGCC32ABI = false; } else { @@ -360,7 +368,8 @@ } if (isCore) { - long addr = lookupByName0(objectName, symbol); + // MacOSX symbol with "_" as leading + long addr = lookupByName0(objectName, isDarwin ? "_" + symbol : symbol); return (addr == 0)? null : new BsdAddress(this, handleGCC32ABI(addr, symbol)); } else { class LookupByNameTask implements WorkerThreadTask { @@ -403,12 +412,12 @@ public ThreadProxy getThreadForIdentifierAddress(Address threadIdAddr, Address uniqueThreadIdAddr) { return new BsdThread(this, threadIdAddr, uniqueThreadIdAddr); } + @Override public ThreadProxy getThreadForIdentifierAddress(Address addr) { throw new RuntimeException("unimplemented"); } - /** From the ThreadAccess interface via Debugger and JVMDebugger */ public ThreadProxy getThreadForThreadId(long id) { return new BsdThread(this, id); @@ -601,6 +610,33 @@ throw new DebuggerException("Unimplemented"); } + /** this functions used for core file reading and called from native attach0, + it returns an array of long integers as + [thread_id, stack_start, stack_end, thread_id, stack_start, stack_end, ....] for + all java threads recorded in Threads. Also adds the ThreadProxy to threadList */ + public long[] getJavaThreadsInfo() { + requireAttach(); + Threads threads = VM.getVM().getThreads(); + int len = threads.getNumberOfThreads(); + long[] result = new long[len * 3]; // triple + JavaThread t = threads.first(); + long beg, end; + int i = 0; + while (t != null) { + end = t.getStackBaseValue(); + beg = end - t.getStackSize(); + BsdThread bsdt = (BsdThread)t.getThreadProxy(); + long uid = bsdt.getUniqueThreadId(); + if (threadList != null) threadList.add(bsdt); + result[i] = uid; + result[i + 1] = beg; + result[i + 2] = end; + t = t.next(); + i += 3; + } + return result; + } + static { System.loadLibrary("saproc"); init0();
--- a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,8 @@ BsdThread(BsdDebugger debugger, long id) { this.debugger = debugger; - this.thread_id = (int) id; + // use unique_thread_id to identify thread + this.unique_thread_id = id; } public boolean equals(Object obj) { @@ -52,7 +53,7 @@ return false; } - return (((BsdThread) obj).thread_id == thread_id); + return (((BsdThread) obj).unique_thread_id == unique_thread_id); } public int hashCode() { @@ -80,4 +81,9 @@ throws IllegalThreadStateException, DebuggerException { throw new DebuggerException("Unimplemented"); } + + /** this is not interface function, used in core file to get unique thread id on Macosx*/ + public long getUniqueThreadId() { + return unique_thread_id; + } }
--- a/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -320,6 +320,10 @@ return stackBaseField.getValue(addr); } + public long getStackBaseValue() { + return VM.getVM().getAddressValue(getStackBase()); + } + public long getStackSize() { return stackSizeField.getValue(addr); }
--- a/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/Threads.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ public class Threads { private static JavaThreadFactory threadFactory; private static AddressField threadListField; + private static CIntegerField numOfThreadsField; private static VirtualConstructor virtualConstructor; private static JavaThreadPDAccess access; @@ -57,6 +58,7 @@ Type type = db.lookupType("Threads"); threadListField = type.getAddressField("_thread_list"); + numOfThreadsField = type.getCIntegerField("_number_of_threads"); // Instantiate appropriate platform-specific JavaThreadFactory String os = VM.getVM().getOS(); @@ -102,6 +104,10 @@ } else if (cpu.equals("amd64") || cpu.equals("x86_64")) { access = new BsdAMD64JavaThreadPDAccess(); } + } else if (os.equals("darwin")) { + if (cpu.equals("amd64") || cpu.equals("x86_64")) { + access = new BsdAMD64JavaThreadPDAccess(); + } } if (access == null) { @@ -144,6 +150,10 @@ return createJavaThreadWrapper(threadAddr); } + public int getNumberOfThreads() { + return (int) numOfThreadsField.getValue(); + } + /** Routine for instantiating appropriately-typed wrapper for a JavaThread. Currently needs to be public for OopUtilities to access it. */
--- a/agent/src/share/classes/sun/jvm/hotspot/tools/PStack.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/tools/PStack.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.oops.*; import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.PlatformInfo; public class PStack extends Tool { // in non-verbose mode, Method*s are not printed in java frames @@ -54,6 +55,11 @@ } public void run(PrintStream out, Debugger dbg) { + if (PlatformInfo.getOS().equals("darwin")) { + out.println("Not available on Darwin"); + return; + } + CDebugger cdbg = dbg.getCDebugger(); if (cdbg != null) { ConcurrentLocksPrinter concLocksPrinter = null;
--- a/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicTypeDataBase.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/types/basic/BasicTypeDataBase.java Sun Mar 24 12:43:13 2013 -0700 @@ -24,10 +24,15 @@ package sun.jvm.hotspot.types.basic; -import java.util.*; -import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.types.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.debugger.MachineDescription; import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; /** <P> This is a basic implementation of the TypeDataBase interface. It allows an external type database builder to add types to be @@ -150,7 +155,7 @@ return VM.getVM().getOopSize(); } - static HashMap typeToVtbl = new HashMap(); + HashMap typeToVtbl = new HashMap(); private Address vtblForType(Type type) { Address vtblAddr = (Address)typeToVtbl.get(type);
--- a/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/utilities/PlatformInfo.java Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,8 +43,8 @@ return "bsd"; } else if (os.equals("OpenBSD")) { return "bsd"; - } else if (os.equals("Darwin") || os.contains("OS X")) { - return "bsd"; + } else if (os.contains("Darwin") || os.contains("OS X")) { + return "darwin"; } else if (os.startsWith("Windows")) { return "win32"; } else {
--- a/agent/src/share/native/sadis.c Sat Mar 23 06:22:07 2013 -0700 +++ b/agent/src/share/native/sadis.c Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,10 @@ #include <string.h> #include <dlfcn.h> + +#ifndef __APPLE__ #include <link.h> +#endif #endif @@ -109,9 +112,7 @@ jstring libname_s) { uintptr_t func = 0; const char* error_message = NULL; - const char* java_home; jboolean isCopy; - uintptr_t *handle = NULL; const char * jrepath = (*env)->GetStringUTFChars(env, jrepath_s, &isCopy); // like $JAVA_HOME/jre/lib/sparc/ const char * libname = (*env)->GetStringUTFChars(env, libname_s, &isCopy); @@ -167,7 +168,8 @@ void* event_stream, int (*printf_callback)(void*, const char*, ...), void* printf_stream, - const char* options); + const char* options, + int newline); /* container for call back state when decoding instructions */ typedef struct { @@ -281,7 +283,7 @@ end - start, &event_to_env, (void*) &denv, &printf_to_env, (void*) &denv, - options); + options, 0 /* newline */); /* cleanup */ (*env)->ReleaseByteArrayElements(env, code, start, JNI_ABORT);
--- a/make/bsd/makefiles/saproc.make Sat Mar 23 06:22:07 2013 -0700 +++ b/make/bsd/makefiles/saproc.make Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ # Rules to build serviceability agent library, used by vm.make -# libsaproc.so: serviceability agent +# libsaproc.so(dylib): serviceability agent SAPROC = saproc ifeq ($(OS_VENDOR), Darwin) @@ -37,7 +37,7 @@ SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) -NON_STUB_SASRCFILES = $(SASRCDIR)/salibelf.c \ +BSD_NON_STUB_SASRCFILES = $(SASRCDIR)/salibelf.c \ $(SASRCDIR)/symtab.c \ $(SASRCDIR)/libproc_impl.c \ $(SASRCDIR)/ps_proc.c \ @@ -45,13 +45,19 @@ $(SASRCDIR)/BsdDebuggerLocal.c \ $(AGENT_DIR)/src/share/native/sadis.c +DARWIN_NON_STUB_SASRCFILES = $(SASRCDIR)/symtab.c \ + $(SASRCDIR)/libproc_impl.c \ + $(SASRCDIR)/ps_core.c \ + $(SASRCDIR)/MacosxDebuggerLocal.m \ + $(AGENT_DIR)/src/share/native/sadis.c + ifeq ($(OS_VENDOR), FreeBSD) - SASRCFILES = $(NON_STUB_SASRCFILES) + SASRCFILES = $(BSD_NON_STUB_SASRCFILES) SALIBS = -lutil -lthread_db SAARCH = $(ARCHFLAG) else ifeq ($(OS_VENDOR), Darwin) - SASRCFILES = $(SASRCDIR)/MacosxDebuggerLocal.m + SASRCFILES = $(DARWIN_NON_STUB_SASRCFILES) SALIBS = -g -framework Foundation -F/System/Library/Frameworks/JavaVM.framework/Frameworks -framework JavaNativeFoundation -framework Security -framework CoreFoundation #objc compiler blows up on -march=i586, perhaps it should not be included in the macosx intel 32-bit C++ compiles? SAARCH = $(subst -march=i586,,$(ARCHFLAG)) @@ -102,7 +108,7 @@ fi @echo Making SA debugger back-end... $(QUIETLY) $(CC) -D$(BUILDARCH) -D_GNU_SOURCE \ - $(SYMFLAG) $(SAARCH) $(SHARED_FLAG) $(PICFLAG) \ + $(SYMFLAG) $(SAARCH) $(SHARED_FLAG) $(PICFLAG) \ -I$(SASRCDIR) \ -I$(GENERATED) \ $(BOOT_JAVA_INCLUDES) \
--- a/make/hotspot_version Sat Mar 23 06:22:07 2013 -0700 +++ b/make/hotspot_version Sun Mar 24 12:43:13 2013 -0700 @@ -35,7 +35,7 @@ HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=24 +HS_BUILD_NUMBER=25 JDK_MAJOR_VER=1 JDK_MINOR_VER=8
--- a/make/jprt.properties Sat Mar 23 06:22:07 2013 -0700 +++ b/make/jprt.properties Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -97,15 +97,18 @@ jprt.my.linux.ppcsflt.jdk7u8=${jprt.my.linux.ppcsflt.jdk7} jprt.my.linux.ppcsflt=${jprt.my.linux.ppcsflt.${jprt.tools.default.release}} -jprt.my.linux.armvfp.jdk8=linux_armvfp_2.6 -jprt.my.linux.armvfp.jdk7=linux_armvfp_2.6 -jprt.my.linux.armvfp.jdk7u8=${jprt.my.linux.armvfp.jdk7} -jprt.my.linux.armvfp=${jprt.my.linux.armvfp.${jprt.tools.default.release}} +jprt.my.linux.armvfpsflt.jdk8=linux_armvfpsflt_2.6 +jprt.my.linux.armvfpsflt=${jprt.my.linux.armvfpsflt.${jprt.tools.default.release}} + +jprt.my.linux.armvfphflt.jdk8=linux_armvfphflt_2.6 +jprt.my.linux.armvfphflt=${jprt.my.linux.armvfphflt.${jprt.tools.default.release}} -jprt.my.linux.armv6.jdk8=linux_armv6_2.6 -jprt.my.linux.armv6.jdk7=linux_armv6_2.6 -jprt.my.linux.armv6.jdk7u8=${jprt.my.linux.armv6.jdk7} -jprt.my.linux.armv6=${jprt.my.linux.armv6.${jprt.tools.default.release}} +# The ARM GP vfp-sflt build is not currently supported +#jprt.my.linux.armvs.jdk8=linux_armvs_2.6 +#jprt.my.linux.armvs=${jprt.my.linux.armvs.${jprt.tools.default.release}} + +jprt.my.linux.armvh.jdk8=linux_armvh_2.6 +jprt.my.linux.armvh=${jprt.my.linux.armvh.${jprt.tools.default.release}} jprt.my.linux.armsflt.jdk8=linux_armsflt_2.6 jprt.my.linux.armsflt.jdk7=linux_armsflt_2.6 @@ -139,7 +142,7 @@ ${jprt.my.macosx.x64}-{product|fastdebug|debug}, \ ${jprt.my.windows.i586}-{product|fastdebug|debug}, \ ${jprt.my.windows.x64}-{product|fastdebug|debug}, \ - ${jprt.my.linux.armv6}-{product|fastdebug} + ${jprt.my.linux.armvh}-{product|fastdebug} jprt.build.targets.open= \ ${jprt.my.solaris.i586}-{productOpen}, \ @@ -151,7 +154,8 @@ ${jprt.my.linux.ppc}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.ppcv2}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.ppcsflt}-{productEmb|fastdebugEmb}, \ - ${jprt.my.linux.armvfp}-{productEmb|fastdebugEmb}, \ + ${jprt.my.linux.armvfpsflt}-{productEmb|fastdebugEmb}, \ + ${jprt.my.linux.armvfphflt}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.armsflt}-{productEmb|fastdebugEmb} jprt.build.targets.all=${jprt.build.targets.standard}, \
--- a/src/share/vm/classfile/classLoaderData.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/classfile/classLoaderData.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -105,6 +105,7 @@ void ClassLoaderData::classes_do(KlassClosure* klass_closure) { for (Klass* k = _klasses; k != NULL; k = k->next_link()) { klass_closure->do_klass(k); + assert(k != k->next_link(), "no loops!"); } } @@ -113,6 +114,7 @@ if (k->oop_is_instance()) { f(InstanceKlass::cast(k)); } + assert(k != k->next_link(), "no loops!"); } } @@ -258,6 +260,7 @@ return; } prev = k; + assert(k != k->next_link(), "no loops!"); } ShouldNotReachHere(); // should have found this class!! } @@ -439,6 +442,7 @@ while (k != NULL) { out->print_cr("klass "PTR_FORMAT", %s, CT: %d, MUT: %d", k, k->name()->as_C_string(), k->has_modified_oops(), k->has_accumulated_modified_oops()); + assert(k != k->next_link(), "no loops!"); k = k->next_link(); } } @@ -465,6 +469,7 @@ for (Klass* k = _klasses; k != NULL; k = k->next_link()) { guarantee(k->class_loader_data() == this, "Must be the same"); k->verify(); + assert(k != k->next_link(), "no loops!"); } }
--- a/src/share/vm/classfile/systemDictionary.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/classfile/systemDictionary.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -804,6 +804,32 @@ } } // load_instance_class loop + if (HAS_PENDING_EXCEPTION) { + // An exception, such as OOM could have happened at various places inside + // load_instance_class. We might have partially initialized a shared class + // and need to clean it up. + if (class_loader.is_null()) { + // In some cases k may be null. Let's find the shared class again. + instanceKlassHandle ik(THREAD, find_shared_class(name)); + if (ik.not_null()) { + if (ik->class_loader_data() == NULL) { + // We didn't go as far as Klass::restore_unshareable_info(), + // so nothing to clean up. + } else { + MutexLocker mu(SystemDictionary_lock, THREAD); + Klass* kk = find_class(name, ik->class_loader_data()); + if (kk != NULL) { + // No clean up is needed if the shared class has been entered + // into system dictionary, as load_shared_class() won't be called + // again. + } else { + clean_up_shared_class(ik, class_loader, THREAD); + } + } + } + } + } + if (load_instance_added == true) { // clean up placeholder entries for LOAD_INSTANCE success or error // This brackets the SystemDictionary updates for both defining @@ -1140,11 +1166,6 @@ return load_shared_class(ik, class_loader, THREAD); } -// Note well! Changes to this method may affect oop access order -// in the shared archive. Please take care to not make changes that -// adversely affect cold start time by changing the oop access order -// that is specified in dump.cpp MarkAndMoveOrderedReadOnly and -// MarkAndMoveOrderedReadWrite closures. instanceKlassHandle SystemDictionary::load_shared_class( instanceKlassHandle ik, Handle class_loader, TRAPS) { assert(class_loader.is_null(), "non-null classloader for shared class?"); @@ -1205,6 +1226,19 @@ return ik; } +void SystemDictionary::clean_up_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS) { + // Updating methods must be done under a lock so multiple + // threads don't update these in parallel + // Shared classes are all currently loaded by the bootstrap + // classloader, so this will never cause a deadlock on + // a custom class loader lock. + { + Handle lockObject = compute_loader_lock_object(class_loader, THREAD); + check_loader_lock_contention(lockObject, THREAD); + ObjectLocker ol(lockObject, THREAD, true); + ik->remove_unshareable_info(); + } +} instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) { instanceKlassHandle nh = instanceKlassHandle(); // null Handle
--- a/src/share/vm/classfile/systemDictionary.hpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/classfile/systemDictionary.hpp Sun Mar 24 12:43:13 2013 -0700 @@ -621,6 +621,7 @@ Handle class_loader, TRAPS); static instanceKlassHandle load_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS); + static void clean_up_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS); static instanceKlassHandle load_instance_class(Symbol* class_name, Handle class_loader, TRAPS); static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS);
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -6068,6 +6068,10 @@ verify_work_stacks_empty(); verify_overflow_empty(); + if (should_unload_classes()) { + ClassLoaderDataGraph::purge(); + } + _intra_sweep_timer.stop(); _intra_sweep_estimate.sample(_intra_sweep_timer.seconds());
--- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -784,7 +784,7 @@ } } -void ConcurrentMark::set_phase(uint active_tasks, bool concurrent) { +void ConcurrentMark::set_concurrency(uint active_tasks) { assert(active_tasks <= _max_worker_id, "we should not have more"); _active_tasks = active_tasks; @@ -793,6 +793,10 @@ _terminator = ParallelTaskTerminator((int) active_tasks, _task_queues); _first_overflow_barrier_sync.set_n_workers((int) active_tasks); _second_overflow_barrier_sync.set_n_workers((int) active_tasks); +} + +void ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) { + set_concurrency(active_tasks); _concurrent = concurrent; // We propagate this to all tasks, not just the active ones. @@ -806,7 +810,9 @@ // false before we start remark. At this point we should also be // in a STW phase. assert(!concurrent_marking_in_progress(), "invariant"); - assert(_finger == _heap_end, "only way to get here"); + assert(_finger == _heap_end, + err_msg("only way to get here: _finger: "PTR_FORMAT", _heap_end: "PTR_FORMAT, + _finger, _heap_end)); update_g1_committed(true); } } @@ -974,20 +980,28 @@ gclog_or_tty->print_cr("[%u] leaving first barrier", worker_id); } - // let the task associated with with worker 0 do this - if (worker_id == 0) { - // task 0 is responsible for clearing the global data structures - // We should be here because of an overflow. During STW we should - // not clear the overflow flag since we rely on it being true when - // we exit this method to abort the pause and restart concurent - // marking. - reset_marking_state(concurrent() /* clear_overflow */); - force_overflow()->update(); - - if (G1Log::fine()) { - gclog_or_tty->date_stamp(PrintGCDateStamps); - gclog_or_tty->stamp(PrintGCTimeStamps); - gclog_or_tty->print_cr("[GC concurrent-mark-reset-for-overflow]"); + // If we're executing the concurrent phase of marking, reset the marking + // state; otherwise the marking state is reset after reference processing, + // during the remark pause. + // If we reset here as a result of an overflow during the remark we will + // see assertion failures from any subsequent set_concurrency_and_phase() + // calls. + if (concurrent()) { + // let the task associated with with worker 0 do this + if (worker_id == 0) { + // task 0 is responsible for clearing the global data structures + // We should be here because of an overflow. During STW we should + // not clear the overflow flag since we rely on it being true when + // we exit this method to abort the pause and restart concurent + // marking. + reset_marking_state(true /* clear_overflow */); + force_overflow()->update(); + + if (G1Log::fine()) { + gclog_or_tty->date_stamp(PrintGCDateStamps); + gclog_or_tty->stamp(PrintGCTimeStamps); + gclog_or_tty->print_cr("[GC concurrent-mark-reset-for-overflow]"); + } } } @@ -1007,7 +1021,7 @@ if (concurrent()) { ConcurrentGCThread::stsJoin(); } - // at this point everything should be re-initialised and ready to go + // at this point everything should be re-initialized and ready to go if (verbose_low()) { gclog_or_tty->print_cr("[%u] leaving second barrier", worker_id); @@ -1065,8 +1079,8 @@ double mark_step_duration_ms = G1ConcMarkStepDurationMillis; the_task->do_marking_step(mark_step_duration_ms, - true /* do_stealing */, - true /* do_termination */); + true /* do_termination */, + false /* is_serial*/); double end_time_sec = os::elapsedTime(); double end_vtime_sec = os::elapsedVTime(); @@ -1222,8 +1236,8 @@ uint active_workers = MAX2(1U, parallel_marking_threads()); - // Parallel task terminator is set in "set_phase()" - set_phase(active_workers, true /* concurrent */); + // Parallel task terminator is set in "set_concurrency_and_phase()" + set_concurrency_and_phase(active_workers, true /* concurrent */); CMConcurrentMarkingTask markingTask(this, cmThread()); if (use_parallel_marking_threads()) { @@ -1275,12 +1289,22 @@ if (has_overflown()) { // Oops. We overflowed. Restart concurrent marking. _restart_for_overflow = true; + if (G1TraceMarkStackOverflow) { + gclog_or_tty->print_cr("\nRemark led to restart for overflow."); + } + + // Verify the heap w.r.t. the previous marking bitmap. + if (VerifyDuringGC) { + HandleMark hm; // handle scope + gclog_or_tty->print(" VerifyDuringGC:(overflow)"); + Universe::heap()->prepare_for_verify(); + Universe::verify(/* silent */ false, + /* option */ VerifyOption_G1UsePrevMarking); + } + // Clear the marking state because we will be restarting // marking due to overflowing the global mark stack. reset_marking_state(); - if (G1TraceMarkStackOverflow) { - gclog_or_tty->print_cr("\nRemark led to restart for overflow."); - } } else { // Aggregate the per-task counting data that we have accumulated // while marking. @@ -2184,14 +2208,17 @@ // operating on the global stack. class G1CMKeepAliveAndDrainClosure: public OopClosure { - ConcurrentMark* _cm; - CMTask* _task; - int _ref_counter_limit; - int _ref_counter; + ConcurrentMark* _cm; + CMTask* _task; + int _ref_counter_limit; + int _ref_counter; + bool _is_serial; public: - G1CMKeepAliveAndDrainClosure(ConcurrentMark* cm, CMTask* task) : - _cm(cm), _task(task), _ref_counter_limit(G1RefProcDrainInterval) { + G1CMKeepAliveAndDrainClosure(ConcurrentMark* cm, CMTask* task, bool is_serial) : + _cm(cm), _task(task), _is_serial(is_serial), + _ref_counter_limit(G1RefProcDrainInterval) { assert(_ref_counter_limit > 0, "sanity"); + assert(!_is_serial || _task->worker_id() == 0, "only task 0 for serial code"); _ref_counter = _ref_counter_limit; } @@ -2230,8 +2257,8 @@ do { double mark_step_duration_ms = G1ConcMarkStepDurationMillis; _task->do_marking_step(mark_step_duration_ms, - false /* do_stealing */, - false /* do_termination */); + false /* do_termination */, + _is_serial); } while (_task->has_aborted() && !_cm->has_overflown()); _ref_counter = _ref_counter_limit; } @@ -2253,27 +2280,18 @@ class G1CMDrainMarkingStackClosure: public VoidClosure { ConcurrentMark* _cm; CMTask* _task; - bool _do_stealing; - bool _do_termination; + bool _is_serial; public: - G1CMDrainMarkingStackClosure(ConcurrentMark* cm, CMTask* task, bool is_par) : - _cm(cm), _task(task) { - assert(is_par || _task->worker_id() == 0, - "Only task for worker 0 should be used if ref processing is single threaded"); - // We only allow stealing and only enter the termination protocol - // in CMTask::do_marking_step() if this closure is being instantiated - // for parallel reference processing. - _do_stealing = _do_termination = is_par; + G1CMDrainMarkingStackClosure(ConcurrentMark* cm, CMTask* task, bool is_serial) : + _cm(cm), _task(task), _is_serial(is_serial) { + assert(!_is_serial || _task->worker_id() == 0, "only task 0 for serial code"); } void do_void() { do { if (_cm->verbose_high()) { - gclog_or_tty->print_cr("\t[%u] Drain: Calling do_marking_step - " - "stealing: %s, termination: %s", - _task->worker_id(), - BOOL_TO_STR(_do_stealing), - BOOL_TO_STR(_do_termination)); + gclog_or_tty->print_cr("\t[%u] Drain: Calling do_marking_step - serial: %s", + _task->worker_id(), BOOL_TO_STR(_is_serial)); } // We call CMTask::do_marking_step() to completely drain the local @@ -2294,8 +2312,8 @@ // has_aborted() flag that the marking step has completed. _task->do_marking_step(1000000000.0 /* something very large */, - _do_stealing, - _do_termination); + true /* do_termination */, + _is_serial); } while (_task->has_aborted() && !_cm->has_overflown()); } }; @@ -2328,7 +2346,6 @@ ProcessTask& _proc_task; G1CollectedHeap* _g1h; ConcurrentMark* _cm; - bool _processing_is_mt; public: G1CMRefProcTaskProxy(ProcessTask& proc_task, @@ -2336,15 +2353,15 @@ ConcurrentMark* cm) : AbstractGangTask("Process reference objects in parallel"), _proc_task(proc_task), _g1h(g1h), _cm(cm) { - ReferenceProcessor* rp = _g1h->ref_processor_cm(); - _processing_is_mt = rp->processing_is_mt(); - } + ReferenceProcessor* rp = _g1h->ref_processor_cm(); + assert(rp->processing_is_mt(), "shouldn't be here otherwise"); + } virtual void work(uint worker_id) { - CMTask* marking_task = _cm->task(worker_id); + CMTask* task = _cm->task(worker_id); G1CMIsAliveClosure g1_is_alive(_g1h); - G1CMKeepAliveAndDrainClosure g1_par_keep_alive(_cm, marking_task); - G1CMDrainMarkingStackClosure g1_par_drain(_cm, marking_task, _processing_is_mt); + G1CMKeepAliveAndDrainClosure g1_par_keep_alive(_cm, task, false /* is_serial */); + G1CMDrainMarkingStackClosure g1_par_drain(_cm, task, false /* is_serial */); _proc_task.work(worker_id, g1_is_alive, g1_par_keep_alive, g1_par_drain); } @@ -2356,9 +2373,11 @@ G1CMRefProcTaskProxy proc_task_proxy(proc_task, _g1h, _cm); - // We need to reset the phase for each task execution so that - // the termination protocol of CMTask::do_marking_step works. - _cm->set_phase(_active_workers, false /* concurrent */); + // We need to reset the concurrency level before each + // proxy task execution, so that the termination protocol + // and overflow handling in CMTask::do_marking_step() knows + // how many workers to wait for. + _cm->set_concurrency(_active_workers); _g1h->set_par_threads(_active_workers); _workers->run_task(&proc_task_proxy); _g1h->set_par_threads(0); @@ -2384,12 +2403,29 @@ G1CMRefEnqueueTaskProxy enq_task_proxy(enq_task); + // Not strictly necessary but... + // + // We need to reset the concurrency level before each + // proxy task execution, so that the termination protocol + // and overflow handling in CMTask::do_marking_step() knows + // how many workers to wait for. + _cm->set_concurrency(_active_workers); _g1h->set_par_threads(_active_workers); _workers->run_task(&enq_task_proxy); _g1h->set_par_threads(0); } void ConcurrentMark::weakRefsWork(bool clear_all_soft_refs) { + if (has_overflown()) { + // Skip processing the discovered references if we have + // overflown the global marking stack. Reference objects + // only get discovered once so it is OK to not + // de-populate the discovered reference lists. We could have, + // but the only benefit would be that, when marking restarts, + // less reference objects are discovered. + return; + } + ResourceMark rm; HandleMark hm; @@ -2415,26 +2451,39 @@ rp->setup_policy(clear_all_soft_refs); assert(_markStack.isEmpty(), "mark stack should be empty"); - // Non-MT instances 'Keep Alive' and 'Complete GC' oop closures. - G1CMKeepAliveAndDrainClosure g1_keep_alive(this, task(0)); - G1CMDrainMarkingStackClosure g1_drain_mark_stack(this, task(0), false); - - // We need at least one active thread. If reference processing is - // not multi-threaded we use the current (ConcurrentMarkThread) thread, - // otherwise we use the work gang from the G1CollectedHeap and we - // utilize all the worker threads we can. - uint active_workers = (rp->processing_is_mt() && g1h->workers() != NULL - ? g1h->workers()->active_workers() - : 1U); - + // Instances of the 'Keep Alive' and 'Complete GC' closures used + // in serial reference processing. Note these closures are also + // used for serially processing (by the the current thread) the + // JNI references during parallel reference processing. + // + // These closures do not need to synchronize with the worker + // threads involved in parallel reference processing as these + // instances are executed serially by the current thread (e.g. + // reference processing is not multi-threaded and is thus + // performed by the current thread instead of a gang worker). + // + // The gang tasks involved in parallel reference procssing create + // their own instances of these closures, which do their own + // synchronization among themselves. + G1CMKeepAliveAndDrainClosure g1_keep_alive(this, task(0), true /* is_serial */); + G1CMDrainMarkingStackClosure g1_drain_mark_stack(this, task(0), true /* is_serial */); + + // We need at least one active thread. If reference processing + // is not multi-threaded we use the current (VMThread) thread, + // otherwise we use the work gang from the G1CollectedHeap and + // we utilize all the worker threads we can. + bool processing_is_mt = rp->processing_is_mt() && g1h->workers() != NULL; + uint active_workers = (processing_is_mt ? g1h->workers()->active_workers() : 1U); active_workers = MAX2(MIN2(active_workers, _max_worker_id), 1U); + // Parallel processing task executor. G1CMRefProcTaskExecutor par_task_executor(g1h, this, g1h->workers(), active_workers); - - AbstractRefProcTaskExecutor* executor = (rp->processing_is_mt() - ? &par_task_executor - : NULL); + AbstractRefProcTaskExecutor* executor = (processing_is_mt ? &par_task_executor : NULL); + + // Set the concurrency level. The phase was already set prior to + // executing the remark task. + set_concurrency(active_workers); // Set the degree of MT processing here. If the discovery was done MT, // the number of threads involved during discovery could differ from @@ -2454,6 +2503,7 @@ assert(_markStack.overflow() || _markStack.isEmpty(), "mark stack should be empty (unless it overflowed)"); + if (_markStack.overflow()) { // This should have been done already when we tried to push an // entry on to the global mark stack. But let's do it again. @@ -2482,8 +2532,8 @@ class CMRemarkTask: public AbstractGangTask { private: - ConcurrentMark *_cm; - + ConcurrentMark* _cm; + bool _is_serial; public: void work(uint worker_id) { // Since all available tasks are actually started, we should @@ -2493,8 +2543,8 @@ task->record_start_time(); do { task->do_marking_step(1000000000.0 /* something very large */, - true /* do_stealing */, - true /* do_termination */); + true /* do_termination */, + _is_serial); } while (task->has_aborted() && !_cm->has_overflown()); // If we overflow, then we do not want to restart. We instead // want to abort remark and do concurrent marking again. @@ -2502,8 +2552,8 @@ } } - CMRemarkTask(ConcurrentMark* cm, int active_workers) : - AbstractGangTask("Par Remark"), _cm(cm) { + CMRemarkTask(ConcurrentMark* cm, int active_workers, bool is_serial) : + AbstractGangTask("Par Remark"), _cm(cm), _is_serial(is_serial) { _cm->terminator()->reset_for_reuse(active_workers); } }; @@ -2524,30 +2574,40 @@ active_workers = (uint) ParallelGCThreads; g1h->workers()->set_active_workers(active_workers); } - set_phase(active_workers, false /* concurrent */); + set_concurrency_and_phase(active_workers, false /* concurrent */); // Leave _parallel_marking_threads at it's // value originally calculated in the ConcurrentMark // constructor and pass values of the active workers // through the gang in the task. - CMRemarkTask remarkTask(this, active_workers); + CMRemarkTask remarkTask(this, active_workers, false /* is_serial */); + // We will start all available threads, even if we decide that the + // active_workers will be fewer. The extra ones will just bail out + // immediately. g1h->set_par_threads(active_workers); g1h->workers()->run_task(&remarkTask); g1h->set_par_threads(0); } else { G1CollectedHeap::StrongRootsScope srs(g1h); - // this is remark, so we'll use up all available threads uint active_workers = 1; - set_phase(active_workers, false /* concurrent */); - - CMRemarkTask remarkTask(this, active_workers); - // We will start all available threads, even if we decide that the - // active_workers will be fewer. The extra ones will just bail out - // immediately. + set_concurrency_and_phase(active_workers, false /* concurrent */); + + // Note - if there's no work gang then the VMThread will be + // the thread to execute the remark - serially. We have + // to pass true for the is_serial parameter so that + // CMTask::do_marking_step() doesn't enter the sync + // barriers in the event of an overflow. Doing so will + // cause an assert that the current thread is not a + // concurrent GC thread. + CMRemarkTask remarkTask(this, active_workers, true /* is_serial*/); remarkTask.work(0); } SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); - guarantee(satb_mq_set.completed_buffers_num() == 0, "invariant"); + guarantee(has_overflown() || + satb_mq_set.completed_buffers_num() == 0, + err_msg("Invariant: has_overflown = %s, num buffers = %d", + BOOL_TO_STR(has_overflown()), + satb_mq_set.completed_buffers_num())); print_stats(); } @@ -3854,8 +3914,8 @@ /***************************************************************************** - The do_marking_step(time_target_ms) method is the building block - of the parallel marking framework. It can be called in parallel + The do_marking_step(time_target_ms, ...) method is the building + block of the parallel marking framework. It can be called in parallel with other invocations of do_marking_step() on different tasks (but only one per task, obviously) and concurrently with the mutator threads, or during remark, hence it eliminates the need @@ -3865,7 +3925,7 @@ pauses too, since do_marking_step() ensures that it aborts before it needs to yield. - The data structures that is uses to do marking work are the + The data structures that it uses to do marking work are the following: (1) Marking Bitmap. If there are gray objects that appear only @@ -3914,7 +3974,7 @@ (2) When a global overflow (on the global stack) has been triggered. Before the task aborts, it will actually sync up with the other tasks to ensure that all the marking data structures - (local queues, stacks, fingers etc.) are re-initialised so that + (local queues, stacks, fingers etc.) are re-initialized so that when do_marking_step() completes, the marking phase can immediately restart. @@ -3951,11 +4011,25 @@ place, it was natural to piggy-back all the other conditions on it too and not constantly check them throughout the code. + If do_termination is true then do_marking_step will enter its + termination protocol. + + The value of is_serial must be true when do_marking_step is being + called serially (i.e. by the VMThread) and do_marking_step should + skip any synchronization in the termination and overflow code. + Examples include the serial remark code and the serial reference + processing closures. + + The value of is_serial must be false when do_marking_step is + being called by any of the worker threads in a work gang. + Examples include the concurrent marking code (CMMarkingTask), + the MT remark code, and the MT reference processing closures. + *****************************************************************************/ void CMTask::do_marking_step(double time_target_ms, - bool do_stealing, - bool do_termination) { + bool do_termination, + bool is_serial) { assert(time_target_ms >= 1.0, "minimum granularity is 1ms"); assert(concurrent() == _cm->concurrent(), "they should be the same"); @@ -3976,6 +4050,12 @@ _start_time_ms = os::elapsedVTime() * 1000.0; statsOnly( _interval_start_time_ms = _start_time_ms ); + // If do_stealing is true then do_marking_step will attempt to + // steal work from the other CMTasks. It only makes sense to + // enable stealing when the termination protocol is enabled + // and do_marking_step() is not being called serially. + bool do_stealing = do_termination && !is_serial; + double diff_prediction_ms = g1_policy->get_new_prediction(&_marking_step_diffs_ms); _time_target_ms = time_target_ms - diff_prediction_ms; @@ -4237,10 +4317,12 @@ } _termination_start_time_ms = os::elapsedVTime() * 1000.0; + // The CMTask class also extends the TerminatorTerminator class, // hence its should_exit_termination() method will also decide // whether to exit the termination protocol or not. - bool finished = _cm->terminator()->offer_termination(this); + bool finished = (is_serial || + _cm->terminator()->offer_termination(this)); double termination_end_time_ms = os::elapsedVTime() * 1000.0; _termination_time_ms += termination_end_time_ms - _termination_start_time_ms; @@ -4320,20 +4402,28 @@ gclog_or_tty->print_cr("[%u] detected overflow", _worker_id); } - _cm->enter_first_sync_barrier(_worker_id); - // When we exit this sync barrier we know that all tasks have - // stopped doing marking work. So, it's now safe to - // re-initialise our data structures. At the end of this method, - // task 0 will clear the global data structures. + if (!is_serial) { + // We only need to enter the sync barrier if being called + // from a parallel context + _cm->enter_first_sync_barrier(_worker_id); + + // When we exit this sync barrier we know that all tasks have + // stopped doing marking work. So, it's now safe to + // re-initialise our data structures. At the end of this method, + // task 0 will clear the global data structures. + } statsOnly( ++_aborted_overflow ); // We clear the local state of this task... clear_region_fields(); - // ...and enter the second barrier. - _cm->enter_second_sync_barrier(_worker_id); - // At this point everything has bee re-initialised and we're + if (!is_serial) { + // ...and enter the second barrier. + _cm->enter_second_sync_barrier(_worker_id); + } + // At this point, if we're during the concurrent phase of + // marking, everything has been re-initialized and we're // ready to restart. }
--- a/src/share/vm/gc_implementation/g1/concurrentMark.hpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.hpp Sun Mar 24 12:43:13 2013 -0700 @@ -166,7 +166,7 @@ class CMMarkStack VALUE_OBJ_CLASS_SPEC { VirtualSpace _virtual_space; // Underlying backing store for actual stack ConcurrentMark* _cm; - oop* _base; // bottom of stack + oop* _base; // bottom of stack jint _index; // one more than last occupied index jint _capacity; // max #elements jint _saved_index; // value of _index saved at start of GC @@ -491,9 +491,12 @@ // structures are initialised to a sensible and predictable state. void set_non_marking_state(); + // Called to indicate how many threads are currently active. + void set_concurrency(uint active_tasks); + // It should be called to indicate which phase we're in (concurrent // mark or remark) and how many threads are currently active. - void set_phase(uint active_tasks, bool concurrent); + void set_concurrency_and_phase(uint active_tasks, bool concurrent); // prints all gathered CM-related statistics void print_stats(); @@ -1146,7 +1149,9 @@ // trying not to exceed the given duration. However, it might exit // prematurely, according to some conditions (i.e. SATB buffers are // available for processing). - void do_marking_step(double target_ms, bool do_stealing, bool do_termination); + void do_marking_step(double target_ms, + bool do_termination, + bool is_serial); // These two calls start and stop the timer void record_start_time() {
--- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -656,7 +656,7 @@ tty->print_cr("[Accumulated GC generation 0 time %3.7f secs]", time); } if (TraceGen1Time) { - double time = PSMarkSweep::accumulated_time()->seconds(); + double time = UseParallelOldGC ? PSParallelCompact::accumulated_time()->seconds() : PSMarkSweep::accumulated_time()->seconds(); tty->print_cr("[Accumulated GC generation 1 time %3.7f secs]", time); } }
--- a/src/share/vm/memory/genCollectedHeap.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/memory/genCollectedHeap.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -554,6 +554,8 @@ } if (complete) { + // Delete metaspaces for unloaded class loaders and clean up loader_data graph + ClassLoaderDataGraph::purge(); // Resize the metaspace capacity after full collections MetaspaceGC::compute_new_size(); update_full_collections_completed(); @@ -564,11 +566,6 @@ gc_epilogue(complete); - // Delete metaspaces for unloaded class loaders and clean up loader_data graph - if (complete) { - ClassLoaderDataGraph::purge(); - } - if (must_restore_marks_for_biased_locking) { BiasedLocking::restore_marks(); }
--- a/src/share/vm/memory/metaspace.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/memory/metaspace.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -334,6 +334,9 @@ // byte_size is the size of the associated virtualspace. VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL), _rs(0) { + // align up to vm allocation granularity + byte_size = align_size_up(byte_size, os::vm_allocation_granularity()); + // This allocates memory with mmap. For DumpSharedspaces, allocate the // space at low memory so that other shared images don't conflict. // This is the same address as memory needed for UseCompressedOops but @@ -1100,25 +1103,24 @@ } bool MetaspaceGC::should_expand(VirtualSpaceList* vsl, size_t word_size) { + // If the user wants a limit, impose one. + if (!FLAG_IS_DEFAULT(MaxMetaspaceSize) && + MetaspaceAux::reserved_in_bytes() >= MaxMetaspaceSize) { + return false; + } // Class virtual space should always be expanded. Call GC for the other // metadata virtual space. if (vsl == Metaspace::class_space_list()) return true; - // If the user wants a limit, impose one. - size_t max_metaspace_size_words = MaxMetaspaceSize / BytesPerWord; - size_t metaspace_size_words = MetaspaceSize / BytesPerWord; - if (!FLAG_IS_DEFAULT(MaxMetaspaceSize) && - vsl->capacity_words_sum() >= max_metaspace_size_words) { - return false; - } - // If this is part of an allocation after a GC, expand // unconditionally. if(MetaspaceGC::expand_after_GC()) { return true; } + size_t metaspace_size_words = MetaspaceSize / BytesPerWord; + // If the capacity is below the minimum capacity, allow the // expansion. Also set the high-water-mark (capacity_until_GC) // to that minimum capacity so that a GC will not be induced @@ -1308,8 +1310,7 @@ gclog_or_tty->print_cr(" metaspace HWM: %.1fK", new_capacity_until_GC / (double) K); } } - assert(vsl->used_bytes_sum() == used_after_gc && - used_after_gc <= vsl->capacity_bytes_sum(), + assert(used_after_gc <= vsl->capacity_bytes_sum(), "sanity check"); } @@ -1969,6 +1970,9 @@ } SpaceManager::~SpaceManager() { + // This call this->_lock which can't be done while holding expand_lock() + const size_t in_use_before = sum_capacity_in_chunks_in_use(); + MutexLockerEx fcl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); @@ -1986,7 +1990,7 @@ // Have to update before the chunks_in_use lists are emptied // below. - chunk_manager->inc_free_chunks_total(sum_capacity_in_chunks_in_use(), + chunk_manager->inc_free_chunks_total(in_use_before, sum_count_in_chunks_in_use()); // Add all the chunks in use by this space manager
--- a/src/share/vm/oops/klass.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/oops/klass.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -486,6 +486,12 @@ } void Klass::remove_unshareable_info() { + if (!DumpSharedSpaces) { + // Clean up after OOM during class loading + if (class_loader_data() != NULL) { + class_loader_data()->remove_class(this); + } + } set_subklass(NULL); set_next_sibling(NULL); // Clear the java mirror
--- a/src/share/vm/oops/method.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/oops/method.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -798,7 +798,15 @@ backedge_counter()->reset(); _adapter = NULL; _from_compiled_entry = NULL; - assert(_method_data == NULL, "unexpected method data?"); + + // In case of DumpSharedSpaces, _method_data should always be NULL. + // + // During runtime (!DumpSharedSpaces), when we are cleaning a + // shared class that failed to load, this->link_method() may + // have already been called (before an exception happened), so + // this->_method_data may not be NULL. + assert(!DumpSharedSpaces || _method_data == NULL, "unexpected method data?"); + set_method_data(NULL); set_interpreter_throwout_count(0); set_interpreter_invocation_count(0);
--- a/src/share/vm/prims/jni.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/prims/jni.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -92,7 +92,7 @@ # include "os_bsd.inline.hpp" #endif -static jint CurrentVersion = JNI_VERSION_1_6; +static jint CurrentVersion = JNI_VERSION_1_8; // The DT_RETURN_MARK macros create a scoped object to fire the dtrace
--- a/src/share/vm/prims/jni.h Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/prims/jni.h Sun Mar 24 12:43:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1951,6 +1951,7 @@ #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 #ifdef __cplusplus } /* extern "C" */
--- a/src/share/vm/runtime/thread.cpp Sat Mar 23 06:22:07 2013 -0700 +++ b/src/share/vm/runtime/thread.cpp Sun Mar 24 12:43:13 2013 -0700 @@ -4061,6 +4061,7 @@ if (version == JNI_VERSION_1_2) return JNI_TRUE; if (version == JNI_VERSION_1_4) return JNI_TRUE; if (version == JNI_VERSION_1_6) return JNI_TRUE; + if (version == JNI_VERSION_1_8) return JNI_TRUE; return JNI_FALSE; }