Skip to content

Commit f17b2bc

Browse files
author
Alan Bateman
committed
8356870: HotSpotDiagnosticMXBean.dumpThreads and jcmd Thread.dump_to_file updates
Reviewed-by: sspitsyn, kevinw
1 parent ebd8528 commit f17b2bc

File tree

9 files changed

+1547
-421
lines changed

9 files changed

+1547
-421
lines changed

src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java

Lines changed: 421 additions & 168 deletions
Large diffs are not rendered by default.

src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,13 @@ private ThreadSnapshot() {}
5252

5353
/**
5454
* Take a snapshot of a Thread to get all information about the thread.
55+
* @throws UnsupportedOperationException if not supported by VM
5556
*/
5657
static ThreadSnapshot of(Thread thread) {
5758
ThreadSnapshot snapshot = create(thread);
59+
if (snapshot == null) {
60+
throw new UnsupportedOperationException();
61+
}
5862
if (snapshot.stackTrace == null) {
5963
snapshot.stackTrace = EMPTY_STACK;
6064
}

src/jdk.management/share/classes/com/sun/management/HotSpotDiagnosticMXBean.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -116,6 +116,13 @@ public interface HotSpotDiagnosticMXBean extends PlatformManagedObject {
116116
* {@code outputFile} parameter must be an absolute path to a file that
117117
* does not exist.
118118
*
119+
* <p> When the format is specified as {@link ThreadDumpFormat#JSON JSON}, the
120+
* thread dump is generated in JavaScript Object Notation.
121+
* <a href="doc-files/threadDump.schema.json">threadDump.schema.json</a>
122+
* describes the thread dump format in draft
123+
* <a href="https://tools.ietf.org/html/draft-json-schema-language-02">
124+
* JSON Schema Language version 2</a>.
125+
*
119126
* <p> The thread dump will include output for all platform threads. It may
120127
* include output for some or all virtual threads.
121128
*
@@ -151,6 +158,7 @@ public static enum ThreadDumpFormat {
151158
TEXT_PLAIN,
152159
/**
153160
* JSON (JavaScript Object Notation) format.
161+
* @spec https://datatracker.ietf.org/doc/html/rfc8259 JavaScript Object Notation
154162
*/
155163
JSON,
156164
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"threadDump": {
5+
"type": "object",
6+
"properties": {
7+
"processId": {
8+
"type": "string",
9+
"description": "The native process id of the Java virtual machine."
10+
},
11+
"time": {
12+
"type": "string",
13+
"description": "The time in ISO 8601 format when the thread dump was generated."
14+
},
15+
"runtimeVersion": {
16+
"type": "string",
17+
"description": "The runtime version, see java.lang.Runtime.Version"
18+
},
19+
"threadContainers": {
20+
"type": "array",
21+
"description": "The array of thread containers (thread groupings).",
22+
"items": [
23+
{
24+
"type": "object",
25+
"properties": {
26+
"container": {
27+
"type": "string",
28+
"description": "The container name. The container name is unique."
29+
},
30+
"parent": {
31+
"type": [
32+
"string",
33+
"null"
34+
],
35+
"description": "The parent container name or null for the root container."
36+
},
37+
"owner": {
38+
"type": [
39+
"string",
40+
"null"
41+
],
42+
"description": "The thread identifier of the owner thread if owned."
43+
},
44+
"threads": {
45+
"type": "array",
46+
"description": "The array of threads in the thread container.",
47+
"items": [
48+
{
49+
"type": "object",
50+
"properties": {
51+
"tid": {
52+
"type": "string",
53+
"description": "The thread identifier."
54+
},
55+
"time": {
56+
"type": "string",
57+
"description": "The time in ISO 8601 format that the thread was sampled."
58+
},
59+
"name": {
60+
"type": "string",
61+
"description": "The thread name."
62+
},
63+
"state": {
64+
"type": "string",
65+
"description": "The thread state (Thread::getState)."
66+
},
67+
"virtual" : {
68+
"type": "boolean",
69+
"description": "true for a virtual thread."
70+
},
71+
"parkBlocker": {
72+
"type": [
73+
"object"
74+
],
75+
"properties": {
76+
"object": {
77+
"type": "string",
78+
"description": "The blocker object responsible for the thread parking."
79+
}
80+
},
81+
"required": [
82+
"object"
83+
]
84+
},
85+
"blockedOn": {
86+
"type": "string",
87+
"description": "The object that the thread is blocked on waiting to enter/re-enter a synchronization block/method."
88+
},
89+
"waitingOn": {
90+
"type": "string",
91+
"description": "The object that the thread is waiting to be notified (Object.wait)."
92+
},
93+
"stack": {
94+
"type": "array",
95+
"description": "The thread stack. The first element is the top of the stack.",
96+
"items": [
97+
{
98+
"type": "string",
99+
"description": "A stack trace element (java.lang.StackTraceElement)."
100+
}
101+
]
102+
},
103+
"monitorsOwned": {
104+
"type": "array",
105+
"description": "The objects for which monitors are owned by the thread.",
106+
"items": {
107+
"type": "object",
108+
"properties": {
109+
"depth": {
110+
"type": "integer",
111+
"description": "The stack depth at which the monitors are owned."
112+
},
113+
"locks": {
114+
"type": "array",
115+
"items": {
116+
"type": [
117+
"string",
118+
null
119+
],
120+
"description": "The object for which the monitor is owned by the thread, null if eliminated"
121+
}
122+
}
123+
},
124+
"required": [
125+
"depth",
126+
"locks"
127+
]
128+
}
129+
},
130+
"carrier": {
131+
"type": "string",
132+
"description": "The thread identifier of the carrier thread if mounted."
133+
}
134+
},
135+
"required": [
136+
"tid",
137+
"time",
138+
"name",
139+
"state",
140+
"stack"
141+
]
142+
}
143+
]
144+
},
145+
"threadCount": {
146+
"type": "string",
147+
"description": "The number of threads in the thread container."
148+
}
149+
},
150+
"required": [
151+
"container",
152+
"parent",
153+
"owner",
154+
"threads"
155+
]
156+
}
157+
]
158+
}
159+
},
160+
"required": [
161+
"processId",
162+
"time",
163+
"runtimeVersion",
164+
"threadContainers"
165+
]
166+
}
167+
},
168+
"required": [
169+
"threadDump"
170+
]
171+
}

src/jdk.management/share/classes/com/sun/management/internal/HotSpotDiagnostic.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -153,7 +153,7 @@ public void dumpThreads(String outputFile, ThreadDumpFormat format) throws IOExc
153153
throw new IllegalArgumentException("'outputFile' not absolute path");
154154

155155
try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) {
156-
dumpThreads(out, format);
156+
dumpThreads(out, format);
157157
}
158158
}
159159

test/hotspot/jtreg/serviceability/dcmd/thread/ThreadDumpToFileTest.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
2525
* @test
2626
* @bug 8284161 8287008
2727
* @summary Basic test for jcmd Thread.dump_to_file
28+
* @modules jdk.jcmd
2829
* @library /test/lib
2930
* @run junit/othervm ThreadDumpToFileTest
3031
*/
@@ -66,7 +67,8 @@ void testPlainThreadDump() throws IOException {
6667
@Test
6768
void testJsonThreadDump() throws IOException {
6869
Path file = genThreadDumpPath(".json");
69-
jcmdThreadDumpToFile(file, "-format=json").shouldMatch("Created");
70+
jcmdThreadDumpToFile(file, "-format=json")
71+
.shouldMatch("Created");
7072

7173
// parse the JSON text
7274
String jsonText = Files.readString(file);
@@ -89,7 +91,8 @@ void testDoNotOverwriteFile() throws IOException {
8991
Path file = genThreadDumpPath(".txt");
9092
Files.writeString(file, "xxx");
9193

92-
jcmdThreadDumpToFile(file, "").shouldMatch("exists");
94+
jcmdThreadDumpToFile(file, "")
95+
.shouldMatch("exists");
9396

9497
// file should not be overridden
9598
assertEquals("xxx", Files.readString(file));
@@ -102,7 +105,23 @@ void testDoNotOverwriteFile() throws IOException {
102105
void testOverwriteFile() throws IOException {
103106
Path file = genThreadDumpPath(".txt");
104107
Files.writeString(file, "xxx");
105-
jcmdThreadDumpToFile(file, "-overwrite");
108+
jcmdThreadDumpToFile(file, "-overwrite")
109+
.shouldMatch("Created");
110+
}
111+
112+
/**
113+
* Test output file cannot be created.
114+
*/
115+
@Test
116+
void testFileCreateFails() throws IOException {
117+
Path badFile = Path.of(".").toAbsolutePath()
118+
.resolve("does-not-exist")
119+
.resolve("does-not-exist")
120+
.resolve("threads.bad");
121+
jcmdThreadDumpToFile(badFile, "-format=plain")
122+
.shouldMatch("Failed");
123+
jcmdThreadDumpToFile(badFile, "-format=json")
124+
.shouldMatch("Failed");
106125
}
107126

108127
/**

0 commit comments

Comments
 (0)