aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Erik Gahlin <egahlin@openjdk.org> 2021-04-16 17:26:13 +0000
committerGravatar Erik Gahlin <egahlin@openjdk.org> 2021-04-16 17:26:13 +0000
commit7c37c022a1664437bf8d2c8d76ad039521f3ffa7 (patch)
tree821fb7330b426f6a54f97574e11daf5e05e1498a
parent79adc16fd8ba47a8d9438f3467f92ab241766c1d (diff)
downloadjdk-7c37c022a1664437bf8d2c8d76ad039521f3ffa7.tar.gz
jdk-7c37c022a1664437bf8d2c8d76ad039521f3ffa7.zip
8244190: JFR: When starting a JVM with -XX:StartFlightRecording, output is written to stdout
Reviewed-by: mgronlun
-rw-r--r--src/hotspot/share/jfr/dcmd/jfrDcmds.cpp83
-rw-r--r--src/hotspot/share/logging/logTag.hpp1
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java22
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java2
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java4
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java2
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java2
-rw-r--r--src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java2
-rw-r--r--test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java2
-rw-r--r--test/jdk/jdk/jfr/jcmd/JcmdAsserts.java2
-rw-r--r--test/jdk/jdk/jfr/startupargs/TestStartupMessage.java81
11 files changed, 163 insertions, 40 deletions
diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
index 67c18a7ea3e..59c2c84395b 100644
--- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
+++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp
@@ -30,7 +30,11 @@
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "logging/log.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logMessage.hpp"
#include "memory/resourceArea.hpp"
+#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "runtime/handles.inline.hpp"
@@ -128,33 +132,65 @@ static bool invalid_state(outputStream* out, TRAPS) {
return is_disabled(out) || !is_module_available(out, THREAD);
}
-static void print_pending_exception(outputStream* output, oop throwable) {
+static void handle_pending_exception(outputStream* output, bool startup, oop throwable) {
assert(throwable != NULL, "invariant");
oop msg = java_lang_Throwable::message(throwable);
-
- if (msg != NULL) {
- char* text = java_lang_String::as_utf8_string(msg);
- output->print_raw_cr(text);
+ if (msg == NULL) {
+ return;
+ }
+ char* text = java_lang_String::as_utf8_string(msg);
+ if (text != NULL) {
+ if (startup) {
+ log_error(jfr,startup)("%s", text);
+ } else {
+ output->print_cr("%s", text);
+ }
}
}
-static void print_message(outputStream* output, const char* message) {
- if (message != NULL) {
- output->print_raw(message);
+static void print_message(outputStream* output, oop content, TRAPS) {
+ objArrayOop lines = objArrayOop(content);
+ assert(lines != NULL, "invariant");
+ assert(lines->is_array(), "must be array");
+ const int length = lines->length();
+ for (int i = 0; i < length; ++i) {
+ const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);
+ if (text == NULL) {
+ // An oome has been thrown and is pending.
+ break;
+ }
+ output->print_cr("%s", text);
}
}
+static void log(oop content, TRAPS) {
+ LogMessage(jfr,startup) msg;
+ objArrayOop lines = objArrayOop(content);
+ assert(lines != NULL, "invariant");
+ assert(lines->is_array(), "must be array");
+ const int length = lines->length();
+ for (int i = 0; i < length; ++i) {
+ const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);
+ if (text == NULL) {
+ // An oome has been thrown and is pending.
+ break;
+ }
+ msg.info("%s", text);
+ }
+}
+
static void handle_dcmd_result(outputStream* output,
const oop result,
const DCmdSource source,
TRAPS) {
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
assert(output != NULL, "invariant");
+ const bool startup = DCmd_Source_Internal == source;
if (HAS_PENDING_EXCEPTION) {
- print_pending_exception(output, PENDING_EXCEPTION);
+ handle_pending_exception(output, startup, PENDING_EXCEPTION);
// Don't clear excption on startup, JVM should fail initialization.
- if (DCmd_Source_Internal != source) {
+ if (!startup) {
CLEAR_PENDING_EXCEPTION;
}
return;
@@ -162,9 +198,20 @@ static void handle_dcmd_result(outputStream* output,
assert(!HAS_PENDING_EXCEPTION, "invariant");
- if (result != NULL) {
- const char* result_chars = java_lang_String::as_utf8_string(result);
- print_message(output, result_chars);
+ if (startup) {
+ if (log_is_enabled(Warning, jfr, startup)) {
+ // if warning is set, assume user hasn't configured log level
+ // Log to Info and reset to Warning. This way user can disable
+ // default output by setting -Xlog:jfr+startup=error/off
+ LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(jfr, startup));
+ log(result, THREAD);
+ LogConfiguration::configure_stdout(LogLevel::Warning, true, LOG_TAGS(jfr, startup));
+ } else {
+ log(result, THREAD);
+ }
+ } else {
+ // Print output for jcmd or MXBean
+ print_message(output, result, THREAD);
}
}
@@ -261,7 +308,7 @@ void JfrDumpFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdDump";
static const char method[] = "execute";
- static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/String;";
+ static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)[Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
@@ -327,7 +374,7 @@ void JfrCheckFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdCheck";
static const char method[] = "execute";
- static const char signature[] = "(Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/String;";
+ static const char signature[] = "(Ljava/lang/String;Ljava/lang/Boolean;)[Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
@@ -471,7 +518,7 @@ void JfrStartFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
static const char method[] = "execute";
static const char signature[] = "(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Long;"
"Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/String;"
- "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)Ljava/lang/String;";
+ "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Boolean;)[Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
@@ -541,7 +588,7 @@ void JfrStopFlightRecordingDCmd::execute(DCmdSource source, TRAPS) {
static const char klass[] = "jdk/jfr/internal/dcmd/DCmdStop";
static const char method[] = "execute";
- static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;";
+ static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
@@ -654,7 +701,7 @@ void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) {
static const char method[] = "execute";
static const char signature[] = "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;"
"Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;"
- "Ljava/lang/Long;Ljava/lang/Boolean;)Ljava/lang/String;";
+ "Ljava/lang/Long;Ljava/lang/Boolean;)[Ljava/lang/String;";
JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
execute_args.set_receiver(h_dcmd_instance);
diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp
index 69927badcfa..0d869fce9ae 100644
--- a/src/hotspot/share/logging/logTag.hpp
+++ b/src/hotspot/share/logging/logTag.hpp
@@ -165,6 +165,7 @@
LOG_TAG(stacktrace) \
LOG_TAG(stackwalk) \
LOG_TAG(start) \
+ LOG_TAG(startup) \
LOG_TAG(startuptime) \
LOG_TAG(state) \
LOG_TAG(stats) \
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java
index 466a0babd62..e03ebf7090f 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java
@@ -25,8 +25,6 @@
package jdk.jfr.internal.dcmd;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
@@ -50,20 +48,15 @@ import jdk.jfr.internal.Utils;
*/
abstract class AbstractDCmd {
- private final StringWriter result;
- private final PrintWriter log;
-
- protected AbstractDCmd() {
- result = new StringWriter();
- log = new PrintWriter(result);
- }
+ private final StringBuilder currentLine = new StringBuilder(80);
+ private final List<String> lines = new ArrayList<>();
protected final FlightRecorder getFlightRecorder() {
return FlightRecorder.getFlightRecorder();
}
- public final String getResult() {
- return result.toString();
+ public final String[] getResult() {
+ return lines.toArray(new String[lines.size()]);
}
public String getPid() {
@@ -136,15 +129,16 @@ abstract class AbstractDCmd {
}
protected final void println() {
- log.println();
+ lines.add(currentLine.toString());
+ currentLine.setLength(0);
}
protected final void print(String s) {
- log.print(s);
+ currentLine.append(s);
}
protected final void print(String s, Object... args) {
- log.printf(s, args);
+ currentLine.append(String.format(s, args));
}
protected final void println(String s, Object... args) {
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java
index 70b11250c93..19474d03ef0 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java
@@ -58,7 +58,7 @@ final class DCmdCheck extends AbstractDCmd {
*
* @throws DCmdException if the check could not be completed.
*/
- public String execute(String recordingText, Boolean verbose) throws DCmdException {
+ public String[] execute(String recordingText, Boolean verbose) throws DCmdException {
executeInternal(recordingText, verbose);
return getResult();
}
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
index 2b8b7bf4db8..78127138b8f 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java
@@ -59,7 +59,7 @@ final class DCmdConfigure extends AbstractDCmd {
* @throws DCmdException
* if the dump could not be completed
*/
- public String execute
+ public String[] execute
(
boolean verbose,
String repositoryPath,
@@ -177,7 +177,7 @@ final class DCmdConfigure extends AbstractDCmd {
updated = true;
}
if (!verbose) {
- return "";
+ return new String[0];
}
if (!updated) {
println("Current configuration:");
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java
index dbfbeb78c7f..d29c41f2971 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java
@@ -69,7 +69,7 @@ final class DCmdDump extends AbstractDCmd {
*
* @throws DCmdException if the dump could not be completed
*/
- public String execute(String name, String filename, Long maxAge, Long maxSize, String begin, String end, Boolean pathToGcRoots) throws DCmdException {
+ public String[] execute(String name, String filename, Long maxAge, Long maxSize, String begin, String end, Boolean pathToGcRoots) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG,
"Executing DCmdDump: name=" + name +
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
index d9d1c149d6e..b980fdfab41 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
@@ -82,7 +82,7 @@ final class DCmdStart extends AbstractDCmd {
* @throws DCmdException if recording could not be started
*/
@SuppressWarnings("resource")
- public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
+ public String[] execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Long flush, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name +
", settings=" + Arrays.asList(settings) +
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java
index 90190829be6..e6a6c6770ba 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java
@@ -55,7 +55,7 @@ final class DCmdStop extends AbstractDCmd {
*
* @throws DCmdException if recording could not be stopped
*/
- public String execute(String name, String filename) throws DCmdException {
+ public String[] execute(String name, String filename) throws DCmdException {
if (Logger.shouldLog(LogTag.JFR_DCMD, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + ", filename=" + filename);
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java
index f4012ac26d2..d5edcc84f5a 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java
@@ -34,7 +34,7 @@
public class ModulePathAndCP_JFR {
public static void main(String... args) throws Exception {
- ModulePathAndCP.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug");
+ ModulePathAndCP.run("-XX:StartFlightRecording=dumponexit=true", "-Xlog:cds+jvmti=debug,jfr+startup=off");
}
}
diff --git a/test/jdk/jdk/jfr/jcmd/JcmdAsserts.java b/test/jdk/jdk/jfr/jcmd/JcmdAsserts.java
index 01caec69cc3..17113da9403 100644
--- a/test/jdk/jdk/jfr/jcmd/JcmdAsserts.java
+++ b/test/jdk/jdk/jfr/jcmd/JcmdAsserts.java
@@ -35,7 +35,7 @@ import jdk.test.lib.process.OutputAnalyzer;
public class JcmdAsserts {
- private static final String NEW_LINE = System.getProperty("line.separator");
+ private static final String NEW_LINE = "\n";
public static void assertJfrNotUsed(OutputAnalyzer output) {
output.shouldMatch("Flight Recorder has not been used");
diff --git a/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java
new file mode 100644
index 00000000000..c0bd66a7ecb
--- /dev/null
+++ b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 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
+ * 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.jfr.startupargs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+/**
+ * @test
+ * @key jfr
+ * @requires vm.hasJFR
+ * @library /test/lib /test/jdk
+ * @run main jdk.jfr.startupargs.TestStartupMessage
+ */
+public class TestStartupMessage {
+
+ public static class TestMessage {
+ public static void main(String[] args) throws Exception {
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ startJfrJvm("-Xlog:jfr+startup=off")
+ .shouldNotContain("[jfr,startup")
+ .shouldNotContain("Started recording")
+ .shouldNotContain("Use jcmd");
+
+ startJfrJvm("-Xlog:jfr+startup=error")
+ .shouldNotContain("[jfr,startup")
+ .shouldNotContain("Started recording")
+ .shouldNotContain("Use jcmd");
+
+ // Known limitation.
+ // Can't turn off log with -Xlog:jfr+startup=warning
+
+ startJfrJvm()
+ .shouldContain("[info][jfr,startup")
+ .shouldContain("Started recording")
+ .shouldContain("Use jcmd");
+
+ startJfrJvm("-Xlog:jfr+startup=info")
+ .shouldContain("[info][jfr,startup")
+ .shouldContain("Started recording")
+ .shouldContain("Use jcmd");
+ }
+
+ private static OutputAnalyzer startJfrJvm(String... args) throws Exception {
+ List<String> commands = new ArrayList<>(Arrays.asList(args));
+ commands.add("-XX:StartFlightRecording");
+ commands.add(TestMessage.class.getName());
+ ProcessBuilder pb = ProcessTools.createTestJvm(commands);
+ OutputAnalyzer out = ProcessTools.executeProcess(pb);
+ out.shouldHaveExitValue(0);
+ return out;
+ }
+}