我有以下代码在抛出有效的异常时获取当前堆栈帧位置的行号,但我通过实验而不是通过规范来弄清楚。我只留下了相关的代码。
int max_frame_count = 50;
jvmtiFrameInfo frames[max_frame_count];
jint count;
(*jvmti_env)->GetStackTrace(jvmti_env, thread, 0, max_frame_count, frames, &count);
for (int loop = 0; loop < count; loop++) {
jvmtiFrameInfo frame = frames[loop];
jmethodID currentMethod = frame.method;
(*jvmti_env)->GetMethodName(jvmti_env, currentMethod, &name_ptr, &signature_ptr, &generic_ptr);
(*jvmti_env)->GetLineNumberTable(jvmti_env, currentMethod, &entry_count_ptr, &table_ptr);
jint lineNumber = -1;
int lineNumberCount;
jlocation prevLocationId = -1;
if (frame.location != -1) {
for (int lineNumberLoop = entry_count_ptr - 1; lineNumberLoop >= 0; lineNumberLoop--) {
jvmtiLineNumberEntry lineNumberEntry = table_ptr[lineNumberLoop];
if (frame.location >= lineNumberEntry.start_location) {
lineNumber = lineNumberEntry.line_number;
break;
}
}
}
}
GetLineNumberTable
返回所有currentMethod
行的line_number
和相应的start_location
。现在我的困惑开始了:为什么我不能将frame.location
与GetLineNumberTable
返回的start_location
相匹配?是否有任何规范这些如何匹配?虽然我的代码似乎有效,但我无法相信这是一个始终有效的解决方案。它向后搜索大于或等于lineNumberEntry.start_location
的第一个frame.location
。
感谢您的任何提示和指点!
你的想法是对的。要按jlocation
查找行号,您需要找到start_location
最接近且小于或等于jlocation
的jvmtiLineNumberEntry
。
实际上,官方的 JVM TI 示例也进行了类似的搜索:
堆跟踪器.c
error = (*jvmti)->GetLineNumberTable(jvmti, finfo->method, &lineCount, &lineTable);
if ( error == JVMTI_ERROR_NONE ) {
/* Search for line */
lineNumber = lineTable[0].line_number;
for ( i = 1 ; i < lineCount ; i++ ) {
if ( finfo->location < lineTable[i].start_location ) {
break;
}
lineNumber = lineTable[i].line_number;
}
} else if ( error != JVMTI_ERROR_ABSENT_INFORMATION ) {
check_jvmti_error(jvmti, error, "Cannot get method line table");
}
hprof_util.c
for ( i = start ; i < count ; i++ ) {
if ( location < table[i].start_location ) {
HPROF_ASSERT( ((int)location) < ((int)table[i].start_location) );
break;
}
line_number = table[i].line_number;
}
在阅读了有关jlocation
的更多信息后,上面的代码应该适用于所有情况。
jlocation 值表示虚拟机字节码索引,即偏移到方法的虚拟机代码中。
由于一行Java可以生成多行字节代码,因此lineNumberEntry.start_location
可以但不必匹配frame.location
是有道理的。lineNumberEntry.start_location
是 java 代码行的第一个字节码指令。
例如,如果我们有以下行号表:
line_number: 10- , start_location: 10
- line_number: 11, start_location: 18
- line_number: 12, start_location: 22
和以下字节码:
LINENUMBER 11 L3
18: NEW java/lang/IllegalStateException
19: DUP
20: INVOKESPECIAL java/lang/IllegalStateException.<init> ()V
21: ATHROW
并且异常在字节码索引 21 处的 java 行 11 上抛出,上面的代码将找到正确的行号。