OpenJDK / jdk / jdk
changeset 10945:c3c042052bae
Merge
author | lana |
---|---|
date | Tue, 25 Oct 2011 10:52:03 -0700 |
parents | d91978895fac cc1f5ce8e504 |
children | dff783bb105d |
files | hotspot/agent/src/os/solaris/dbx/Makefile hotspot/agent/src/os/solaris/dbx/README hotspot/agent/src/os/solaris/dbx/README-commands.txt hotspot/agent/src/os/solaris/dbx/helloWorld.cpp hotspot/agent/src/os/solaris/dbx/proc_service_2.h hotspot/agent/src/os/solaris/dbx/shell_imp.h hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.hpp hotspot/agent/src/os/win32/BasicList.hpp hotspot/agent/src/os/win32/Buffer.cpp hotspot/agent/src/os/win32/Buffer.hpp hotspot/agent/src/os/win32/Dispatcher.cpp hotspot/agent/src/os/win32/Dispatcher.hpp hotspot/agent/src/os/win32/Handler.hpp hotspot/agent/src/os/win32/IOBuf.cpp hotspot/agent/src/os/win32/IOBuf.hpp hotspot/agent/src/os/win32/LockableList.hpp hotspot/agent/src/os/win32/Makefile hotspot/agent/src/os/win32/Message.hpp hotspot/agent/src/os/win32/Monitor.cpp hotspot/agent/src/os/win32/Monitor.hpp hotspot/agent/src/os/win32/README-commands.txt hotspot/agent/src/os/win32/README.txt hotspot/agent/src/os/win32/Reaper.cpp hotspot/agent/src/os/win32/Reaper.hpp hotspot/agent/src/os/win32/SwDbgSrv.cpp hotspot/agent/src/os/win32/SwDbgSrv.dsp hotspot/agent/src/os/win32/SwDbgSrv.dsw hotspot/agent/src/os/win32/SwDbgSub.cpp hotspot/agent/src/os/win32/SwDbgSub.dsp hotspot/agent/src/os/win32/initWinsock.cpp hotspot/agent/src/os/win32/initWinsock.hpp hotspot/agent/src/os/win32/ioUtils.cpp hotspot/agent/src/os/win32/ioUtils.hpp hotspot/agent/src/os/win32/isNT4.cpp hotspot/agent/src/os/win32/isNT4.hpp hotspot/agent/src/os/win32/libInfo.cpp hotspot/agent/src/os/win32/libInfo.hpp hotspot/agent/src/os/win32/nt4internals.cpp hotspot/agent/src/os/win32/nt4internals.hpp hotspot/agent/src/os/win32/ports.h hotspot/agent/src/os/win32/procList.cpp hotspot/agent/src/os/win32/procList.hpp hotspot/agent/src/os/win32/serverLists.cpp hotspot/agent/src/os/win32/serverLists.hpp hotspot/agent/src/os/win32/toolHelp.cpp hotspot/agent/src/os/win32/toolHelp.hpp hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxAddress.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebugger.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxDebuggerLocal.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxOopHandle.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/DbxThreadFactory.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThread.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadContext.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/sparc/DbxSPARCThreadFactory.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86Thread.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadContext.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/dbx/x86/DbxX86ThreadFactory.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/AddressDataSource.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/DLL.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestDebugger.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/TestHelloWorld.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Address.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugInfoBuilder.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32CDebugger.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Debugger.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32DebuggerLocal.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntry.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32LDTEntryConstants.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32OopHandle.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32Thread.java hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/Win32ThreadContext.java hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64RegisterMap.java hotspot/make/solaris/makefiles/mapfile-vers-nonproduct hotspot/make/templates/bsd-header hotspot/src/share/vm/runtime/reflectionCompat.hpp jdk/src/share/classes/java/util/XMLUtils.java jdk/src/share/classes/sun/tools/jar/JarImageSource.java jdk/src/share/native/sun/awt/libpng/pnggccrd.c jdk/src/share/native/sun/awt/libpng/pngvcrd.c |
diffstat | 1171 files changed, 76033 insertions(+), 44569 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Tue Oct 25 15:40:34 2011 +0100 +++ b/.hgtags Tue Oct 25 10:52:03 2011 -0700 @@ -127,3 +127,8 @@ 6815e85bf96d6d3875954f9777660372cd70d065 jdk8-b03 31f5c34d78081572ad9a2401c0bb0c6b9711dd65 jdk8-b04 c4f9ea1ecb55ff44e0dd21d2888ead308c86a3aa jdk8-b05 +429da7734bf491bccde2a752fae97e9f225896dc jdk8-b06 +bc5710332b294676661103bb20d47d2ea3ba8def jdk8-b07 +24ee504f80412770c6874836cd9e55b536427b1d jdk8-b08 +fbf3cabc9e3bb1fcf710941d777cb0400505fbe6 jdk8-b09 +f651ce87127980c58e3599daba964eba2f3b4026 jdk8-b10
--- a/.hgtags-top-repo Tue Oct 25 15:40:34 2011 +0100 +++ b/.hgtags-top-repo Tue Oct 25 10:52:03 2011 -0700 @@ -127,3 +127,8 @@ 587bb549dff83131b65f40aa51864f69562f34a7 jdk8-b03 0b66a233bfb9ba2ebda1e5cdfdb0373d6c1e3c69 jdk8-b04 b910aac18c772b823b1f7da03e2c6528725cc6de jdk8-b05 +28cf2aec4dd7c3c75efc1c15078522467c781a6d jdk8-b06 +0db7ae9f2b1017124c779bccd016c976928859a0 jdk8-b07 +fb1bc13260d76447e269e843859eb593fe2a8ab2 jdk8-b08 +8adb70647b5af5273dfe6a540f07be667cd50216 jdk8-b09 +a6c4c248e8fa350c35014fa94bab5ac1a1ac3299 jdk8-b10
--- a/corba/.hgtags Tue Oct 25 15:40:34 2011 +0100 +++ b/corba/.hgtags Tue Oct 25 10:52:03 2011 -0700 @@ -127,3 +127,8 @@ cd0da00694fbce642db9be936d3e4909a71d911d jdk8-b03 60a68d688e24473cf84dedd1e60901a61ab82555 jdk8-b04 cc1b599b986a37cb57de4584c5e58169766ca535 jdk8-b05 +45c43dde7ba7f176333a51a98f086275478836fa jdk8-b06 +3d61f0856f349e2163bf98146465dab3b7437f63 jdk8-b07 +0d52b1c87aa8fdea7fdc9c4126ea58f95ca6b351 jdk8-b08 +a891732c1a83082177ff7a4cf1506068d9cc0a47 jdk8-b09 +cda87f7fefcee3b89742a57ce5ad9b03a54c210d jdk8-b10
--- a/hotspot/.hgignore Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/.hgignore Tue Oct 25 10:52:03 2011 -0700 @@ -6,3 +6,4 @@ ^src/share/tools/IdealGraphVisualizer/build/ ^src/share/tools/IdealGraphVisualizer/dist/ ^.hgtip +.DS_Store
--- a/hotspot/.hgtags Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/.hgtags Tue Oct 25 10:52:03 2011 -0700 @@ -179,3 +179,17 @@ 3a2fb61165dfc72e398179a2796d740c8da5b8c0 jdk8-b03 0fa3ace511fe98fe948e751531f3e2b7c60c8376 jdk8-b04 dce7d24674f4d0bed00de24f00119057fdce7cfb jdk8-b05 +0db80d8e77fccddf5e6fa49963226b54ac7d0f62 jdk8-b06 +3f0cf875af83f55ec5e1a5cea80455315f9322a2 jdk8-b07 +0cc8a70952c368e06de2adab1f2649a408f5e577 hs22-b01 +7c29742c41b44fb0cd5a13c7ac8834f3f2ca649e hs22-b02 +3a2fb61165dfc72e398179a2796d740c8da5b8c0 hs22-b03 +ce9bde819dcba4a5d2822229d9183e69c74326ca hs22-b04 +513a84dd0f8b56dc0836b4e0bdd5dd0a778fc634 hs22-b05 +650d15d8f37255d3b805aa00c5bd1c30984b203d hs22-b06 +da883b9e6d3788057f9577e72712998ed82c9b7e hs23-b01 +49ed7eacfd16616166ff066493143889741097af jdk8-b08 +7c20d272643f47195478708eff593a9cce40fec4 jdk8-b09 +e4f412d2b75d2c797acff965aa2c420e3d358f09 hs23-b02 +d815de2e85e511b7deab2a83cf80c0224d011da9 jdk8-b10 +4d3850d9d326ac3a9bee2d867727e954322d014e hs23-b03
--- a/hotspot/agent/make/Makefile Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/agent/make/Makefile Tue Oct 25 10:52:03 2011 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2011, 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,17 +48,18 @@ sun.jvm.hotspot.bugspot \ sun.jvm.hotspot.bugspot.tree \ sun.jvm.hotspot.c1 \ +sun.jvm.hotspot.ci \ sun.jvm.hotspot.code \ sun.jvm.hotspot.compiler \ sun.jvm.hotspot.debugger \ sun.jvm.hotspot.debugger.amd64 \ +sun.jvm.hotspot.debugger.bsd \ +sun.jvm.hotspot.debugger.bsd.amd64 \ +sun.jvm.hotspot.debugger.bsd.x86 \ sun.jvm.hotspot.debugger.cdbg \ sun.jvm.hotspot.debugger.cdbg.basic \ sun.jvm.hotspot.debugger.cdbg.basic.amd64 \ sun.jvm.hotspot.debugger.cdbg.basic.x86 \ -sun.jvm.hotspot.debugger.dbx \ -sun.jvm.hotspot.debugger.dbx.sparc \ -sun.jvm.hotspot.debugger.dbx.x86 \ sun.jvm.hotspot.debugger.dummy \ sun.jvm.hotspot.debugger.ia64 \ sun.jvm.hotspot.debugger.linux \ @@ -76,7 +77,6 @@ sun.jvm.hotspot.debugger.remote.sparc \ sun.jvm.hotspot.debugger.remote.x86 \ sun.jvm.hotspot.debugger.sparc \ -sun.jvm.hotspot.debugger.win32 \ sun.jvm.hotspot.debugger.win32.coff \ sun.jvm.hotspot.debugger.windbg \ sun.jvm.hotspot.debugger.windbg.amd64 \ @@ -84,6 +84,7 @@ sun.jvm.hotspot.debugger.windbg.x86 \ sun.jvm.hotspot.debugger.x86 \ sun.jvm.hotspot.gc_implementation \ +sun.jvm.hotspot.gc_implementation.g1 \ sun.jvm.hotspot.gc_implementation.parallelScavenge \ sun.jvm.hotspot.gc_implementation.shared \ sun.jvm.hotspot.gc_interface \ @@ -91,9 +92,14 @@ sun.jvm.hotspot.jdi \ sun.jvm.hotspot.livejvm \ sun.jvm.hotspot.memory \ +sun.jvm.hotspot.opto \ sun.jvm.hotspot.oops \ +sun.jvm.hotspot.prims \ sun.jvm.hotspot.runtime \ sun.jvm.hotspot.runtime.amd64 \ +sun.jvm.hotspot.runtime.bsd \ +sun.jvm.hotspot.runtime.bsd_amd64 \ +sun.jvm.hotspot.runtime.bsd_x86 \ sun.jvm.hotspot.runtime.ia64 \ sun.jvm.hotspot.runtime.linux \ sun.jvm.hotspot.runtime.linux_amd64 \ @@ -139,17 +145,18 @@ sun/jvm/hotspot/bugspot/*.java \ sun/jvm/hotspot/bugspot/tree/*.java \ sun/jvm/hotspot/c1/*.java \ +sun/jvm/hotspot/ci/*.java \ sun/jvm/hotspot/code/*.java \ sun/jvm/hotspot/compiler/*.java \ sun/jvm/hotspot/debugger/*.java \ sun/jvm/hotspot/debugger/amd64/*.java \ +sun/jvm/hotspot/debugger/bsd/*.java \ +sun/jvm/hotspot/debugger/bsd/amd64/*.java \ +sun/jvm/hotspot/debugger/bsd/x86/*.java \ sun/jvm/hotspot/debugger/cdbg/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/x86/*.java \ -sun/jvm/hotspot/debugger/dbx/*.java \ -sun/jvm/hotspot/debugger/dbx/sparc/*.java \ -sun/jvm/hotspot/debugger/dbx/x86/*.java \ sun/jvm/hotspot/debugger/dummy/*.java \ sun/jvm/hotspot/debugger/ia64/*.java \ sun/jvm/hotspot/debugger/linux/*.java \ @@ -165,19 +172,26 @@ sun/jvm/hotspot/debugger/remote/sparc/*.java \ sun/jvm/hotspot/debugger/remote/x86/*.java \ sun/jvm/hotspot/debugger/sparc/*.java \ -sun/jvm/hotspot/debugger/win32/*.java \ sun/jvm/hotspot/debugger/win32/coff/*.java \ sun/jvm/hotspot/debugger/windbg/*.java \ sun/jvm/hotspot/debugger/windbg/ia64/*.java \ sun/jvm/hotspot/debugger/windbg/x86/*.java \ sun/jvm/hotspot/debugger/x86/*.java \ +sun/jvm/hotspot/gc_implementation/g1/*.java \ +sun/jvm/hotspot/gc_implementation/parallelScavenge/*.java \ +sun/jvm/hotspot/gc_implementation/shared/*.java \ sun/jvm/hotspot/interpreter/*.java \ sun/jvm/hotspot/jdi/*.java \ sun/jvm/hotspot/livejvm/*.java \ sun/jvm/hotspot/memory/*.java \ sun/jvm/hotspot/oops/*.java \ +sun/jvm/hotspot/opto/*.java \ +sun/jvm/hotspot/prims/*.java \ sun/jvm/hotspot/runtime/*.java \ sun/jvm/hotspot/runtime/amd64/*.java \ +sun/jvm/hotspot/runtime/bsd/*.java \ +sun/jvm/hotspot/runtime/bsd_amd64/*.java \ +sun/jvm/hotspot/runtime/bsd_x86/*.java \ sun/jvm/hotspot/runtime/ia64/*.java \ sun/jvm/hotspot/runtime/linux/*.java \ sun/jvm/hotspot/runtime/linux_amd64/*.java \
--- a/hotspot/agent/make/saenv.sh Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/agent/make/saenv.sh Tue Oct 25 10:52:03 2011 -0700 @@ -70,6 +70,14 @@ SA_CLASSPATH=$STARTDIR/../build/classes:$STARTDIR/../src/share/lib/js.jar:$STARTDIR/sa.jar:$STARTDIR/lib/js.jar +if [ ! -z "$SA_TYPEDB" ]; then + if [ ! -f $SA_TYPEDB ]; then + echo "$SA_TYPEDB is unreadable" + exit 1 + fi + OPTIONS="-Dsun.jvm.hotspot.typedb=$SA_TYPEDB ${OPTIONS}" +fi + OPTIONS="-Djava.system.class.loader=sun.jvm.hotspot.SALauncherLoader ${OPTIONS}" SA_JAVA_CMD="$SA_PREFIX_CMD $SA_JAVA -showversion ${OPTIONS} -cp $SA_CLASSPATH $SA_OPTIONS"
--- a/hotspot/agent/make/saenv64.sh Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/agent/make/saenv64.sh Tue Oct 25 10:52:03 2011 -0700 @@ -67,6 +67,14 @@ SA_CLASSPATH=$STARTDIR/../build/classes:$STARTDIR/../src/share/lib/js.jar:$STARTDIR/sa.jar::$STARTDIR/lib/js.jar +if [ ! -z "$SA_TYPEDB" ]; then + if [ ! -f $SA_TYPEDB ]; then + echo "$SA_TYPEDB is unreadable" + exit 1 + fi + OPTIONS="-Dsun.jvm.hotspot.typedb=$SA_TYPEDB ${OPTIONS}" +fi + OPTIONS="-Djava.system.class.loader=sun.jvm.hotspot.SALauncherLoader ${OPTIONS}" SA_JAVA_CMD="$SA_PREFIX_CMD $SA_JAVA -d64 -showversion ${OPTIONS} -cp $SA_CLASSPATH $SA_OPTIONS"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/BsdDebuggerLocal.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2002, 2007, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <stdlib.h> +#include <jni.h> +#include "libproc.h" + +#if defined(x86_64) && !defined(amd64) +#define amd64 1 +#endif + +#ifdef i386 +#include "sun_jvm_hotspot_debugger_x86_X86ThreadContext.h" +#endif + +#ifdef amd64 +#include "sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext.h" +#endif + +#if defined(sparc) || defined(sparcv9) +#include "sun_jvm_hotspot_debugger_sparc_SPARCThreadContext.h" +#endif + +static jfieldID p_ps_prochandle_ID = 0; +static jfieldID threadList_ID = 0; +static jfieldID loadObjectList_ID = 0; + +static jmethodID createClosestSymbol_ID = 0; +static jmethodID createLoadObject_ID = 0; +static jmethodID getThreadForThreadId_ID = 0; +static jmethodID listAdd_ID = 0; + +#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; } +#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;} +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; } +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;} + +static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { + (*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; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: init0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0 + (JNIEnv *env, jclass cls) { + jclass listClass; + + if (init_libproc(getenv("LIBSAPROC_DEBUG") != NULL) != true) { + THROW_NEW_DEBUGGER_EXCEPTION("can't initialize libproc"); + } + + // fields we use + p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J"); + CHECK_EXCEPTION; + threadList_ID = (*env)->GetFieldID(env, cls, "threadList", "Ljava/util/List;"); + 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; + getThreadForThreadId_ID = (*env)->GetMethodID(env, cls, "getThreadForThreadId", + "(J)Lsun/jvm/hotspot/debugger/ThreadProxy;"); + CHECK_EXCEPTION; + // java.util.List method we call + listClass = (*env)->FindClass(env, "java/util/List"); + CHECK_EXCEPTION; + listAdd_ID = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_EXCEPTION; +} + +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize + (JNIEnv *env, jclass cls) +{ +#ifdef _LP64 + return 8; +#else + return 4; +#endif + +} + + +static void fillThreadsAndLoadObjects(JNIEnv* env, jobject this_obj, struct ps_prochandle* ph) { + int n = 0, i = 0; + + // add threads + n = get_num_threads(ph); + for (i = 0; i < n; i++) { + jobject thread; + jobject threadList; + lwpid_t lwpid; + + lwpid = get_lwp_id(ph, i); + thread = (*env)->CallObjectMethod(env, this_obj, getThreadForThreadId_ID, + (jlong)lwpid); + CHECK_EXCEPTION; + threadList = (*env)->GetObjectField(env, this_obj, threadList_ID); + CHECK_EXCEPTION; + (*env)->CallBooleanMethod(env, threadList, listAdd_ID, thread); + CHECK_EXCEPTION; + } + + // 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: (I)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I + (JNIEnv *env, jobject this_obj, jint jpid) { + + struct ps_prochandle* ph; + if ( (ph = Pgrab(jpid)) == NULL) { + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); + } + (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph); + fillThreadsAndLoadObjects(env, this_obj, ph); +} + +/* + * 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; + + 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); + fillThreadsAndLoadObjects(env, this_obj, ph); +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: detach0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0 + (JNIEnv *env, jobject this_obj) { + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + if (ph != NULL) { + Prelease(ph); + } +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0 + (JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + const char *objectName_cstr, *symbolName_cstr; + jlong addr; + jboolean isCopy; + struct ps_prochandle* ph = get_proc_handle(env, this_obj); + + 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); + + 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; +} + +/* + * 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); +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0 + (JNIEnv *env, 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(get_proc_handle(env, this_obj), (psaddr_t) (uintptr_t)addr, bufPtr, numBytes); + (*env)->ReleaseByteArrayElements(env, array, bufPtr, 0); + return (err == PS_OK)? array : 0; +} + +JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0 + (JNIEnv *env, jobject this_obj, jint lwp_id) { + + 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 +#ifdef i386 +#define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG +#endif +#ifdef ia64 +#define NPRGREG IA64_REG_COUNT +#endif +#ifdef amd64 +#define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG +#endif +#if defined(sparc) || defined(sparcv9) +#define NPRGREG sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_NPRGREG +#endif + + array = (*env)->NewLongArray(env, NPRGREG); + CHECK_EXCEPTION_(0); + regs = (*env)->GetLongArrayElements(env, array, &isCopy); + +#undef REG_INDEX + +#ifdef i386 +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_x86_X86ThreadContext_##reg + + regs[REG_INDEX(GS)] = (uintptr_t) gregs.r_gs; + regs[REG_INDEX(FS)] = (uintptr_t) gregs.r_fs; + regs[REG_INDEX(ES)] = (uintptr_t) gregs.r_es; + regs[REG_INDEX(DS)] = (uintptr_t) gregs.r_ds; + regs[REG_INDEX(EDI)] = (uintptr_t) gregs.r_edi; + regs[REG_INDEX(ESI)] = (uintptr_t) gregs.r_esi; + regs[REG_INDEX(FP)] = (uintptr_t) gregs.r_ebp; + regs[REG_INDEX(SP)] = (uintptr_t) gregs.r_isp; + regs[REG_INDEX(EBX)] = (uintptr_t) gregs.r_ebx; + regs[REG_INDEX(EDX)] = (uintptr_t) gregs.r_edx; + regs[REG_INDEX(ECX)] = (uintptr_t) gregs.r_ecx; + regs[REG_INDEX(EAX)] = (uintptr_t) gregs.r_eax; + regs[REG_INDEX(PC)] = (uintptr_t) gregs.r_eip; + regs[REG_INDEX(CS)] = (uintptr_t) gregs.r_cs; + regs[REG_INDEX(SS)] = (uintptr_t) gregs.r_ss; + +#endif /* i386 */ + +#if ia64 + regs = (*env)->GetLongArrayElements(env, array, &isCopy); + int i; + for (i = 0; i < NPRGREG; i++ ) { + regs[i] = 0xDEADDEAD; + } +#endif /* ia64 */ + +#ifdef amd64 +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg + + 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)] = gregs.fs_base; +// regs[REG_INDEX(GSBASE)] = gregs.gs_base; +// regs[REG_INDEX(DS)] = gregs.ds; +// regs[REG_INDEX(ES)] = gregs.es; +// regs[REG_INDEX(FS)] = gregs.fs; +// regs[REG_INDEX(GS)] = gregs.gs; + +#endif /* amd64 */ + +#if defined(sparc) || defined(sparcv9) + +#define REG_INDEX(reg) sun_jvm_hotspot_debugger_sparc_SPARCThreadContext_##reg + +#ifdef _LP64 + regs[REG_INDEX(R_PSR)] = gregs.tstate; + regs[REG_INDEX(R_PC)] = gregs.tpc; + regs[REG_INDEX(R_nPC)] = gregs.tnpc; + regs[REG_INDEX(R_Y)] = gregs.y; +#else + regs[REG_INDEX(R_PSR)] = gregs.psr; + regs[REG_INDEX(R_PC)] = gregs.pc; + regs[REG_INDEX(R_nPC)] = gregs.npc; + regs[REG_INDEX(R_Y)] = gregs.y; +#endif + regs[REG_INDEX(R_G0)] = 0 ; + regs[REG_INDEX(R_G1)] = gregs.u_regs[0]; + regs[REG_INDEX(R_G2)] = gregs.u_regs[1]; + regs[REG_INDEX(R_G3)] = gregs.u_regs[2]; + regs[REG_INDEX(R_G4)] = gregs.u_regs[3]; + regs[REG_INDEX(R_G5)] = gregs.u_regs[4]; + regs[REG_INDEX(R_G6)] = gregs.u_regs[5]; + regs[REG_INDEX(R_G7)] = gregs.u_regs[6]; + regs[REG_INDEX(R_O0)] = gregs.u_regs[7]; + regs[REG_INDEX(R_O1)] = gregs.u_regs[8]; + regs[REG_INDEX(R_O2)] = gregs.u_regs[ 9]; + regs[REG_INDEX(R_O3)] = gregs.u_regs[10]; + regs[REG_INDEX(R_O4)] = gregs.u_regs[11]; + regs[REG_INDEX(R_O5)] = gregs.u_regs[12]; + regs[REG_INDEX(R_O6)] = gregs.u_regs[13]; + regs[REG_INDEX(R_O7)] = gregs.u_regs[14]; +#endif /* sparc */ + + + (*env)->ReleaseLongArrayElements(env, array, regs, JNI_COMMIT); + return array; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2002, 2007, 2011, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <objc/objc-runtime.h> +#import <Foundation/Foundation.h> +#import <JavaNativeFoundation/JavaNativeFoundation.h> + +#include <JavaVM/jni.h> + +#import <mach/mach.h> +#import <mach/mach_types.h> +#import <sys/sysctl.h> +#import <stdlib.h> + +jboolean debug = JNI_FALSE; + +static jfieldID symbolicatorID = 0; // set in _init0 +static jfieldID taskID = 0; // set in _init0 + +static void putSymbolicator(JNIEnv *env, jobject this_obj, id symbolicator) { + (*env)->SetLongField(env, this_obj, symbolicatorID, (jlong)(intptr_t)symbolicator); +} + +static id getSymbolicator(JNIEnv *env, jobject this_obj) { + jlong ptr = (*env)->GetLongField(env, this_obj, symbolicatorID); + return (id)(intptr_t)ptr; +} + +static void putTask(JNIEnv *env, jobject this_obj, task_t task) { + (*env)->SetLongField(env, this_obj, taskID, (jlong)task); +} + +static task_t getTask(JNIEnv *env, jobject this_obj) { + jlong ptr = (*env)->GetLongField(env, this_obj, taskID); + return (task_t)ptr; +} + +#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; } +#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;} +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; } +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;} + +static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { + (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); +} + +#if defined(__i386__) + #define hsdb_thread_state_t x86_thread_state32_t + #define hsdb_float_state_t x86_float_state32_t + #define HSDB_THREAD_STATE x86_THREAD_STATE32 + #define HSDB_FLOAT_STATE x86_FLOAT_STATE32 + #define HSDB_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT + #define HSDB_FLOAT_STATE_COUNT x86_FLOAT_STATE32_COUNT +#elif defined(__x86_64__) + #define hsdb_thread_state_t x86_thread_state64_t + #define hsdb_float_state_t x86_float_state64_t + #define HSDB_THREAD_STATE x86_THREAD_STATE64 + #define HSDB_FLOAT_STATE x86_FLOAT_STATE64 + #define HSDB_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT + #define HSDB_FLOAT_STATE_COUNT x86_FLOAT_STATE64_COUNT +#else + #error "Unsupported architecture" +#endif + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: init0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) { + symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J"); + taskID = (*env)->GetFieldID(env, cls, "task", "J"); + CHECK_EXCEPTION; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0(JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + jlong address = 0; + +JNF_COCOA_ENTER(env); + NSString *symbolNameString = JNFJavaToNSString(env, symbolName); + + if (debug) { + printf("lookupInProcess called for %s\n", [symbolNameString UTF8String]); + } + + id symbolicator = getSymbolicator(env, this_obj); + if (symbolicator != nil) { + uint64_t (*dynamicCall)(id, SEL, NSString *) = (uint64_t (*)(id, SEL, NSString *))&objc_msgSend; + address = (jlong) dynamicCall(symbolicator, @selector(addressForSymbol:), symbolNameString); + } + + if (debug) { + printf("address of symbol %s = %llx\n", [symbolNameString UTF8String], address); + } +JNF_COCOA_EXIT(env); + + return address; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0(JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) { + if (debug) printf("readBytesFromProcess called. addr = %llx numBytes = %lld\n", addr, numBytes); + + // must allocate storage instead of using former parameter buf + jboolean isCopy; + jbyteArray array; + jbyte *bufPtr; + + array = (*env)->NewByteArray(env, numBytes); + CHECK_EXCEPTION_(0); + + unsigned long alignedAddress; + unsigned long alignedLength; + kern_return_t result; + vm_offset_t *pages; + int *mapped; + long pageCount; + uint byteCount; + int i; + unsigned long remaining; + + alignedAddress = trunc_page(addr); + if (addr != alignedAddress) { + alignedLength += addr - alignedAddress; + } + alignedLength = round_page(numBytes); + pageCount = alignedLength/vm_page_size; + + // Allocate storage for pages and flags. + pages = malloc(pageCount * sizeof(vm_offset_t)); + mapped = calloc(pageCount, sizeof(int)); + + task_t gTask = getTask(env, this_obj); + // Try to read each of the pages. + for (i = 0; i < pageCount; i++) { + result = vm_read(gTask, alignedAddress + i*vm_page_size, vm_page_size, + &pages[i], &byteCount); + mapped[i] = (result == KERN_SUCCESS); + // assume all failures are unmapped pages + } + + if (debug) fprintf(stderr, "%ld pages\n", pageCount); + + remaining = numBytes; + + for (i = 0; i < pageCount; i++) { + unsigned long len = vm_page_size; + unsigned long start = 0; + + if (i == 0) { + start = addr - alignedAddress; + len = vm_page_size - start; + } + + if (i == (pageCount - 1)) { + len = remaining; + } + + if (mapped[i]) { + if (debug) fprintf(stderr, "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); + } + + remaining -= len; + } + + free (pages); + free (mapped); + return array; +} + +/* + * Class: sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal + * Method: getThreadIntegerRegisterSet0 + * Signature: (I)[J + */ +JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0(JNIEnv *env, jobject this_obj, jint lwp_id) { + if (debug) + printf("getThreadRegisterSet0 called\n"); + + 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; + + tid = lwp_id; + + 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); + 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); + + 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; + + if (debug) printf("set registers\n"); + + (*env)->ReleaseLongArrayElements(env, registerArray, primitiveArray, 0); +#else +#error Unsupported architecture +#endif + + return registerArray; +} + +/* + * Class: sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal + * Method: translateTID0 + * Signature: (I)I + */ +JNIEXPORT jint JNICALL +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); + + kern_return_t result; + thread_t foreign_tid, usable_tid; + mach_msg_type_name_t type; + + foreign_tid = tid; + + task_t gTask = getTask(env, this_obj); + result = mach_port_extract_right(gTask, foreign_tid, + MACH_MSG_TYPE_COPY_SEND, + &usable_tid, &type); + if (result != KERN_SUCCESS) + return -1; + + if (debug) + printf("translateTID0: 0x%x -> 0x%x\n", foreign_tid, usable_tid); + + return (jint) usable_tid; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: attach0 + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I(JNIEnv *env, jobject this_obj, jint 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); + + 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); + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); + } + putTask(env, this_obj, gTask); + + id symbolicator = nil; + id jrsSymbolicator = objc_lookUpClass("JRSSymbolicator"); + if (jrsSymbolicator != nil) { + id (*dynamicCall)(id, SEL, pid_t) = (id (*)(id, SEL, pid_t))&objc_msgSend; + symbolicator = dynamicCall(jrsSymbolicator, @selector(symbolicatorForPid:), (pid_t)jpid); + } + if (symbolicator != nil) { + CFRetain(symbolicator); // pin symbolicator while in java heap + } + + putSymbolicator(env, this_obj, symbolicator); + if (symbolicator == nil) { + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach symbolicator to the process"); + } + +JNF_COCOA_EXIT(env); +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: detach0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0(JNIEnv *env, jobject this_obj) { +JNF_COCOA_ENTER(env); + if (debug) printf("detach0 called\n"); + + task_t gTask = getTask(env, this_obj); + mach_port_deallocate(mach_task_self(), gTask); + id symbolicator = getSymbolicator(env, this_obj); + if (symbolicator != nil) { + CFRelease(symbolicator); + } +JNF_COCOA_EXIT(env); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/Makefile Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,77 @@ +# +# Copyright (c) 2002, 2009, 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 +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +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 ) +GCC = gcc + +JAVAH = ${JAVA_HOME}/bin/javah + +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) + +LIBS = -lutil -lthread_db + +CFLAGS = -c -fPIC -g -Wall -D_ALLBSD_SOURCE -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) + +LIBSA = $(ARCH)/libsaproc.so + +all: $(LIBSA) + +BsdDebuggerLocal.o: BsdDebuggerLocal.c + $(JAVAH) -jni -classpath ../../../../../build/bsd-i586/hotspot/outputdir/bsd_i486_compiler2/generated/saclasses \ + sun.jvm.hotspot.debugger.x86.X86ThreadContext \ + sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(GCC) $(CFLAGS) $< + +.c.obj: + $(GCC) $(CFLAGS) + +ifndef LDNOMAP + LFLAGS_LIBSA = -Xlinker --version-script=mapfile +endif + +$(LIBSA): $(OBJS) mapfile + if [ ! -d $(ARCH) ] ; then mkdir $(ARCH) ; fi + $(GCC) -shared $(LFLAGS_LIBSA) -o $(LIBSA) $(OBJS) $(LIBS) + +test.o: $(LIBSA) test.c + $(GCC) -c -o test.o -g -D_GNU_SOURCE -D$(ARCH) $(INCLUDES) test.c + +test: test.o + $(GCC) -o test test.o -L$(ARCH) -lsaproc $(LIBS) + +clean: + rm -f $(LIBSA) + rm -f $(OBJS) + rm -f test.o + -rmdir $(ARCH) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/StubDebuggerLocal.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2009, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <stdlib.h> +#include <jni.h> + +#define CHECK_EXCEPTION_(value) if ((*env)->ExceptionOccurred(env)) { return value; } +#define CHECK_EXCEPTION if ((*env)->ExceptionOccurred(env)) { return;} +#define THROW_NEW_DEBUGGER_EXCEPTION_(str, value) { throw_new_debugger_exception(env, str); return value; } +#define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;} + +static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { + (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: init0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0 + (JNIEnv *env, jclass cls) { +} + +JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize + (JNIEnv *env, jclass cls) +{ +#ifdef _LP64 + return 8; +#else + return 4; +#endif + +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: attach0 + * Signature: (I)V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I + (JNIEnv *env, jobject this_obj, jint jpid) { + + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); +} + +/* + * 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) { + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file"); +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: detach0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0 + (JNIEnv *env, jobject this_obj) { +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: lookupByName0 + * Signature: (Ljava/lang/String;Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0 + (JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { + return 0; +} + +/* + * 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) { + return 0; +} + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal + * Method: readBytesFromProcess0 + * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; + */ +JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0 + (JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) { + return 0; +} + +JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0 + (JNIEnv *env, jobject this_obj, jint lwp_id) { + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/elfmacros.h Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003, 2006, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _ELFMACROS_H_ +#define _ELFMACROS_H_ + +#define ELF_NHDR Elf_Note + +#if defined(_LP64) +#define ELF_EHDR Elf64_Ehdr +#define ELF_SHDR Elf64_Shdr +#define ELF_PHDR Elf64_Phdr +#define ELF_SYM Elf64_Sym +#define ELF_DYN Elf64_Dyn +#define ELF_ADDR Elf64_Addr + +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE ELF64_ST_TYPE +#endif + +#else + +#define ELF_EHDR Elf32_Ehdr +#define ELF_SHDR Elf32_Shdr +#define ELF_PHDR Elf32_Phdr +#define ELF_SYM Elf32_Sym +#define ELF_DYN Elf32_Dyn +#define ELF_ADDR Elf32_Addr + +#ifndef ELF_ST_TYPE +#define ELF_ST_TYPE ELF32_ST_TYPE +#endif + +#endif + + +#endif /* _ELFMACROS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/libproc.h Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2003, 2007, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _LIBPROC_H_ +#define _LIBPROC_H_ + +#include <unistd.h> +#include <stdint.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 + otherwise it should be from /usr/include/asm-sparc + These two files define pt_regs structure differently +*/ +#ifdef _LP64 +#include "asm-sparc64/ptrace.h" +#else +#include "asm-sparc/ptrace.h" +#endif + +#endif //sparc or sparcv9 + +/************************************************************************************ + +0. This is very minimal subset of Solaris libproc just enough for current application. +Please note that the bulk of the functionality is from proc_service interface. This +adds Pgrab__ and some missing stuff. We hide the difference b/w live process and core +file by this interface. + +1. pthread_id is unique. We store this in OSThread::_pthread_id in JVM code. + +2. All threads see the same pid when they call getpid(). +We used to save the result of ::getpid() call in OSThread::_thread_id. +Because gettid returns actual pid of thread (lwp id), this is +unique again. We therefore use OSThread::_thread_id as unique identifier. + +3. There is a unique LWP id under both thread libraries. libthread_db maps pthread_id +to its underlying lwp_id under both the thread libraries. thread_info.lwp_id stores +lwp_id of the thread. The lwp id is nothing but the actual pid of clone'd processes. But +unfortunately libthread_db does not work very well for core dumps. So, we get pthread_id +only for processes. For core dumps, we don't use libthread_db at all (like gdb). + +4. ptrace operates on this LWP id under both the thread libraries. When we say 'pid' for +ptrace call, we refer to lwp_id of the thread. + +5. for core file, we parse ELF files and read data from them. For processes we use +combination of ptrace and /proc calls. + +*************************************************************************************/ + +// 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 ps_prochandle; + +// attach to a process +struct ps_prochandle* Pgrab(pid_t pid); + +// attach to a core dump +struct ps_prochandle* Pgrab_core(const char* execfile, const char* corefile); + +// release a process or core +void Prelease(struct ps_prochandle* ph); + +// functions not directly available in Solaris libproc + +// initialize libproc (call this only once per app) +// pass true to make library verbose +bool init_libproc(bool verbose); + +// get number of threads +int get_num_threads(struct ps_prochandle* ph); + +// get lwp_id of n'th thread +lwpid_t get_lwp_id(struct ps_prochandle* ph, int index); + +// get regs for a given lwp +bool get_lwp_regs(struct ps_prochandle* ph, lwpid_t lid, struct reg* regs); + +// get number of shared objects +int get_num_libs(struct ps_prochandle* ph); + +// get name of n'th lib +const char* get_lib_name(struct ps_prochandle* ph, int index); + +// get base of lib +uintptr_t get_lib_base(struct ps_prochandle* ph, int index); + +// returns true if given library is found in lib list +bool find_lib(struct ps_prochandle* ph, const char *lib_name); + +// symbol lookup +uintptr_t lookup_symbol(struct ps_prochandle* ph, const char* object_name, + const char* sym_name); + +// address->nearest symbol lookup. return NULL for no symbol +const char* symbol_for_pc(struct ps_prochandle* ph, uintptr_t addr, uintptr_t* poffset); + +#endif //__LIBPROC_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/libproc_impl.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2003, 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * 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; +static int alt_root_len = -1; + +#define SA_ALTROOT "SA_ALTROOT" + +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; + } + } +} + +int pathmap_open(const char* name) { + int fd; + char alt_path[PATH_MAX + 1]; + + init_alt_root(); + fd = open(name, O_RDONLY); + if (fd >= 0) { + return fd; + } + + 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 (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; +} + +static bool _libsaproc_debug; + +void print_debug(const char* format,...) { + if (_libsaproc_debug) { + va_list alist; + + va_start(alist, format); + fputs("libsaproc DEBUG: ", stderr); + vfprintf(stderr, format, alist); + va_end(alist); + } +} + +bool is_debug() { + return _libsaproc_debug; +} + +// initialize libproc +bool init_libproc(bool debug) { + // init debug mode + _libsaproc_debug = debug; + + // initialize the thread_db library + if (td_init() != TD_OK) { + print_debug("libthread_db's td_init failed\n"); + return false; + } + + 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; + } +} + +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; + } +} + +// 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); +} + +lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t 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; + + 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 (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; + } + + // 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; + } + + 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 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. + + 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", + sym_name, object_name); + 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; +} + +// 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; + } + + // 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; +} + + +// struct used for client data from thread_db callback +struct thread_db_client_data { + struct ps_prochandle* ph; + thread_info_callback callback; +}; + +// callback function for libthread_db +static int thread_db_callback(const td_thrhandle_t *th_p, void *data) { + struct thread_db_client_data* ptr = (struct thread_db_client_data*) data; + td_thrinfo_t ti; + td_err_e err; + + memset(&ti, 0, sizeof(ti)); + err = td_thr_get_info(th_p, &ti); + if (err != TD_OK) { + print_debug("libthread_db : td_thr_get_info failed, can't get thread info\n"); + return err; + } + + print_debug("thread_db : pthread %d (lwp %d)\n", ti.ti_tid, ti.ti_lid); + + if (ptr->callback(ptr->ph, (pthread_t)ti.ti_tid, ti.ti_lid) != true) + return TD_ERR; + + return TD_OK; +} + +// read thread_info using libthread_db +bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb) { + struct thread_db_client_data mydata; + td_thragent_t* thread_agent = NULL; + if (td_ta_new(ph, &thread_agent) != TD_OK) { + print_debug("can't create libthread_db agent\n"); + return false; + } + + mydata.ph = ph; + mydata.callback = cb; + + // we use libthread_db iterator to iterate thru list of threads. + if (td_ta_thr_iter(thread_agent, thread_db_callback, &mydata, + TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, + TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS) != TD_OK) { + td_ta_delete(thread_agent); + return false; + } + + // delete thread agent + td_ta_delete(thread_agent); + return true; +} + + +// get number of threads +int get_num_threads(struct ps_prochandle* ph) { + return ph->num_threads; +} + +// 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; +} + +// 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); +} + +// get number of shared objects +int get_num_libs(struct ps_prochandle* ph) { + 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; +} + +// 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; +} + +bool find_lib(struct ps_prochandle* ph, const char *lib_name) { + lib_info *p = ph->libs; + while (p) { + if (strcmp(p->name, lib_name) == 0) { + return true; + } + p = p->next; + } + return false; +} + +//-------------------------------------------------------------------------- +// proc service functions + +// 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) { + *sym_addr = (psaddr_t) lookup_symbol(ph, object_name, sym_name); + return (*sym_addr ? PS_OK : PS_NOSYM); +} + +// 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) { + return ph->ops->p_pread(ph, (uintptr_t) addr, buf, size)? PS_OK: PS_ERR; +} + +// 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) { + return ph->ops->p_pwrite(ph, (uintptr_t)addr, buf, size)? PS_OK: PS_ERR; +} + +// fill in ptrace_lwpinfo for lid +ps_err_e ps_linfo(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) { + return ph->ops->get_lwp_info(ph, lwp_id, linfo)? PS_OK: PS_ERR; +} + +// needed for when libthread_db is compiled with TD_DEBUG defined +void +ps_plog (const char *format, ...) +{ + va_list alist; + + va_start(alist, format); + vfprintf(stderr, format, alist); + va_end(alist); +} + +// ------------------------------------------------------------------------ +// Functions below this point are not yet implemented. They are here only +// to make the linker happy. + +ps_err_e ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lid, const prfpregset_t *fpregs) { + print_debug("ps_lsetfpregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lsetregs(struct ps_prochandle *ph, lwpid_t lid, const prgregset_t gregset) { + print_debug("ps_lsetregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lid, prfpregset_t *fpregs) { + print_debug("ps_lgetfpregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lgetregs(struct ps_prochandle *ph, lwpid_t lid, prgregset_t gregset) { + print_debug("ps_lgetfpregs not implemented\n"); + return PS_OK; +} + +ps_err_e ps_lstop(struct ps_prochandle *ph, lwpid_t lid) { + print_debug("ps_lstop not implemented\n"); + return PS_OK; +} + +ps_err_e ps_pcontinue(struct ps_prochandle *ph) { + print_debug("ps_pcontinue not implemented\n"); + return PS_OK; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/libproc_impl.h Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2003, 2005, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _LIBPROC_IMPL_H_ +#define _LIBPROC_IMPL_H_ + +#include <unistd.h> +#include <limits.h> +#include "libproc.h" +#include "symtab.h" + +// data structures in this file mimic those of Solaris 8.0 - libproc's Pcontrol.h + +#define BUF_SIZE (PATH_MAX + NAME_MAX + 1) + +// list of shared objects +typedef struct lib_info { + char name[BUF_SIZE]; + uintptr_t base; + struct symtab* symtab; + int fd; // file descriptor for lib + struct lib_info* next; +} lib_info; + +// list of threads +typedef struct thread_info { + lwpid_t lwp_id; + pthread_t pthread_id; // not used cores, always -1 + struct reg regs; // not for process, core uses for caching regset + struct thread_info* next; +} thread_info; + +// list of virtual memory maps +typedef struct map_info { + int fd; // file descriptor + off_t offset; // file offset of this mapping + uintptr_t vaddr; // starting virtual address + size_t memsz; // size of the mapping + struct map_info* next; +} map_info; + +// vtable for ps_prochandle +typedef struct ps_prochandle_ops { + // "derived class" clean-up + void (*release)(struct ps_prochandle* ph); + // read from debuggee + bool (*p_pread)(struct ps_prochandle *ph, + uintptr_t addr, char *buf, size_t size); + // write into debuggee + bool (*p_pwrite)(struct ps_prochandle *ph, + uintptr_t addr, const char *buf , size_t size); + // get integer regset of a thread + bool (*get_lwp_regs)(struct ps_prochandle* ph, lwpid_t lwp_id, struct reg* regs); + // get info on thread + bool (*get_lwp_info)(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo); +} ps_prochandle_ops; + +// the ps_prochandle + +struct core_data { + int core_fd; // file descriptor of core file + int exec_fd; // file descriptor of exec file + int interp_fd; // file descriptor of interpreter (ld-elf.so.1) + // part of the class sharing workaround + int classes_jsa_fd; // file descriptor of class share archive + uintptr_t dynamic_addr; // address of dynamic section of a.out + uintptr_t ld_base_addr; // base address of ld.so + size_t num_maps; // number of maps. + map_info* maps; // maps in a linked list + // 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 +}; + +struct ps_prochandle { + ps_prochandle_ops* ops; // vtable ptr + pid_t pid; + int num_libs; + 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 + 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,...); +bool is_debug(); + +typedef bool (*thread_info_callback)(struct ps_prochandle* ph, pthread_t pid, lwpid_t lwpid); + +// reads thread info using libthread_db and calls above callback for each thread +bool read_thread_info(struct ps_prochandle* ph, thread_info_callback cb); + +// adds a new shared object to lib list, returns NULL on failure +lib_info* add_lib_info(struct ps_prochandle* ph, const char* libname, uintptr_t base); + +// adds a new shared object to lib list, supply open lib file descriptor as well +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); + +// a test for ELF signature without using libelf +bool is_elf_file(int fd); + +#endif //_LIBPROC_IMPL_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/mapfile Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,66 @@ +# + +# +# Copyright (c) 2003, 2006, 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 +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + + # native methods of BsdDebuggerLocal class + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getAddressSize; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByAddress0; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0; + Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0; + + # proc_service.h functions - to be used by libthread_db + ps_getpid; + ps_pglobal_lookup; + ps_pread; + ps_pwrite; + ps_lsetfpregs; + ps_lsetregs; + ps_lgetfpregs; + ps_lgetregs; + ps_lcontinue; + ps_lgetxmmregs; + ps_lsetxmmregs; + ps_lstop; + ps_linfo; + + # used by attach test program + init_libproc; + Pgrab; + Pgrab_core; + Prelease; + + local: + *; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/ps_core.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,1023 @@ +/* + * Copyright (c) 2003, 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <jni.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <elf.h> +#include <link.h> +#include "libproc_impl.h" +#include "salibelf.h" + +// This file has the libproc implementation to read core files. +// For live processes, refer to ps_proc.c. Portions of this is adapted +// /modelled after Solaris libproc.so (in particular Pcore.c) + +//---------------------------------------------------------------------- +// ps_prochandle cleanup helper functions + +// close all file descriptors +static void close_elf_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 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 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; + } +} + +// 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; + } + + if (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; + } +} + +// 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); + } +} + +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; + } + + // 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; + } + + // add this to map list + map->next = ph->core->maps; + ph->core->maps = map; + ph->core->num_maps++; + + 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->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; + + 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 >= 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; + } + + print_debug("can't locate map_info at 0x%lx\n", addr); + return (NULL); +} + +//--------------------------------------------------------------- +// Part of the class sharing workaround: +// +// With class sharing, pages are mapped from classes[_g].jsa file. +// The read-only class sharing pages are mapped as MAP_SHARED, +// PROT_READ pages. These pages are not dumped into core dump. +// With this workaround, these pages are read from classes[_g].jsa. + +// FIXME: !HACK ALERT! +// The format of sharing achive file header is needed to read shared heap +// file mappings. For now, I am hard coding portion of FileMapHeader here. +// Refer to filemap.hpp. + +// FileMapHeader describes the shared space data in the file to be +// mapped. This structure gets written to a file. It is not a class, +// so that the compilers don't add any compiler-private data to it. + +// Refer to CompactingPermGenGen::n_regions in compactingPermGenGen.hpp +#define NUM_SHARED_MAPS 4 + +// Refer to FileMapInfo::_current_version in filemap.hpp +#define CURRENT_ARCHIVE_VERSION 1 + +struct FileMapHeader { + int _magic; // identify file type. + int _version; // (from enum, above.) + size_t _alignment; // how shared archive should be aligned + + struct space_info { + int _file_offset; // sizeof(this) rounded to vm page size + char* _base; // copy-on-write base address + size_t _capacity; // for validity checking + size_t _used; // for setting space top on read + + // 4991491 NOTICE These are C++ bool's in filemap.hpp and must match up with + // the C type matching the C++ bool type on any given platform. For + // Hotspot on BSD we assume the corresponding C type is char but + // licensees on BSD versions may need to adjust the type of these fields. + char _read_only; // read only space? + char _allow_exec; // executable code in space? + + } _space[NUM_SHARED_MAPS]; // was _space[CompactingPermGenGen::n_regions]; + + // Ignore the rest of the FileMapHeader. We don't need those fields here. +}; + +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; + } +} + +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; + } +} + +// 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 = ' '; + + 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; +} + +#define USE_SHARED_SPACES_SYM "UseSharedSpaces" +// mangled name of Arguments::SharedArchivePath +#define SHARED_ARCHIVE_PATH_SYM "_ZN9Arguments17SharedArchivePathE" + +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; + } + + // 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; + } + + if ((int)useSharedSpaces == 0) { + print_debug("UseSharedSpaces is false, assuming -Xshare:off!\n"); + return true; + } + + 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_pointer(ph, sharedArchivePathAddrAddr, &sharedArchivePathAddr) != true) { + print_debug("can't read shared archive path pointer\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; + } + + 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); + } + + // 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 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; + } + lib = lib->next; + } + return true; +} + + +//--------------------------------------------------------------------------- +// functions to handle map_info + +// Order mappings based on virtual address. We use this function as the +// 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); + + if (lhs->vaddr == rhs->vaddr) + return (0); + + 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; + + // 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; + } + + // 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); + } + } + + return true; +} + +#ifndef MIN +#define MIN(x, y) (((x) < (y))? (x): (y)) +#endif + +static bool core_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) { + ssize_t resid = size; + int page_size=sysconf(_SC_PAGE_SIZE); + while (resid != 0) { + map_info *mp = core_lookup(ph, addr); + uintptr_t mapoff; + ssize_t len, rem; + off_t off; + int fd; + + 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) + break; + + resid -= len; + addr += len; + buf = (char *)buf + len; + + // mappings always start at page boundary. But, may end in fractional + // page. fill zeros for possible fractional page at the end of a mapping. + rem = mp->memsz % page_size; + if (rem > 0) { + rem = page_size - rem; + len = MIN(resid, rem); + resid -= len; + addr += len; + // we are not assuming 'buf' to be zero initialized. + memset(buf, 0, len); + buf += len; + } + } + + if (resid) { + print_debug("core read failed for %d byte(s) @ 0x%lx (%d more bytes)\n", + size, addr, resid); + return false; + } else { + return true; + } +} + +// null implementation for write +static bool core_write_data(struct ps_prochandle* ph, + uintptr_t addr, const char *buf , size_t size) { + return false; +} + +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; + while (thr) { + if (thr->lwp_id == lwp_id) { + memcpy(regs, &thr->regs, sizeof(struct reg)); + return true; + } + thr = thr->next; + } + return false; +} + +static bool core_get_lwp_info(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) { + print_debug("core_get_lwp_info not implemented\n"); + return false; +} + +static ps_prochandle_ops core_ops = { + .release= core_release, + .p_pread= core_read_data, + .p_pwrite= core_write_data, + .get_lwp_regs= core_get_lwp_regs, + .get_lwp_info= core_get_lwp_info +}; + +// read regs and create thread from NT_PRSTATUS entries 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; + 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) + return false; + + // copy regs + memcpy(&newthr->regs, &prstat->pr_reg, sizeof(struct reg)); + + if (is_debug()) { + print_debug("integer regset\n"); +#ifdef i386 + // print the regset + print_debug("\teax = 0x%x\n", newthr->regs.r_eax); + print_debug("\tebx = 0x%x\n", newthr->regs.r_ebx); + print_debug("\tecx = 0x%x\n", newthr->regs.r_ecx); + print_debug("\tedx = 0x%x\n", newthr->regs.r_edx); + print_debug("\tesp = 0x%x\n", newthr->regs.r_esp); + print_debug("\tebp = 0x%x\n", newthr->regs.r_ebp); + print_debug("\tesi = 0x%x\n", newthr->regs.r_esi); + print_debug("\tedi = 0x%x\n", newthr->regs.r_edi); + print_debug("\teip = 0x%x\n", newthr->regs.r_eip); +#endif + +#if defined(amd64) || defined(x86_64) + // print the regset + print_debug("\tr15 = 0x%lx\n", newthr->regs.r_r15); + print_debug("\tr14 = 0x%lx\n", newthr->regs.r_r14); + print_debug("\tr13 = 0x%lx\n", newthr->regs.r_r13); + print_debug("\tr12 = 0x%lx\n", newthr->regs.r_r12); + print_debug("\trbp = 0x%lx\n", newthr->regs.r_rbp); + print_debug("\trbx = 0x%lx\n", newthr->regs.r_rbx); + print_debug("\tr11 = 0x%lx\n", newthr->regs.r_r11); + print_debug("\tr10 = 0x%lx\n", newthr->regs.r_r10); + print_debug("\tr9 = 0x%lx\n", newthr->regs.r_r9); + print_debug("\tr8 = 0x%lx\n", newthr->regs.r_r8); + print_debug("\trax = 0x%lx\n", newthr->regs.r_rax); + print_debug("\trcx = 0x%lx\n", newthr->regs.r_rcx); + print_debug("\trdx = 0x%lx\n", newthr->regs.r_rdx); + print_debug("\trsi = 0x%lx\n", newthr->regs.r_rsi); + print_debug("\trdi = 0x%lx\n", newthr->regs.r_rdi); + //print_debug("\torig_rax = 0x%lx\n", newthr->regs.orig_rax); + print_debug("\trip = 0x%lx\n", newthr->regs.r_rip); + print_debug("\tcs = 0x%lx\n", newthr->regs.r_cs); + //print_debug("\teflags = 0x%lx\n", newthr->regs.eflags); + print_debug("\trsp = 0x%lx\n", newthr->regs.r_rsp); + print_debug("\tss = 0x%lx\n", newthr->regs.r_ss); + //print_debug("\tfs_base = 0x%lx\n", newthr->regs.fs_base); + //print_debug("\tgs_base = 0x%lx\n", newthr->regs.gs_base); + //print_debug("\tds = 0x%lx\n", newthr->regs.ds); + //print_debug("\tes = 0x%lx\n", newthr->regs.es); + //print_debug("\tfs = 0x%lx\n", newthr->regs.fs); + //print_debug("\tgs = 0x%lx\n", newthr->regs.gs); +#endif + } + + return true; +} + +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) + +// read NT_PRSTATUS entries from core NOTE segment +static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { + char* buf = NULL; + char* p = NULL; + size_t size = note_phdr->p_filesz; + + // we are interested in just prstatus entries. we will ignore the rest. + // Advance the seek pointer to the start of the PT_NOTE data + if (lseek(ph->core->core_fd, note_phdr->p_offset, SEEK_SET) == (off_t)-1) { + print_debug("failed to lseek to PT_NOTE data\n"); + return false; + } + + // Now process the PT_NOTE structures. Each one is preceded by + // an Elf{32/64}_Nhdr structure describing its type and size. + if ( (buf = (char*) malloc(size)) == NULL) { + print_debug("can't allocate memory for reading core notes\n"); + goto err; + } + + // read notes into buffer + if (read(ph->core->core_fd, buf, size) != size) { + print_debug("failed to read notes, core file must have been truncated\n"); + goto err; + } + + p = buf; + while (p < buf + size) { + ELF_NHDR* notep = (ELF_NHDR*) p; + char* descdata = p + sizeof(ELF_NHDR) + ROUNDUP(notep->n_namesz, 4); + print_debug("Note header with n_type = %d and n_descsz = %u\n", + notep->n_type, notep->n_descsz); + + if (notep->n_type == NT_PRSTATUS) { + if (core_handle_prstatus(ph, descdata, notep->n_descsz) != true) + return false; + } + p = descdata + ROUNDUP(notep->n_descsz, 4); + } + + free(buf); + return true; + +err: + if (buf) free(buf); + return false; +} + +// read all segments from core file +static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { + int i = 0; + ELF_PHDR* phbuf = NULL; + ELF_PHDR* core_php = NULL; + + if ((phbuf = read_program_header_table(ph->core->core_fd, core_ehdr)) == NULL) + return false; + + /* + * Now iterate through the program headers in the core file. + * We're interested in two types of Phdrs: PT_NOTE (which + * contains a set of saved /proc structures), and PT_LOAD (which + * represents a memory mapping from the process's address space). + * + * Difference b/w Solaris PT_NOTE and BSD PT_NOTE: + * + * In Solaris there are two PT_NOTE segments the first PT_NOTE (if present) + * contains /proc structs in the pre-2.6 unstructured /proc format. the last + * PT_NOTE has data in new /proc format. + * + * In Solaris, there is only one pstatus (process status). pstatus contains + * integer register set among other stuff. For each LWP, we have one lwpstatus + * entry that has integer regset for that LWP. + * + * Linux threads are actually 'clone'd processes. To support core analysis + * of "multithreaded" process, Linux creates more than one pstatus (called + * "prstatus") entry in PT_NOTE. Each prstatus entry has integer regset for one + * "thread". Please refer to Linux kernel src file 'fs/binfmt_elf.c', in particular + * function "elf_core_dump". + */ + + 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; + break; + + case PT_LOAD: { + if (core_php->p_filesz != 0) { + if (add_map_info(ph, ph->core->core_fd, core_php->p_offset, + core_php->p_vaddr, core_php->p_filesz) == NULL) goto err; + } + break; + } + } + + core_php++; + } + + free(phbuf); + return true; +err: + free(phbuf); + return false; +} + +// read segments of a shared object +static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* lib_ehdr, uintptr_t lib_base) { + int i = 0; + ELF_PHDR* phbuf; + ELF_PHDR* lib_php = NULL; + + if ((phbuf = read_program_header_table(lib_fd, lib_ehdr)) == NULL) + return false; + + // we want to process only PT_LOAD segments that are not writable. + // i.e., text segments. The read/write/exec (data) segments would + // have been already added from core file segments. + for (lib_php = phbuf, i = 0; i < lib_ehdr->e_phnum; i++) { + if ((lib_php->p_type == PT_LOAD) && !(lib_php->p_flags & PF_W) && (lib_php->p_filesz != 0)) { + if (add_map_info(ph, lib_fd, lib_php->p_offset, lib_php->p_vaddr + lib_base, lib_php->p_filesz) == NULL) + goto err; + } + lib_php++; + } + + free(phbuf); + return true; +err: + free(phbuf); + return false; +} + +// process segments from interpreter (ld-elf.so.1) +static bool read_interp_segments(struct ps_prochandle* ph) { + ELF_EHDR interp_ehdr; + + if (read_elf_header(ph->core->interp_fd, &interp_ehdr) != true) { + print_debug("interpreter is not a valid ELF file\n"); + return false; + } + + if (read_lib_segments(ph, ph->core->interp_fd, &interp_ehdr, ph->core->ld_base_addr) != true) { + print_debug("can't read segments of interpreter\n"); + return false; + } + + return true; +} + +// process segments of a a.out +static bool read_exec_segments(struct ps_prochandle* ph, ELF_EHDR* exec_ehdr) { + int i = 0; + ELF_PHDR* phbuf = NULL; + ELF_PHDR* exec_php = NULL; + + if ((phbuf = read_program_header_table(ph->core->exec_fd, exec_ehdr)) == NULL) + return false; + + for (exec_php = phbuf, i = 0; i < exec_ehdr->e_phnum; i++) { + switch (exec_php->p_type) { + + // add mappings for PT_LOAD segments + case PT_LOAD: { + // add only non-writable segments of non-zero filesz + if (!(exec_php->p_flags & PF_W) && exec_php->p_filesz != 0) { + if (add_map_info(ph, ph->core->exec_fd, exec_php->p_offset, exec_php->p_vaddr, exec_php->p_filesz) == NULL) goto err; + } + break; + } + + // read the interpreter and it's segments + case PT_INTERP: { + char interp_name[BUF_SIZE]; + + pread(ph->core->exec_fd, interp_name, MIN(exec_php->p_filesz, BUF_SIZE), exec_php->p_offset); + print_debug("ELF interpreter %s\n", interp_name); + // read interpreter segments as well + if ((ph->core->interp_fd = pathmap_open(interp_name)) < 0) { + print_debug("can't open runtime loader\n"); + goto err; + } + break; + } + + // from PT_DYNAMIC we want to read address of first link_map addr + case PT_DYNAMIC: { + ph->core->dynamic_addr = exec_php->p_vaddr; + print_debug("address of _DYNAMIC is 0x%lx\n", ph->core->dynamic_addr); + break; + } + + } // switch + exec_php++; + } // for + + free(phbuf); + return true; +err: + free(phbuf); + 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) +#define LINK_MAP_NAME_OFFSET offsetof(struct link_map, l_name) +#define LINK_MAP_NEXT_OFFSET offsetof(struct link_map, l_next) + +// 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; + + // _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); + } + + // 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; + + 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; + + // 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); + + 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; + } + + // 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; + } + + 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... + } + } + } + + // 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; +} + +// 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; + + 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; + } + + 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; + + // 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; + } + + 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; + } + + // 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; + + // 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; + + 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; + + if (init_classsharing_workaround(ph) != true) + goto err; + + return ph; + +err: + Prelease(ph); + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/ps_proc.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2003, 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ptrace.h> +#include <sys/param.h> +#include <sys/user.h> +#include <elf.h> +#include <sys/elf_common.h> +#include <sys/link_elf.h> +#include <libutil.h> +#include "libproc_impl.h" +#include "elfmacros.h" + +// This file has the libproc implementation specific to live process +// For core files, refer to ps_core.c + +static inline uintptr_t align(uintptr_t ptr, size_t size) { + return (ptr & ~(size - 1)); +} + +// --------------------------------------------- +// ptrace functions +// --------------------------------------------- + +// read "size" bytes of data from "addr" within the target process. +// unlike the standard ptrace() function, process_read_data() can handle +// unaligned address - alignment check, if required, should be done +// before calling process_read_data. + +static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) { + int rslt; + size_t i, words; + uintptr_t end_addr = addr + size; + uintptr_t aligned_addr = align(addr, sizeof(int)); + + if (aligned_addr != addr) { + char *ptr = (char *)&rslt; + errno = 0; + rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0); + if (errno) { + print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + for (; aligned_addr != addr; aligned_addr++, ptr++); + for (; ((intptr_t)aligned_addr % sizeof(int)) && aligned_addr < end_addr; + aligned_addr++) + *(buf++) = *(ptr++); + } + + words = (end_addr - aligned_addr) / sizeof(int); + + // assert((intptr_t)aligned_addr % sizeof(int) == 0); + for (i = 0; i < words; i++) { + errno = 0; + rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0); + if (errno) { + print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + *(int *)buf = rslt; + buf += sizeof(int); + aligned_addr += sizeof(int); + } + + if (aligned_addr != end_addr) { + char *ptr = (char *)&rslt; + errno = 0; + rslt = ptrace(PT_READ_D, ph->pid, (caddr_t) aligned_addr, 0); + if (errno) { + print_debug("ptrace(PT_READ_D, ..) failed for %d bytes @ %lx\n", size, addr); + return false; + } + for (; aligned_addr != end_addr; aligned_addr++) + *(buf++) = *(ptr++); + } + return true; +} + +// null implementation for write +static bool process_write_data(struct ps_prochandle* ph, + uintptr_t addr, const char *buf , size_t size) { + return false; +} + +// "user" should be a pointer to a reg +static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct reg *user) { + // we have already attached to all thread 'pid's, just use ptrace call + // to get regset now. Note that we don't cache regset upfront for processes. + if (ptrace(PT_GETREGS, pid, (caddr_t) user, 0) < 0) { + print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid); + return false; + } + return true; +} + +// fill in ptrace_lwpinfo for lid +static bool process_get_lwp_info(struct ps_prochandle *ph, lwpid_t lwp_id, void *linfo) { + errno = 0; + ptrace(PT_LWPINFO, lwp_id, linfo, sizeof(struct ptrace_lwpinfo)); + + return (errno == 0)? true: false; +} + +// attach to a process/thread specified by "pid" +static bool ptrace_attach(pid_t pid) { + if (ptrace(PT_ATTACH, pid, NULL, 0) < 0) { + print_debug("ptrace(PTRACE_ATTACH, ..) failed for %d\n", pid); + return false; + } else { + int ret; + int status; + do { + // Wait for debuggee to stop. + ret = waitpid(pid, &status, 0); + if (ret >= 0) { + if (WIFSTOPPED(status)) { + // Debuggee stopped. + return true; + } else { + print_debug("waitpid(): Child process exited/terminated (status = 0x%x)\n", status); + return false; + } + } else { + switch (errno) { + case EINTR: + continue; + break; + case ECHILD: + print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid); + break; + case EINVAL: + print_debug("waitpid() failed. Invalid options argument.\n"); + break; + default: + print_debug("waitpid() failed. Unexpected error %d\n",errno); + } + return false; + } + } while(true); + } +} + +// ------------------------------------------------------- +// functions for obtaining library information +// ------------------------------------------------------- + +// callback for read_thread_info +static bool add_new_thread(struct ps_prochandle* ph, pthread_t pthread_id, lwpid_t lwp_id) { + return add_thread_info(ph, pthread_id, lwp_id) != NULL; +} + +#if defined(__FreeBSD__) && __FreeBSD_version < 701000 +/* + * TEXT_START_ADDR from binutils/ld/emulparams/<arch_spec>.sh + * Not the most robust but good enough. + */ + +#if defined(amd64) || defined(x86_64) +#define TEXT_START_ADDR 0x400000 +#elif defined(i386) +#define TEXT_START_ADDR 0x8048000 +#else +#error TEXT_START_ADDR not defined +#endif + +#define BUF_SIZE (PATH_MAX + NAME_MAX + 1) + +uintptr_t linkmap_addr(struct ps_prochandle *ph) { + uintptr_t ehdr_addr, phdr_addr, dyn_addr, dmap_addr, lmap_addr; + ELF_EHDR ehdr; + ELF_PHDR *phdrs, *phdr; + ELF_DYN *dyns, *dyn; + struct r_debug dmap; + unsigned long hdrs_size; + unsigned int i; + + /* read ELF_EHDR at TEXT_START_ADDR and validate */ + + ehdr_addr = (uintptr_t)TEXT_START_ADDR; + + if (process_read_data(ph, ehdr_addr, (char *)&ehdr, sizeof(ehdr)) != true) { + print_debug("process_read_data failed for ehdr_addr %p\n", ehdr_addr); + return (0); + } + + if (!IS_ELF(ehdr) || + ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS || + ehdr.e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr.e_ident[EI_VERSION] != EV_CURRENT || + ehdr.e_phentsize != sizeof(ELF_PHDR) || + ehdr.e_version != ELF_TARG_VER || + ehdr.e_machine != ELF_TARG_MACH) { + print_debug("not an ELF_EHDR at %p\n", ehdr_addr); + return (0); + } + + /* allocate space for all ELF_PHDR's and read */ + + phdr_addr = ehdr_addr + ehdr.e_phoff; + hdrs_size = ehdr.e_phnum * sizeof(ELF_PHDR); + + if ((phdrs = malloc(hdrs_size)) == NULL) + return (0); + + if (process_read_data(ph, phdr_addr, (char *)phdrs, hdrs_size) != true) { + print_debug("process_read_data failed for phdr_addr %p\n", phdr_addr); + return (0); + } + + /* find PT_DYNAMIC section */ + + for (i = 0, phdr = phdrs; i < ehdr.e_phnum; i++, phdr++) { + if (phdr->p_type == PT_DYNAMIC) + break; + } + + if (i >= ehdr.e_phnum) { + print_debug("PT_DYNAMIC section not found!\n"); + free(phdrs); + return (0); + } + + /* allocate space and read in ELF_DYN headers */ + + dyn_addr = phdr->p_vaddr; + hdrs_size = phdr->p_memsz; + free(phdrs); + + if ((dyns = malloc(hdrs_size)) == NULL) + return (0); + + if (process_read_data(ph, dyn_addr, (char *)dyns, hdrs_size) != true) { + print_debug("process_read_data failed for dyn_addr %p\n", dyn_addr); + free(dyns); + return (0); + } + + /* find DT_DEBUG */ + + dyn = dyns; + while (dyn->d_tag != DT_DEBUG && dyn->d_tag != DT_NULL) { + dyn++; + } + + if (dyn->d_tag != DT_DEBUG) { + print_debug("failed to find DT_DEBUG\n"); + free(dyns); + return (0); + } + + /* read struct r_debug into dmap */ + + dmap_addr = (uintptr_t)dyn->d_un.d_ptr; + free(dyns); + + if (process_read_data(ph, dmap_addr, (char *)&dmap, sizeof(dmap)) != true) { + print_debug("process_read_data failed for dmap_addr %p\n", dmap_addr); + return (0); + } + + lmap_addr = (uintptr_t)dmap.r_map; + + return (lmap_addr); +} +#endif // __FreeBSD__ && __FreeBSD_version < 701000 + +static bool read_lib_info(struct ps_prochandle* ph) { +#if defined(__FreeBSD__) && __FreeBSD_version >= 701000 + struct kinfo_vmentry *freep, *kve; + int i, cnt; + + freep = kinfo_getvmmap(ph->pid, &cnt); + if (freep == NULL) { + print_debug("can't get vm map for pid\n", ph->pid); + return false; + } + + for (i = 0; i < cnt; i++) { + kve = &freep[i]; + if ((kve->kve_flags & KVME_FLAG_COW) && + kve->kve_path != NULL && + strlen(kve->kve_path) > 0) { + + if (find_lib(ph, kve->kve_path) == false) { + lib_info* lib; + if ((lib = add_lib_info(ph, kve->kve_path, + (uintptr_t) kve->kve_start)) == NULL) + continue; // ignore, add_lib_info prints error + + // we don't need to keep the library open, symtab is already + // built. Only for core dump we need to keep the fd open. + close(lib->fd); + lib->fd = -1; + } + } + } + + free(freep); + + return true; +#else + char *l_name; + struct link_map *lmap; + uintptr_t lmap_addr; + + if ((l_name = malloc(BUF_SIZE)) == NULL) + return false; + + if ((lmap = malloc(sizeof(*lmap))) == NULL) { + free(l_name); + return false; + } + + lmap_addr = linkmap_addr(ph); + + if (lmap_addr == 0) { + free(l_name); + free(lmap); + return false; + } + + do { + if (process_read_data(ph, lmap_addr, (char *)lmap, sizeof(*lmap)) != true) { + print_debug("process_read_data failed for lmap_addr %p\n", lmap_addr); + free (l_name); + free (lmap); + return false; + } + + if (process_read_data(ph, (uintptr_t)lmap->l_name, l_name, + BUF_SIZE) != true) { + print_debug("process_read_data failed for lmap->l_name %p\n", + lmap->l_name); + free (l_name); + free (lmap); + return false; + } + + if (find_lib(ph, l_name) == false) { + lib_info* lib; + if ((lib = add_lib_info(ph, l_name, + (uintptr_t) lmap->l_addr)) == NULL) + continue; // ignore, add_lib_info prints error + + // we don't need to keep the library open, symtab is already + // built. Only for core dump we need to keep the fd open. + close(lib->fd); + lib->fd = -1; + } + lmap_addr = (uintptr_t)lmap->l_next; + } while (lmap->l_next != NULL); + + free (l_name); + free (lmap); + + return true; +#endif +} + +// detach a given pid +static bool ptrace_detach(pid_t pid) { + if (pid && ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) { + print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n", pid); + return false; + } else { + return true; + } +} + +static void process_cleanup(struct ps_prochandle* ph) { + ptrace_detach(ph->pid); +} + +static ps_prochandle_ops process_ops = { + .release= process_cleanup, + .p_pread= process_read_data, + .p_pwrite= process_write_data, + .get_lwp_regs= process_get_lwp_regs, + .get_lwp_info= process_get_lwp_info +}; + +// attach to the process. One and only one exposed stuff +struct ps_prochandle* Pgrab(pid_t pid) { + struct ps_prochandle* ph = NULL; + thread_info* thr = NULL; + + if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) { + print_debug("can't allocate memory for ps_prochandle\n"); + return NULL; + } + + if (ptrace_attach(pid) != true) { + free(ph); + return NULL; + } + + // initialize ps_prochandle + ph->pid = pid; + + // initialize vtable + ph->ops = &process_ops; + + // read library info and symbol tables, must do this before attaching threads, + // as the symbols in the pthread library will be used to figure out + // the list of threads within the same process. + if (read_lib_info(ph) != true) { + ptrace_detach(pid); + free(ph); + return NULL; + } + + // read thread info + read_thread_info(ph, add_new_thread); + + return ph; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/salibelf.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2003, 2006, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "salibelf.h" +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +extern void print_debug(const char*,...); + +// ELF file parsing helpers. Note that we do *not* use libelf here. +int read_elf_header(int fd, ELF_EHDR* ehdr) { + if (pread(fd, ehdr, sizeof (ELF_EHDR), 0) != sizeof (ELF_EHDR) || + memcmp(&ehdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 || + ehdr->e_version != EV_CURRENT) { + return 0; + } + return 1; +} + +bool is_elf_file(int fd) { + ELF_EHDR ehdr; + return read_elf_header(fd, &ehdr); +} + +// read program header table of an ELF file +ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr) { + ELF_PHDR* phbuf = 0; + // allocate memory for program header table + size_t nbytes = hdr->e_phnum * hdr->e_phentsize; + + if ((phbuf = (ELF_PHDR*) malloc(nbytes)) == NULL) { + print_debug("can't allocate memory for reading program header table\n"); + return NULL; + } + + if (pread(fd, phbuf, nbytes, hdr->e_phoff) != nbytes) { + print_debug("ELF file is truncated! can't read program header table\n"); + free(phbuf); + return NULL; + } + + return phbuf; +} + +// read section header table of an ELF file +ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr) { + ELF_SHDR* shbuf = 0; + // allocate memory for section header table + size_t nbytes = hdr->e_shnum * hdr->e_shentsize; + + if ((shbuf = (ELF_SHDR*) malloc(nbytes)) == NULL) { + print_debug("can't allocate memory for reading section header table\n"); + return NULL; + } + + if (pread(fd, shbuf, nbytes, hdr->e_shoff) != nbytes) { + print_debug("ELF file is truncated! can't read section header table\n"); + free(shbuf); + return NULL; + } + + return shbuf; +} + +// read a particular section's data +void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr) { + void *buf = NULL; + if (shdr->sh_type == SHT_NOBITS || shdr->sh_size == 0) { + return buf; + } + if ((buf = calloc(shdr->sh_size, 1)) == NULL) { + print_debug("can't allocate memory for reading section data\n"); + return NULL; + } + if (pread(fd, buf, shdr->sh_size, shdr->sh_offset) != shdr->sh_size) { + free(buf); + print_debug("section data read failed\n"); + return NULL; + } + return buf; +} + +uintptr_t find_base_address(int fd, ELF_EHDR* ehdr) { + uintptr_t baseaddr = (uintptr_t)-1; + int cnt; + ELF_PHDR *phbuf, *phdr; + + // read program header table + if ((phbuf = read_program_header_table(fd, ehdr)) == NULL) { + goto quit; + } + + // the base address of a shared object is the lowest vaddr of + // its loadable segments (PT_LOAD) + for (phdr = phbuf, cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) { + if (phdr->p_type == PT_LOAD && phdr->p_vaddr < baseaddr) { + baseaddr = phdr->p_vaddr; + } + } + +quit: + if (phbuf) free(phbuf); + return baseaddr; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/salibelf.h Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003, 2005, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _SALIBELF_H_ +#define _SALIBELF_H_ + +#include <elf.h> +#include "elfmacros.h" +#include "libproc_impl.h" + +// read ELF file header. +int read_elf_header(int fd, ELF_EHDR* ehdr); + +// is given file descriptor corresponds to an ELF file? +bool is_elf_file(int fd); + +// read program header table of an ELF file. caller has to +// free the result pointer after use. NULL on failure. +ELF_PHDR* read_program_header_table(int fd, ELF_EHDR* hdr); + +// read section header table of an ELF file. caller has to +// free the result pointer after use. NULL on failure. +ELF_SHDR* read_section_header_table(int fd, ELF_EHDR* hdr); + +// read a particular section's data. caller has to free the +// result pointer after use. NULL on failure. +void* read_section_data(int fd, ELF_EHDR* ehdr, ELF_SHDR* shdr); + +// find the base address at which the library wants to load itself +uintptr_t find_base_address(int fd, ELF_EHDR* ehdr); +#endif /* _SALIBELF_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/symtab.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2003, 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <unistd.h> +#include <search.h> +#include <stdlib.h> +#include <string.h> +#include <db.h> +#include <fcntl.h> +#include "symtab.h" +#include "salibelf.h" + + +// ---------------------------------------------------- +// functions for symbol lookups +// ---------------------------------------------------- + +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; + struct symtab* symtab = NULL; + + // Reading of elf header + struct elf_section *scn_cache = NULL; + int cnt = 0; + ELF_SHDR* shbuf = NULL; + ELF_SHDR* cursct = NULL; + ELF_PHDR* phbuf = NULL; + int symtab_found = 0; + int dynsym_found = 0; + uint32_t symsection = SHT_SYMTAB; + + uintptr_t baseaddr = (uintptr_t)-1; + + lseek(fd, (off_t)0L, SEEK_SET); + if (! read_elf_header(fd, &ehdr)) { + // not an elf + return NULL; + } + + // read ELF header + if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) { + goto quit; + } + + baseaddr = find_base_address(fd, &ehdr); + + scn_cache = calloc(ehdr.e_shnum, sizeof(*scn_cache)); + if (scn_cache == NULL) { + goto quit; + } + + for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) { + scn_cache[cnt].c_shdr = cursct; + if (cursct->sh_type == SHT_SYMTAB || + cursct->sh_type == SHT_STRTAB || + cursct->sh_type == SHT_DYNSYM) { + if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) { + goto quit; + } + } + + if (cursct->sh_type == SHT_SYMTAB) + symtab_found++; + + if (cursct->sh_type == SHT_DYNSYM) + dynsym_found++; + + cursct++; + } + + if (!symtab_found && dynsym_found) + symsection = SHT_DYNSYM; + + for (cnt = 1; cnt < ehdr.e_shnum; cnt++) { + ELF_SHDR *shdr = scn_cache[cnt].c_shdr; + + if (shdr->sh_type == symsection) { + ELF_SYM *syms; + int j, n; + size_t size; + + // FIXME: there could be multiple data buffers associated with the + // same ELF section. Here we can handle only one buffer. See man page + // for elf_getdata on Solaris. + + // guarantee(symtab == NULL, "multiple symtab"); + symtab = calloc(1, sizeof(*symtab)); + if (symtab == NULL) { + goto quit; + } + // the symbol table + syms = (ELF_SYM *)scn_cache[cnt].c_data; + + // number of symbols + n = shdr->sh_size / shdr->sh_entsize; + + // 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); + // guarantee(symtab->hash_table, "unexpected failure: dbopen"); + if (symtab->hash_table == NULL) + goto bad; + + // shdr->sh_link points to the section that contains the actual strings + // for symbol names. the st_name field in ELF_SYM is just the + // string table index. we make a copy of the string table so the + // strings will not be destroyed by elf_end. + size = scn_cache[shdr->sh_link].c_shdr->sh_size; + symtab->strs = malloc(size); + if (symtab->strs == NULL) + goto bad; + memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size); + + // allocate memory for storing symbol offset and size; + symtab->num_symbols = n; + symtab->symbols = calloc(n , sizeof(*symtab->symbols)); + if (symtab->symbols == NULL) + goto bad; + + // copy symbols info our symtab and enter them info the hash table + for (j = 0; j < n; j++, syms++) { + DBT key, value; + char *sym_name = symtab->strs + syms->st_name; + + // skip non-object and non-function symbols + int st_type = ELF_ST_TYPE(syms->st_info); + if ( st_type != STT_FUNC && st_type != STT_OBJECT) + continue; + // skip empty strings and undefined symbols + if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue; + + symtab->symbols[j].name = sym_name; + symtab->symbols[j].offset = syms->st_value - baseaddr; + symtab->symbols[j].size = syms->st_size; + + key.data = sym_name; + key.size = strlen(sym_name) + 1; + value.data = &(symtab->symbols[j]); + value.size = sizeof(void *); + (*symtab->hash_table->put)(symtab->hash_table, &key, &value, 0); + } + } + } + goto quit; + +bad: + destroy_symtab(symtab); + symtab = NULL; + +quit: + if (shbuf) free(shbuf); + if (phbuf) free(phbuf); + if (scn_cache) { + for (cnt = 0; cnt < ehdr.e_shnum; cnt++) { + if (scn_cache[cnt].c_data != NULL) { + free(scn_cache[cnt].c_data); + } + } + free(scn_cache); + } + return symtab; +} + +void destroy_symtab(struct symtab* 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); +} + +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) + 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; + uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset); + if (sym_size) *sym_size = sym->size; + return rslt; + } + + return 0; +} + +const char* nearest_symbol(struct symtab* symtab, uintptr_t offset, + uintptr_t* poffset) { + int n = 0; + if (!symtab) return NULL; + for (; n < symtab->num_symbols; n++) { + struct elf_symbol* sym = &(symtab->symbols[n]); + if (sym->name != NULL && + offset >= sym->offset && offset < sym->offset + sym->size) { + if (poffset) *poffset = (offset - sym->offset); + return sym->name; + } + } + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/symtab.h Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2003, 2010, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef _SYMTAB_H_ +#define _SYMTAB_H_ + +#include <stdint.h> + +// interface to manage ELF symbol tables + +struct symtab; + +// build symbol table for a given ELF file descriptor +struct symtab* build_symtab(int fd); + +// destroy the symbol table +void destroy_symtab(struct symtab* symtab); + +// search for symbol in the given symbol table. Adds offset +// to the base uintptr_t supplied. Returns NULL if not found. +uintptr_t search_symbol(struct symtab* symtab, uintptr_t base, + const char *sym_name, int *sym_size); + +// look for nearest symbol for a given offset (not address - base +// subtraction done by caller +const char* nearest_symbol(struct symtab* symtab, uintptr_t offset, + uintptr_t* poffset); + +#endif /*_SYMTAB_H_*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/os/bsd/test.c Tue Oct 25 10:52:03 2011 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2003, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include "libproc.h" + +int main(int argc, char** argv) { + struct ps_prochandle* ph; + + init_libproc(true); + switch (argc) { + case 2: { + // process + ph = Pgrab(atoi(argv[1])); + break; + } + + case 3: { + // core + ph = Pgrab_core(argv[1], argv[2]); + break; + } + + default: { + fprintf(stderr, "usage %s <pid> or %s <exec file> <core file>\n", argv[0], argv[0]); + return 1; + } + } + + if (ph) { + Prelease(ph); + return 0; + } else { + printf("can't connect to debuggee\n"); + return 1; + } +}
--- a/hotspot/agent/src/os/solaris/Makefile Tue Oct 25 15:40:34 2011 +0100 +++ b/hotspot/agent/src/os/solaris/Makefile Tue Oct 25 10:52:03 2011 -0700 @@ -24,9 +24,7 @@ all: - cd dbx; $(MAKE) all cd proc; $(MAKE) all clean: - cd dbx; $(MAKE) clean cd proc; $(MAKE) clean
--- a/hotspot/agent/src/os/solaris/dbx/Makefile Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -# -# Copyright (c) 2000, 2003, 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 -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# -# - - -# Targets are: -# 32bit: Build the 32 bit version in ./32bit -# 64bit: Build the 64 bit version in ./64bit -# helloWorld: Build the helloWorld test program -# all: Build all of the above. This is the default. -# -# NOTE: This makefile uses IOBuf.cpp, IOBuf.hpp, Buffer.cpp, and -# Buffer.hpp from the src/os/win32/agent directory. - -.PHONY: 32bit 64bit - -ARCH_ORIG = $(shell uname -p) - -# C++ := /java/devtools/$(ARCH_ORIG)/SUNWspro/SC6.1/bin/CC - -C++ := CC -RM := /usr/bin/rm -MKDIRS := /usr/bin/mkdir -p - - -WIN32_DIR := ../../win32 -ARCH := $(subst i386,i486,$(ARCH_ORIG)) -# INCLUDES := -I/net/sparcworks.eng/export/set/sparcworks5/dbx_62_intg/dev/src/dbx -I$(WIN32_DIR) -INCLUDES := -I. -I$(WIN32_DIR) -CFLAGS_32bit := -xarch=v8 -CFLAGS_64bit := -xarch=v9 -CFLAGS := -PIC -xO3 $(INCLUDES) -LIBS := -lsocket -lnsl -lrtld_db -LDFLAGS := -G - -ifneq "$(ARCH)" "i486" - CFLAGS += $(CFLAGS_$(VERSION)) - LDFLAGS += $(CFLAGS_$(VERSION)) -endif - -# We use IOBuf.hpp, IOBuf.cpp, Buffer.hpp, and Buffer.cpp from the win32 dir. -vpath %.cpp .:$(WIN32_DIR) -vpath %.hpp .:$(WIN32_DIR) - -OBJS = $(VERSION)/svc_agent_dbx.o $(VERSION)/IOBuf.o $(VERSION)/Buffer.o - - - -# The default is to make both 32 bit and 64 bit versions. -all:: 32bit 64bit - -32bit 64bit:: - $(MKDIRS) $@ - $(MAKE) $@/libsvc_agent_dbx.so helloWorld VERSION=$@ - -$(VERSION)/IOBuf.o: IOBuf.hpp -$(VERSION)/Buffer.o: Buffer.hpp -$(VERSION)/svc_agent_dbx.o: svc_agent_dbx.hpp - -$(VERSION)/%.o: %.cpp - $(C++) $(CFLAGS) -c $< -o $@ - -$(VERSION)/libsvc_agent_dbx.so:: $(OBJS) - $(C++) $(LDFLAGS) -o $(VERSION)/libsvc_agent_dbx.so $(OBJS) $(LIBS) - -# Would be nice to move this into a shared directory -helloWorld:: helloWorld.cpp - $(C++) -g $< -o $@ - -clean:: - $(RM) -rf 32bit 64bit *.o helloWorld
--- a/hotspot/agent/src/os/solaris/dbx/README Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -shell_impl.h -proc_service_2.h - -The above files are captured from the dbx build environment. -Rather then use a -I that points to stuff in .eng domain that -may not be accessible in other domains these files are just -copied here so local builds in other domains will work. -These files rarely change so the fact that we might have to -strobe in new ones on rare occasions is no big deal.
--- a/hotspot/agent/src/os/solaris/dbx/README-commands.txt Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -This import module uses a largely text-based protocol, except for -certain bulk data transfer operations. All text is in single-byte -US-ASCII. - -Commands understood: - -address_size ::= <int result> - - Returns 32 if attached to 32-bit process, 64 if 64-bit. - -peek_fail_fast <bool arg> ::= - - Indicates whether "peek" requests should "fail fast"; that is, if - any of the addresses in the requested range are unmapped, report - the entire range as unmapped. This is substantially faster than - the alternative, which is to read the entire range byte-by-byte. - However, it should only be used when it is guaranteed by the - client application that peeks come from at most one page. The - default is that peek_fast_fail is not enabled. - -peek <address addr> <unsigned int numBytes> ::= - B<binary char success> - [<binary unsigned int len> <binary char isMapped> [<binary char data>]...]... - - NOTE that the binary portion of this message is prefixed by the - uppercase US-ASCII letter 'B', allowing easier synchronization by - clients. There is no data between the 'B' and the rest of the - message. - - May only be called once attached. Reads the address space of the - target process starting at the given address (see below for format - specifications) and extending the given number of bytes. Whether - the read succeeded is indicated by a single byte containing a 1 or - 0 (success or failure). If successful, the return result is given - in a sequence of ranges. _len_, the length of each range, is - indicated by a 32-bit unsigned integer transmitted with big-endian - byte ordering (i.e., most significant byte first). _isMapped_ - indicates whether the range is mapped or unmapped in the target - process's address space, and will contain the value 1 or 0 for - mapped or unmapped, respectively. If the range is mapped, - _isMapped_ is followed by _data_, containing the raw binary data - for the range. The sum of all ranges' lengths is guaranteed to be - equivalent to the number of bytes requested. - -poke <address addr> <int numBytes> B[<binary char data>]... ::= <bool result> - - NOTE that the binary portion of this message is prefixed by the - uppercase US-ASCII letter 'B', allowing easier synchronization by - clients. There is no data between the 'B' and the rest of the - message. - - Writes the given data to the target process starting at the given - address. Returns 1 on success, 0 on failure (i.e., one or more of - target addresses were unmapped). - -mapped <address addr> <int numBytes> ::= <bool result> - - Returns 1 if entire address range [address...address + int arg) is - mapped in target process's address space, 0 if not - -lookup <symbol objName> <symbol sym> ::= <address addr> - - First symbol is object name; second is symbol to be looked up. - Looks up symbol in target process's symbol table and returns - address. Returns NULL (0x0) if symbol is not found. - -thr_gregs <int tid> ::= <int numAddresses> <address...> - - Fetch the "general" (integer) register set for the given thread. - Returned as a series of hexidecimal values. NOTE: the meaning of - the return value is architecture-dependent. In general it is the - contents of the prgregset_t. - -exit ::= - - Exits the serviceability agent dbx module, returning control to - the dbx prompt. - -// Data formats and example values: -<address> ::= 0x12345678[9ABCDEF0] /* up to 64-bit hex value */ -<unsigned int> ::= 5 /* up to 32-bit integer number; no leading sign */ -<bool> ::= 1 /* ASCII '0' or '1' */
--- a/hotspot/agent/src/os/solaris/dbx/helloWorld.cpp Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2000, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include <stdio.h> -#include <inttypes.h> - -extern "C" { - const char* helloWorldString = "Hello, world!"; - // Do not change these values without changing TestDebugger.java as well - // FIXME: should make these jbyte, jshort, etc... - volatile int8_t testByte = 132; - volatile int16_t testShort = 27890; - volatile int32_t testInt = 1020304050; - volatile int64_t testLong = 102030405060708090LL; - volatile float testFloat = 35.4F; - volatile double testDouble = 1.23456789; - - volatile int helloWorldTrigger = 0; -} - -int -main(int, char**) { - while (1) { - while (helloWorldTrigger == 0) { - } - - fprintf(stderr, "%s\n", helloWorldString); - fprintf(stderr, "testByte=%d\n", testByte); - fprintf(stderr, "testShort=%d\n", testShort); - fprintf(stderr, "testInt=%d\n", testInt); - fprintf(stderr, "testLong=%d\n", testLong); - fprintf(stderr, "testFloat=%d\n", testFloat); - fprintf(stderr, "testDouble=%d\n", testDouble); - - while (helloWorldTrigger != 0) { - } - } -}
--- a/hotspot/agent/src/os/solaris/dbx/proc_service_2.h Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2002, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef _PROC_SERVICE_2_H -#define _PROC_SERVICE_2_H - -/* - * Types, function definitions for the provider of services beyond - * proc_service. This interface will be used by import modules like - * BAT/prex, NEO debugger etc. - */ - -/* - CCR info - - Version history: - - 1.0 - Initial CCR release - - 1.1 - Changes for GLUE/neo. - New entry points ps_svnt_generic() and ps_svc_generic() - - New entry point ps_getpid() - - Release information for automatic CCR updates: - BEGIN RELEASE NOTES: (signifies what gets put into CCR release notes) - 1.2 - Changes to support Solaris 2.7 - - END RELEASE NOTES: (signifies what gets put into CCR release notes) - - Following is used for CCR version number: - -#define CCR_PROC_SERVICE_2_VERSION 1.2 - -*/ - - -#include <proc_service.h> -#include <sys/types.h> - -#ifdef __cplusplus -extern "C" { -#endif - -struct ps_loadobj { - int objfd; /* fd of the load object or executable - * -1 implies its not available. - * This file decriptor is live only during the - * particular call to ps_iter_f(). If you - * need it beyond that you need to dup() it. - */ - psaddr_t - text_base; /* address where text of loadobj was mapped */ - psaddr_t - data_base; /* address where data of loadobj was mapped */ - const char *objname; /* loadobj name */ -}; - -typedef int ps_iter_f(const struct ps_prochandle *, const struct ps_loadobj *, - void *cd); - -/* - * Returns the ps_prochandle for the current process under focus. Returns - * NULL if there is none. - */ - -const struct ps_prochandle * -ps_get_prochandle(void); - -/* - * Returns the ps_prochandle for the current process(allows core files to - * be specified) under focus. Returns NULL if there is none. - */ -const struct ps_prochandle * -ps_get_prochandle2(int cores_too); - -/* - * Returns the pid of the process referred to by the ps_prochandle. - * - * 0 is returned in case the ps_prochandle is not valid or refers to dead - * process. - * - */ -pid_t -ps_getpid(const struct ps_prochandle *); - -/* - * Iteration function that iterates over all load objects *and the - * executable* - * - * If the callback routine returns: - * 0 - continue processing link objects - * non zero - stop calling the callback function - * - */ - -ps_err_e -ps_loadobj_iter(const struct ps_prochandle *, ps_iter_f *, void *clnt_data); - -/* - * Address => function name mapping - * - * Given an address, returns a pointer to the function's - * linker name (null terminated). - */ - -ps_err_e -ps_find_fun_name(const struct ps_prochandle *, psaddr_t addr, - const char **name); - -/* - * Interface to LD_PRELOAD. LD_PRELOAD given library across the - * program 'exec'. - * - */ - -/* - * Append/Prepend the 'lib' (has to be library name as understood by LD_PRELOAD) - * to the LD_PRELOAD variable setting to be used by the debugee - * Returns a cookie (in id). - */ -ps_err_e -ps_ld_preload_append(const char *lib, int *id); -ps_err_e -ps_ld_preload_prepend(const char *lib, int *id); - -/* - * Remove the library associated with 'id' from the LD_PRELOAD setting. - * - */ -ps_err_e -ps_ld_preload_remove(int id); - -#ifdef __cplusplus -} -#endif - -/* - * The following are C++ only interfaces - */ -#ifdef __cplusplus - -/* - * classes ServiceDbx and ServantDbx and defined in "gp_dbx_svc.h" which is - * accessed via CCR - */ -extern class ServantDbx *ps_svnt_generic(); -extern class ServiceDbx *ps_svc_generic(); - -#endif - -#endif /* _PROC_SERVICE_2_H */
--- a/hotspot/agent/src/os/solaris/dbx/shell_imp.h Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2001, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHELL_IMP_H -#define SHELL_IMP_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdio.h> - -/* - CCR info - - Vesrion history: - - 1.0 - Initial CCR release - - Release information for automatic CCR updates: - - BEGIN RELEASE NOTES: (signifies what gets put into CCR release notes) - 1.1 - - Entry points for va_list style msgs; new shell_imp_vmsg() - and shell_imp_verrmsg() - - shell_imp_env_checker() is now shell_imp_var_checker(). - Also the var_checker callback gets passed interp. - 1.2 - interposition framework (used by jdbx) - - access to input FILE pointer. - - END RELEASE NOTES: (signifies what gets put into CCR release notes) - -Following is used as a CCR version number: -#define CCR_SHELL_IMP_VERSION 1.1 -*/ - -#include <stdarg.h> - -#define SHELL_IMP_MAJOR 1 -#define SHELL_IMP_MINOR 2 -#define SHELL_IMP_FLAG_GLOB 0x1 -#define SHELL_IMP_FLAG_ARGQ 0x2 - -typedef void *shell_imp_interp_t; -typedef void *shell_imp_command_t; -typedef int shell_imp_fun_t(shell_imp_interp_t, int, char **, void *); - -int -shell_imp_init( - int, /* major version number */ - int, /* minor version number */ - shell_imp_interp_t, /* interpreter */ - int, /* argc */ - char *[] /* argv */ -); - -int -shell_imp_fini(shell_imp_interp_t); - -shell_imp_command_t -shell_imp_define_command(char *, /* command name e.g. "tnf" */ - shell_imp_fun_t *, /* callback function */ - int, /* SHELL_IMP_FLAG_* bit vector */ - void *, /* client_data Passed as last arg to - /* callback function */ - char * /* help message, e.g. */ - /* "enable the specified tnf probes" */ - ); - -int -shell_imp_undefine_command(shell_imp_command_t); - -int -shell_imp_var_checker(shell_imp_interp_t, - const char *, /* var name */ - int (*)(shell_imp_interp_t, const char*) /* env checker */ - ); - -int -shell_imp_execute(shell_imp_interp_t, const char *); - -const char * -shell_imp_get_var(shell_imp_interp_t, const char *); - -void -shell_imp_msg(shell_imp_interp_t, const char *, ...); - -void -shell_imp_errmsg(shell_imp_interp_t, const char *, ...); - -void -shell_imp_vmsg(shell_imp_interp_t, const char *, va_list); - -void -shell_imp_verrmsg(shell_imp_interp_t, const char *, va_list); - - - -/* - * Stuff added for 1.2 - */ - -struct shell_imp_interposition_info_t { - shell_imp_fun_t * - new_func; - void * new_client_data; - shell_imp_fun_t * - original_func; - void * original_client_data; - int original_flags; -}; - -typedef int shell_imp_dispatcher_t(shell_imp_interp_t, int, char **, - shell_imp_interposition_info_t *); - -shell_imp_command_t -shell_imp_interpose(char *name, - shell_imp_fun_t *new_func, - int flags, - void *client_data, - char * description, - shell_imp_dispatcher_t *); - -int shell_imp_uninterpose(shell_imp_command_t); - -int -shell_imp_dispatch_interposition(shell_imp_interp_t, - shell_imp_interposition_info_t *, - int argc, char *argv[]); - -int -shell_imp_dispatch_original(shell_imp_interp_t, - shell_imp_interposition_info_t *, - int argc, char *argv[]); - -FILE * -shell_imp_cur_input(shell_imp_interp_t); - -#ifdef __cplusplus -} -#endif - -#endif
--- a/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.cpp Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1068 +0,0 @@ -/* - * Copyright (c) 2000, 2002, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -// This is the implementation of a very simple dbx import module which -// handles requests from the VM which come in over a socket. The -// higher-level Java wrapper for dbx starts the debugger, attaches to -// the process, imports this command, and runs it. After that, the SA -// writes commands to this agent via its own private communications -// channel. The intent is to move away from the text-based front-end -// completely in the near future (no more calling "debug" by printing -// text to dbx's stdin). - -#include <stdio.h> -#include <errno.h> -#include <ctype.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <unistd.h> -#include <string.h> -#include <stropts.h> -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <proc_service.h> -#include <sys/procfs_isa.h> -#include <rtld_db.h> -#include "proc_service_2.h" -#include "svc_agent_dbx.hpp" - -static ServiceabilityAgentDbxModule* module = NULL; -#define NEEDS_CLEANUP - -// Useful for debugging -#define VERBOSE_DEBUGGING - -#ifdef VERBOSE_DEBUGGING -# define debug_only(x) x -#else -# define debug_only(x) -#endif - -// For profiling -//#define PROFILING - -#ifdef PROFILING -#define PROFILE_COUNT 200 -static Timer scanTimer; -static Timer workTimer; -static Timer writeTimer; -static int numRequests = 0; -#endif /* PROFILING */ - -const char* ServiceabilityAgentDbxModule::CMD_ADDRESS_SIZE = "address_size"; -const char* ServiceabilityAgentDbxModule::CMD_PEEK_FAIL_FAST = "peek_fail_fast"; -const char* ServiceabilityAgentDbxModule::CMD_PEEK = "peek"; -const char* ServiceabilityAgentDbxModule::CMD_POKE = "poke"; -const char* ServiceabilityAgentDbxModule::CMD_MAPPED = "mapped"; -const char* ServiceabilityAgentDbxModule::CMD_LOOKUP = "lookup"; -const char* ServiceabilityAgentDbxModule::CMD_THR_GREGS = "thr_gregs"; -const char* ServiceabilityAgentDbxModule::CMD_EXIT = "exit"; - -// The initialization routines must not have C++ name mangling -extern "C" { - -/** This is the initialization routine called by dbx upon importing of - this module. Returns 0 upon successful initialization, -1 upon - failure. */ -int shell_imp_init(int major, int minor, - shell_imp_interp_t interp, int argc, char *argv[]) -{ - // Ensure shell interpreter data structure is laid out the way we - // expect - if (major != SHELL_IMP_MAJOR) { - debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MAJOR (got %d, expected %d)\n", major, SHELL_IMP_MAJOR);) - return -1; - } - if (minor < SHELL_IMP_MINOR) { - debug_only(fprintf(stderr, "Serviceability agent: unexpected value for SHELL_IMP_MINOR (got %d, expected >= %d)\n", minor, SHELL_IMP_MINOR);) - return -1; - } - - if (module != NULL) { - debug_only(fprintf(stderr, "Serviceability agent: module appears to already be initialized (should not happen)\n");) - // Already initialized. Should not happen. - return -1; - } - - module = new ServiceabilityAgentDbxModule(major, minor, interp, argc, argv); - if (!module->install()) { - debug_only(fprintf(stderr, "Serviceability agent: error installing import module\n");) - delete module; - module = NULL; - return -1; - } - - // Installation was successful. Next step will be for the user to - // enter the appropriate command on the command line, which will - // make the SA's dbx module wait for commands to come in over the - // socket. - return 0; -} - -/** This is the routine called by dbx upon unloading of this module. - Returns 0 upon success, -1 upon failure. */ -int -shell_imp_fini(shell_imp_interp_t) -{ - if (module == NULL) { - return -1; - } - - bool res = module->uninstall(); - delete module; - module = NULL; - if (!res) { - return -1; - } - return 0; -} - -} // extern "C" - -/** This is the routine which is called by the dbx shell when the user - requests the serviceability agent module to run. This delegates to - ServiceabilityAgentDbxModule::run. This routine's signature must - match that of shell_imp_fun_t. */ -extern "C" { -static int -svc_agent_run(shell_imp_interp_t, int, char **, void *) { - if (module == NULL) { - return -1; - } - - module->run(); - return 0; -} -} - -/* - * Implementation of ServiceabilityAgentDbxModule class - */ - -// NOTE: we need to forward declare the special "ps_get_prochandle2" -// function which allows examination of core files as well. It isn't -// currently in proc_service_2.h. Note also that it has name mangling -// because it isn't declared extern "C". -//const struct ps_prochandle *ps_get_prochandle2(int cores_too); - -ServiceabilityAgentDbxModule::ServiceabilityAgentDbxModule(int, int, shell_imp_interp_t interp, - int argc, char *argv[]) - :myComm(32768, 131072) -{ - _interp = interp; - _argc = argc; - _argv = argv; - _tdb_agent = NULL; - peek_fail_fast = false; - libThreadName = NULL; -} - -ServiceabilityAgentDbxModule::~ServiceabilityAgentDbxModule() { - if (_command != NULL) { - uninstall(); - } -} - -char* -readCStringFromProcess(psaddr_t addr) { - char c; - int num = 0; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - - // Search for null terminator - do { - if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { - return NULL; - } - ++num; - } while (c != 0); - - // Allocate string - char* res = new char[num]; - if (ps_pread(cur_proc, addr, res, num) != PS_OK) { - delete[] res; - return NULL; - } - return res; -} - -int -findLibThreadCB(const rd_loadobj_t* lo, void* data) { - ServiceabilityAgentDbxModule* module = (ServiceabilityAgentDbxModule*) data; - char* name = readCStringFromProcess(lo->rl_nameaddr); - if (strstr(name, "libthread.so") != NULL) { - module->libThreadName = name; - return 0; - } else { - delete[] name; - return 1; - } -} - -bool -ServiceabilityAgentDbxModule::install() { - // NOTE interdependency between here and Java side wrapper - // FIXME: casts of string literal to char * to match prototype - _command = shell_imp_define_command((char *) "svc_agent_run", - &svc_agent_run, - 0, - NULL, - (char *) "Run the serviceability agent's dbx module.\n" - "This routine causes the module to listen on a socket for requests.\n" - "It does not return until the Java-side code tells it to exit, at\n" - "which point control is returned to the dbx shell."); - if (_command == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to install svc_agent_run command\n")); - return false; - } - - // This is fairly painful. Since dbx doesn't currently load - // libthread_db with RTLD_GLOBAL, we can't just use RTLD_DEFAULT for - // the argument to dlsym. Instead, we have to use rtld_db to search - // through the loaded objects in the target process for libthread.so and - - // Try rtld_db - if (rd_init(RD_VERSION) != RD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Unable to init rtld_db\n")); - return false; - } - - rd_agent_t* rda = rd_new((struct ps_prochandle*) ps_get_prochandle2(1)); - if (rda == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Unable to allocate rtld_db agent\n")); - return false; - } - - if (rd_loadobj_iter(rda, (rl_iter_f*) findLibThreadCB, this) != RD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Loadobject iteration failed\n")); - return false; - } - - if (libThreadName == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to find pathname to libthread.so in target process\n")); - return false; - } - - // Find and open libthread_db.so - char* slash = strrchr(libThreadName, '/'); - if (slash == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: can't parse path to libthread.so \"%s\"\n")); - return false; - } - - int slashPos = slash - libThreadName; - char* buf = new char[slashPos + strlen("libthread_db.so") + 20]; // slop - if (buf == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: error allocating libthread_db.so pathname\n")); - return false; - } - strncpy(buf, libThreadName, slashPos + 1); - - // Check dbx's data model; use sparcv9/ subdirectory if 64-bit and - // if target process is 32-bit - if ((sizeof(void*) == 8) && - (strstr(libThreadName, "sparcv9") == NULL)) { - strcpy(buf + slashPos + 1, "sparcv9/"); - slashPos += strlen("sparcv9/"); - } - - strcpy(buf + slashPos + 1, "libthread_db.so"); - - libThreadDB = dlopen(buf, RTLD_LAZY); - void* tmpDB = libThreadDB; - if (libThreadDB == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Warning: unable to find libthread_db.so at \"%s\"\n", buf)); - // Would like to handle this case as well. Maybe dbx has a better - // idea of where libthread_db.so lies. If the problem with dbx - // loading libthread_db without RTLD_GLOBAL specified ever gets - // fixed, we could run this code all the time. - tmpDB = RTLD_DEFAULT; - } - - delete[] buf; - - // Initialize access to libthread_db - td_init_fn = (td_init_fn_t*) dlsym(tmpDB, "td_init"); - td_ta_new_fn = (td_ta_new_fn_t*) dlsym(tmpDB, "td_ta_new"); - td_ta_delete_fn = (td_ta_delete_fn_t*) dlsym(tmpDB, "td_ta_delete"); - td_ta_map_id2thr_fn = (td_ta_map_id2thr_fn_t*) dlsym(tmpDB, "td_ta_map_id2thr"); - td_thr_getgregs_fn = (td_thr_getgregs_fn_t*) dlsym(tmpDB, "td_thr_getgregs"); - - if (td_init_fn == NULL || - td_ta_new_fn == NULL || - td_ta_delete_fn == NULL || - td_ta_map_id2thr_fn == NULL || - td_thr_getgregs_fn == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to find one or more libthread_db symbols:\n")); - debug_only(if (td_init_fn == NULL) fprintf(stderr, " td_init\n")); - debug_only(if (td_ta_new_fn == NULL) fprintf(stderr, " td_ta_new\n")); - debug_only(if (td_ta_delete_fn == NULL) fprintf(stderr, " td_ta_delete\n")); - debug_only(if (td_ta_map_id2thr_fn == NULL) fprintf(stderr, " td_ta_map_id2thr\n")); - debug_only(if (td_thr_getgregs_fn == NULL) fprintf(stderr, " td_thr_getgregs\n")); - return false; - } - - if ((*td_init_fn)() != TD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to initialize libthread_db\n")); - return false; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::uninstall() { - if (_command == NULL) { - return false; - } - - if (libThreadDB != NULL) { - dlclose(libThreadDB); - libThreadDB = NULL; - } - - int res = shell_imp_undefine_command(_command); - - if (res != 0) { - return false; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::run() { - // This is where most of the work gets done. - // The command processor loop looks like the following: - // - create listening socket - // - accept a connection (only one for now) - // - while that connection is open and the "exit" command has not - // been received: - // - read command - // - if it's the exit command, cleanup and return - // - otherwise, process command and write result - - int listening_socket = socket(AF_INET, SOCK_STREAM, 0); - if (listening_socket < 0) { - return false; - } - - // Set the SO_REUSEADDR property on the listening socket. This - // prevents problems with calls to bind() to the same port failing - // after this process exits. This seems to work on all platforms. - int reuse_address = 1; - if (setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, - (char *)&reuse_address, sizeof(reuse_address)) < 0) { - close(listening_socket); - return false; - } - - sockaddr_in server_address; - // Build the server address. We can bind the listening socket to the - // INADDR_ANY internet address. - memset((char*)&server_address, 0, sizeof(server_address)); - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = (unsigned long)htonl(INADDR_ANY); - server_address.sin_port = htons((short)PORT); - - // Bind socket to port - if (bind(listening_socket, (sockaddr*) &server_address, - sizeof(server_address)) < 0) { - close(listening_socket); - return false; - } - - // Arbitrarily chosen backlog of 5 (shouldn't matter since we expect - // at most one connection) - if (listen(listening_socket, 5) < 0) { - close(listening_socket); - return false; - } - - // OK, now ready to wait for a data connection. This call to - // accept() will block. - struct sockaddr_in client_address; - int address_len = sizeof(client_address); - int client_socket = accept(listening_socket, (sockaddr*) &client_address, - &address_len); - // Close listening socket regardless of whether accept() succeeded. - // (FIXME: this may be annoying, especially during debugging, but I - // really feel that robustness and multiple connections should be - // handled higher up, e.g., at the Java level -- multiple clients - // could conceivably connect to the SA via RMI, and that would be a - // more robust solution than implementing multiple connections at - // this level) - NEEDS_CLEANUP; - - // NOTE: the call to shutdown() usually fails, so don't panic if this happens - shutdown(listening_socket, 2); - - if (close(listening_socket) < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Error closing listening socket\n")); - return false; - } - - if (client_socket < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to open client socket\n")); - // No more cleanup necessary - return false; - } - - // Attempt to disable TCP buffering on this socket. We send small - // amounts of data back and forth and don't want buffering. - int buffer_val = 1; - if (setsockopt(client_socket, IPPROTO_IP, TCP_NODELAY, (char *) &buffer_val, sizeof(buffer_val)) < 0) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to set TCP_NODELAY option on client socket\n")); - cleanup(client_socket); - return false; - } - - // OK, we have the data socket through which we will communicate - // with the Java side. Wait for commands or until reading or writing - // caused an error. - - bool should_continue = true; - - myComm.setSocket(client_socket); - -#ifdef PROFILING - scanTimer.reset(); - workTimer.reset(); - writeTimer.reset(); -#endif - - // Allocate a new thread agent for libthread_db - if ((*td_ta_new_fn)((ps_prochandle*) ps_get_prochandle2(1), &_tdb_agent) != - TD_OK) { - debug_only(fprintf(stderr, "Serviceability agent: Failed to allocate thread agent\n")); - cleanup(client_socket); - return false; - } - - do { - // Decided to use text to communicate between these processes. - // Probably will make debugging easier -- could telnet in if - // necessary. Will make scanning harder, but probably doesn't - // matter. - - // Why not just do what workshop does and parse dbx's console? - // Probably could do that, but at least this way we are in control - // of the text format on both ends. - - // FIXME: should have some way of synchronizing these commands - // between the C and Java sources. - - NEEDS_CLEANUP; - - // Do a blocking read of a line from the socket. - char *input_buffer = myComm.readLine(); - if (input_buffer == NULL) { - debug_only(fprintf(stderr, "Serviceability agent: error during read: errno = %d\n", errno)); - debug_only(perror("Serviceability agent")); - // Error occurred during read. - // FIXME: should guard against SIGPIPE - cleanup(client_socket); - return false; - } - - // OK, now ready to scan. See README-commands.txt for syntax - // descriptions. - - bool res = false; - if (!strncmp(input_buffer, CMD_ADDRESS_SIZE, strlen(CMD_ADDRESS_SIZE))) { - res = handleAddressSize(input_buffer + strlen(CMD_ADDRESS_SIZE)); - } else if (!strncmp(input_buffer, CMD_PEEK_FAIL_FAST, strlen(CMD_PEEK_FAIL_FAST))) { - res = handlePeekFailFast(input_buffer + strlen(CMD_PEEK_FAIL_FAST)); - } else if (!strncmp(input_buffer, CMD_PEEK, strlen(CMD_PEEK))) { - res = handlePeek(input_buffer + strlen(CMD_PEEK)); - } else if (!strncmp(input_buffer, CMD_POKE, strlen(CMD_POKE))) { - res = handlePoke(input_buffer + strlen(CMD_POKE)); - } else if (!strncmp(input_buffer, CMD_MAPPED, strlen(CMD_MAPPED))) { - res = handleMapped(input_buffer + strlen(CMD_MAPPED)); - } else if (!strncmp(input_buffer, CMD_LOOKUP, strlen(CMD_LOOKUP))) { - res = handleLookup(input_buffer + strlen(CMD_LOOKUP)); - } else if (!strncmp(input_buffer, CMD_THR_GREGS, strlen(CMD_THR_GREGS))) { - res = handleThrGRegs(input_buffer + strlen(CMD_THR_GREGS)); - } else if (!strncmp(input_buffer, CMD_EXIT, strlen(CMD_EXIT))) { - should_continue = false; - } - - if (should_continue) { - if (!res) { - cleanup(client_socket); - return false; - } - } - -#ifdef PROFILING - if (++numRequests == PROFILE_COUNT) { - fprintf(stderr, "%d requests: %d ms scanning, %d ms work, %d ms writing\n", - PROFILE_COUNT, scanTimer.total(), workTimer.total(), writeTimer.total()); - fflush(stderr); - scanTimer.reset(); - workTimer.reset(); - writeTimer.reset(); - numRequests = 0; - } -#endif - - } while (should_continue); - - // Successful exit - cleanup(client_socket); - return true; -} - -void -ServiceabilityAgentDbxModule::cleanup(int client_socket) { - shutdown(client_socket, 2); - close(client_socket); - if (_tdb_agent != NULL) { - (*td_ta_delete_fn)(_tdb_agent); - } -} - -bool -ServiceabilityAgentDbxModule::handleAddressSize(char* data) { - int data_model; - ps_err_e result = ps_pdmodel((ps_prochandle*) ps_get_prochandle2(1), - &data_model); - if (result != PS_OK) { - myComm.writeString("0"); - myComm.flush(); - return false; - } - - int val; - switch (data_model) { - case PR_MODEL_ILP32: - val = 32; - break; - case PR_MODEL_LP64: - val = 64; - break; - default: - val = 0; - break; - } - - if (!myComm.writeInt(val)) { - return false; - } - if (!myComm.writeEOL()) { - return false; - } - return myComm.flush(); -} - -bool -ServiceabilityAgentDbxModule::handlePeekFailFast(char* data) { - unsigned int val; - if (!scanUnsignedInt(&data, &val)) { - return false; - } - peek_fail_fast = (val ? true : false); - return true; -} - -bool -ServiceabilityAgentDbxModule::handlePeek(char* data) { - // Scan hex address, return false if failed - psaddr_t addr; -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - if (!scanAddress(&data, &addr)) { - return false; - } - unsigned int num; - if (!scanUnsignedInt(&data, &num)) { - return false; - } - if (num == 0) { -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(0); - myComm.writeBinChar(0); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - return true; - } -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - char* buf = new char[num]; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - ps_err_e result = ps_pread(cur_proc, addr, buf, num); - if (result == PS_OK) { - // Fast case; entire read succeeded. -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(num); - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, num); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } else { -#ifdef PROFILING - workTimer.stop(); -#endif /* PROFILING */ - - if (peek_fail_fast) { -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - // Fail fast - myComm.writeBinChar('B'); - myComm.writeBinChar(1); - myComm.writeBinUnsignedInt(num); - myComm.writeBinChar(0); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } else { - // Slow case: try to read one byte at a time - // FIXME: need better way of handling this, a la VirtualQuery - - unsigned int strideLen = 0; - int bufIdx = 0; - bool lastByteMapped = (ps_pread(cur_proc, addr, buf, 1) == PS_OK ? true : false); - -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinChar('B'); - myComm.writeBinChar(1); -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - for (int i = 0; i < num; ++i, ++addr) { -#ifdef PROFILING - workTimer.start(); -#endif /* PROFILING */ - result = ps_pread(cur_proc, addr, &buf[bufIdx], 1); -#ifdef PROFILING - workTimer.stop(); -#endif /* PROFILING */ - bool tmpMapped = (result == PS_OK ? true : false); -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - if (tmpMapped != lastByteMapped) { - // State change. Write the length of the last stride. - myComm.writeBinUnsignedInt(strideLen); - if (lastByteMapped) { - // Stop gathering data. Write the data of the last stride. - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, strideLen); - bufIdx = 0; - } else { - // Start gathering data to write. - myComm.writeBinChar(0); - } - strideLen = 0; - lastByteMapped = tmpMapped; - } -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - if (lastByteMapped) { - ++bufIdx; - } - ++strideLen; - } - - // Write last stride (must be at least one byte long by definition) -#ifdef PROFILING - writeTimer.start(); -#endif /* PROFILING */ - myComm.writeBinUnsignedInt(strideLen); - if (lastByteMapped) { - myComm.writeBinChar(1); - myComm.writeBinBuf(buf, strideLen); - } else { - myComm.writeBinChar(0); - } -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - } - } - delete[] buf; - myComm.flush(); - return true; -} - -bool -ServiceabilityAgentDbxModule::handlePoke(char* data) { - // FIXME: not yet implemented - NEEDS_CLEANUP; - bool res = myComm.writeBoolAsInt(false); - myComm.flush(); - return res; -} - -bool -ServiceabilityAgentDbxModule::handleMapped(char* data) { - // Scan address - psaddr_t addr; - if (!scanAddress(&data, &addr)) { - return false; - } - unsigned int num; - if (!scanUnsignedInt(&data, &num)) { - return false; - } - unsigned char val; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - char* buf = new char[num]; - if (ps_pread(cur_proc, addr, buf, num) == PS_OK) { - myComm.writeBoolAsInt(true); - } else { - myComm.writeBoolAsInt(false); - } - delete[] buf; - myComm.writeEOL(); - myComm.flush(); - return true; -} - -extern "C" -int loadobj_iterator(const rd_loadobj_t* loadobj, void *) { - if (loadobj != NULL) { - fprintf(stderr, "loadobj_iterator: visited loadobj \"%p\"\n", (void*) loadobj->rl_nameaddr); - return 1; - } - - fprintf(stderr, "loadobj_iterator: NULL loadobj\n"); - return 0; -} - -bool -ServiceabilityAgentDbxModule::handleLookup(char* data) { - // Debugging: iterate over loadobjs - /* - rd_agent_t* rld_agent = rd_new((ps_prochandle*) ps_get_prochandle2(1)); - rd_loadobj_iter(rld_agent, &loadobj_iterator, NULL); - rd_delete(rld_agent); - */ - -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - - char* object_name = scanSymbol(&data); - if (object_name == NULL) { - return false; - } - char* symbol_name = scanSymbol(&data); - if (symbol_name == NULL) { - delete[] object_name; - return false; - } - -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - - ps_sym_t sym; - // FIXME: check return values from write routines - ps_prochandle* process = (ps_prochandle*) ps_get_prochandle2(1); - ps_err_e lookup_res = ps_pglobal_sym(process, - object_name, symbol_name, &sym); -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - - delete[] object_name; - delete[] symbol_name; - if (lookup_res != PS_OK) { - // This is too noisy - // debug_only(fprintf(stderr, "ServiceabilityAgentDbxModule::handleLookup: error %d\n", lookup_res)); - myComm.writeString("0x0"); - } else { - myComm.writeAddress((void *)sym.st_value); - } - myComm.writeEOL(); - myComm.flush(); - -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - return true; -} - -bool -ServiceabilityAgentDbxModule::handleThrGRegs(char* data) { -#ifdef PROFILING - scanTimer.start(); -#endif /* PROFILING */ - - unsigned int num; - // Get the thread ID - if (!scanUnsignedInt(&data, &num)) { - return false; - } - -#ifdef PROFILING - scanTimer.stop(); - workTimer.start(); -#endif /* PROFILING */ - - // Map tid to thread handle - td_thrhandle_t thread_handle; - if ((*td_ta_map_id2thr_fn)(_tdb_agent, num, &thread_handle) != TD_OK) { - // fprintf(stderr, "Error mapping thread ID %d to thread handle\n", num); - return false; - } - - // Fetch register set - prgregset_t reg_set; - memset(reg_set, 0, sizeof(reg_set)); - td_err_e result = (*td_thr_getgregs_fn)(&thread_handle, reg_set); - if ((result != TD_OK) && (result != TD_PARTIALREG)) { - // fprintf(stderr, "Error fetching registers for thread handle %d: error = %d\n", num, result); - return false; - } - -#ifdef PROFILING - workTimer.stop(); - writeTimer.start(); -#endif /* PROFILING */ - -#if (defined(__sparc) || defined(__i386)) - myComm.writeInt(NPRGREG); - myComm.writeSpace(); - for (int i = 0; i < NPRGREG; i++) { - myComm.writeAddress((void *)reg_set[i]); - if (i == NPRGREG - 1) { - myComm.writeEOL(); - } else { - myComm.writeSpace(); - } - } -#else -#error Please port ServiceabilityAgentDbxModule::handleThrGRegs to your current platform -#endif - - myComm.flush(); - -#ifdef PROFILING - writeTimer.stop(); -#endif /* PROFILING */ - - return true; -} - -// -// Input routines -// - -bool -ServiceabilityAgentDbxModule::scanAddress(char** data, psaddr_t* addr) { - *addr = 0; - - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return false; - } - - if (strncmp(*data, "0x", 2) != 0) { - return false; - } - - *data += 2; - - while ((**data != 0) && (!isspace(**data))) { - int val; - bool res = charToNibble(**data, &val); - if (!res) { - return false; - } - *addr <<= 4; - *addr |= val; - ++*data; - } - - return true; -} - -bool -ServiceabilityAgentDbxModule::scanUnsignedInt(char** data, unsigned int* num) { - *num = 0; - - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return false; - } - - while ((**data != 0) && (!isspace(**data))) { - char cur = **data; - if ((cur < '0') || (cur > '9')) { - return false; - } - *num *= 10; - *num += cur - '0'; - ++*data; - } - - return true; -} - -char* -ServiceabilityAgentDbxModule::scanSymbol(char** data) { - // Skip whitespace - while ((**data != 0) && (isspace(**data))) { - ++*data; - } - - if (**data == 0) { - return NULL; - } - - // First count length - int len = 1; // Null terminator - char* tmpData = *data; - while ((*tmpData != 0) && (!isspace(*tmpData))) { - ++tmpData; - ++len; - } - char* buf = new char[len]; - strncpy(buf, *data, len - 1); - buf[len - 1] = 0; - *data += len - 1; - return buf; -} - -bool -ServiceabilityAgentDbxModule::charToNibble(char ascii, int* value) { - if (ascii >= '0' && ascii <= '9') { - *value = ascii - '0'; - return true; - } else if (ascii >= 'A' && ascii <= 'F') { - *value = 10 + ascii - 'A'; - return true; - } else if (ascii >= 'a' && ascii <= 'f') { - *value = 10 + ascii - 'a'; - return true; - } - - return false; -} - - -char* -ServiceabilityAgentDbxModule::readCStringFromProcess(psaddr_t addr) { - char c; - int num = 0; - ps_prochandle* cur_proc = (ps_prochandle*) ps_get_prochandle2(1); - - // Search for null terminator - do { - if (ps_pread(cur_proc, addr + num, &c, 1) != PS_OK) { - return NULL; - } - ++num; - } while (c != 0); - - // Allocate string - char* res = new char[num]; - if (ps_pread(cur_proc, addr, res, num) != PS_OK) { - delete[] res; - return NULL; - } - return res; -} - - -//-------------------------------------------------------------------------------- -// Class Timer -// - -Timer::Timer() { - reset(); -} - -Timer::~Timer() { -} - -void -Timer::start() { - gettimeofday(&startTime, NULL); -} - -void -Timer::stop() { - struct timeval endTime; - gettimeofday(&endTime, NULL); - totalMicroseconds += timevalDiff(&startTime, &endTime); - ++counter; -} - -long -Timer::total() { - return (totalMicroseconds / 1000); -} - -long -Timer::average() { - return (long) ((double) total() / (double) counter); -} - -void -Timer::reset() { - totalMicroseconds = 0; - counter = 0; -} - -long long -Timer::timevalDiff(struct timeval* start, struct timeval* end) { - long long secs = end->tv_sec - start->tv_sec; - secs *= 1000000; - long long usecs = end->tv_usec - start->tv_usec; - return (secs + usecs); -}
--- a/hotspot/agent/src/os/solaris/dbx/svc_agent_dbx.hpp Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "shell_imp.h" -#include "IOBuf.hpp" -#include <sys/time.h> -#include <thread_db.h> - -typedef td_err_e td_init_fn_t(); -typedef td_err_e td_ta_new_fn_t(struct ps_prochandle *, td_thragent_t **); -typedef td_err_e td_ta_delete_fn_t(td_thragent_t *); -typedef td_err_e td_ta_map_id2thr_fn_t(const td_thragent_t *, thread_t, td_thrhandle_t *); -typedef td_err_e td_thr_getgregs_fn_t(const td_thrhandle_t *, prgregset_t); - -class ServiceabilityAgentDbxModule { -public: - ServiceabilityAgentDbxModule(int major, int minor, - shell_imp_interp_t interp, int argc, char *argv[]); - ~ServiceabilityAgentDbxModule(); - - bool install(); - bool uninstall(); - - /* This is invoked through the dbx command interpreter. It listens - on a socket for commands and does not return until it receives an - "exit" command. At that point control is returned to dbx's main - loop, at which point if the user sends an exit command to dbx's - shell the dbx process will exit. Returns true if completed - successfully, false if an error occurred while running (for - example, unable to bind listening socket). */ - bool run(); - -private: - - // This must be shared between the Java and C layers - static const int PORT = 21928; - - // Command handlers - bool handleAddressSize(char* data); - bool handlePeekFailFast(char* data); - bool handlePeek(char* data); - bool handlePoke(char* data); - bool handleMapped(char* data); - bool handleLookup(char* data); - bool handleThrGRegs(char* data); - - // Input routines - - // May mutate addr argument even if result is false - bool scanAddress(char** data, psaddr_t* addr); - // May mutate num argument even if result is false - bool scanUnsignedInt(char** data, unsigned int* num); - // Returns NULL if error occurred while scanning. Otherwise, returns - // newly-allocated character array which must be freed with delete[]. - char* scanSymbol(char** data); - // Helper routine: converts ASCII to 4-bit integer. Returns true if - // character is in range, false otherwise. - bool charToNibble(char ascii, int* value); - - // Output routines - - // Writes an int with no leading or trailing spaces - bool writeInt(int val, int fd); - // Writes an address in hex format with no leading or trailing - // spaces - bool writeAddress(psaddr_t addr, int fd); - // Writes a register in hex format with no leading or trailing - // spaces (addresses and registers might be of different size) - bool writeRegister(prgreg_t reg, int fd); - // Writes a space to given file descriptor - bool writeSpace(int fd); - // Writes carriage return to given file descriptor - bool writeCR(int fd); - // Writes a bool as [0|1] - bool writeBoolAsInt(bool val, int fd); - // Helper routine: converts low 4 bits to ASCII [0..9][A..F] - char nibbleToChar(unsigned char nibble); - - // Base routine called by most of the above - bool writeString(const char* str, int fd); - - // Writes a binary character - bool writeBinChar(char val, int fd); - // Writes a binary unsigned int in network (big-endian) byte order - bool writeBinUnsignedInt(unsigned int val, int fd); - // Writes a binary buffer - bool writeBinBuf(char* buf, int size, int fd); - - // Routine to flush the socket - bool flush(int client_socket); - - void cleanup(int client_socket); - - // The shell interpreter on which we can invoke commands (?) - shell_imp_interp_t _interp; - - // The "command line" arguments passed to us by dbx (?) - int _argc; - char **_argv; - - // The installed command in the dbx shell - shell_imp_command_t _command; - - // Access to libthread_db (dlsym'ed to be able to pick up the - // version loaded by dbx) - td_init_fn_t* td_init_fn; - td_ta_new_fn_t* td_ta_new_fn; - td_ta_delete_fn_t* td_ta_delete_fn; - td_ta_map_id2thr_fn_t* td_ta_map_id2thr_fn; - td_thr_getgregs_fn_t* td_thr_getgregs_fn; - - // Our "thread agent" -- access to libthread_db - td_thragent_t* _tdb_agent; - - // Path to libthread.so in target process; free with delete[] - char* libThreadName; - - // Handle to dlopen'ed libthread_db.so - void* libThreadDB; - - // Helper callback for finding libthread_db.so - friend int findLibThreadCB(const rd_loadobj_t* lo, void* data); - - // Support for reading C strings out of the target process (so we - // can find the correct libthread_db). Returns newly-allocated char* - // which must be freed with delete[], or null if the read failed. - char* readCStringFromProcess(psaddr_t addr); - - IOBuf myComm; - - // Output buffer support (used by writeString, writeChar, flush) - char* output_buffer; - int output_buffer_size; - int output_buffer_pos; - - // "Fail fast" flag - bool peek_fail_fast; - - // Commands - static const char* CMD_ADDRESS_SIZE; - static const char* CMD_PEEK_FAIL_FAST; - static const char* CMD_PEEK; - static const char* CMD_POKE; - static const char* CMD_MAPPED; - static const char* CMD_LOOKUP; - static const char* CMD_THR_GREGS; - static const char* CMD_EXIT; -}; - -// For profiling. Times reported are in milliseconds. -class Timer { -public: - Timer(); - ~Timer(); - - void start(); - void stop(); - long total(); - long average(); - void reset(); - -private: - struct timeval startTime; - long long totalMicroseconds; // stored internally in microseconds - int counter; - long long timevalDiff(struct timeval* startTime, struct timeval* endTime); -};
--- a/hotspot/agent/src/os/win32/BasicList.hpp Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2000, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef _BASIC_LIST_ -#define _BASIC_LIST_ - -#include <vector> - -template<class T> -class BasicList { -protected: - typedef std::vector<T> InternalListType; - InternalListType internalList; - -public: - BasicList() { - } - virtual ~BasicList() { - } - - void add(T arg) { - internalList.push_back(arg); - } - - bool remove(T arg) { - for (InternalListType::iterator iter = internalList.begin(); - iter != internalList.end(); iter++) { - if (*iter == arg) { - internalList.erase(iter); - return true; - } - } - return false; - } - - int size() { - return internalList.size(); - } - - T get(int index) { - return internalList[index]; - } -}; - -#endif // #defined _BASIC_LIST_
--- a/hotspot/agent/src/os/win32/Buffer.cpp Tue Oct 25 15:40:34 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2001, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions.