javajvmjava-native-interfacejvmti

JVMTI - Get line number from jvmtiFrameInfo


I have the following code to get the line numbers at the current stack frame locations when an exception is thrown that works but I figured it out by experimenting rather than through a specification. I only left the relevant code.

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 the line_number and the corresponding start_location for all lines of currentMethod. Now here is where my confusion starts: Why can't I match frame.location to a start_location returned by GetLineNumberTable? Is there any specification how these match? While my code seems to work, I can't belief that this is a solution that always works. It searches backwards for the first frame.location that is greater or equal to lineNumberEntry.start_location.

Thanks for any hints and pointers!


Solution

  • Your idea is right. To find a line number by jlocation, you need to find jvmtiLineNumberEntry with the closest start_location which is less or equal jlocation.

    Actually, the official JVM TI samples do a similar search:

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