Skip to content

Commit

Permalink
Merge pull request #8744 from JasonFengJ9/defaultdump
Browse files Browse the repository at this point in the history
Support Jcmd dump with default settings
  • Loading branch information
pshipton committed Mar 7, 2020
2 parents e8d0351 + 48dbf74 commit 03c0d48
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*[INCLUDE-IF Sidecar18-SE]*/
/*******************************************************************************
* Copyright (c) 2019, 2019 IBM Corp. and others
* Copyright (c) 2019, 2020 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
Expand Down Expand Up @@ -47,7 +47,7 @@ public class DiagnosticUtils {

@SuppressWarnings("nls")
private static final String FILE_PATH_HELP = " <file path>%n"
+ " <file path> is mandatory: there is no default.%n"
+ " <file path> is optional, otherwise a default path/name is used."
+ " Relative paths are resolved to the target's working directory.%n"
+ " The dump agent may choose a different file path if the requested file exists.%n";

Expand Down Expand Up @@ -254,7 +254,9 @@ private static DiagnosticProperties doDump(String diagnosticCommand) {
DiagnosticProperties result = null;
String[] parts = diagnosticCommand.split(DIAGNOSTICS_OPTION_SEPARATOR);
IPC.logMessage("doDump: ", diagnosticCommand); //$NON-NLS-1$
if (parts.length != 2) {
if (parts.length == 0 || parts.length > 2) {
// The argument could be just Dump command which is going to use default configurations like -Xdump,
// or there is an optional path/name argument for the dump file generated.
result = DiagnosticProperties.makeErrorProperties("Error: wrong number of arguments"); //$NON-NLS-1$
} else {
String dumpType = ""; //$NON-NLS-1$
Expand All @@ -270,9 +272,11 @@ private static DiagnosticProperties doDump(String diagnosticCommand) {
}
}
if (!dumpType.isEmpty()) {
String fileDirective = ("system".equals(dumpType) && IPC.isZOS) ? ":dsn=" : ":file="; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

String request = dumpType + fileDirective + parts[1];
String request = dumpType;
if (parts.length == 2) {
String fileDirective = ("system".equals(dumpType) && IPC.isZOS) ? ":dsn=" : ":file="; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
request += fileDirective + parts[1];
}
try {
String actualDumpFile = triggerDumpsImpl(request, dumpType + "DumpToFile"); //$NON-NLS-1$
result = DiagnosticProperties.makeStringResult("Dump written to " + actualDumpFile); //$NON-NLS-1$
Expand Down
182 changes: 126 additions & 56 deletions test/functional/Java8andUp/src/org/openj9/test/attachAPI/TestJcmd.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2019 IBM Corp. and others
* Copyright (c) 2019, 2020 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
Expand All @@ -25,11 +25,13 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand All @@ -44,24 +46,31 @@

import static org.openj9.test.attachAPI.TargetManager.getVmId;

@SuppressWarnings("nls")
@Test(groups = { "level.extended" })
public class TestJcmd extends AttachApiTest {

private static final String EXPECTED_STRING_FOUND = "Expected string found"; //$NON-NLS-1$
private static final String EXPECTED_STRING_FOUND = "Expected string found";
private static final String JCMD_OUTPUT_START_STRING = "Dump written to ";
private static final String TARGET_DUMP_WRITTEN_STRING = " dump written to ";
private static final String ERROR_EXPECTED_STRING_NOT_FOUND = "Expected string not found";
private static final String ERROR_TARGET_NOT_LAUNCH = "target did not launch";

private static final String JCMD_COMMAND = "jcmd"; //$NON-NLS-1$
private static final String JCMD_COMMAND = "jcmd";

private static final String DUMP_HEAP = "Dump.heap"; //$NON-NLS-1$
private static final String DUMP_JAVA = "Dump.java"; //$NON-NLS-1$
private static final String DUMP_SNAP = "Dump.snap"; //$NON-NLS-1$
private static final String DUMP_SYSTEM = "Dump.system"; //$NON-NLS-1$
private static final String GC_CLASS_HISTOGRAM = "GC.class_histogram"; //$NON-NLS-1$
private static final String GC_HEAP_DUMP = "GC.heap_dump"; //$NON-NLS-1$
private static final String GC_RUN = "GC.run"; //$NON-NLS-1$
private static final String HELP_COMMAND = "help"; //$NON-NLS-1$
private static final String THREAD_PRINT = "Thread.print"; //$NON-NLS-1$
private static final String DUMP_HEAP = "Dump.heap";
private static final String DUMP_JAVA = "Dump.java";
private static final String DUMP_SNAP = "Dump.snap";
private static final String DUMP_SYSTEM = "Dump.system";
private static final String GC_CLASS_HISTOGRAM = "GC.class_histogram";
private static final String GC_HEAP_DUMP = "GC.heap_dump";
private static final String GC_RUN = "GC.run";
private static final String HELP_COMMAND = "help";
private static final String THREAD_PRINT = "Thread.print";
private static String[] JCMD_COMMANDS = {DUMP_HEAP, DUMP_JAVA, DUMP_SNAP,
DUMP_SYSTEM, GC_CLASS_HISTOGRAM, GC_HEAP_DUMP, GC_RUN, HELP_COMMAND, THREAD_PRINT};
DUMP_SYSTEM, GC_CLASS_HISTOGRAM, GC_HEAP_DUMP, GC_RUN, HELP_COMMAND, THREAD_PRINT};
private static String[] JCMD_COMMANDS_REQUIRE_OPTION = {GC_CLASS_HISTOGRAM, GC_RUN, HELP_COMMAND, THREAD_PRINT};
private static String[] JCMD_COMMANDS_DUMP = {DUMP_HEAP, DUMP_JAVA, DUMP_SNAP, DUMP_SYSTEM, GC_HEAP_DUMP};

/*
* Contains strings expected to be contained in the outputs of various commands
Expand All @@ -77,14 +86,13 @@ public class TestJcmd extends AttachApiTest {
*/
@Test
public void testJcmdHelps() throws IOException {
@SuppressWarnings("nls")
String[] HELP_OPTIONS = { "-h", HELP_COMMAND, "-help", "--help" };
for (String helpOption : HELP_OPTIONS) {
List<String> jcmdOutput = runCommandAndLogOutput(Collections.singletonList(helpOption));
/* Sample of the text from Jcmd.HELPTEXT */
String expectedString = "list JVM processes on the local machine."; //$NON-NLS-1$
String expectedString = "list JVM processes on the local machine.";
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Help text corrupt: " + jcmdOutput); //$NON-NLS-1$
assertTrue(searchResult.isPresent(), "Help text corrupt: " + jcmdOutput);
}
}

Expand All @@ -100,10 +108,10 @@ public void testCommandHelps() throws IOException {
args.add(HELP_COMMAND);
args.add(command);
List<String> jcmdOutput = runCommandAndLogOutput(args);
String expectedString = command + ":"; //$NON-NLS-1$
log("Expected string: " + expectedString); //$NON-NLS-1$
String expectedString = command + ":";
log("Expected string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Help text corrupt: " + jcmdOutput); //$NON-NLS-1$
assertTrue(searchResult.isPresent(), "Help text corrupt: " + jcmdOutput);
log(EXPECTED_STRING_FOUND);
}
}
Expand All @@ -114,34 +122,50 @@ public void testListVms() throws IOException {
List<String> args = new ArrayList<>();
List<String> jcmdOutput = runCommandAndLogOutput(args);
Optional<String> searchResult = StringUtilities.searchSubstring(myId, jcmdOutput);
String errorMessage = "My VMID missing from VM list"; //$NON-NLS-1$
String errorMessage = "My VMID missing from VM list";
assertTrue(searchResult.isPresent(), errorMessage);
args.add("-l"); //$NON-NLS-1$
args.add("-l");
jcmdOutput = runCommandAndLogOutput(args);
searchResult = StringUtilities.searchSubstring(myId, jcmdOutput);
assertTrue(searchResult.isPresent(), errorMessage + jcmdOutput);
log(EXPECTED_STRING_FOUND);
}

@Test
public void testCommandNoOptions() throws IOException {
for (String command : JCMD_COMMANDS) {
public void testCommandWrongArgumentNumber() throws IOException {
for (String command : JCMD_COMMANDS_DUMP) {
List<String> args = new ArrayList<>();
args.add(getVmId());
args.add(command);
args.add(new File(userDir, "my" + command + "Dump").getAbsolutePath());
args.add("wrongargument");
List<String> jcmdOutput = runCommandAndLogOutput(args);
String expectedString = commandExpectedOutputs.getOrDefault(command, "Test error: expected output not defined"); //$NON-NLS-1$
log("Expected string: " + expectedString); //$NON-NLS-1$
String expectedString = commandExpectedOutputs.getOrDefault(command, "Test error: expected output not defined");
log("Expected string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Expected string \"" + expectedString + "\" not found"); //$NON-NLS-1$ //$NON-NLS-2$
assertTrue(searchResult.isPresent(), "Expected string \"" + expectedString + "\" not found");
log(EXPECTED_STRING_FOUND);
}
}

@Test
public void testNoCommandOptions() throws IOException {
for (String command : JCMD_COMMANDS_REQUIRE_OPTION) {
List<String> args = new ArrayList<>();
args.add(getVmId());
args.add(command);
List<String> jcmdOutput = runCommandAndLogOutput(args);
String expectedString = commandExpectedOutputs.getOrDefault(command, "Test error: expected output not defined");
log("Expected string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Expected string \"" + expectedString + "\" not found");
log(EXPECTED_STRING_FOUND);
}
}

@Test
public void testThreadPrint() throws IOException {
String LockedSyncsOption = "-l"; //$NON-NLS-1$
@SuppressWarnings("nls")
String LockedSyncsOption = "-l";
String[] options = {"", LockedSyncsOption, LockedSyncsOption + "=true", LockedSyncsOption + "=false"};
for (String option : options) {
List<String> args = new ArrayList<>();
Expand All @@ -150,12 +174,12 @@ public void testThreadPrint() throws IOException {
if (!option.isEmpty()) {
args.add(option);
}
boolean addSynchronizers = !option.endsWith("false") && !option.isEmpty(); //$NON-NLS-1$
boolean addSynchronizers = !option.endsWith("false") && !option.isEmpty();
List<String> jcmdOutput = runCommandAndLogOutput(args);
String expectedString = "Locked ownable synchronizers"; //$NON-NLS-1$
log("Expected string: " + expectedString); //$NON-NLS-1$
String expectedString = "Locked ownable synchronizers";
log("Expected string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertEquals(searchResult.isPresent(), addSynchronizers, "Output contains locked synchronizer information: " + expectedString); //$NON-NLS-1$
assertEquals(searchResult.isPresent(), addSynchronizers, "Output contains locked synchronizer information: " + expectedString);
log(EXPECTED_STRING_FOUND);
}
}
Expand All @@ -165,75 +189,121 @@ public void testClassHistogramAll() throws IOException {
List<String> args = new ArrayList<>();
args.add(getVmId());
args.add(GC_CLASS_HISTOGRAM);
args.add("all"); //$NON-NLS-1$
args.add("all");
List<String> jcmdOutput = runCommandAndLogOutput(args);
String expectedString = commandExpectedOutputs.getOrDefault(GC_CLASS_HISTOGRAM, "Test error: expected output not defined"); //$NON-NLS-1$
log("Expected string: " + expectedString); //$NON-NLS-1$
String expectedString = commandExpectedOutputs.getOrDefault(GC_CLASS_HISTOGRAM, "Test error: expected output not defined");
log("Expected string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Expected string not found: " + expectedString); //$NON-NLS-1$
assertTrue(searchResult.isPresent(), "Expected string not found: " + expectedString);
log(EXPECTED_STRING_FOUND);
}

@Test
public void testDumps() throws IOException {
@SuppressWarnings("nls")
String[][] commandsAndDumpTypes = {{DUMP_HEAP, "Heap"}, {GC_HEAP_DUMP, "Heap"}, {DUMP_JAVA, "Java"}, {DUMP_SNAP, "Snap"}, {DUMP_SYSTEM, "System"}};
for (String[] commandAndDumpName : commandsAndDumpTypes) {
TargetManager tgt = new TargetManager(TestConstants.TARGET_VM_CLASS, null,
Collections.emptyList(), Collections.singletonList("-Xmx10M")); //$NON-NLS-1$
Collections.singletonList("-Xmx10M"), Collections.emptyList());
tgt.syncWithTarget();
String targetId = tgt.targetId;
assertNotNull(targetId, "target did not launch"); //$NON-NLS-1$
assertNotNull(targetId, ERROR_TARGET_NOT_LAUNCH);
String dumpType = commandAndDumpName[1];
@SuppressWarnings("nls")
File dumpFileLocation = new File(userDir, "my" + dumpType + "Dump");
dumpFileLocation.delete();
try {
String dumpFilePath = dumpFileLocation.getAbsolutePath();
List<String> args = new ArrayList<>();
args.add(targetId);
String command = commandAndDumpName[0];
log("test " + command); //$NON-NLS-1$
log("test " + command);
args.add(command);
args.add(dumpFilePath);
List<String> jcmdOutput = runCommandAndLogOutput(args);

assertTrue(dumpFileLocation.exists(), "dump file " + dumpFilePath + "missing"); //$NON-NLS-1$//$NON-NLS-2$
String expectedString = "Dump written to " + dumpFilePath; //$NON-NLS-1$
log("Expected jcmd output string: " + expectedString); //$NON-NLS-1$
assertTrue(dumpFileLocation.exists(), "dump file " + dumpFilePath + "missing");
String expectedString = JCMD_OUTPUT_START_STRING + dumpFilePath;
log("Expected jcmd output string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
assertTrue(searchResult.isPresent(), "Expected string not found in jcmd output: " + expectedString); //$NON-NLS-1$
log(EXPECTED_STRING_FOUND + " in jcmd output: " + expectedString); //$NON-NLS-1$
assertTrue(searchResult.isPresent(), ERROR_EXPECTED_STRING_NOT_FOUND + " in jcmd output: " + expectedString);
log(EXPECTED_STRING_FOUND + " in jcmd output: " + expectedString);

expectedString = dumpType + " dump written to " + dumpFilePath; //$NON-NLS-1$
expectedString = dumpType + TARGET_DUMP_WRITTEN_STRING + dumpFilePath;
searchResult = StringUtilities.searchSubstring(expectedString,tgt.getTargetErrReader().lines());
assertTrue(searchResult.isPresent(), "Expected string not found in target standard error: " + expectedString); //$NON-NLS-1$
log(EXPECTED_STRING_FOUND + " in target standard error: " + expectedString); //$NON-NLS-1$
assertTrue(searchResult.isPresent(), ERROR_EXPECTED_STRING_NOT_FOUND + " in target standard error: " + expectedString);
log(EXPECTED_STRING_FOUND + " in target standard error: " + expectedString);
} finally {
tgt.terminateTarget();
dumpFileLocation.delete();
}
}
}

static void cleanupFile(String output) {
if (output.indexOf(JCMD_OUTPUT_START_STRING) != -1) {
String filePathName = output.substring(JCMD_OUTPUT_START_STRING.length()).trim();
if (new File(filePathName).delete()) {
System.out.println("Deleted the temparary dump file : " + filePathName);
} else {
fail("Failed to delete the temparary dump file : " + filePathName);
}
}
}

@Test
public void testDumpsDefaultSettings() throws IOException {
String[][] commandsAndDefaultDumpNames = {{DUMP_HEAP, "heapdump"}, {GC_HEAP_DUMP, "heapdump"}, {DUMP_JAVA, "javacore"}, {DUMP_SNAP, "Snap"}, {DUMP_SYSTEM, "core"}};
for (String[] commandAndDumpName : commandsAndDefaultDumpNames) {
TargetManager tgt = new TargetManager(TestConstants.TARGET_VM_CLASS, null,
Collections.singletonList("-Xmx10M"), Collections.emptyList());
tgt.syncWithTarget();
String targetId = tgt.targetId;
assertNotNull(targetId, ERROR_TARGET_NOT_LAUNCH);
String defaultDumpName = commandAndDumpName[1];
List<String> jcmdOutput = null;
try {
List<String> args = new ArrayList<>();
args.add(targetId);
String command = commandAndDumpName[0];
log("test " + command);
args.add(command);
jcmdOutput = runCommandAndLogOutput(args);

String expectedDefaultDumpName = String.format("%1$s.%2$tY%2$tm%2$td", defaultDumpName, Calendar.getInstance());
Optional<String> searchResult = StringUtilities.searchTwoSubstrings(JCMD_OUTPUT_START_STRING, expectedDefaultDumpName, jcmdOutput);
assertTrue(searchResult.isPresent(), ERROR_EXPECTED_STRING_NOT_FOUND + " in jcmd output: " + JCMD_OUTPUT_START_STRING + " AND " + expectedDefaultDumpName);
log(EXPECTED_STRING_FOUND + " in jcmd output: " + JCMD_OUTPUT_START_STRING + " AND " + expectedDefaultDumpName);

searchResult = StringUtilities.searchTwoSubstrings(TARGET_DUMP_WRITTEN_STRING, expectedDefaultDumpName, tgt.getTargetErrReader().lines());
assertTrue(searchResult.isPresent(), ERROR_EXPECTED_STRING_NOT_FOUND + " in target standard error: " +
JCMD_OUTPUT_START_STRING + " AND " + expectedDefaultDumpName);
log(EXPECTED_STRING_FOUND + " in target standard error: " + JCMD_OUTPUT_START_STRING + " AND " + expectedDefaultDumpName);
} finally {
tgt.terminateTarget();
if (jcmdOutput != null) {
jcmdOutput.forEach(TestJcmd::cleanupFile);
}
}
}
}

@BeforeMethod
protected void setUp(Method testMethod) {
testName = testMethod.getName();
log("------------------------------------\nstarting " + testName); //$NON-NLS-1$
log("------------------------------------\nstarting " + testName);
}

@BeforeSuite
protected void setupSuite() {
assertTrue(PlatformInfo.isOpenJ9(), "This test is valid only on OpenJ9"); //$NON-NLS-1$
userDir = new File(System.getProperty("user.dir")); //$NON-NLS-1$
assertTrue(PlatformInfo.isOpenJ9(), "This test is valid only on OpenJ9");
userDir = new File(System.getProperty("user.dir"));
getJdkUtilityPath(JCMD_COMMAND);
commandExpectedOutputs = new HashMap<>();
commandExpectedOutputs.put(HELP_COMMAND, THREAD_PRINT);
commandExpectedOutputs.put(GC_CLASS_HISTOGRAM, "java.util.HashMap"); //$NON-NLS-1$
commandExpectedOutputs.put(GC_RUN, "Command succeeded"); //$NON-NLS-1$
commandExpectedOutputs.put(THREAD_PRINT, "Attach API wait loop"); //$NON-NLS-1$
commandExpectedOutputs.put(GC_CLASS_HISTOGRAM, "java.util.HashMap");
commandExpectedOutputs.put(GC_RUN, "Command succeeded");
commandExpectedOutputs.put(THREAD_PRINT, "Attach API wait loop");
/* add the expected outputs for dump commands with no arguments */
String WRONG_NUMBER_OF_ARGUMENTS = "Error: wrong number of arguments"; //$NON-NLS-1$
String WRONG_NUMBER_OF_ARGUMENTS = "Error: wrong number of arguments";
commandExpectedOutputs.put(DUMP_HEAP, WRONG_NUMBER_OF_ARGUMENTS);
commandExpectedOutputs.put(GC_HEAP_DUMP, WRONG_NUMBER_OF_ARGUMENTS);
commandExpectedOutputs.put(DUMP_JAVA, WRONG_NUMBER_OF_ARGUMENTS);
Expand Down
Loading

0 comments on commit 03c0d48

Please sign in to comment.