8248194: Need better support for running SA tests on core files
Reviewed-by: amenkov, lmesnik
This commit is contained in:
parent
f243b281ea
commit
db2d4e8f5a
@ -27,7 +27,6 @@
|
||||
* @summary Test the clhsdb commands 'printmdo', 'printall', 'jstack' on a CDS enabled corefile.
|
||||
* @requires vm.cds
|
||||
* @requires vm.hasSA
|
||||
* @requires os.family != "windows"
|
||||
* @requires vm.flavor == "server"
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
@ -37,16 +36,12 @@
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
@ -56,7 +51,7 @@ import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.SA.SATestUtils;
|
||||
import jdk.test.lib.util.CoreUtils;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
|
||||
@ -67,12 +62,8 @@ class CrashApp {
|
||||
}
|
||||
|
||||
public class ClhsdbCDSCore {
|
||||
|
||||
private static final String TEST_CDS_CORE_FILE_NAME = "cds_core_file";
|
||||
private static final String LOCATIONS_STRING = "location: ";
|
||||
private static final String RUN_SHELL_NO_LIMIT = "ulimit -c unlimited && ";
|
||||
private static final String SHARED_ARCHIVE_NAME = "ArchiveForClhsdbCDSCore.jsa";
|
||||
private static final String CORE_PATTERN_FILE_NAME = "/proc/sys/kernel/core_pattern";
|
||||
private static String coreFileName;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("Starting ClhsdbCDSCore test");
|
||||
@ -93,60 +84,23 @@ public class ClhsdbCDSCore {
|
||||
CrashApp.class.getName()
|
||||
};
|
||||
|
||||
OutputAnalyzer crashOut;
|
||||
OutputAnalyzer crashOutput;
|
||||
try {
|
||||
List<String> options = new ArrayList<>();
|
||||
options.addAll(Arrays.asList(jArgs));
|
||||
crashOut =
|
||||
ProcessTools.executeProcess(getTestJvmCommandlineWithPrefix(
|
||||
RUN_SHELL_NO_LIMIT, options.toArray(new String[0])));
|
||||
ProcessBuilder pb = ProcessTools.createTestJvm(options);
|
||||
// Add "ulimit -c unlimited" if we can since we are generating a core file.
|
||||
pb = CoreUtils.addCoreUlimitCommand(pb);
|
||||
crashOutput = ProcessTools.executeProcess(pb);
|
||||
} catch (Throwable t) {
|
||||
throw new Error("Can't execute the java cds process.", t);
|
||||
}
|
||||
|
||||
System.out.println(crashOut.getOutput());
|
||||
String crashOutputString = crashOut.getOutput();
|
||||
SATestUtils.unzipCores(new File("."));
|
||||
String coreFileLocation = getCoreFileLocation(crashOutputString);
|
||||
if (coreFileLocation == null) {
|
||||
if (Platform.isOSX()) {
|
||||
File coresDir = new File("/cores");
|
||||
if (!coresDir.isDirectory()) {
|
||||
cleanup();
|
||||
throw new Error(coresDir + " is not a directory");
|
||||
}
|
||||
// the /cores directory is usually not writable on macOS 10.15
|
||||
if (!coresDir.canWrite()) {
|
||||
cleanup();
|
||||
throw new SkippedException("Directory \"" + coresDir +
|
||||
"\" is not writable");
|
||||
}
|
||||
} else if (Platform.isLinux()) {
|
||||
// Check if a crash report tool is installed.
|
||||
File corePatternFile = new File(CORE_PATTERN_FILE_NAME);
|
||||
try (Scanner scanner = new Scanner(corePatternFile)) {
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
line = line.trim();
|
||||
System.out.println(line);
|
||||
if (line.startsWith("|")) {
|
||||
System.out.println(
|
||||
"\nThis system uses a crash report tool ($cat /proc/sys/kernel/core_pattern).\n" +
|
||||
"Core files might not be generated. Please reset /proc/sys/kernel/core_pattern\n" +
|
||||
"to enable core generation. Skipping this test.");
|
||||
cleanup();
|
||||
throw new SkippedException("This system uses a crash report tool");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error("Couldn't find core file location in: '" + crashOutputString + "'");
|
||||
}
|
||||
try {
|
||||
Asserts.assertGT(new File(coreFileLocation).length(), 0L, "Unexpected core size");
|
||||
Files.move(Paths.get(coreFileLocation), Paths.get(TEST_CDS_CORE_FILE_NAME));
|
||||
} catch (IOException ioe) {
|
||||
throw new Error("Can't move core file: " + ioe, ioe);
|
||||
coreFileName = CoreUtils.getCoreFileLocation(crashOutput.getStdout());
|
||||
} catch (Exception e) {
|
||||
cleanup();
|
||||
throw e;
|
||||
}
|
||||
|
||||
ClhsdbLauncher test = new ClhsdbLauncher();
|
||||
@ -154,8 +108,7 @@ public class ClhsdbCDSCore {
|
||||
// Ensure that UseSharedSpaces is turned on.
|
||||
List<String> cmds = List.of("flags UseSharedSpaces");
|
||||
|
||||
String useSharedSpacesOutput = test.runOnCore(TEST_CDS_CORE_FILE_NAME, cmds,
|
||||
null, null);
|
||||
String useSharedSpacesOutput = test.runOnCore(coreFileName, cmds, null, null);
|
||||
|
||||
if (useSharedSpacesOutput == null) {
|
||||
// Output could be null due to attach permission issues.
|
||||
@ -200,7 +153,7 @@ public class ClhsdbCDSCore {
|
||||
"Method*"));
|
||||
unExpStrMap.put("jstack -v", List.of(
|
||||
"sun.jvm.hotspot.debugger.UnmappedAddressException"));
|
||||
test.runOnCore(TEST_CDS_CORE_FILE_NAME, cmds, expStrMap, unExpStrMap);
|
||||
test.runOnCore(coreFileName, cmds, expStrMap, unExpStrMap);
|
||||
} catch (SkippedException e) {
|
||||
throw e;
|
||||
} catch (Exception ex) {
|
||||
@ -210,60 +163,8 @@ public class ClhsdbCDSCore {
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
|
||||
// lets search for a few possible locations using process output and return existing location
|
||||
private static String getCoreFileLocation(String crashOutputString) {
|
||||
Asserts.assertTrue(crashOutputString.contains(LOCATIONS_STRING),
|
||||
"Output doesn't contain the location of core file.");
|
||||
String stringWithLocation = Arrays.stream(crashOutputString.split("\\r?\\n"))
|
||||
.filter(str -> str.contains(LOCATIONS_STRING))
|
||||
.findFirst()
|
||||
.get();
|
||||
stringWithLocation = stringWithLocation.substring(stringWithLocation
|
||||
.indexOf(LOCATIONS_STRING) + LOCATIONS_STRING.length());
|
||||
System.out.println("getCoreFileLocation found stringWithLocation = " + stringWithLocation);
|
||||
String coreWithPid;
|
||||
if (stringWithLocation.contains("or ")) {
|
||||
Matcher m = Pattern.compile("or.* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation);
|
||||
if (!m.find()) {
|
||||
throw new Error("Couldn't find path to core inside location string");
|
||||
}
|
||||
coreWithPid = m.group(1);
|
||||
} else {
|
||||
coreWithPid = stringWithLocation.trim();
|
||||
}
|
||||
if (new File(coreWithPid).exists()) {
|
||||
return coreWithPid;
|
||||
}
|
||||
String justCore = Paths.get("core").toString();
|
||||
if (new File(justCore).exists()) {
|
||||
return justCore;
|
||||
}
|
||||
Path coreWithPidPath = Paths.get(coreWithPid);
|
||||
String justFile = coreWithPidPath.getFileName().toString();
|
||||
if (new File(justFile).exists()) {
|
||||
return justFile;
|
||||
}
|
||||
Path parent = coreWithPidPath.getParent();
|
||||
if (parent != null) {
|
||||
String coreWithoutPid = parent.resolve("core").toString();
|
||||
if (new File(coreWithoutPid).exists()) {
|
||||
return coreWithoutPid;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String[] getTestJvmCommandlineWithPrefix(String prefix, String... args) {
|
||||
try {
|
||||
String cmd = ProcessTools.getCommandLine(ProcessTools.createTestJvm(args));
|
||||
return new String[]{"sh", "-c", prefix + cmd};
|
||||
} catch (Throwable t) {
|
||||
throw new Error("Can't create process builder: " + t, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void cleanup() {
|
||||
remove(TEST_CDS_CORE_FILE_NAME);
|
||||
if (coreFileName != null) remove(coreFileName);
|
||||
remove(SHARED_ARCHIVE_NAME);
|
||||
}
|
||||
|
||||
|
@ -27,38 +27,63 @@ import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jdk.test.lib.apps.LingeredApp;
|
||||
import jdk.test.lib.util.CoreUtils;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8193124
|
||||
* @summary Test the clhsdb 'findpc' command
|
||||
* @summary Test the clhsdb 'findpc' command with Xcomp on live process
|
||||
* @requires vm.hasSA
|
||||
* @requires vm.compiler1.enabled
|
||||
* @requires vm.opt.DeoptimizeALot != true
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC true
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC true false
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8193124
|
||||
* @summary Test the clhsdb 'findpc' command
|
||||
* @summary Test the clhsdb 'findpc' command with Xcomp on core file
|
||||
* @requires vm.compMode != "Xcomp"
|
||||
* @requires vm.hasSA
|
||||
* @requires vm.compiler1.enabled
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC false
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC true true
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8193124
|
||||
* @summary Test the clhsdb 'findpc' command w/o Xcomp on live process
|
||||
* @requires vm.hasSA
|
||||
* @requires vm.compiler1.enabled
|
||||
* @requires vm.opt.DeoptimizeALot != true
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC false false
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8193124
|
||||
* @summary Test the clhsdb 'findpc' command w/o Xcomp on core file
|
||||
* @requires vm.compMode != "Xcomp"
|
||||
* @requires vm.hasSA
|
||||
* @requires vm.compiler1.enabled
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=480 ClhsdbFindPC false true
|
||||
*/
|
||||
|
||||
public class ClhsdbFindPC {
|
||||
|
||||
private static void testFindPC(boolean withXcomp) throws Exception {
|
||||
private static void testFindPC(boolean withXcomp, boolean withCore) throws Exception {
|
||||
LingeredApp theApp = null;
|
||||
String coreFileName = null;
|
||||
try {
|
||||
ClhsdbLauncher test = new ClhsdbLauncher();
|
||||
|
||||
theApp = new LingeredAppWithTrivialMain();
|
||||
theApp.setForceCrash(withCore);
|
||||
if (withXcomp) {
|
||||
LingeredApp.startApp(theApp, "-Xcomp");
|
||||
} else {
|
||||
@ -72,27 +97,41 @@ public class ClhsdbFindPC {
|
||||
}
|
||||
System.out.println("with pid " + theApp.getPid());
|
||||
|
||||
// Run 'jstack -v' command to get the pc
|
||||
// Get the core file name if we are debugging a core instead of live process
|
||||
if (withCore) {
|
||||
coreFileName = CoreUtils.getCoreFileLocation(theApp.getOutput().getStdout());
|
||||
}
|
||||
|
||||
// Run 'jstack -v' command to get the findpc address
|
||||
List<String> cmds = List.of("jstack -v");
|
||||
String output = test.run(theApp.getPid(), cmds, null, null);
|
||||
String output;
|
||||
if (withCore) {
|
||||
output = test.runOnCore(coreFileName, cmds, null, null);
|
||||
} else {
|
||||
output = test.run(theApp.getPid(), cmds, null, null);
|
||||
}
|
||||
|
||||
// Test the 'findpc' command passing in the pc obtained from
|
||||
// the 'jstack -v' command
|
||||
cmds = new ArrayList<String>();
|
||||
|
||||
String cmdStr = null;
|
||||
// Extract pc address from the following line:
|
||||
// - LingeredAppWithTrivialMain.main(java.lang.String[]) @bci=1, line=33, pc=0x00007ff18ff519f0, ...
|
||||
String pcAddress = null;
|
||||
String[] parts = output.split("LingeredAppWithTrivialMain.main");
|
||||
String[] tokens = parts[1].split(" ");
|
||||
for (String token : tokens) {
|
||||
if (token.contains("pc")) {
|
||||
String[] address = token.split("=");
|
||||
// address[1] represents the address of the Method
|
||||
cmdStr = "findpc " + address[1].replace(",","");
|
||||
cmds.add(cmdStr);
|
||||
String[] addresses = token.split("=");
|
||||
// addresses[1] represents the address of the Method
|
||||
pcAddress = addresses[1].replace(",","");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pcAddress == null) {
|
||||
throw new RuntimeException("Cannot find LingeredAppWithTrivialMain.main pc in output");
|
||||
}
|
||||
|
||||
// Test the 'findpc' command passing in the pc obtained from above
|
||||
cmds = new ArrayList<String>();
|
||||
String cmdStr = "findpc " + pcAddress;
|
||||
cmds.add(cmdStr);
|
||||
Map<String, List<String>> expStrMap = new HashMap<>();
|
||||
if (withXcomp) {
|
||||
expStrMap.put(cmdStr, List.of(
|
||||
@ -105,20 +144,27 @@ public class ClhsdbFindPC {
|
||||
"In interpreter codelet"));
|
||||
}
|
||||
|
||||
test.run(theApp.getPid(), cmds, expStrMap, null);
|
||||
if (withCore) {
|
||||
test.runOnCore(coreFileName, cmds, expStrMap, null);
|
||||
} else {
|
||||
test.run(theApp.getPid(), cmds, expStrMap, null);
|
||||
}
|
||||
} catch (SkippedException se) {
|
||||
throw se;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Test ERROR " + ex, ex);
|
||||
} finally {
|
||||
LingeredApp.stopApp(theApp);
|
||||
if (!withCore) {
|
||||
LingeredApp.stopApp(theApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
boolean xComp = Boolean.parseBoolean(args[0]);
|
||||
boolean withXcomp = Boolean.parseBoolean(args[0]);
|
||||
boolean withCore = Boolean.parseBoolean(args[1]);
|
||||
System.out.println("Starting the ClhsdbFindPC test");
|
||||
testFindPC(xComp);
|
||||
testFindPC(withXcomp, withCore);
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
}
|
||||
|
@ -40,20 +40,16 @@ import jdk.test.lib.classloader.GeneratingClassLoader;
|
||||
import jdk.test.lib.hprof.HprofParser;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.SA.SATestUtils;
|
||||
import jdk.test.lib.util.CoreUtils;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
public class TestJmapCore {
|
||||
static final String pidSeparator = ":KILLED_PID";
|
||||
|
||||
public static final String HEAP_OOME = "heap";
|
||||
public static final String METASPACE_OOME = "metaspace";
|
||||
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (args.length == 1) {
|
||||
// If 1 argument is set prints pid so main process could find corefile
|
||||
System.out.println(ProcessHandle.current().pid() + pidSeparator);
|
||||
try {
|
||||
if (args[0].equals(HEAP_OOME)) {
|
||||
Object[] oa = new Object[Integer.MAX_VALUE / 2];
|
||||
@ -74,50 +70,17 @@ public class TestJmapCore {
|
||||
test(args[1]);
|
||||
}
|
||||
|
||||
// Test tries to run java with ulimit unlimited if it is possible
|
||||
static boolean useDefaultUlimit() {
|
||||
if (Platform.isWindows()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
OutputAnalyzer output = ProcessTools.executeProcess("sh", "-c", "ulimit -c unlimited && ulimit -c");
|
||||
return !(output.getExitValue() == 0 && output.getStdout().contains("unlimited"));
|
||||
} catch (Throwable t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void test(String type) throws Throwable {
|
||||
ProcessBuilder pb = ProcessTools.createTestJvm("-XX:+CreateCoredumpOnCrash",
|
||||
"-Xmx512m", "-XX:MaxMetaspaceSize=64m", "-XX:+CrashOnOutOfMemoryError",
|
||||
TestJmapCore.class.getName(), type);
|
||||
|
||||
boolean useDefaultUlimit = useDefaultUlimit();
|
||||
System.out.println("Run test with ulimit: " + (useDefaultUlimit ? "default" : "unlimited"));
|
||||
OutputAnalyzer output = useDefaultUlimit
|
||||
? ProcessTools.executeProcess(pb)
|
||||
: ProcessTools.executeProcess("sh", "-c", "ulimit -c unlimited && "
|
||||
+ ProcessTools.getCommandLine(pb));
|
||||
File pwd = new File(".");
|
||||
SATestUtils.unzipCores(pwd);
|
||||
File core;
|
||||
String pattern = Platform.isWindows() ? ".*\\.mdmp" : "core(\\.\\d+)?";
|
||||
File[] cores = pwd.listFiles((dir, name) -> name.matches(pattern));
|
||||
if (cores.length == 0) {
|
||||
// /cores/core.$pid might be generated on macosx by default
|
||||
String pid = output.firstMatch("^(\\d+)" + pidSeparator, 1);
|
||||
core = new File("cores/core." + pid);
|
||||
if (!core.exists()) {
|
||||
throw new SkippedException("Has not been able to find coredump");
|
||||
}
|
||||
} else {
|
||||
Asserts.assertTrue(cores.length == 1,
|
||||
"There are unexpected files containing core "
|
||||
+ ": " + String.join(",", pwd.list()) + ".");
|
||||
core = cores[0];
|
||||
}
|
||||
System.out.println("Found corefile: " + core.getAbsolutePath());
|
||||
// If we are going to force a core dump, apply "ulimit -c unlimited" if we can.
|
||||
pb = CoreUtils.addCoreUlimitCommand(pb);
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
|
||||
String coreFileName = CoreUtils.getCoreFileLocation(output.getStdout());
|
||||
File core = new File(coreFileName);
|
||||
File dumpFile = new File("heap.hprof");
|
||||
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
|
||||
launcher.addVMArgs(Utils.getTestJavaOpts());
|
||||
|
@ -26,7 +26,6 @@ import jdk.test.lib.JDKToolLauncher;
|
||||
import jdk.test.lib.Platform;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
@ -35,9 +34,8 @@ import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class SATestUtils {
|
||||
/**
|
||||
@ -209,17 +207,4 @@ public class SATestUtils {
|
||||
// Otherwise expect to be permitted:
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void unzipCores(File dir) {
|
||||
File[] gzCores = dir.listFiles((directory, name) -> name.matches("core(\\.\\d+)?\\.gz"));
|
||||
for (File gzCore : gzCores) {
|
||||
String coreFileName = gzCore.getName().replace(".gz", "");
|
||||
System.out.println("Unzipping core into " + coreFileName);
|
||||
try (GZIPInputStream gzis = new GZIPInputStream(Files.newInputStream(gzCore.toPath()))) {
|
||||
Files.copy(gzis, Paths.get(coreFileName));
|
||||
} catch (IOException e) {
|
||||
throw new SkippedException("Not able to unzip file: " + gzCore.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import jdk.test.lib.JDKToolFinder;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.process.OutputBuffer;
|
||||
import jdk.test.lib.process.StreamPumper;
|
||||
import jdk.test.lib.util.CoreUtils;
|
||||
|
||||
/**
|
||||
* This is a framework to launch an app that could be synchronized with caller
|
||||
@ -93,6 +94,8 @@ public class LingeredApp {
|
||||
protected static final int appWaitTime = 100;
|
||||
protected final String lockFileName;
|
||||
|
||||
protected boolean forceCrash = false; // set true to force a crash and core file
|
||||
|
||||
/**
|
||||
* Create LingeredApp object on caller side. Lock file have be a valid filename
|
||||
* at writable location
|
||||
@ -108,6 +111,12 @@ public class LingeredApp {
|
||||
this.lockFileName = lockName;
|
||||
}
|
||||
|
||||
public void setForceCrash(boolean forceCrash) {
|
||||
this.forceCrash = forceCrash;
|
||||
}
|
||||
|
||||
native private static int crash();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return name of lock file
|
||||
@ -263,7 +272,11 @@ public class LingeredApp {
|
||||
|
||||
// Make sure process didn't already exit
|
||||
if (!appProcess.isAlive()) {
|
||||
throw new IOException("App exited unexpectedly with " + appProcess.exitValue());
|
||||
if (forceCrash) {
|
||||
return; // This is expected. Just return.
|
||||
} else {
|
||||
throw new IOException("App exited unexpectedly with " + appProcess.exitValue());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -289,6 +302,11 @@ public class LingeredApp {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add(JDKToolFinder.getTestJDKTool("java"));
|
||||
Collections.addAll(cmd, vmArguments);
|
||||
if (forceCrash) {
|
||||
cmd.add("-XX:+CreateCoredumpOnCrash");
|
||||
// We need to find libLingeredApp.so for the crash() native method
|
||||
cmd.add("-Djava.library.path=" + System.getProperty("java.library.path"));
|
||||
}
|
||||
|
||||
// Make sure we set correct classpath to run the app
|
||||
cmd.add("-cp");
|
||||
@ -329,10 +347,17 @@ public class LingeredApp {
|
||||
|
||||
runAddAppName(cmd);
|
||||
cmd.add(lockFileName);
|
||||
if (forceCrash) {
|
||||
cmd.add("forceCrash"); // Let the subprocess know to force a crash
|
||||
}
|
||||
|
||||
printCommandLine(cmd);
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(cmd);
|
||||
if (forceCrash) {
|
||||
// If we are going to force a core dump, apply "ulimit -c unlimited" if we can.
|
||||
pb = CoreUtils.addCoreUlimitCommand(pb);
|
||||
}
|
||||
// ProcessBuilder.start can throw IOException
|
||||
appProcess = pb.start();
|
||||
|
||||
@ -470,19 +495,37 @@ public class LingeredApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* This part is the application it self
|
||||
* This part is the application itself. First arg is optional "forceCrash".
|
||||
* Following arg is the lock file name.
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
boolean forceCrash = false;
|
||||
|
||||
if (args.length != 1) {
|
||||
if (args.length == 0) {
|
||||
System.err.println("Lock file name is not specified");
|
||||
System.exit(7);
|
||||
} else if (args.length > 2) {
|
||||
System.err.println("Too many arguments specified: " + args.length);
|
||||
System.exit(7);
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
if (args[1].equals("forceCrash")) {
|
||||
forceCrash = true;
|
||||
} else {
|
||||
System.err.println("Invalid 1st argment: " + args[1]);
|
||||
System.exit(7);
|
||||
}
|
||||
}
|
||||
|
||||
String theLockFileName = args[0];
|
||||
Path path = Paths.get(theLockFileName);
|
||||
|
||||
try {
|
||||
if (forceCrash) {
|
||||
System.loadLibrary("LingeredApp"); // location of native crash() method
|
||||
crash();
|
||||
}
|
||||
while (Files.exists(path)) {
|
||||
// Touch the lock to indicate our readiness
|
||||
setLastModified(theLockFileName, epoch());
|
||||
|
@ -23,12 +23,20 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
/*
|
||||
* Class: jdk_test_lib_apps_LingeredApp
|
||||
* Method: crashMe
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_jdk_test_lib_apps_LingeredApp_crashMe(JNIEnv *env, jclass klass) {
|
||||
*((volatile int*)(1)) = 1;
|
||||
// Borrowed from hotspot vmError.cpp.
|
||||
// Returns an address which is guaranteed to generate a SIGSEGV on read,
|
||||
// which is not NULL and contains bits in every word
|
||||
void* get_segfault_address() {
|
||||
return (void*)
|
||||
#ifdef _LP64
|
||||
0xABC0000000000ABCULL;
|
||||
#else
|
||||
0x00000ABC;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_jdk_test_lib_apps_LingeredApp_crash(JNIEnv *env, jclass clss)
|
||||
{
|
||||
return *(jint *)get_segfault_address();
|
||||
}
|
||||
|
216
test/lib/jdk/test/lib/util/CoreUtils.java
Normal file
216
test/lib/jdk/test/lib/util/CoreUtils.java
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.test.lib.util;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.Scanner;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class CoreUtils {
|
||||
|
||||
private static final String RUN_SHELL_NO_LIMIT = "ulimit -c unlimited && ";
|
||||
|
||||
/**
|
||||
* Returns a {@code ulimit} command that will allow for an unlimited core file size
|
||||
* if the platform supports it.
|
||||
*
|
||||
* @return {@code String} for the ulimit command if supported by the platform,
|
||||
* otherwise {@code null}.
|
||||
*/
|
||||
private static String getCoreUlimitCommand() {
|
||||
String result = null;
|
||||
try {
|
||||
OutputAnalyzer output = ProcessTools.executeProcess("sh", "-c", RUN_SHELL_NO_LIMIT + "ulimit -c");
|
||||
if (output.getExitValue() != 0) {
|
||||
result = null;
|
||||
} else if (!output.getStdout().contains("unlimited")) {
|
||||
result = null;
|
||||
} else {
|
||||
result = RUN_SHELL_NO_LIMIT; // success
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
System.out.println("Exception in getCoreUlimitCommand(): " + t.toString());
|
||||
result = null;
|
||||
}
|
||||
System.out.println("Run test with ulimit -c: " +
|
||||
(result == null ? "default" : "unlimited"));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@code ProcessBuilder} that has been prefixed with
|
||||
* a {@code ulimit} command to allow for an unlimited core file size.
|
||||
*
|
||||
* @param pb {@code ProcessBuilder} to prefix with the ulimit command
|
||||
* @return New {@code ProcessBuilder} with prefixed {@code ulimit} command if
|
||||
* supported. Otherwise the passed in {@code ProcessBuilder} is returned.
|
||||
*/
|
||||
public static ProcessBuilder addCoreUlimitCommand(ProcessBuilder pb) {
|
||||
String cmd = ProcessTools.getCommandLine(pb);
|
||||
String ulimitCmd = getCoreUlimitCommand();
|
||||
if (ulimitCmd == null) {
|
||||
return pb;
|
||||
} else {
|
||||
if (Platform.isWindows()) {
|
||||
// In order to launch on Windows using "sh -c", we need to first
|
||||
// convert the path to use forward slashes and do some extra quoting.
|
||||
cmd = cmd.replace('\\', '/').replace(";", "\\;").replace("|", "\\|");
|
||||
}
|
||||
return new ProcessBuilder("sh", "-c", ulimitCmd + cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the path to the core file mentioned in the output and return its path.
|
||||
*
|
||||
* @param crashOutputString {@code String} to search in for the core file path
|
||||
* @return Location of core file if found in the output, otherwise {@code null}.
|
||||
*/
|
||||
public static String getCoreFileLocation(String crashOutputString) throws IOException {
|
||||
unzipCores(new File("."));
|
||||
|
||||
// Find the core file
|
||||
String coreFileLocation = parseCoreFileLocationFromOutput(crashOutputString);
|
||||
if (coreFileLocation != null) {
|
||||
Asserts.assertGT(new File(coreFileLocation).length(), 0L, "Unexpected core size");
|
||||
System.out.println("Found core file: " + coreFileLocation);
|
||||
return coreFileLocation; // success!
|
||||
}
|
||||
|
||||
// See if we can figure out the likely reason the core file was not found.
|
||||
// Throw SkippedException if appropriate.
|
||||
if (Platform.isOSX()) {
|
||||
File coresDir = new File("/cores");
|
||||
if (!coresDir.isDirectory()) {
|
||||
throw new RuntimeException(coresDir + " is not a directory");
|
||||
}
|
||||
// The /cores directory is usually not writable on macOS 10.15
|
||||
if (!coresDir.canWrite()) {
|
||||
throw new SkippedException("Directory \"" + coresDir + "\" is not writable");
|
||||
}
|
||||
} else if (Platform.isLinux()) {
|
||||
// Check if a crash report tool is installed.
|
||||
File corePatternFile = new File(CORE_PATTERN_FILE_NAME);
|
||||
try (Scanner scanner = new Scanner(corePatternFile)) {
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
line = line.trim();
|
||||
System.out.println(line);
|
||||
if (line.startsWith("|")) {
|
||||
System.out.println(
|
||||
"\nThis system uses a crash report tool ($cat /proc/sys/kernel/core_pattern).\n" +
|
||||
"Core files might not be generated. Please reset /proc/sys/kernel/core_pattern\n" +
|
||||
"to enable core generation. Skipping this test.");
|
||||
throw new SkippedException("This system uses a crash report tool");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Couldn't find core file location in: '" + crashOutputString + "'");
|
||||
}
|
||||
|
||||
private static final String CORE_PATTERN_FILE_NAME = "/proc/sys/kernel/core_pattern";
|
||||
private static final String LOCATION_STRING = "location: ";
|
||||
|
||||
private static String parseCoreFileLocationFromOutput(String crashOutputString) {
|
||||
System.out.println("crashOutputString = [" + crashOutputString + "]");
|
||||
|
||||
// Find the line of output that contains LOCATION_STRING
|
||||
Asserts.assertTrue(crashOutputString.contains(LOCATION_STRING),
|
||||
"Output doesn't contain the location of core file.");
|
||||
String stringWithLocation = Arrays.stream(crashOutputString.split("\\r?\\n"))
|
||||
.filter(str -> str.contains(LOCATION_STRING))
|
||||
.findFirst()
|
||||
.get();
|
||||
stringWithLocation = stringWithLocation.substring(stringWithLocation
|
||||
.indexOf(LOCATION_STRING) + LOCATION_STRING.length());
|
||||
System.out.println("getCoreFileLocation found stringWithLocation = " + stringWithLocation);
|
||||
|
||||
// Find the core file name in the output.
|
||||
String coreWithPid;
|
||||
if (stringWithLocation.contains("or ") && !Platform.isWindows()) {
|
||||
Matcher m = Pattern.compile("or.* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation);
|
||||
if (!m.find()) {
|
||||
throw new RuntimeException("Couldn't find path to core inside location string");
|
||||
}
|
||||
coreWithPid = m.group(1);
|
||||
} else {
|
||||
coreWithPid = stringWithLocation.trim();
|
||||
}
|
||||
if (new File(coreWithPid).exists()) {
|
||||
return coreWithPid;
|
||||
}
|
||||
|
||||
// Look for file named "core" in the cwd.
|
||||
String justCore = Paths.get("core").toString();
|
||||
if (new File(justCore).exists()) {
|
||||
return justCore;
|
||||
}
|
||||
|
||||
// Look for the core file name found in the output, but do so in the cwd.
|
||||
Path coreWithPidPath = Paths.get(coreWithPid);
|
||||
String justFile = coreWithPidPath.getFileName().toString();
|
||||
if (new File(justFile).exists()) {
|
||||
return justFile;
|
||||
}
|
||||
|
||||
// Look for file named "core" in the path to the core file found in the output.
|
||||
Path parent = coreWithPidPath.getParent();
|
||||
if (parent != null) {
|
||||
String coreWithoutPid = parent.resolve("core").toString();
|
||||
if (new File(coreWithoutPid).exists()) {
|
||||
return coreWithoutPid;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void unzipCores(File dir) {
|
||||
File[] gzCores = dir.listFiles((directory, name) -> name.matches("core(\\.\\d+)?\\.gz"));
|
||||
for (File gzCore : gzCores) {
|
||||
String coreFileName = gzCore.getName().replace(".gz", "");
|
||||
System.out.println("Unzipping core into " + coreFileName);
|
||||
try (GZIPInputStream gzis = new GZIPInputStream(Files.newInputStream(gzCore.toPath()))) {
|
||||
Files.copy(gzis, Paths.get(coreFileName));
|
||||
} catch (IOException e) {
|
||||
throw new SkippedException("Not able to unzip file: " + gzCore.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user