mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-11 22:19:43 +00:00
7090324: gclog rotation via external tool
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
This commit is contained in:
parent
0b0fdb97ce
commit
57d7e49ab1
@ -1907,24 +1907,22 @@ static bool verify_serial_gc_flags() {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2423,9 +2423,9 @@ class CommandLineFlags {
|
||||
"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 */ \
|
||||
\
|
||||
|
||||
@ -535,7 +535,7 @@ void SafepointSynchronize::do_cleanup_tasks() {
|
||||
|
||||
// rotate log files?
|
||||
if (UseGCLogFileRotation) {
|
||||
gclog_or_tty->rotate_log();
|
||||
gclog_or_tty->rotate_log(false);
|
||||
}
|
||||
|
||||
if (MemTracker::is_on()) {
|
||||
|
||||
@ -96,6 +96,7 @@
|
||||
template(JFRCheckpoint) \
|
||||
template(Exit) \
|
||||
template(LinuxDllLoad) \
|
||||
template(RotateGCLog) \
|
||||
|
||||
class VM_Operation: public CHeapObj<mtInternal> {
|
||||
public:
|
||||
@ -399,4 +400,15 @@ class VM_Exit: public VM_Operation {
|
||||
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
|
||||
|
||||
@ -55,6 +55,7 @@ void DCmdRegistrant::register_dcmds(){
|
||||
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 @@ void VMDynamicLibrariesDCmd::execute(DCmdSource source, TRAPS) {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -383,4 +383,21 @@ public:
|
||||
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
|
||||
|
||||
@ -662,13 +662,13 @@ void gcLogFileStream::write(const char* s, size_t len) {
|
||||
// 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 @@ void gcLogFileStream::rotate_log() {
|
||||
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 @@ void gcLogFileStream::rotate_log() {
|
||||
_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 @@ void gcLogFileStream::rotate_log() {
|
||||
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) {
|
||||
|
||||
@ -115,7 +115,7 @@ class outputStream : public ResourceObj {
|
||||
// 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 @@ class gcLogFileStream : public fileStream {
|
||||
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
|
||||
|
||||
77
hotspot/test/gc/TestGCLogRotationViaJcmd.java
Normal file
77
hotspot/test/gc/TestGCLogRotationViaJcmd.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user