Mon, 31 Mar 2014 13:09:35 -0700
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
1.1 --- a/src/share/vm/runtime/arguments.cpp Fri Mar 28 15:29:23 2014 -0700 1.2 +++ b/src/share/vm/runtime/arguments.cpp Mon Mar 31 13:09:35 2014 -0700 1.3 @@ -1880,24 +1880,22 @@ 1.4 // check if do gclog rotation 1.5 // +UseGCLogFileRotation is a must, 1.6 // no gc log rotation when log file not supplied or 1.7 -// NumberOfGCLogFiles is 0, or GCLogFileSize is 0 1.8 +// NumberOfGCLogFiles is 0 1.9 void check_gclog_consistency() { 1.10 if (UseGCLogFileRotation) { 1.11 - if ((Arguments::gc_log_filename() == NULL) || 1.12 - (NumberOfGCLogFiles == 0) || 1.13 - (GCLogFileSize == 0)) { 1.14 + if ((Arguments::gc_log_filename() == NULL) || (NumberOfGCLogFiles == 0)) { 1.15 jio_fprintf(defaultStream::output_stream(), 1.16 - "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" 1.17 - "where num_of_file > 0 and num_of_size > 0\n" 1.18 + "To enable GC log rotation, use -Xloggc:<filename> -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=<num_of_files>\n" 1.19 + "where num_of_file > 0\n" 1.20 "GC log rotation is turned off\n"); 1.21 UseGCLogFileRotation = false; 1.22 } 1.23 } 1.24 1.25 - if (UseGCLogFileRotation && GCLogFileSize < 8*K) { 1.26 - FLAG_SET_CMDLINE(uintx, GCLogFileSize, 8*K); 1.27 - jio_fprintf(defaultStream::output_stream(), 1.28 - "GCLogFileSize changed to minimum 8K\n"); 1.29 + if (UseGCLogFileRotation && (GCLogFileSize != 0) && (GCLogFileSize < 8*K)) { 1.30 + FLAG_SET_CMDLINE(uintx, GCLogFileSize, 8*K); 1.31 + jio_fprintf(defaultStream::output_stream(), 1.32 + "GCLogFileSize changed to minimum 8K\n"); 1.33 } 1.34 } 1.35
2.1 --- a/src/share/vm/runtime/globals.hpp Fri Mar 28 15:29:23 2014 -0700 2.2 +++ b/src/share/vm/runtime/globals.hpp Mon Mar 31 13:09:35 2014 -0700 2.3 @@ -2422,9 +2422,9 @@ 2.4 "Number of gclog files in rotation " \ 2.5 "(default: 0, no rotation)") \ 2.6 \ 2.7 - product(uintx, GCLogFileSize, 0, \ 2.8 - "GC log file size (default: 0 bytes, no rotation). " \ 2.9 - "It requires UseGCLogFileRotation") \ 2.10 + product(uintx, GCLogFileSize, 8*K, \ 2.11 + "GC log file size, requires UseGCLogFileRotation. " \ 2.12 + "Set to 0 to only trigger rotation via jcmd") \ 2.13 \ 2.14 /* JVMTI heap profiling */ \ 2.15 \
3.1 --- a/src/share/vm/runtime/safepoint.cpp Fri Mar 28 15:29:23 2014 -0700 3.2 +++ b/src/share/vm/runtime/safepoint.cpp Mon Mar 31 13:09:35 2014 -0700 3.3 @@ -535,7 +535,7 @@ 3.4 3.5 // rotate log files? 3.6 if (UseGCLogFileRotation) { 3.7 - gclog_or_tty->rotate_log(); 3.8 + gclog_or_tty->rotate_log(false); 3.9 } 3.10 3.11 if (MemTracker::is_on()) {
4.1 --- a/src/share/vm/runtime/vm_operations.hpp Fri Mar 28 15:29:23 2014 -0700 4.2 +++ b/src/share/vm/runtime/vm_operations.hpp Mon Mar 31 13:09:35 2014 -0700 4.3 @@ -94,6 +94,7 @@ 4.4 template(JFRCheckpoint) \ 4.5 template(Exit) \ 4.6 template(LinuxDllLoad) \ 4.7 + template(RotateGCLog) \ 4.8 4.9 class VM_Operation: public CHeapObj<mtInternal> { 4.10 public: 4.11 @@ -397,4 +398,15 @@ 4.12 void doit(); 4.13 }; 4.14 4.15 + 4.16 +class VM_RotateGCLog: public VM_Operation { 4.17 + private: 4.18 + outputStream* _out; 4.19 + 4.20 + public: 4.21 + VM_RotateGCLog(outputStream* st) : _out(st) {} 4.22 + VMOp_Type type() const { return VMOp_RotateGCLog; } 4.23 + void doit() { gclog_or_tty->rotate_log(true, _out); } 4.24 +}; 4.25 + 4.26 #endif // SHARE_VM_RUNTIME_VM_OPERATIONS_HPP
5.1 --- a/src/share/vm/services/diagnosticCommand.cpp Fri Mar 28 15:29:23 2014 -0700 5.2 +++ b/src/share/vm/services/diagnosticCommand.cpp Mon Mar 31 13:09:35 2014 -0700 5.3 @@ -53,6 +53,7 @@ 5.4 DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false)); 5.5 #endif // INCLUDE_SERVICES 5.6 DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false)); 5.7 + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false)); 5.8 5.9 // Enhanced JMX Agent Support 5.10 // These commands won't be exported via the DiagnosticCommandMBean until an 5.11 @@ -650,3 +651,11 @@ 5.12 JavaCalls::call_static(&result, ik, vmSymbols::stopRemoteAgent_name(), vmSymbols::void_method_signature(), CHECK); 5.13 } 5.14 5.15 +void RotateGCLogDCmd::execute(DCmdSource source, TRAPS) { 5.16 + if (UseGCLogFileRotation) { 5.17 + VM_RotateGCLog rotateop(output()); 5.18 + VMThread::execute(&rotateop); 5.19 + } else { 5.20 + output()->print_cr("Target VM does not support GC log file rotation."); 5.21 + } 5.22 +}
6.1 --- a/src/share/vm/services/diagnosticCommand.hpp Fri Mar 28 15:29:23 2014 -0700 6.2 +++ b/src/share/vm/services/diagnosticCommand.hpp Mon Mar 31 13:09:35 2014 -0700 6.3 @@ -360,4 +360,21 @@ 6.4 virtual void execute(DCmdSource source, TRAPS); 6.5 }; 6.6 6.7 +class RotateGCLogDCmd : public DCmd { 6.8 +public: 6.9 + RotateGCLogDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} 6.10 + static const char* name() { return "GC.rotate_log"; } 6.11 + static const char* description() { 6.12 + return "Force the GC log file to be rotated."; 6.13 + } 6.14 + static const char* impact() { return "Low"; } 6.15 + virtual void execute(DCmdSource source, TRAPS); 6.16 + static int num_arguments() { return 0; } 6.17 + static const JavaPermission permission() { 6.18 + JavaPermission p = {"java.lang.management.ManagementPermission", 6.19 + "control", NULL}; 6.20 + return p; 6.21 + } 6.22 +}; 6.23 + 6.24 #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
7.1 --- a/src/share/vm/utilities/ostream.cpp Fri Mar 28 15:29:23 2014 -0700 7.2 +++ b/src/share/vm/utilities/ostream.cpp Mon Mar 31 13:09:35 2014 -0700 7.3 @@ -662,13 +662,13 @@ 7.4 // write to gc log file at safepoint. If in future, changes made for mutator threads or 7.5 // concurrent GC threads to run parallel with VMThread at safepoint, write and rotate_log 7.6 // must be synchronized. 7.7 -void gcLogFileStream::rotate_log() { 7.8 +void gcLogFileStream::rotate_log(bool force, outputStream* out) { 7.9 char time_msg[FILENAMEBUFLEN]; 7.10 char time_str[EXTRACHARLEN]; 7.11 char current_file_name[FILENAMEBUFLEN]; 7.12 char renamed_file_name[FILENAMEBUFLEN]; 7.13 7.14 - if (_bytes_written < (jlong)GCLogFileSize) { 7.15 + if (!should_rotate(force)) { 7.16 return; 7.17 } 7.18 7.19 @@ -685,6 +685,11 @@ 7.20 jio_snprintf(time_msg, sizeof(time_msg), "File %s rotated at %s\n", 7.21 _file_name, os::local_time_string((char *)time_str, sizeof(time_str))); 7.22 write(time_msg, strlen(time_msg)); 7.23 + 7.24 + if (out != NULL) { 7.25 + out->print(time_msg); 7.26 + } 7.27 + 7.28 dump_loggc_header(); 7.29 return; 7.30 } 7.31 @@ -706,12 +711,18 @@ 7.32 _file_name, _cur_file_num); 7.33 jio_snprintf(current_file_name, filename_len + EXTRACHARLEN, "%s.%d" CURRENTAPPX, 7.34 _file_name, _cur_file_num); 7.35 - jio_snprintf(time_msg, sizeof(time_msg), "%s GC log file has reached the" 7.36 - " maximum size. Saved as %s\n", 7.37 - os::local_time_string((char *)time_str, sizeof(time_str)), 7.38 - renamed_file_name); 7.39 + 7.40 + const char* msg = force ? "GC log rotation request has been received." 7.41 + : "GC log file has reached the maximum size."; 7.42 + jio_snprintf(time_msg, sizeof(time_msg), "%s %s Saved as %s\n", 7.43 + os::local_time_string((char *)time_str, sizeof(time_str)), 7.44 + msg, renamed_file_name); 7.45 write(time_msg, strlen(time_msg)); 7.46 7.47 + if (out != NULL) { 7.48 + out->print(time_msg); 7.49 + } 7.50 + 7.51 fclose(_file); 7.52 _file = NULL; 7.53 7.54 @@ -752,6 +763,11 @@ 7.55 os::local_time_string((char *)time_str, sizeof(time_str)), 7.56 current_file_name); 7.57 write(time_msg, strlen(time_msg)); 7.58 + 7.59 + if (out != NULL) { 7.60 + out->print(time_msg); 7.61 + } 7.62 + 7.63 dump_loggc_header(); 7.64 // remove the existing file 7.65 if (access(current_file_name, F_OK) == 0) {
8.1 --- a/src/share/vm/utilities/ostream.hpp Fri Mar 28 15:29:23 2014 -0700 8.2 +++ b/src/share/vm/utilities/ostream.hpp Mon Mar 31 13:09:35 2014 -0700 8.3 @@ -115,7 +115,7 @@ 8.4 // flushing 8.5 virtual void flush() {} 8.6 virtual void write(const char* str, size_t len) = 0; 8.7 - virtual void rotate_log() {} // GC log rotation 8.8 + virtual void rotate_log(bool force, outputStream* out = NULL) {} // GC log rotation 8.9 virtual ~outputStream() {} // close properly on deletion 8.10 8.11 void dec_cr() { dec(); cr(); } 8.12 @@ -240,8 +240,15 @@ 8.13 gcLogFileStream(const char* file_name); 8.14 ~gcLogFileStream(); 8.15 virtual void write(const char* c, size_t len); 8.16 - virtual void rotate_log(); 8.17 + virtual void rotate_log(bool force, outputStream* out = NULL); 8.18 void dump_loggc_header(); 8.19 + 8.20 + /* If "force" sets true, force log file rotation from outside JVM */ 8.21 + bool should_rotate(bool force) { 8.22 + return force || 8.23 + ((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize)); 8.24 + } 8.25 + 8.26 }; 8.27 8.28 #ifndef PRODUCT
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/test/gc/TestGCLogRotationViaJcmd.java Mon Mar 31 13:09:35 2014 -0700 9.3 @@ -0,0 +1,77 @@ 9.4 +/* 9.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 9.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 9.7 + * 9.8 + * This code is free software; you can redistribute it and/or modify it 9.9 + * under the terms of the GNU General Public License version 2 only, as 9.10 + * published by the Free Software Foundation. 9.11 + * 9.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 9.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 9.15 + * version 2 for more details (a copy is included in the LICENSE file that 9.16 + * accompanied this code). 9.17 + * 9.18 + * You should have received a copy of the GNU General Public License version 9.19 + * 2 along with this work; if not, write to the Free Software Foundation, 9.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 9.21 + * 9.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 9.23 + * or visit www.oracle.com if you need additional information or have any 9.24 + * questions. 9.25 + */ 9.26 + 9.27 +/* 9.28 + * @test TestGCLogRotationViaJcmd.java 9.29 + * @bug 7090324 9.30 + * @summary test for gc log rotation via jcmd 9.31 + * @library /testlibrary 9.32 + * @run main/othervm -Xloggc:test.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 TestGCLogRotationViaJcmd 9.33 + * 9.34 + */ 9.35 +import com.oracle.java.testlibrary.*; 9.36 +import java.io.File; 9.37 +import java.io.FilenameFilter; 9.38 + 9.39 +public class TestGCLogRotationViaJcmd { 9.40 + 9.41 + static final File currentDirectory = new File("."); 9.42 + static final String LOG_FILE_NAME = "test.log"; 9.43 + static final int NUM_LOGS = 3; 9.44 + 9.45 + static FilenameFilter logFilter = new FilenameFilter() { 9.46 + @Override 9.47 + public boolean accept(File dir, String name) { 9.48 + return name.startsWith(LOG_FILE_NAME); 9.49 + } 9.50 + }; 9.51 + 9.52 + public static void main(String[] args) throws Exception { 9.53 + // Grab the pid from the current java process 9.54 + String pid = Integer.toString(ProcessTools.getProcessId()); 9.55 + 9.56 + // Create a JDKToolLauncher 9.57 + JDKToolLauncher jcmd = JDKToolLauncher.create("jcmd") 9.58 + .addToolArg(pid) 9.59 + .addToolArg("GC.rotate_log"); 9.60 + 9.61 + for (int times = 1; times < NUM_LOGS; times++) { 9.62 + // Run jcmd <pid> GC.rotate_log 9.63 + ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand()); 9.64 + 9.65 + // Make sure we didn't crash 9.66 + OutputAnalyzer output = new OutputAnalyzer(pb.start()); 9.67 + output.shouldHaveExitValue(0); 9.68 + } 9.69 + 9.70 + // GC log check 9.71 + File[] logs = currentDirectory.listFiles(logFilter); 9.72 + if (logs.length != NUM_LOGS) { 9.73 + throw new Error("There are only " + logs.length 9.74 + + " logs instead " + NUM_LOGS); 9.75 + } 9.76 + 9.77 + } 9.78 + 9.79 +} 9.80 +