mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
[debugger] Provide async stack traces for all running coroutines.
The new method com.intellij.rt.debugger.agent.CaptureStorage#getAllCapturedStacks introduced in the debugger-agent allows to obtain captured stack traces for all the threads. Async stack traces for coroutines are computed in the same evaluation as the dump. IDEA-366087 GitOrigin-RevId: be52ccd3b391c347536daef0e2567554024bdae3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
e9e52e34cd
commit
9d49a47f1d
+39
-10
@@ -17,10 +17,12 @@ public final class CoroutinesDebugHelper {
|
||||
private static final String COROUTINE_CONTEXT_FQN = "kotlin.coroutines.CoroutineContext";
|
||||
private static final String COROUTINE_JOB_FQN = "kotlinx.coroutines.Job";
|
||||
private static final String COROUTINE_CONTEXT_KEY_FQN = "kotlin.coroutines.CoroutineContext$Key";
|
||||
private static final String DEBUGGER_AGENT_CAPTURE_STORAGE_FQN = "com.intellij.rt.debugger.agent.CaptureStorage";
|
||||
|
||||
public static long[] getCoroutinesRunningOnCurrentThread(Object debugProbes, Thread currentThread) throws ReflectiveOperationException {
|
||||
public static long[] getCoroutinesRunningOnCurrentThread(Class<?> debugProbesImplClass, Thread currentThread) throws ReflectiveOperationException {
|
||||
Object debugProbesImplInstance = debugProbesImplClass.getField("INSTANCE").get(null);
|
||||
List<Long> coroutinesIds = new ArrayList<>();
|
||||
List infos = (List)invoke(debugProbes, "dumpCoroutinesInfo");
|
||||
List infos = (List)invoke(debugProbesImplInstance, "dumpCoroutinesInfo");
|
||||
for (Object info : infos) {
|
||||
if (invoke(info, "getLastObservedThread") == currentThread) {
|
||||
coroutinesIds.add((Long)invoke(info, "getSequenceNumber"));
|
||||
@@ -154,10 +156,8 @@ public final class CoroutinesDebugHelper {
|
||||
return current.getClass().getSimpleName().contains(COROUTINE_OWNER_CLASS);
|
||||
}
|
||||
|
||||
public static Object[] dumpCoroutinesInfoAsJsonAndReferences() throws ReflectiveOperationException {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
public static Object[] dumpCoroutinesInfoAsJsonAndReferences(Class<?> debugProbesImplClass) {
|
||||
try {
|
||||
Class<?> debugProbesImplClass = classLoader.loadClass("kotlinx.coroutines.debug.internal.DebugProbesImpl");
|
||||
Object debugProbesImplInstance = debugProbesImplClass.getField("INSTANCE").get(null);
|
||||
Object[] infos = (Object[])invoke(debugProbesImplInstance, "dumpCoroutinesInfoAsJsonAndReferences");
|
||||
return infos;
|
||||
@@ -166,10 +166,8 @@ public final class CoroutinesDebugHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Object[] dumpCoroutinesWithStacktracesAsJson() throws ReflectiveOperationException {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
public static Object[] dumpCoroutinesWithStacktracesAsJson(Class<?> debugProbesImplClass) {
|
||||
try {
|
||||
Class<?> debugProbesImplClass = classLoader.loadClass("kotlinx.coroutines.debug.internal.DebugProbesImpl");
|
||||
Object debugProbesImplInstance = debugProbesImplClass.getField("INSTANCE").get(null);
|
||||
Object[] dump = (Object[])invoke(debugProbesImplInstance, "dumpCoroutinesInfoAsJsonAndReferences");
|
||||
Object[] coroutineInfos = (Object[])dump[3];
|
||||
@@ -177,19 +175,50 @@ public final class CoroutinesDebugHelper {
|
||||
for (int i = 0; i < coroutineInfos.length; i++) {
|
||||
lastObservedStackTraces[i] = lastObservedStackTrace(coroutineInfos[i]);
|
||||
}
|
||||
dump = Arrays.copyOf(dump, dump.length + 1);
|
||||
dump = Arrays.copyOf(dump, dump.length + 2);
|
||||
dump[4] = lastObservedStackTraces;
|
||||
|
||||
Object[] lastObservedThreads = (Object[])dump[1];
|
||||
dump[5] = getAsyncStackTracesForThreads(lastObservedThreads);
|
||||
return dump;
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String lastObservedStackTrace(Object debugCoroutineInfo) throws ReflectiveOperationException {
|
||||
private static String lastObservedStackTrace(Object debugCoroutineInfo) throws ReflectiveOperationException {
|
||||
List<StackTraceElement> stackTrace = (List<StackTraceElement>)invoke(debugCoroutineInfo, "lastObservedStackTrace");
|
||||
return JsonUtils.dumpStackTraceElements(stackTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes com.intellij.rt.debugger.agent.CaptureStorage#getAllCapturedStacks method
|
||||
* which returns a map of threads to their captured async stack traces.
|
||||
* If `debugger.async.stack.trace.for.all.threads` is false, only the current thread's stack trace is returned.
|
||||
*
|
||||
* If debugger-agent is not available, e.g. in attach, returns null
|
||||
*/
|
||||
private static String[] getAsyncStackTracesForThreads(Object[] threads) {
|
||||
try {
|
||||
Class<?> captureStorageClass = Class.forName(DEBUGGER_AGENT_CAPTURE_STORAGE_FQN, false, null);
|
||||
Method getAllCapturedStacks = captureStorageClass.getMethod("getAllCapturedStacks", int.class);
|
||||
|
||||
Map<Thread, String> threadToStackTrace = (Map<Thread, String>)invoke(null, getAllCapturedStacks, 500);
|
||||
|
||||
String[] asyncStackTraces = new String[threads.length];
|
||||
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
Object thread = threads[i];
|
||||
if (thread != null) {
|
||||
asyncStackTraces[i] = threadToStackTrace.get(thread);
|
||||
}
|
||||
}
|
||||
return asyncStackTraces;
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes the array of {@link kotlinx.coroutines.debug.internal.DebugCoroutineInfo} instances
|
||||
* and for each coroutine finds it's job and the first parent, which corresponds to some coroutine, captured in the dump.
|
||||
|
||||
Reference in New Issue
Block a user