8341295: Add some useful debugging APIs to the debug agent

Reviewed-by: amenkov, sspitsyn
This commit is contained in:
Chris Plummer 2024-10-04 21:17:21 +00:00
parent a3e23572d5
commit 33e4bfdf91
2 changed files with 188 additions and 0 deletions

View File

@ -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
*/

View File

@ -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);