8275375: [REDO] JDK-8271949 dumppath in -XX:FlightRecorderOptions does not affect

Reviewed-by: egahlin, mgronlun
This commit is contained in:
Yasumasa Suenaga 2021-12-07 09:26:13 +00:00
parent 24877ac078
commit 07669e3bc6
11 changed files with 179 additions and 39 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -35,6 +35,7 @@
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/recorder/repository/jfrChunkRotation.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/service/jfrEventThrottler.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
@ -315,6 +316,20 @@ JVM_ENTRY_NO_ENV(void, jfr_set_repository_location(JNIEnv* env, jobject repo, js
return JfrRepository::set_path(location, thread);
JVM_END
NO_TRANSITION(void, jfr_set_dump_path(JNIEnv* env, jobject jvm, jstring dumppath))
if (dumppath == NULL) {
JfrEmergencyDump::set_dump_path(NULL);
} else {
const char* dump_path = env->GetStringUTFChars(dumppath, NULL);
JfrEmergencyDump::set_dump_path(dump_path);
env->ReleaseStringUTFChars(dumppath, dump_path);
}
NO_TRANSITION_END
NO_TRANSITION(jstring, jfr_get_dump_path(JNIEnv* env, jobject jvm))
return env->NewStringUTF(JfrEmergencyDump::get_dump_path());
NO_TRANSITION_END
JVM_ENTRY_NO_ENV(void, jfr_uncaught_exception(JNIEnv* env, jobject jvm, jobject t, jthrowable throwable))
JfrJavaSupport::uncaught_exception(throwable, thread);
JVM_END

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -113,6 +113,10 @@ jlong JNICALL jfr_type_id(JNIEnv* env, jobject jvm, jclass jc);
void JNICALL jfr_set_repository_location(JNIEnv* env, jobject repo, jstring location);
void JNICALL jfr_set_dump_path(JNIEnv* env, jobject jvm, jstring dumppath);
jstring JNICALL jfr_get_dump_path(JNIEnv* env, jobject jvm);
jobject JNICALL jfr_get_event_writer(JNIEnv* env, jclass cls);
jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass cls);

View File

@ -75,6 +75,8 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) {
(char*)"flush", (char*)"(Ljdk/jfr/internal/EventWriter;II)Z", (void*)jfr_event_writer_flush,
(char*)"flush", (char*)"()V", (void*)jfr_flush,
(char*)"setRepositoryLocation", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_repository_location,
(char*)"setDumpPath", (char*)"(Ljava/lang/String;)V", (void*)jfr_set_dump_path,
(char*)"getDumpPath", (char*)"()Ljava/lang/String;", (void*)jfr_get_dump_path,
(char*)"abort", (char*)"(Ljava/lang/String;)V", (void*)jfr_abort,
(char*)"addStringConstant", (char*)"(JLjava/lang/String;)Z", (void*)jfr_add_string_constant,
(char*)"uncaughtException", (char*)"(Ljava/lang/Thread;Ljava/lang/Throwable;)V", (void*)jfr_uncaught_exception,

View File

@ -41,6 +41,8 @@
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"
char JfrEmergencyDump::_dump_path[JVM_MAXPATHLEN] = { 0 };
static const char vm_error_filename_fmt[] = "hs_err_pid%p.jfr";
static const char vm_oom_filename_fmt[] = "hs_oom_pid%p.jfr";
static const char vm_soe_filename_fmt[] = "hs_soe_pid%p.jfr";
@ -66,12 +68,17 @@ static bool is_path_empty() {
}
// returns with an appended file separator (if successful)
static size_t get_current_directory() {
if (os::get_current_directory(_path_buffer, sizeof(_path_buffer)) == NULL) {
return 0;
static size_t get_dump_directory() {
const char* dump_path = JfrEmergencyDump::get_dump_path();
if (*dump_path == '\0') {
if (os::get_current_directory(_path_buffer, sizeof(_path_buffer)) == NULL) {
return 0;
}
} else {
strcpy(_path_buffer, dump_path);
}
const size_t cwd_len = strlen(_path_buffer);
const int result = jio_snprintf(_path_buffer + cwd_len,
const size_t path_len = strlen(_path_buffer);
const int result = jio_snprintf(_path_buffer + path_len,
sizeof(_path_buffer),
"%s",
os::file_separator());
@ -105,7 +112,7 @@ static void close_emergency_dump_file() {
static const char* create_emergency_dump_path() {
assert(is_path_empty(), "invariant");
const size_t path_len = get_current_directory();
const size_t path_len = get_dump_directory();
if (path_len == 0) {
return NULL;
}
@ -125,12 +132,21 @@ static const char* create_emergency_dump_path() {
return result ? _path_buffer : NULL;
}
static bool open_emergency_dump_file() {
bool JfrEmergencyDump::open_emergency_dump_file() {
if (is_emergency_dump_file_open()) {
// opened already
return true;
}
return open_emergency_dump_fd(create_emergency_dump_path());
bool result = open_emergency_dump_fd(create_emergency_dump_path());
if (!result && *_dump_path != '\0') {
log_warning(jfr)("Unable to create an emergency dump file at the location set by dumppath=%s", _dump_path);
// Fallback. Try to create it in the current directory.
*_dump_path = '\0';
*_path_buffer = '\0';
result = open_emergency_dump_fd(create_emergency_dump_path());
}
return result;
}
static void report(outputStream* st, bool emergency_file_opened, const char* repository_path) {
@ -150,6 +166,21 @@ static void report(outputStream* st, bool emergency_file_opened, const char* rep
}
}
void JfrEmergencyDump::set_dump_path(const char* dump_path) {
if (dump_path == NULL || *dump_path == '\0') {
os::get_current_directory(_dump_path, sizeof(_dump_path));
} else {
if (strlen(dump_path) < JVM_MAXPATHLEN) {
strncpy(_dump_path, dump_path, JVM_MAXPATHLEN);
_dump_path[JVM_MAXPATHLEN - 1] = '\0';
}
}
}
const char* JfrEmergencyDump::get_dump_path() {
return _dump_path;
}
void JfrEmergencyDump::on_vm_error_report(outputStream* st, const char* repository_path) {
assert(st != NULL, "invariant");
Thread* thread = Thread::current_or_null_safe();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, 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
@ -32,7 +32,14 @@
// Responsible for creating an hs_err<pid>.jfr file in exceptional shutdown situations (crash, OOM)
//
class JfrEmergencyDump : AllStatic {
private:
static char _dump_path[JVM_MAXPATHLEN];
static bool open_emergency_dump_file();
public:
static void set_dump_path(const char* dump_path);
static const char* get_dump_path();
static const char* chunk_path(const char* repository_path);
static void on_vm_error(const char* repository_path);
static void on_vm_error_report(outputStream* st, const char* repository_path);

View File

@ -163,6 +163,7 @@ bool JfrOptionSet::allow_event_retransforms() {
// default options for the dcmd parser
const char* const default_repository = NULL;
const char* const default_dumppath = NULL;
const char* const default_global_buffer_size = "512k";
const char* const default_num_global_buffers = "20";
const char* const default_memory_size = "10m";
@ -182,6 +183,13 @@ static DCmdArgument<char*> _dcmd_repository(
false,
default_repository);
static DCmdArgument<char*> _dcmd_dumppath(
"dumppath",
"Path to emergency dump",
"STRING",
false,
default_dumppath);
static DCmdArgument<MemorySizeArgument> _dcmd_threadbuffersize(
"threadbuffersize",
"Thread buffer size",
@ -258,6 +266,7 @@ static DCmdParser _parser;
static void register_parser_options() {
_parser.add_dcmd_option(&_dcmd_repository);
_parser.add_dcmd_option(&_dcmd_dumppath);
_parser.add_dcmd_option(&_dcmd_threadbuffersize);
_parser.add_dcmd_option(&_dcmd_memorysize);
_parser.add_dcmd_option(&_dcmd_globalbuffersize);
@ -346,6 +355,18 @@ bool JfrOptionSet::configure(TRAPS) {
configure._repository_path.set_value(repo_copy);
}
configure._dump_path.set_is_set(_dcmd_dumppath.is_set());
char* dumppath = _dcmd_dumppath.value();
if (dumppath != NULL) {
const size_t len = strlen(dumppath);
char* dumppath_copy = JfrCHeapObj::new_array<char>(len + 1);
if (NULL == dumppath_copy) {
return false;
}
strncpy(dumppath_copy, dumppath, len + 1);
configure._dump_path.set_value(dumppath_copy);
}
configure._stack_depth.set_is_set(_dcmd_stackdepth.is_set());
configure._stack_depth.set_value(_dcmd_stackdepth.value());
@ -373,6 +394,7 @@ bool JfrOptionSet::configure(TRAPS) {
if (HAS_PENDING_EXCEPTION) {
java_lang_Throwable::print(PENDING_EXCEPTION, tty);
CLEAR_PENDING_EXCEPTION;
tty->cr(); // java_lang_Throwable::print will not print '\n'
return false;
}
return true;

View File

@ -473,13 +473,26 @@ public final class JVM {
public native void flush();
/**
* Sets the location of the disk repository, to be used at an emergency
* dump.
* Sets the location of the disk repository.
*
* @param dirText
*/
public native void setRepositoryLocation(String dirText);
/**
* Sets the path to emergency dump.
*
* @param dumpPathText
*/
public native void setDumpPath(String dumpPathText);
/**
* Gets the path to emergency dump.
*
* @return The path to emergency dump.
*/
public native String getDumpPath();
/**
* Access to VM termination support.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2021, 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
@ -25,9 +25,16 @@
package jdk.jfr.internal;
import java.io.IOException;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.internal.misc.Unsafe;
import static java.nio.file.LinkOption.*;
/**
* Options that control Flight Recorder.
*
@ -48,7 +55,7 @@ public final class Options {
private static final int DEFAULT_STACK_DEPTH = 64;
private static final boolean DEFAULT_SAMPLE_THREADS = true;
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME;
private static final SafePath DEFAULT_DUMP_PATH = null;
private static long memorySize;
private static long globalBufferSize;
@ -57,7 +64,6 @@ public final class Options {
private static int stackDepth;
private static boolean sampleThreads;
private static long maxChunkSize;
private static SafePath dumpPath;
static {
final long pageSize = Unsafe.getUnsafe().pageSize();
@ -113,12 +119,19 @@ public final class Options {
globalBufferSize = globalBufsize;
}
public static synchronized void setDumpPath(SafePath path) {
dumpPath = path;
public static synchronized void setDumpPath(SafePath path) throws IOException {
if (path != null) {
if (SecuritySupport.isWritable(path)) {
path = SecuritySupport.toRealPath(path, NOFOLLOW_LINKS);
} else {
throw new IOException("Cannot write JFR emergency dump to " + path.toString());
}
}
jvm.setDumpPath(path == null ? null : path.toString());
}
public static synchronized SafePath getDumpPath() {
return dumpPath;
return new SafePath(jvm.getDumpPath());
}
public static synchronized void setStackDepth(Integer stackTraceDepth) {
@ -144,7 +157,11 @@ public final class Options {
setMemorySize(DEFAULT_MEMORY_SIZE);
setGlobalBufferSize(DEFAULT_GLOBAL_BUFFER_SIZE);
setGlobalBufferCount(DEFAULT_GLOBAL_BUFFER_COUNT);
setDumpPath(DEFAULT_DUMP_PATH);
try {
setDumpPath(DEFAULT_DUMP_PATH);
} catch (IOException e) {
// Ignore (depends on default value in JVM: it would be NULL)
}
setSampleThreads(DEFAULT_SAMPLE_THREADS);
setStackDepth(DEFAULT_STACK_DEPTH);
setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE);

View File

@ -41,6 +41,7 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
@ -77,7 +78,6 @@ public final class SecuritySupport {
private static final Module JFR_MODULE = Event.class.getModule();
public static final SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
public static final FileAccess PRIVILEGED = new Privileged();
static final SafePath USER_HOME = getPathInProperty("user.home", null);
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
static {
@ -365,8 +365,8 @@ public final class SecuritySupport {
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
}
static SafePath toRealPath(SafePath safePath) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath()));
static SafePath toRealPath(SafePath safePath, LinkOption... options) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath(options)));
}
static boolean existDirectory(SafePath directory) throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2021, 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
@ -25,7 +25,7 @@
package jdk.jfr.internal.dcmd;
import java.io.IOException;
import jdk.jfr.FlightRecorder;
import jdk.jfr.internal.LogLevel;
@ -106,7 +106,11 @@ final class DCmdConfigure extends AbstractDCmd {
}
if (dumpPath != null) {
Options.setDumpPath(new SafePath(dumpPath));
try {
Options.setDumpPath(new SafePath(dumpPath));
} catch (IOException e) {
throw new DCmdException("Could not set " + dumpPath + " to emergency dump path. " + e.getMessage(), e);
}
Logger.log(LogTag.JFR, LogLevel.INFO, "Emergency dump path set to " + dumpPath);
if (verbose) {
printDumpPath();
@ -183,6 +187,7 @@ final class DCmdConfigure extends AbstractDCmd {
println("Current configuration:");
println();
printRepositoryPath();
printDumpPath();
printStackDepth();
printGlobalBufferCount();
printGlobalBufferSize();

View File

@ -26,6 +26,7 @@ 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.List;
import jdk.internal.misc.Unsafe;
@ -74,22 +75,42 @@ public class TestDumpOnCrash {
}
public static void main(String[] args) throws Exception {
// Test without dumppath
test(CrasherIllegalAccess.class, "", true);
test(CrasherIllegalAccess.class, "", false);
test(CrasherHalt.class, "", true);
test(CrasherHalt.class, "", false);
// Test with dumppath
Path dumppath = Files.createTempDirectory(null);
try {
test(CrasherIllegalAccess.class, "", true, dumppath.toString());
test(CrasherIllegalAccess.class, "", false, dumppath.toString());
test(CrasherHalt.class, "", true, dumppath.toString());
test(CrasherHalt.class, "", false, dumppath.toString());
} finally {
dumppath.toFile().delete();
}
// Test is excluded until 8219680 is fixed
// @ignore 8219680
// test(CrasherSig.class, "FPE", true);
}
private static void test(Class<?> crasher, String signal, boolean disk) throws Exception {
test(crasher, signal, disk, null);
}
private static void test(Class<?> crasher, String signal, boolean disk, String dumppath) throws Exception {
test(crasher, signal, disk, dumppath, dumppath);
}
private static void test(Class<?> crasher, String signal, boolean disk, String dumppath, String expectedPath) throws Exception {
// The JVM may be in a state it can't recover from, so try three times
// before concluding functionality is not working.
for (int attempt = 0; attempt < ATTEMPTS; attempt++) {
try {
verify(runProcess(crasher, signal, disk));
verify(runProcess(crasher, signal, disk, dumppath), expectedPath);
return;
} catch (Exception e) {
System.out.println("Attempt " + attempt + ". Verification failed:");
@ -105,17 +126,19 @@ public class TestDumpOnCrash {
throw new Exception(ATTEMPTS + " attempts with failure!");
}
private static long runProcess(Class<?> crasher, String signal, boolean disk) throws Exception {
private static long runProcess(Class<?> crasher, String signal, boolean disk, String dumppath) throws Exception {
System.out.println("Test case for crasher " + crasher.getName());
final String flightRecordingOptions = "dumponexit=true,disk=" + Boolean.toString(disk);
Process p = ProcessTools.createTestJvm(
"-Xmx64m",
"-XX:-CreateCoredumpOnCrash",
"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
"-XX:StartFlightRecording:" + flightRecordingOptions,
crasher.getName(),
signal)
.start();
List<String> options = new ArrayList<>();
options.add("-Xmx64m");
options.add("-XX:-CreateCoredumpOnCrash");
options.add("--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED");
options.add("-XX:StartFlightRecording:dumponexit=true,disk=" + Boolean.toString(disk));
if (dumppath != null) {
options.add("-XX:FlightRecorderOptions=dumppath=" + dumppath);
}
options.add(crasher.getName());
options.add(signal);
Process p = ProcessTools.createTestJvm(options).start();
OutputAnalyzer output = new OutputAnalyzer(p);
System.out.println("========== Crasher process output:");
@ -125,9 +148,10 @@ public class TestDumpOnCrash {
return p.pid();
}
private static void verify(long pid) throws IOException {
private static void verify(long pid, String dumppath) throws IOException {
String fileName = "hs_err_pid" + pid + ".jfr";
Path file = Paths.get(fileName).toAbsolutePath().normalize();
Path file = (dumppath == null) ? Paths.get(fileName) : Paths.get(dumppath, fileName);
file = file.toAbsolutePath().normalize();
Asserts.assertTrue(Files.exists(file), "No emergency jfr recording file " + file + " exists");
Asserts.assertNotEquals(Files.size(file), 0L, "File length 0. Should at least be some bytes");