7090324: gclog rotation via external tool
authorminqi
Fri, 28 Mar 2014 21:04:37 -0700
changeset 23517 27c4307d6cda
parent 23516 b32d8f04648b
child 23518 00028482ef09
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
hotspot/src/share/vm/runtime/arguments.cpp
hotspot/src/share/vm/runtime/globals.hpp
hotspot/src/share/vm/runtime/safepoint.cpp
hotspot/src/share/vm/runtime/vm_operations.hpp
hotspot/src/share/vm/services/diagnosticCommand.cpp
hotspot/src/share/vm/services/diagnosticCommand.hpp
hotspot/src/share/vm/utilities/ostream.cpp
hotspot/src/share/vm/utilities/ostream.hpp
hotspot/test/gc/TestGCLogRotationViaJcmd.java
--- 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);
+        }
+
+    }
+
+}
+