diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c index 7e198be9a4637..335acc62dcf4f 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c @@ -1652,6 +1652,185 @@ setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue) } } +#ifdef DEBUG +// APIs that can be called when debugging the debug agent + +#define check_jvmti_status(err, msg) \ + if (err != JVMTI_ERROR_NONE) { \ + EXIT_ERROR(err, msg); \ + } + +char* +translateThreadState(jint flags) { + char str[15 * 20]; + str[0] = '\0'; + + if (flags & JVMTI_THREAD_STATE_ALIVE) { + strcat(str, " ALIVE"); + } + if (flags & JVMTI_THREAD_STATE_TERMINATED) { + strcat(str, " TERMINATED"); + } + if (flags & JVMTI_THREAD_STATE_RUNNABLE) { + strcat(str, " RUNNABLE"); + } + if (flags & JVMTI_THREAD_STATE_WAITING) { + strcat(str, " WAITING"); + } + if (flags & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) { + strcat(str, " WAITING_INDEFINITELY"); + } + if (flags & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) { + strcat(str, " WAITING_WITH_TIMEOUT"); + } + if (flags & JVMTI_THREAD_STATE_SLEEPING) { + strcat(str, " SLEEPING"); + } + if (flags & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) { + strcat(str, " IN_OBJECT_WAIT"); + } + if (flags & JVMTI_THREAD_STATE_PARKED) { + strcat(str, " PARKED"); + } + if (flags & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) { + strcat(str, " BLOCKED_ON_MONITOR_ENTER"); + } + if (flags & JVMTI_THREAD_STATE_SUSPENDED) { + strcat(str, " SUSPENDED"); + } + if (flags & JVMTI_THREAD_STATE_INTERRUPTED) { + strcat(str, " INTERRUPTED"); + } + if (flags & JVMTI_THREAD_STATE_IN_NATIVE) { + strcat(str, " IN_NATIVE"); + } + + if (strlen(str) == 0) { + strcpy(str, "<none>"); + } + + char* tstate = (char*)jvmtiAllocate((int)strlen(str) + 1); + strcpy(tstate, str); + + return tstate; +} + +char* +getThreadName(jthread thread) { + jvmtiThreadInfo thr_info; + jvmtiError err; + + memset(&thr_info, 0, sizeof(thr_info)); + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, thread, &thr_info); + if (err == JVMTI_ERROR_WRONG_PHASE || err == JVMTI_ERROR_THREAD_NOT_ALIVE) { + return NULL; // VM or target thread completed its work + } + check_jvmti_status(err, "getThreadName: error in JVMTI GetThreadInfo call"); + + char* tname = thr_info.name; + if (tname == NULL) { + const char* UNNAMED_STR = "<Unnamed thread>"; + size_t UNNAMED_LEN = strlen(UNNAMED_STR); + tname = (char*)jvmtiAllocate((int)UNNAMED_LEN + 1); + strcpy(tname, UNNAMED_STR); + } + return tname; +} + +char* +getMethodName(jmethodID method) { + char* mname = NULL; + jvmtiError err; + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodName) + (gdata->jvmti, method, &mname, NULL, NULL); + check_jvmti_status(err, "getMethodName: error in JVMTI GetMethodName call"); + + return mname; +} + +static char* +get_method_class_name(jmethodID method) { + jclass klass = NULL; + char* cname = NULL; + char* result = NULL; + jvmtiError err; + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodDeclaringClass) + (gdata->jvmti, method, &klass); + check_jvmti_status(err, "get_method_class_name: error in JVMTI GetMethodDeclaringClass"); + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetClassSignature) + (gdata->jvmti, klass, &cname, NULL); + check_jvmti_status(err, "get_method_class_name: error in JVMTI GetClassSignature"); + + size_t len = strlen(cname) - 2; // get rid of leading 'L' and trailing ';' + result = (char*)jvmtiAllocate((int)len + 1); + strncpy(result, cname + 1, len); // skip leading 'L' + result[len] = '\0'; + jvmtiDeallocate((void*)cname); + return result; +} + +static void +print_method(jmethodID method, jint depth) { + char* cname = NULL; + char* mname = NULL; + char* msign = NULL; + jvmtiError err; + + cname = get_method_class_name(method); + + err = JVMTI_FUNC_PTR(gdata->jvmti,GetMethodName) + (gdata->jvmti, method, &mname, &msign, NULL); + check_jvmti_status(err, "print_method: error in JVMTI GetMethodName"); + + tty_message("%2d: %s: %s%s", depth, cname, mname, msign); + jvmtiDeallocate((void*)cname); + jvmtiDeallocate((void*)mname); + jvmtiDeallocate((void*)msign); +} + +#define MAX_FRAME_COUNT_PRINT_STACK_TRACE 200 + +void +printStackTrace(jthread thread) { + jvmtiFrameInfo frames[MAX_FRAME_COUNT_PRINT_STACK_TRACE]; + char* tname = getThreadName(thread); + jint count = 0; + + jvmtiError err = JVMTI_FUNC_PTR(gdata->jvmti,GetStackTrace) + (gdata->jvmti, thread, 0, MAX_FRAME_COUNT_PRINT_STACK_TRACE, frames, &count); + check_jvmti_status(err, "printStackTrace: error in JVMTI GetStackTrace"); + + tty_message("JVMTI Stack Trace for thread %s: frame count: %d", tname, count); + for (int depth = 0; depth < count; depth++) { + print_method(frames[depth].method, depth); + } + jvmtiDeallocate((void*)tname); +} + +void +printThreadInfo(jthread thread) { + jvmtiThreadInfo thread_info; + jint thread_state; + jvmtiError err; + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, thread, &thread_info); + check_jvmti_status(err, "Error in GetThreadInfo"); + err = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadState) + (gdata->jvmti, thread, &thread_state); + check_jvmti_status(err, "Error in GetThreadState"); + const char* state = translateThreadState(thread_state); + tty_message("Thread: %p, name: %s, state(%x): %s, attrs: %s %s", + thread, thread_info.name, thread_state, state, + (isVThread(thread) ? "virtual": "platform"), + (thread_info.is_daemon ? "daemon": "")); +} + +#endif /* DEBUG*/ + /** * Return property value as JDWP allocated string in UTF8 encoding */ diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.h b/src/jdk.jdwp.agent/share/native/libjdwp/util.h index 75281813709e0..3d499d7d56985 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.h @@ -386,6 +386,15 @@ jvmtiError allNestedClasses(jclass clazz, jclass **ppnested, jint *pcount); void setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue); +#ifdef DEBUG +// APIs that can be called when debugging the debug agent +char* translateThreadState(jint flags); +char* getThreadName(jthread thread); +char* getMethodName(jmethodID method); +void printStackTrace(jthread thread); +void printThreadInfo(jthread thread); +#endif + void *jvmtiAllocate(jint numBytes); void jvmtiDeallocate(void *buffer);