Skip to content

Commit 990420b

Browse files
authored
Merge pull request #19089 from JasonFengJ9/jcmdoptions-v0.44
(0.44) AttachAPI jcmd supports VMID query via Java process display names
2 parents 6ace28f + 28bc7af commit 990420b

File tree

5 files changed

+157
-53
lines changed

5 files changed

+157
-53
lines changed

jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/AttachHandler.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,27 @@ static final class syncObject {
106106
private static int numberOfTargets;
107107
/**
108108
* As of Java 9, a VM cannot attach to itself unless explicitly enabled.
109-
* Grab the setting before the application has a chance to change it,
110-
* but parse it lazily because we rarely need the value.
111-
*/
112-
public final static String allowAttachSelf =
113-
VM.internalGetProperties().getProperty("jdk.attach.allowAttachSelf" //$NON-NLS-1$
114-
/*[IF JAVA_SPEC_VERSION >= 9]*/
115-
, "false" //$NON-NLS-1$
116-
/*[ELSE] JAVA_SPEC_VERSION >= 9 */
117-
, "true" //$NON-NLS-1$
118-
/*[ENDIF] JAVA_SPEC_VERSION >= 9 */
119-
);
109+
* Grab the setting before the application has a chance to change it.
110+
*/
111+
public static final boolean selfAttachAllowed;
120112

121113
/* only the attach handler thread uses syncFileLock */
122114
/* [PR Jazz 30075] Make syncFileLock an instance variable since it is accessed only by the attachHandler singleton. */
123115
FileLock syncFileLock;
124116

125117
static volatile Thread fileAccessTimeUpdaterThread;
126118

119+
static {
120+
String allowAttachSelf = VM.internalGetProperties().getProperty("jdk.attach.allowAttachSelf" //$NON-NLS-1$
121+
/*[IF JAVA_SPEC_VERSION >= 9]*/
122+
, "false" //$NON-NLS-1$
123+
/*[ELSE] JAVA_SPEC_VERSION >= 9 */
124+
, "true" //$NON-NLS-1$
125+
/*[ENDIF] JAVA_SPEC_VERSION >= 9 */
126+
);
127+
selfAttachAllowed = "".equals(allowAttachSelf) || Boolean.parseBoolean(allowAttachSelf); //$NON-NLS-1$
128+
}
129+
127130
/**
128131
* Keep the constructor private
129132
*/

jcl/src/jdk.attach/share/classes/com/ibm/tools/attach/attacher/OpenJ9VirtualMachine.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,7 @@ private void tryAttachTarget(int timeout) throws IOException,
473473
}
474474

475475
if (descriptor.id().equals(AttachHandler.getVmId())) {
476-
String allowAttachSelf_Value = AttachHandler.allowAttachSelf;
477-
boolean selfAttachAllowed = "".equals(allowAttachSelf_Value) || Boolean.parseBoolean(allowAttachSelf_Value); //$NON-NLS-1$
478-
if (!selfAttachAllowed) {
476+
if (!AttachHandler.selfAttachAllowed) {
479477
/*[MSG "K0646", "Late attach connection to self disabled. Set jdk.attach.allowAttachSelf=true"]*/
480478
throw new IOException(getString("K0646")); //$NON-NLS-1$
481479
}
Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*[INCLUDE-IF Sidecar18-SE]*/
1+
/*[INCLUDE-IF JAVA_SPEC_VERSION >= 8]*/
22
/*******************************************************************************
33
* Copyright IBM Corp. and others 2019
44
*
@@ -20,7 +20,6 @@
2020
*
2121
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
2222
*******************************************************************************/
23-
2423
package openj9.tools.attach.diagnostics.tools;
2524

2625
import java.io.IOException;
@@ -31,6 +30,7 @@
3130
import com.sun.tools.attach.spi.AttachProvider;
3231

3332
import openj9.internal.tools.attach.target.DiagnosticUtils;
33+
import openj9.internal.tools.attach.target.IPC;
3434
import openj9.tools.attach.diagnostics.attacher.AttacherDiagnosticsProvider;
3535

3636
/**
@@ -40,13 +40,15 @@
4040
public class Jcmd {
4141

4242
@SuppressWarnings("nls")
43-
private static final String HELPTEXT = "Usage : jcmd <vmid> <arguments>%n"
43+
private static final String HELPTEXT = "Usage : jcmd <vmid | display name | 0> <arguments>%n"
4444
+ "%n"
4545
+ " -J : supply arguments to the Java VM running jcmd%n"
4646
+ " -l : list JVM processes on the local machine%n"
4747
+ " -h : print this help message%n"
4848
+ "%n"
49-
+ " <vmid> : Attach API VM ID as shown in jps or other Attach API-based tools%n"
49+
+ " <vmid> : Attach API VM ID as shown in jcmd or other Attach API-based tools%n"
50+
+ " <display name> : this argument is used to match (either fully or partially) the display name as shown in jcmd or other Attach API-based tools%n"
51+
+ " <0> : the jcmd command will be sent to all Java processes detected by this utility%n"
5052
+ "%n"
5153
+ " arguments:%n"
5254
+ " help : print the list of diagnostic commands%n"
@@ -65,47 +67,65 @@ public class Jcmd {
6567
* @param args Application arguments. See help text for details.
6668
*/
6769
public static void main(String[] args) {
68-
String command = null;
69-
/* An empty argument list is a request for a list of VMs */
70+
if ((args.length == 1) && Arrays.stream(HELP_OPTIONS).anyMatch(args[0]::equals)) {
71+
System.out.printf(HELPTEXT);
72+
return;
73+
}
74+
75+
List<AttachProvider> providers = AttachProvider.providers();
76+
if ((providers == null) || providers.isEmpty()) {
77+
System.err.println("no attach providers available"); //$NON-NLS-1$
78+
return;
79+
}
80+
81+
AttachProvider openj9Provider = providers.get(0);
82+
List<VirtualMachineDescriptor> vmds = openj9Provider.listVirtualMachines();
83+
if ((vmds == null) || vmds.isEmpty()) {
84+
System.err.println("no VMs found"); //$NON-NLS-1$
85+
return;
86+
}
87+
88+
/* An empty argument list is a request for a list of VMs. */
7089
final String firstArgument = (0 == args.length) ? "-l" : args[0]; //$NON-NLS-1$
7190
if ("-l".equals(firstArgument)) { //$NON-NLS-1$
72-
List<AttachProvider> providers = AttachProvider.providers();
73-
AttachProvider myProvider = null;
74-
if (!providers.isEmpty()) {
75-
myProvider = providers.get(0);
91+
for (VirtualMachineDescriptor vmd : vmds) {
92+
StringBuilder outputBuffer = new StringBuilder(vmd.id());
93+
Util.getTargetInformation(openj9Provider, vmd, false, false, true, outputBuffer);
94+
System.out.println(outputBuffer.toString());
7695
}
77-
if (null == myProvider) {
78-
System.err.println("no attach providers available"); //$NON-NLS-1$
79-
} else {
80-
for (VirtualMachineDescriptor vmd : myProvider.listVirtualMachines()) {
81-
StringBuilder outputBuffer = new StringBuilder(vmd.id());
82-
Util.getTargetInformation(myProvider, vmd, false, false, true, outputBuffer);
83-
System.out.println(outputBuffer.toString());
84-
}
85-
}
86-
} else if ((args.length == 1) && (null != firstArgument) && Arrays.stream(HELP_OPTIONS).anyMatch(firstArgument::equals)) {
87-
System.out.printf(HELPTEXT);
8896
} else {
89-
command = DiagnosticUtils.makeJcmdCommand(args, 1);
97+
String command = DiagnosticUtils.makeJcmdCommand(args, 1);
9098
if (command.isEmpty()) {
9199
System.err.printf("There is no jcmd command.%n"); //$NON-NLS-1$
92100
System.out.printf(HELPTEXT);
93101
} else {
94-
String vmid = firstArgument;
95102
AttacherDiagnosticsProvider diagProvider = new AttacherDiagnosticsProvider();
96-
try {
97-
diagProvider.attach(vmid);
103+
List<String> vmids = Util.findMatchVMIDs(vmds, firstArgument);
104+
boolean exceptionThrown = false;
105+
for (String vmid : vmids) {
106+
if (!vmid.equals(firstArgument)) {
107+
// skip following if firstArgument is a VMID
108+
// keep the output compatible with the old behavior (only one VMID accepted)
109+
System.out.println(vmid + ":"); //$NON-NLS-1$
110+
}
98111
try {
99-
Util.runCommandAndPrintResult(diagProvider, command, command); // $NON-NLS-1$
100-
} finally {
101-
diagProvider.detach();
112+
IPC.logMessage("attaching vmid = " + vmid); //$NON-NLS-1$
113+
diagProvider.attach(vmid);
114+
try {
115+
Util.runCommandAndPrintResult(diagProvider, command, command);
116+
} finally {
117+
diagProvider.detach();
118+
}
119+
} catch (IOException e) {
120+
Util.handleCommandException(vmid, e);
121+
exceptionThrown = true;
122+
// keep iterating the rest of VMIDs
102123
}
103-
} catch (IOException e) {
104-
Util.handleCommandException(vmid, e);
124+
}
125+
if (exceptionThrown) {
105126
System.out.printf(HELPTEXT);
106127
}
107128
}
108129
}
109130
}
110-
111131
}

jcl/src/jdk.jcmd/share/classes/openj9/tools/attach/diagnostics/tools/Util.java

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*[INCLUDE-IF Sidecar18-SE]*/
1+
/*[INCLUDE-IF JAVA_SPEC_VERSION >= 8]*/
22
/*******************************************************************************
33
* Copyright IBM Corp. and others 2019
44
*
@@ -20,29 +20,30 @@
2020
*
2121
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
2222
*******************************************************************************/
23-
2423
package openj9.tools.attach.diagnostics.tools;
2524

25+
import com.sun.tools.attach.AttachNotSupportedException;
26+
import com.sun.tools.attach.VirtualMachine;
27+
import com.sun.tools.attach.VirtualMachineDescriptor;
28+
import com.sun.tools.attach.spi.AttachProvider;
29+
2630
import java.io.BufferedReader;
2731
import java.io.File;
28-
import java.io.IOException;
2932
import java.io.InputStream;
3033
import java.io.InputStreamReader;
34+
import java.io.IOException;
3135
import java.io.PrintStream;
36+
import java.util.ArrayList;
3237
import java.util.Arrays;
3338
import java.util.Collections;
3439
import java.util.List;
3540
import java.util.Map.Entry;
3641
import java.util.Properties;
3742
import java.util.stream.Collectors;
3843

44+
import openj9.internal.tools.attach.target.AttachHandler;
3945
import openj9.internal.tools.attach.target.DiagnosticProperties;
4046
import openj9.internal.tools.attach.target.IPC;
41-
import com.sun.tools.attach.AttachNotSupportedException;
42-
import com.sun.tools.attach.VirtualMachine;
43-
import com.sun.tools.attach.VirtualMachineDescriptor;
44-
import com.sun.tools.attach.spi.AttachProvider;
45-
4647
import openj9.tools.attach.diagnostics.attacher.AttacherDiagnosticsProvider;
4748

4849
/**
@@ -185,4 +186,49 @@ static void exitJVMWithReasonAndHelp(String error, String help) {
185186
static boolean checkHelpOption(String option) {
186187
return Arrays.stream(HELP_OPTIONS).anyMatch(option::equalsIgnoreCase);
187188
}
189+
190+
static List<String> findMatchVMIDs(List<VirtualMachineDescriptor> vmds, String firstArg) {
191+
IPC.logMessage("findMatchVMIDs firstArg = " + firstArg); //$NON-NLS-1$
192+
ArrayList<String> vmids = new ArrayList<>();
193+
String currentVMID = AttachHandler.getVmId();
194+
try {
195+
long pid = Long.parseLong(firstArg);
196+
boolean includeAllVMIDs = (pid == 0);
197+
// this is the virtual machine identifier which is usually an OS process ID
198+
for (VirtualMachineDescriptor vmd : vmds) {
199+
String vmid = vmd.id();
200+
if (includeAllVMIDs || firstArg.equals(vmid)) {
201+
IPC.logMessage("add vmid = " + vmid); //$NON-NLS-1$
202+
if (vmid.equals(currentVMID) && !AttachHandler.selfAttachAllowed) {
203+
IPC.logMessage("skip self, vmid = " + vmid); //$NON-NLS-1$
204+
} else {
205+
vmids.add(vmid);
206+
}
207+
if (!includeAllVMIDs) {
208+
// exit if not include all VMIDs
209+
break;
210+
}
211+
}
212+
}
213+
} catch (NumberFormatException nfe) {
214+
// the firstArg is not 0 or a unique VMID, search for matching
215+
for (VirtualMachineDescriptor vmd : vmds) {
216+
String displayName = vmd.displayName();
217+
if ((displayName != null) && displayName.contains(firstArg)) {
218+
String vmid = vmd.id();
219+
if (vmid.equals(currentVMID) && !AttachHandler.selfAttachAllowed) {
220+
IPC.logMessage("skip self, vmid = " + vmid //$NON-NLS-1$
221+
+ " with displayName = " + displayName); //$NON-NLS-1$
222+
} else {
223+
IPC.logMessage("find a match: vmid = " + vmid //$NON-NLS-1$
224+
+ " in displayName = " + displayName); //$NON-NLS-1$
225+
vmids.add(vmid);
226+
}
227+
} else {
228+
IPC.logMessage("skip displayName = " + displayName); //$NON-NLS-1$
229+
}
230+
}
231+
}
232+
return vmids;
233+
}
188234
}

test/functional/Java8andUp/src/org/openj9/test/attachAPI/TestJcmd.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,43 @@ public void testDumpsDefaultSettings() throws IOException {
351351
}
352352
}
353353

354+
private static final String DISPLAYNAME_GC_CLASS_HISTOGRAM = "TargetVM_GC.class_histogram_all";
355+
356+
private void testDisplayNameHelper(String targetName) throws IOException {
357+
TargetManager tgt = new TargetManager(TestConstants.TARGET_VM_CLASS, null, DISPLAYNAME_GC_CLASS_HISTOGRAM,
358+
Collections.singletonList("-Xmx10M"), Collections.emptyList());
359+
tgt.syncWithTarget();
360+
String targetId = tgt.targetId;
361+
assertNotNull(targetId, ERROR_TARGET_NOT_LAUNCH);
362+
363+
List<String> args = new ArrayList<>();
364+
args.add(targetName);
365+
args.add(GC_CLASS_HISTOGRAM);
366+
args.add("all");
367+
List<String> jcmdOutput = runCommandAndLogOutput(args);
368+
String expectedString = commandExpectedOutputs.getOrDefault(GC_CLASS_HISTOGRAM,
369+
"Test error: expected output not defined");
370+
log("Expected string: " + expectedString);
371+
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
372+
assertTrue(searchResult.isPresent(), "Expected string not found: " + expectedString);
373+
log(EXPECTED_STRING_FOUND);
374+
}
375+
376+
@Test
377+
public void testMatchDisplayNameFully() throws IOException {
378+
testDisplayNameHelper(DISPLAYNAME_GC_CLASS_HISTOGRAM);
379+
}
380+
381+
@Test
382+
public void testMatchDisplayNamePartially() throws IOException {
383+
testDisplayNameHelper(DISPLAYNAME_GC_CLASS_HISTOGRAM.substring(1, DISPLAYNAME_GC_CLASS_HISTOGRAM.length() - 2));
384+
}
385+
386+
@Test
387+
public void testAllVMID() throws IOException {
388+
testDisplayNameHelper("0");
389+
}
390+
354391
@BeforeMethod
355392
protected void setUp(Method testMethod) {
356393
testName = testMethod.getName();

0 commit comments

Comments
 (0)