8248194: Need better support for running SA tests on core files

Reviewed-by: amenkov, lmesnik
This commit is contained in:
Chris Plummer 2020-07-07 13:07:53 -07:00
parent f243b281ea
commit db2d4e8f5a
7 changed files with 365 additions and 203 deletions

View File

@ -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);
}

View File

@ -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");
}
}

View File

@ -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());

View File

@ -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);
}
}
}
}

View File

@ -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());

View File

@ -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();
}

View 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);
}
}
}
}