44 MacOs启动HSDB碰到的几个问题

前言 

最近在 mac 上面启动 HSDB 的时候出现了几个问题, 大致的时间点是 差不多是  07.10号晚上吧

我的系统上面 有两个 jdk, 一个 jdk7u80, 一个 jdk8u211 

我期望 HSDB 需要 attach 到的目标进程是一个简单的 HelloWorld 程序, 然后是 跑在 jdk7u80的vm上面的 

 

1. 使用 jdk8 的运行时环境, 运行 jdk7的 sa-jdi.jar

然后 "java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB", 如下命令 启动 hsdb, 提示权限不够, 增加了一个 "sudo" 来启动 

"sudo java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB" 能够启动起, 但是 attach 到 目标进程的时候 似乎是抛出了如下错误 

    master:Home jerry$ sudo java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    Exception in thread "Thread-1" java.lang.NoSuchMethodError: getJavaThreadsInfo
        at sun.jvm.hotspot.debugger.bsd.BsdDebuggerLocal.init0(Native Method)
        at sun.jvm.hotspot.debugger.bsd.BsdDebuggerLocal.<clinit>(BsdDebuggerLocal.java:600)
        at sun.jvm.hotspot.HotSpotAgent.setupDebuggerBsd(HotSpotAgent.java:587)
        at sun.jvm.hotspot.HotSpotAgent.setupDebugger(HotSpotAgent.java:340)
        at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:313)
        at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:157)
        at sun.jvm.hotspot.HSDB.attach(HSDB.java:1168)
        at sun.jvm.hotspot.HSDB.access$1700(HSDB.java:53)
        at sun.jvm.hotspot.HSDB$25$1.run(HSDB.java:436)
        at sun.jvm.hotspot.utilities.WorkerThread$MainLoop.run(WorkerThread.java:66)
        at java.base/java.lang.Thread.run(Thread.java:835)

 

2. 使用 jdk8 的运行时环境, 运行 jdk8的 sa-jdi.jar

然后, 我怀疑是我 jdk, 和 sa-jdi.jar 的版本不匹配, 然后 java -version, 发现 现在 path 上面的 java命令跑的是基于jdk8u211的jdk 

然后, 换了一下, 我把 sa-jdi.jar 换成了 jdk8u211 的 jar 

"sudo java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

但是 也还是抛出了错误, 错误信息如下  

    master:Home jerry$ sudo java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
    Exception in thread "Thread-1" java.lang.InternalError: void* type hasn't been seen when parsing int*
        at sun.jvm.hotspot.HotSpotTypeDataBase.recursiveCreateBasicPointerType(HotSpotTypeDataBase.java:721)
        at sun.jvm.hotspot.HotSpotTypeDataBase.lookupType(HotSpotTypeDataBase.java:134)
        at sun.jvm.hotspot.HotSpotTypeDataBase.lookupOrCreateClass(HotSpotTypeDataBase.java:631)
        at sun.jvm.hotspot.HotSpotTypeDataBase.createType(HotSpotTypeDataBase.java:751)
        at sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.java:195)
        at sun.jvm.hotspot.HotSpotTypeDataBase.<init>(HotSpotTypeDataBase.java:89)
        at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:403)
        at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:305)
        at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
        at sun.jvm.hotspot.HSDB.attach(HSDB.java:1184)
        at sun.jvm.hotspot.HSDB.access$1700(HSDB.java:53)
        at sun.jvm.hotspot.HSDB$25$1.run(HSDB.java:456)
        at sun.jvm.hotspot.utilities.WorkerThread$MainLoop.run(WorkerThread.java:66)
        at java.base/java.lang.Thread.run(Thread.java:835)

 

3. 使用 jdk7 的运行时环境, 运行 jdk7的 sa-jdi.jar

然后 上面两个都报错, 并且 网上搜索了一下, 感觉 都没有搜索到 什么解决问题的文章.. 

然后 算了算了, 最后尝试使用 基于 7u80 的 jdk, 来运行 7u80 的 sa-jdi.jar 

"sudo /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/bin/java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB"

最后 成功的 attach 到了目标进程 

 

 

问题的细节

上面的 "我怀疑是我 jdk, 和 sa-jdi.jar 的版本不匹配", 其实 这个问题没有解决的那么快, 差不多是 耗费了我 一晚上的时间 

我最开始 一直以为是 mac 上面 运行 HSDB 有兼容问题, 或者是 HSDB本身的其他问题, 然后 一直在网上搜啊搜, 但是 可惜没有搜索到太多的关键点, 大多数是 jinfo, jmap 出现了什么什么问题,  然后解决方案是 升级 jdk 

然后 我也去下载了 jdk12, 那晓得 启动起来 attach 到目标进程还是有问题, 到后面 要下班的时候 才想起可能是 jdk 和 sa-jdi.jar 的版本问题 

所以 这个问题, 值得记录一下 

 

然后 今天的时候, 我在我的 windows 机器上面尝试了一下, 上述的一些异常情况, 结果如下 

# "D:\Program Files\Java\jdk1.8.0_92\bin\java" -cp .;"D:\Program Files\Java\jdk1.7.0_40\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB 
	在 windows 上面能够正常运行 
	
# "D:\Program Files\Java\jdk1.8.0_92\bin\java" -cp .;"D:\Program Files\Java\jdk1.8.0_92\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB 
	C:\Users\Jerry.X.He>"D:\Program Files\Java\jdk1.8.0_92\bin\java" -cp .;"D:\Program Files\Java\jdk1.8.0_92\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB
		Exception in thread "Thread-1" java.lang.InternalError: void* type hasn't been seen when parsing int*
        at sun.jvm.hotspot.HotSpotTypeDataBase.recursiveCreateBasicPointerType(HotSpotTypeDataBase.java:721)
        at sun.jvm.hotspot.HotSpotTypeDataBase.lookupType(HotSpotTypeDataBase.java:134)
        at sun.jvm.hotspot.HotSpotTypeDataBase.lookupOrCreateClass(HotSpotTypeDataBase.java:631)
        at sun.jvm.hotspot.HotSpotTypeDataBase.createType(HotSpotTypeDataBase.java:751)
        at sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.java:195)
        at sun.jvm.hotspot.HotSpotTypeDataBase.<init>(HotSpotTypeDataBase.java:89)
        at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:391)
        at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:305)
        at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
        at sun.jvm.hotspot.HSDB.attach(HSDB.java:1184)
        at sun.jvm.hotspot.HSDB.access$1700(HSDB.java:53)
        at sun.jvm.hotspot.HSDB$25$1.run(HSDB.java:456)
        at sun.jvm.hotspot.utilities.WorkerThread$MainLoop.run(WorkerThread.java:66)
        at java.lang.Thread.run(Thread.java:745)

 

然后 另外 跟踪了一下 相关的代码, 以下代码 如果没有特殊说明, 都是基于 jdk7u40 

 1. 使用 jdk8 的运行时环境, 运行 jdk7的 sa-jdi.jar

BsdDebuggerLocal.<clinit> 

   static {
        System.loadLibrary("saproc");
        init0();
    }

BsdDebuggerLocal.init0 [8u60实现] 

agent/src/os/bsd/MacosxDebuggerLocal.m 
	/*
	 * 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");
	  CHECK_EXCEPTION;
	  taskID = (*env)->GetFieldID(env, cls, "task", "J");
	  CHECK_EXCEPTION;

	  // for core file
	  p_ps_prochandle_ID = (*env)->GetFieldID(env, cls, "p_ps_prochandle", "J");
	  CHECK_EXCEPTION;
	  loadObjectList_ID = (*env)->GetFieldID(env, cls, "loadObjectList", "Ljava/util/List;");
	  CHECK_EXCEPTION;

	  // methods we use
	  createClosestSymbol_ID = (*env)->GetMethodID(env, cls, "createClosestSymbol",
						"(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;");
	  CHECK_EXCEPTION;
	  createLoadObject_ID = (*env)->GetMethodID(env, cls, "createLoadObject",
						"(Ljava/lang/String;JJ)Lsun/jvm/hotspot/debugger/cdbg/LoadObject;");
	  CHECK_EXCEPTION;

	  // java.util.List method we call
	  jclass listClass = (*env)->FindClass(env, "java/util/List");
	  CHECK_EXCEPTION;
	  listAdd_ID = (*env)->GetMethodID(env, listClass, "add", "(Ljava/lang/Object;)Z");
	  CHECK_EXCEPTION;
	  getJavaThreadsInfo_ID = (*env)->GetMethodID(env, cls, "getJavaThreadsInfo",
														 "()[J");
	  CHECK_EXCEPTION;

	  init_libproc(getenv("LIBSAPROC_DEBUG") != NULL);
	}

在 jdk7u40 的 sa-jdi.jar 里面的 BsdDebuggerLocal 是不存在 getJavaThreadsInfo 这个方法的, 因此 抛了一个 NoSuchMethodError 

在 jdk8u60 的 sa-jdi.jar 里面的 BsdDebuggerLocal 是增加了 getJavaThreadsInfo 这个方法的  

 

BsdDebuggerLocal.init0[7u40实现] 

agent/src/os/bsd/MacosxDebuggerLocal.m 
	/*
	 * 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;
	}

 

 

2. 使用 jdk8 的运行时环境, 运行 jdk8的 sa-jdi.jar attach jdk7的进程

这里首先要比较一下 抛出异常的这个方法 在两个不同版本的jdk区别 

HotSpotTypeDataBase.recursiveCreateBasicPointerType [基于7u40]

  private BasicPointerType recursiveCreateBasicPointerType(String typeName) {
    BasicPointerType result = (BasicPointerType)super.lookupType(typeName, false);
    if (result != null) {
      return result;
    }
    String targetTypeName = typeName.substring(0, typeName.lastIndexOf('*')).trim();
    Type targetType = null;
    if (typeNameIsPointerType(targetTypeName)) {
      targetType = lookupType(targetTypeName, false);
      if (targetType == null) {
        targetType = recursiveCreateBasicPointerType(targetTypeName);
      }
    } else {
      targetType = lookupType(targetTypeName, false);
      if (targetType == null) {
        // Workaround for missing C integer types in database.
        // Also looks like we can't throw an exception for other
        // missing target types because there are some in old
        // VMStructs tables that didn't have the target type declared.
        // For this case, we create basic types that never get filled
        // in.

        if (targetTypeName.equals("char") ||
            targetTypeName.equals("const char")) {
          // We don't have a representation of const-ness of C types in the SA
          BasicType basicTargetType = createBasicType(targetTypeName, false, true, false);
          basicTargetType.setSize(1);
          targetType = basicTargetType;
        } else if (targetTypeName.equals("u_char")) {
          BasicType basicTargetType = createBasicType(targetTypeName, false, true, true);
          basicTargetType.setSize(1);
          targetType = basicTargetType;
        } else {
          if (DEBUG) {
            System.err.println("WARNING: missing target type \"" + targetTypeName + "\" for pointer type \"" + typeName + "\"");
          }
          targetType = createBasicType(targetTypeName, false, false, false);
        }
      }
    }
    result = new BasicPointerType(this, typeName, targetType);
    result.setSize(UNINITIALIZED_SIZE);
    addType(result);
    return result;
  }

HotSpotTypeDataBase.recursiveCreateBasicPointerType [基于8u60]

  private BasicPointerType recursiveCreateBasicPointerType(String typeName) {
    BasicPointerType result = (BasicPointerType)super.lookupType(typeName, false);
    if (result != null) {
      return result;
    }
    String targetTypeName = typeName.substring(0, typeName.lastIndexOf('*')).trim();
    Type targetType = null;
    if (typeNameIsPointerType(targetTypeName)) {
      targetType = lookupType(targetTypeName, false);
      if (targetType == null) {
        targetType = recursiveCreateBasicPointerType(targetTypeName);
      }
    } else {
      targetType = lookupType(targetTypeName, false);
      if (targetType == null) {
        // Workaround for missing C integer types in database.
        // Also looks like we can't throw an exception for other
        // missing target types because there are some in old
        // VMStructs tables that didn't have the target type declared.
        // For this case, we create basic types that never get filled
        // in.

        if (targetTypeName.equals("char") ||
            targetTypeName.equals("const char")) {
          // We don't have a representation of const-ness of C types in the SA
          BasicType basicTargetType = createBasicType(targetTypeName, false, true, false);
          basicTargetType.setSize(1);
          targetType = basicTargetType;
        } else if (targetTypeName.equals("u_char")) {
          BasicType basicTargetType = createBasicType(targetTypeName, false, true, true);
          basicTargetType.setSize(1);
          targetType = basicTargetType;
        } else {
          if (DEBUG) {
            System.err.println("WARNING: missing target type \"" + targetTypeName + "\" for pointer type \"" + typeName + "\"");
          }
          targetType = createBasicType(targetTypeName, false, false, false);
        }
      }
    }
    result = new BasicPointerType(this, typeName, targetType);
    if (pointerSize == UNINITIALIZED_SIZE && !typeName.equals("void*")) {
      // void* must be declared early so that other pointer types can use that to set their size.
      throw new InternalError("void* type hasn't been seen when parsing " + typeName);
    }
    result.setSize(pointerSize);
    addType(result);
    return result;
  }

两份代码相比, 可以看出 差别的地方 主要是在于 jdk8u60 里面增加了这段代码 

    if (pointerSize == UNINITIALIZED_SIZE && !typeName.equals("void*")) {
      // void* must be declared early so that other pointer types can use that to set their size.
      throw new InternalError("void* type hasn't been seen when parsing " + typeName);
    }

根据注释, 限定了 "void*" 这个类型, 必须出现在所有的指针类型的第一个 

 

那么类型列表哪里来呢 ?

通过调试, 发现 HotSpotTypeDataBase. readVMTypes 主要是读取了 目标进程 vm 的相关信息, 从目标 vm 的 jvm.dll 里面获取的 

WindbgDebuggerLocal.lookupByName

attach 到 jdk7 的进程的时候

attach 到 jdk8 的进程的时候

 

jdk7 的 jvm.dll 里面的类型顺序前面一部分如下 

jboolean
jbyte
jchar
jdouble
jfloat
jint
jlong
jshort
bool
short
int
long
char
unsigned char
u_char
unsigned int
uint
unsigned short
jushort
unsigned long
u2
u1
unsigned
int*
char*
char**
u_char*
unsigned char*
size_t
// ... 剩余的类型省略 

jdk8 的 jvm.dll 里面的类型顺序前面一部分如下 

jboolean
jbyte
jchar
jdouble
jfloat
jint
jlong
jshort
bool
short
int
long
char
unsigned char
volatile unsigned char
u_char
unsigned int
uint
unsigned short
jushort
unsigned long
u1
u2
u4
u8
unsigned
void*
int*
char*
char**
u_char*
unsigned char*
volatile unsigned char*
size_t
// ... 剩余的类型省略 

jdk7 的 jvm.dll 是不满足 jdk8 的 "HotSpotTypeDataBase.recursiveCreateBasicPointerType" 的限定的, 因此 jdk8 的 HSDB attach 到 跑在jdk7的vm进程 上面是会报上面的错误 

 

 

结论

所以 在 需要attach 的场景, 尽量保证

1. 工具代码的版本 和 跑工具项目的 vm版本 保持一致

2. attach的进程 和 被attach的进程 jdk 版本一致 

以免产生 不必要的麻烦 

 

 

完 

 

参考 

https://blog.csdn.net/Dongguabai/article/details/88736589

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值