JVMTI - 从 jvmtiFrameInfo 获取行号

JVMTI - Get line number from jvmtiFrameInfo

我有以下代码在抛出异常时获取当前堆栈帧位置的行号,但我是通过试验而不是通过规范弄清楚的。我只留下了相关代码。

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 returns line_numbercurrentMethod 的所有行对应的 start_location。现在我的困惑开始于此:为什么我不能将 frame.locationGetLineNumberTable 返回的 start_location 相匹配?有没有说明这些如何匹配?虽然我的代码似乎有效,但我无法相信这是一个始终有效的解决方案。它向后搜索大于或等于 lineNumberEntry.start_location.

的第一个 frame.location

感谢任何提示和指点!

reading 之后更多关于 jlocation 上面的代码应该适用于所有情况。

jlocation values represent virtual machine bytecode indices--that is, offsets into the virtual machine code for a method.

由于一行Java可以生成多行字节码,所以lineNumberEntry.start_location可以但不必匹配frame.location是有道理的。 lineNumberEntry.start_location 是 java 代码行的第一个字节码指令。

例如,如果我们有以下行号 table:

  • 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 的第 11 行 java 抛出异常,上面的代码将找到正确的行号。

你的想法是对的。要按 jlocation 查找行号,您需要找到最接近 start_location 且小于或等于 jlocation.

jvmtiLineNumberEntry

其实官方的JVM TI样本也做了类似的搜索:

heapTracker.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;
}