7090324: gclog rotation via external tool
Summary: GC log rotation can be set via java command line, but customer sometime need to sync with OS level rotation setting.
Reviewed-by: sla, minqi, ehelin
Contributed-by: suenaga.yasumasa@lab.ntt.co.jp
--- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Fri Mar 28 21:04:37 2014 -0700
@@ -1907,24 +1907,22 @@
// check if do gclog rotation
// +UseGCLogFileRotation is a must,
// no gc log rotation when log file not supplied or
-// NumberOfGCLogFiles is 0, or GCLogFileSize is 0
+// NumberOfGCLogFiles is 0
void check_gclog_consistency() {
if (UseGCLogFileRotation) {
- if ((Arguments::gc_log_filename() == NULL) ||
- (NumberOfGCLogFiles == 0) ||
- (GCLogFileSize == 0)) {
+ if ((Arguments::gc_log_filename() == NULL) || (NumberOfGCLogFiles == 0)) {
jio_fprintf(defaultStream::output_stream(),
- "To enable GC log rotation, use -Xloggc:<filename> -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=<num_of_files> -XX:GCLogFileSize=<num_of_size>[k|K|m|M|g|G]\n"
- "where num_of_file > 0 and num_of_size > 0\n"
+ "To enable GC log rotation, use -Xloggc:<filename> -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=<num_of_files>\n"
+ "where num_of_file > 0\n"
"GC log rotation is turned off\n");
UseGCLogFileRotation = false;
}
}
- if (UseGCLogFileRotation && GCLogFileSize < 8*K) {
- FLAG_SET_CMDLINE(uintx, GCLogFileSize, 8*K);
- jio_fprintf(defaultStream::output_stream(),
- "GCLogFileSize changed to minimum 8K\n");
+ if (UseGCLogFileRotation && (GCLogFileSize != 0) && (GCLogFileSize < 8*K)) {
+ FLAG_SET_CMDLINE(uintx, GCLogFileSize, 8*K);
+ jio_fprintf(defaultStream::output_stream(),
+ "GCLogFileSize changed to minimum 8K\n");
}
}
--- a/hotspot/src/share/vm/runtime/globals.hpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp Fri Mar 28 21:04:37 2014 -0700
@@ -2423,9 +2423,9 @@
"Number of gclog files in rotation " \
"(default: 0, no rotation)") \
\
- product(uintx, GCLogFileSize, 0, \
- "GC log file size (default: 0 bytes, no rotation). " \
- "It requires UseGCLogFileRotation") \
+ product(uintx, GCLogFileSize, 8*K, \
+ "GC log file size, requires UseGCLogFileRotation. " \
+ "Set to 0 to only trigger rotation via jcmd") \
\
/* JVMTI heap profiling */ \
\
--- a/hotspot/src/share/vm/runtime/safepoint.cpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/runtime/safepoint.cpp Fri Mar 28 21:04:37 2014 -0700
@@ -535,7 +535,7 @@
// rotate log files?
if (UseGCLogFileRotation) {
- gclog_or_tty->rotate_log();
+ gclog_or_tty->rotate_log(false);
}
if (MemTracker::is_on()) {
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Fri Mar 28 21:04:37 2014 -0700
@@ -96,6 +96,7 @@
template(JFRCheckpoint) \
template(Exit) \
template(LinuxDllLoad) \
+ template(RotateGCLog) \
class VM_Operation: public CHeapObj<mtInternal> {
public:
@@ -399,4 +400,15 @@
void doit();
};
+
+class VM_RotateGCLog: public VM_Operation {
+ private:
+ outputStream* _out;
+
+ public:
+ VM_RotateGCLog(outputStream* st) : _out(st) {}
+ VMOp_Type type() const { return VMOp_RotateGCLog; }
+ void doit() { gclog_or_tty->rotate_log(true, _out); }
+};
+
#endif // SHARE_VM_RUNTIME_VM_OPERATIONS_HPP
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Fri Mar 28 21:04:37 2014 -0700
@@ -55,6 +55,7 @@
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
#endif // INCLUDE_SERVICES
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false));
// Enhanced JMX Agent Support
// These commands won't be exported via the DiagnosticCommandMBean until an
@@ -659,3 +660,13 @@
os::print_dll_info(output());
output()->cr();
}
+
+void RotateGCLogDCmd::execute(DCmdSource source, TRAPS) {
+ if (UseGCLogFileRotation) {
+ VM_RotateGCLog rotateop(output());
+ VMThread::execute(&rotateop);
+ } else {
+ output()->print_cr("Target VM does not support GC log file rotation.");
+ }
+}
+
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp Fri Mar 28 21:04:37 2014 -0700
@@ -383,4 +383,21 @@
virtual void execute(DCmdSource source, TRAPS);
};
+class RotateGCLogDCmd : public DCmd {
+public:
+ RotateGCLogDCmd(outputStream* output, bool heap) : DCmd(output, heap) {}
+ static const char* name() { return "GC.rotate_log"; }
+ static const char* description() {
+ return "Force the GC log file to be rotated.";
+ }
+ static const char* impact() { return "Low"; }
+ virtual void execute(DCmdSource source, TRAPS);
+ static int num_arguments() { return 0; }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "control", NULL};
+ return p;
+ }
+};
+
#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
--- a/hotspot/src/share/vm/utilities/ostream.cpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/utilities/ostream.cpp Fri Mar 28 21:04:37 2014 -0700
@@ -662,13 +662,13 @@
// write to gc log file at safepoint. If in future, changes made for mutator threads or
// concurrent GC threads to run parallel with VMThread at safepoint, write and rotate_log
// must be synchronized.
-void gcLogFileStream::rotate_log() {
+void gcLogFileStream::rotate_log(bool force, outputStream* out) {
char time_msg[FILENAMEBUFLEN];
char time_str[EXTRACHARLEN];
char current_file_name[FILENAMEBUFLEN];
char renamed_file_name[FILENAMEBUFLEN];
- if (_bytes_written < (jlong)GCLogFileSize) {
+ if (!should_rotate(force)) {
return;
}
@@ -685,6 +685,11 @@
jio_snprintf(time_msg, sizeof(time_msg), "File %s rotated at %s\n",
_file_name, os::local_time_string((char *)time_str, sizeof(time_str)));
write(time_msg, strlen(time_msg));
+
+ if (out != NULL) {
+ out->print(time_msg);
+ }
+
dump_loggc_header();
return;
}
@@ -706,12 +711,18 @@
_file_name, _cur_file_num);
jio_snprintf(current_file_name, filename_len + EXTRACHARLEN, "%s.%d" CURRENTAPPX,
_file_name, _cur_file_num);
- jio_snprintf(time_msg, sizeof(time_msg), "%s GC log file has reached the"
- " maximum size. Saved as %s\n",
- os::local_time_string((char *)time_str, sizeof(time_str)),
- renamed_file_name);
+
+ const char* msg = force ? "GC log rotation request has been received."
+ : "GC log file has reached the maximum size.";
+ jio_snprintf(time_msg, sizeof(time_msg), "%s %s Saved as %s\n",
+ os::local_time_string((char *)time_str, sizeof(time_str)),
+ msg, renamed_file_name);
write(time_msg, strlen(time_msg));
+ if (out != NULL) {
+ out->print(time_msg);
+ }
+
fclose(_file);
_file = NULL;
@@ -752,6 +763,11 @@
os::local_time_string((char *)time_str, sizeof(time_str)),
current_file_name);
write(time_msg, strlen(time_msg));
+
+ if (out != NULL) {
+ out->print(time_msg);
+ }
+
dump_loggc_header();
// remove the existing file
if (access(current_file_name, F_OK) == 0) {
--- a/hotspot/src/share/vm/utilities/ostream.hpp Thu Mar 27 22:36:08 2014 +0100
+++ b/hotspot/src/share/vm/utilities/ostream.hpp Fri Mar 28 21:04:37 2014 -0700
@@ -115,7 +115,7 @@
// flushing
virtual void flush() {}
virtual void write(const char* str, size_t len) = 0;
- virtual void rotate_log() {} // GC log rotation
+ virtual void rotate_log(bool force, outputStream* out = NULL) {} // GC log rotation
virtual ~outputStream() {} // close properly on deletion
void dec_cr() { dec(); cr(); }
@@ -240,8 +240,14 @@
gcLogFileStream(const char* file_name);
~gcLogFileStream();
virtual void write(const char* c, size_t len);
- virtual void rotate_log();
+ virtual void rotate_log(bool force, outputStream* out = NULL);
void dump_loggc_header();
+
+ /* If "force" sets true, force log file rotation from outside JVM */
+ bool should_rotate(bool force) {
+ return force ||
+ ((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize));
+ }
};
#ifndef PRODUCT
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/TestGCLogRotationViaJcmd.java Fri Mar 28 21:04:37 2014 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * @test TestGCLogRotationViaJcmd.java
+ * @bug 7090324
+ * @summary test for gc log rotation via jcmd
+ * @library /testlibrary
+ * @run main/othervm -Xloggc:test.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 TestGCLogRotationViaJcmd
+ *
+ */
+import com.oracle.java.testlibrary.*;
+import java.io.File;
+import java.io.FilenameFilter;
+
+public class TestGCLogRotationViaJcmd {
+
+ static final File currentDirectory = new File(".");
+ static final String LOG_FILE_NAME = "test.log";
+ static final int NUM_LOGS = 3;
+
+ static FilenameFilter logFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.startsWith(LOG_FILE_NAME);
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ // Grab the pid from the current java process
+ String pid = Integer.toString(ProcessTools.getProcessId());
+
+ // Create a JDKToolLauncher
+ JDKToolLauncher jcmd = JDKToolLauncher.create("jcmd")
+ .addToolArg(pid)
+ .addToolArg("GC.rotate_log");
+
+ for (int times = 1; times < NUM_LOGS; times++) {
+ // Run jcmd <pid> GC.rotate_log
+ ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
+
+ // Make sure we didn't crash
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldHaveExitValue(0);
+ }
+
+ // GC log check
+ File[] logs = currentDirectory.listFiles(logFilter);
+ if (logs.length != NUM_LOGS) {
+ throw new Error("There are only " + logs.length
+ + " logs instead " + NUM_LOGS);
+ }
+
+ }
+
+}
+