-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(diagnostics): implement endpoint for JMX threadinfo dump
- Loading branch information
1 parent
c9cf7f8
commit a855cf4
Showing
1 changed file
with
97 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,14 +15,24 @@ | |
*/ | ||
package io.cryostat.diagnostic; | ||
|
||
import java.lang.management.ThreadInfo; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import javax.management.openmbean.CompositeData; | ||
|
||
import io.cryostat.targets.Target; | ||
import io.cryostat.targets.TargetConnectionManager; | ||
|
||
import com.fasterxml.jackson.annotation.JsonValue; | ||
import io.smallrye.common.annotation.Blocking; | ||
import jakarta.annotation.security.RolesAllowed; | ||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.POST; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.core.MediaType; | ||
import org.jboss.resteasy.reactive.RestPath; | ||
|
||
@Path("/api/beta/diagnostics/targets/{targetId}") | ||
|
@@ -41,4 +51,91 @@ public void gc(@RestPath long targetId) { | |
conn.invokeMBeanOperation( | ||
"java.lang:type=Memory", "gc", null, null, Void.class)); | ||
} | ||
|
||
@Path("/threads") | ||
@RolesAllowed("write") | ||
@Blocking | ||
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) | ||
@GET | ||
public ThreadDump dumpThreads(@RestPath long targetId) { | ||
return targetConnectionManager.executeConnectedTask( | ||
Target.getTargetById(targetId), | ||
conn -> { | ||
CompositeData[] cd = | ||
conn.invokeMBeanOperation( | ||
"java.lang:type=Threading", | ||
"dumpAllThreads", | ||
new Object[] {true, true}, | ||
new String[] {boolean.class.getName(), boolean.class.getName()}, | ||
CompositeData[].class); | ||
return new ThreadDump(List.of(cd).stream().map(ThreadInfo::from).toList()); | ||
}); | ||
} | ||
|
||
static record ThreadDump(@JsonValue List<ThreadInfo> threadInfos) { | ||
ThreadDump(List<ThreadInfo> threadInfos) { | ||
this.threadInfos = new ArrayList<>(threadInfos); | ||
} | ||
|
||
// FIXME this should return the same text format as jcmd/jstack commands do, ie match the | ||
// HotSpotDiagnosticMXBean dumpThreads operation. | ||
// 2024-09-10 12:36:07 | ||
// Full thread dump OpenJDK 64-Bit Server VM (17.0.12+7 mixed mode, sharing): | ||
|
||
// Threads class SMR info: | ||
// _java_thread_list=0x00007fb9400018a0, length=15, elements={ | ||
// 0x00007fba080278e0, 0x00007fba082158c0, 0x00007fba08216cb0, 0x00007fba0821c1d0, | ||
// 0x00007fba0821d600, 0x00007fba0821ea20, 0x00007fba082203c0, 0x00007fba08221900, | ||
// 0x00007fba08222d80, 0x00007fba0822a4d0, 0x00007fba0822e3d0, 0x00007fba0832b470, | ||
// 0x00007fba08409550, 0x00007fba08630d50, 0x00007fb940000eb0 | ||
// } | ||
|
||
// "main" #1 prio=5 os_prio=0 cpu=395.16ms elapsed=4884.90s allocated=50569K | ||
// defined_classes=2034 tid=0x00007fba080278e0 nid=0x391297 in Object.wait() | ||
// [0x00007fba0e5fe000] | ||
// java.lang.Thread.State: WAITING (on object monitor) | ||
// at java.lang.Object.wait([email protected]/Native Method) | ||
// - waiting on <0x000000045d468440> (a java.lang.ProcessImpl) | ||
// at java.lang.Object.wait([email protected]/Object.java:338) | ||
// at java.lang.ProcessImpl.waitFor([email protected]/ProcessImpl.java:434) | ||
// - locked <0x000000045d468440> (a java.lang.ProcessImpl) | ||
// at io.quarkus.cli.build.ExecuteUtil.executeProcess(ExecuteUtil.java:73) | ||
// at io.quarkus.cli.build.BuildSystemRunner.run(BuildSystemRunner.java:50) | ||
// at io.quarkus.cli.Dev.call(Dev.java:46) | ||
// at io.quarkus.cli.Dev.call(Dev.java:18) | ||
// at picocli.CommandLine.executeUserObject(CommandLine.java:2045) | ||
// at picocli.CommandLine.access$1500(CommandLine.java:148) | ||
// at | ||
// picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2465) | ||
// at picocli.CommandLine$RunLast.handle(CommandLine.java:2457) | ||
// at picocli.CommandLine$RunLast.handle(CommandLine.java:2419) | ||
// at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2277) | ||
// at picocli.CommandLine$RunLast.execute(CommandLine.java:2421) | ||
// at picocli.CommandLine.execute(CommandLine.java:2174) | ||
// at io.quarkus.cli.QuarkusCli.run(QuarkusCli.java:116) | ||
// at | ||
// io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:140) | ||
// at io.quarkus.runtime.Quarkus.run(Quarkus.java:71) | ||
// at io.quarkus.runtime.Quarkus.run(Quarkus.java:44) | ||
// at io.quarkus.cli.Main.main(Main.java:9) | ||
|
||
// Locked ownable synchronizers: | ||
// - None | ||
// | ||
// ... (trimmed) ... | ||
// | ||
// "G1 Service" os_prio=0 cpu=256.32ms elapsed=4884.90s tid=0x00007fba081ddbf0 nid=0x39129c | ||
// runnable | ||
|
||
// "VM Periodic Task Thread" os_prio=0 cpu=743.47ms elapsed=4884.89s tid=0x00007fba0822be20 | ||
// nid=0x3912a7 waiting on condition | ||
|
||
// JNI global refs: 24, weak refs: 0 | ||
@Override | ||
public String toString() { | ||
var sb = new StringBuilder(); | ||
this.threadInfos.forEach(sb::append); | ||
return sb.toString(); | ||
} | ||
} | ||
} |