698 lines
24 KiB
Diff
698 lines
24 KiB
Diff
|
|
From 577f318d824d91e5deb8b6b82dd211583cb93cac Mon Sep 17 00:00:00 2001
|
||
|
|
From: eapen <zhangyipeng7@huawei.com>
|
||
|
|
Date: Thu, 15 Dec 2022 10:37:31 +0800
|
||
|
|
Subject: [PATCH 18/33] I68TO2: 8229517: Support for optional asynchronous/buffered
|
||
|
|
logging
|
||
|
|
---
|
||
|
|
hotspot/src/os/windows/vm/os_windows.cpp | 1 +
|
||
|
|
hotspot/src/share/vm/runtime/arguments.cpp | 10 ++
|
||
|
|
hotspot/src/share/vm/runtime/globals.hpp | 9 ++
|
||
|
|
hotspot/src/share/vm/runtime/init.cpp | 2 +
|
||
|
|
hotspot/src/share/vm/runtime/logAsyncWriter.cpp | 164 ++++++++++++++++++++++++
|
||
|
|
hotspot/src/share/vm/runtime/logAsyncWriter.hpp | 159 +++++++++++++++++++++++
|
||
|
|
hotspot/src/share/vm/runtime/os.hpp | 1 +
|
||
|
|
hotspot/src/share/vm/runtime/thread.cpp | 26 +++-
|
||
|
|
hotspot/src/share/vm/runtime/vmStructs.cpp | 2 +
|
||
|
|
hotspot/src/share/vm/utilities/linkedlist.hpp | 47 +++++--
|
||
|
|
hotspot/src/share/vm/utilities/ostream.cpp | 26 ++++
|
||
|
|
hotspot/src/share/vm/utilities/ostream.hpp | 3 +
|
||
|
|
12 files changed, 440 insertions(+), 10 deletions(-)
|
||
|
|
create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.cpp
|
||
|
|
create mode 100644 hotspot/src/share/vm/runtime/logAsyncWriter.hpp
|
||
|
|
|
||
|
|
diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp
|
||
|
|
index 25122de..cc31126 100644
|
||
|
|
--- a/hotspot/src/os/windows/vm/os_windows.cpp
|
||
|
|
+++ b/hotspot/src/os/windows/vm/os_windows.cpp
|
||
|
|
@@ -562,6 +562,7 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
|
||
|
|
case os::pgc_thread:
|
||
|
|
case os::cgc_thread:
|
||
|
|
case os::watcher_thread:
|
||
|
|
+ case os::asynclog_thread:
|
||
|
|
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp
|
||
|
|
index 91e2ce0..fba3d4b 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/arguments.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/arguments.cpp
|
||
|
|
@@ -2269,6 +2269,16 @@ bool Arguments::verify_percentage(uintx value, const char* name) {
|
||
|
|
// no gc log rotation when log file not supplied or
|
||
|
|
// NumberOfGCLogFiles is 0
|
||
|
|
void check_gclog_consistency() {
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ if (Arguments::gc_log_filename() == NULL) {
|
||
|
|
+ jio_fprintf(defaultStream::output_stream(),
|
||
|
|
+ "To enable Async GC log, use -Xloggc:<filename> -XX:UseAsyncGCLog\n"
|
||
|
|
+ "Async GC log is turned off\n");
|
||
|
|
+ UseAsyncGCLog = false;
|
||
|
|
+
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
if (UseGCLogFileRotation) {
|
||
|
|
if ((Arguments::gc_log_filename() == NULL) || (NumberOfGCLogFiles == 0)) {
|
||
|
|
jio_fprintf(defaultStream::output_stream(),
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
index 41b1392..10e4e7f 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/globals.hpp
|
||
|
|
@@ -4104,6 +4104,15 @@ class CommandLineFlags {
|
||
|
|
\
|
||
|
|
JFR_ONLY(product(bool, LogJFR, false, \
|
||
|
|
"Enable JFR logging (consider +Verbose)")) \
|
||
|
|
+ \
|
||
|
|
+ product(bool, UseAsyncGCLog, false, \
|
||
|
|
+ "Enable asynchronous GC logging") \
|
||
|
|
+ \
|
||
|
|
+ product(uintx, AsyncLogBufferSize, 2*M, \
|
||
|
|
+ "Memory budget (in bytes) for the buffer of Asynchronous") \
|
||
|
|
+ \
|
||
|
|
+ diagnostic(bool, PrintAsyncGCLog, false, \
|
||
|
|
+ "Print some information of Async GC Log") \
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Macros for factoring of globals
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp
|
||
|
|
index d2e0f22..b185409 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/init.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/init.cpp
|
||
|
|
@@ -32,6 +32,7 @@
|
||
|
|
#include "runtime/handles.inline.hpp"
|
||
|
|
#include "runtime/icache.hpp"
|
||
|
|
#include "runtime/init.hpp"
|
||
|
|
+#include "runtime/logAsyncWriter.hpp"
|
||
|
|
#include "runtime/safepoint.hpp"
|
||
|
|
#include "runtime/sharedRuntime.hpp"
|
||
|
|
#include "services/memTracker.hpp"
|
||
|
|
@@ -106,6 +107,7 @@ jint init_globals() {
|
||
|
|
if (status != JNI_OK)
|
||
|
|
return status;
|
||
|
|
|
||
|
|
+ AsyncLogWriter::initialize();
|
||
|
|
interpreter_init(); // before any methods loaded
|
||
|
|
invocationCounter_init(); // before any methods loaded
|
||
|
|
marksweep_init();
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.cpp b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000..750a23f
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/logAsyncWriter.cpp
|
||
|
|
@@ -0,0 +1,164 @@
|
||
|
|
+/*
|
||
|
|
+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||
|
|
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
|
||
|
|
+ *
|
||
|
|
+ */
|
||
|
|
+#include "precompiled.hpp"
|
||
|
|
+#include "runtime/atomic.hpp"
|
||
|
|
+#include "runtime/logAsyncWriter.hpp"
|
||
|
|
+#include "utilities/ostream.hpp"
|
||
|
|
+
|
||
|
|
+class AsyncLogWriter::AsyncLogLocker : public StackObj {
|
||
|
|
+ public:
|
||
|
|
+ AsyncLogLocker() {
|
||
|
|
+ assert(_instance != NULL, "AsyncLogWriter::_lock is unavailable");
|
||
|
|
+ _instance->_lock.wait();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ ~AsyncLogLocker() {
|
||
|
|
+ _instance->_lock.signal();
|
||
|
|
+ }
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::enqueue_locked(const AsyncLogMessage& msg) {
|
||
|
|
+ if (_buffer.size() >= _buffer_max_size) {
|
||
|
|
+ // drop the enqueueing message.
|
||
|
|
+ os::free(msg.message());
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ assert(_buffer.size() < _buffer_max_size, "_buffer is over-sized.");
|
||
|
|
+ _buffer.push_back(msg);
|
||
|
|
+ _sem.signal();
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::enqueue(const char* msg) {
|
||
|
|
+ AsyncLogMessage m(os::strdup(msg));
|
||
|
|
+
|
||
|
|
+ { // critical area
|
||
|
|
+ AsyncLogLocker locker;
|
||
|
|
+ enqueue_locked(m);
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+AsyncLogWriter::AsyncLogWriter()
|
||
|
|
+ : NamedThread(),
|
||
|
|
+ _lock(1), _sem(0), _io_sem(1),
|
||
|
|
+ _initialized(false),
|
||
|
|
+ _buffer_max_size(AsyncLogBufferSize / sizeof(AsyncLogMessage)) {
|
||
|
|
+ if (os::create_thread(this, os::asynclog_thread)) {
|
||
|
|
+ _initialized = true;
|
||
|
|
+ set_name("AsyncLog Thread");
|
||
|
|
+ } else {
|
||
|
|
+ if (PrintAsyncGCLog) {
|
||
|
|
+ tty->print_cr("AsyncLogging failed to create thread. Falling back to synchronous logging.");
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (PrintAsyncGCLog) {
|
||
|
|
+ tty->print_cr("The maximum entries of AsyncLogBuffer: " SIZE_FORMAT ", estimated memory use: " SIZE_FORMAT " bytes",
|
||
|
|
+ _buffer_max_size, AsyncLogBufferSize);
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::write() {
|
||
|
|
+ // Use kind of copy-and-swap idiom here.
|
||
|
|
+ // Empty 'logs' swaps the content with _buffer.
|
||
|
|
+ // Along with logs destruction, all processed messages are deleted.
|
||
|
|
+ //
|
||
|
|
+ // The operation 'pop_all()' is done in O(1). All I/O jobs are then performed without
|
||
|
|
+ // lock protection. This guarantees I/O jobs don't block logsites.
|
||
|
|
+ AsyncLogBuffer logs;
|
||
|
|
+ bool own_io = false;
|
||
|
|
+
|
||
|
|
+ { // critical region
|
||
|
|
+ AsyncLogLocker locker;
|
||
|
|
+
|
||
|
|
+ _buffer.pop_all(&logs);
|
||
|
|
+ own_io = _io_sem.trywait();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ LinkedListIterator<AsyncLogMessage> it(logs.head());
|
||
|
|
+ if (!own_io) {
|
||
|
|
+ _io_sem.wait();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ bool flush = false;
|
||
|
|
+ while (!it.is_empty()) {
|
||
|
|
+ AsyncLogMessage* e = it.next();
|
||
|
|
+ char* msg = e->message();
|
||
|
|
+
|
||
|
|
+ if (msg != NULL) {
|
||
|
|
+ flush = true;
|
||
|
|
+ ((gcLogFileStream*)gclog_or_tty)->write_blocking(msg, strlen(msg));
|
||
|
|
+ os::free(msg);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ if (flush) {
|
||
|
|
+ ((gcLogFileStream*)gclog_or_tty)->fileStream::flush();
|
||
|
|
+ }
|
||
|
|
+ _io_sem.signal();
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::run() {
|
||
|
|
+ while (true) {
|
||
|
|
+ // The value of a semphore cannot be negative. Therefore, the current thread falls asleep
|
||
|
|
+ // when its value is zero. It will be waken up when new messages are enqueued.
|
||
|
|
+ _sem.wait();
|
||
|
|
+ write();
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+AsyncLogWriter* AsyncLogWriter::_instance = NULL;
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::initialize() {
|
||
|
|
+ if (!UseAsyncGCLog) return;
|
||
|
|
+
|
||
|
|
+ assert(_instance == NULL, "initialize() should only be invoked once.");
|
||
|
|
+
|
||
|
|
+ AsyncLogWriter* self = new AsyncLogWriter();
|
||
|
|
+ if (self->_initialized) {
|
||
|
|
+ OrderAccess::release_store_ptr(&AsyncLogWriter::_instance, self);
|
||
|
|
+ os::start_thread(self);
|
||
|
|
+ if (PrintAsyncGCLog) {
|
||
|
|
+ tty->print_cr("Async logging thread started.");
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+AsyncLogWriter* AsyncLogWriter::instance() {
|
||
|
|
+ return _instance;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+// write() acquires and releases _io_sem even _buffer is empty.
|
||
|
|
+// This guarantees all logging I/O of dequeued messages are done when it returns.
|
||
|
|
+void AsyncLogWriter::flush() {
|
||
|
|
+ if (_instance != NULL) {
|
||
|
|
+ _instance->write();
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void AsyncLogWriter::print_on(outputStream* st) const{
|
||
|
|
+ st->print("\"%s\" ", name());
|
||
|
|
+ Thread::print_on(st);
|
||
|
|
+ st->cr();
|
||
|
|
+}
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/logAsyncWriter.hpp b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000..5242426
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/logAsyncWriter.hpp
|
||
|
|
@@ -0,0 +1,159 @@
|
||
|
|
+/*
|
||
|
|
+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||
|
|
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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.
|
||
|
|
+ *
|
||
|
|
+ */
|
||
|
|
+#ifndef SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP
|
||
|
|
+#define SHARE_VM_RUNTIME_LOGASYNCWRITER_HPP
|
||
|
|
+#include "memory/resourceArea.hpp"
|
||
|
|
+#include "runtime/semaphore.hpp"
|
||
|
|
+#include "utilities/linkedlist.hpp"
|
||
|
|
+
|
||
|
|
+template <typename E, MEMFLAGS F>
|
||
|
|
+class LinkedListDeque : private LinkedListImpl<E, ResourceObj::C_HEAP, F> {
|
||
|
|
+ private:
|
||
|
|
+ LinkedListNode<E>* _tail;
|
||
|
|
+ size_t _size;
|
||
|
|
+
|
||
|
|
+ public:
|
||
|
|
+ LinkedListDeque() : _tail(NULL), _size(0) {}
|
||
|
|
+ void push_back(const E& e) {
|
||
|
|
+ if (!_tail) {
|
||
|
|
+ _tail = this->add(e);
|
||
|
|
+ } else {
|
||
|
|
+ _tail = this->insert_after(e, _tail);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ ++_size;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ // pop all elements to logs.
|
||
|
|
+ void pop_all(LinkedList<E>* logs) {
|
||
|
|
+ logs->move(static_cast<LinkedList<E>* >(this));
|
||
|
|
+ _tail = NULL;
|
||
|
|
+ _size = 0;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ void pop_all(LinkedListDeque<E, F>* logs) {
|
||
|
|
+ logs->_size = _size;
|
||
|
|
+ logs->_tail = _tail;
|
||
|
|
+ pop_all(static_cast<LinkedList<E>* >(logs));
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ void pop_front() {
|
||
|
|
+ LinkedListNode<E>* h = this->unlink_head();
|
||
|
|
+ if (h == _tail) {
|
||
|
|
+ _tail = NULL;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (h != NULL) {
|
||
|
|
+ --_size;
|
||
|
|
+ this->delete_node(h);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ size_t size() const { return _size; }
|
||
|
|
+
|
||
|
|
+ const E* front() const {
|
||
|
|
+ return this->_head == NULL ? NULL : this->_head->peek();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ const E* back() const {
|
||
|
|
+ return _tail == NULL ? NULL : _tail->peek();
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ LinkedListNode<E>* head() const {
|
||
|
|
+ return this->_head;
|
||
|
|
+ }
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+class AsyncLogMessage {
|
||
|
|
+ char* _message;
|
||
|
|
+
|
||
|
|
+public:
|
||
|
|
+ AsyncLogMessage(char* msg)
|
||
|
|
+ : _message(msg) {}
|
||
|
|
+
|
||
|
|
+ // placeholder for LinkedListImpl.
|
||
|
|
+ bool equals(const AsyncLogMessage& o) const { return false; }
|
||
|
|
+
|
||
|
|
+ char* message() const { return _message; }
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+typedef LinkedListDeque<AsyncLogMessage, mtInternal> AsyncLogBuffer;
|
||
|
|
+
|
||
|
|
+//
|
||
|
|
+// ASYNC LOGGING SUPPORT
|
||
|
|
+//
|
||
|
|
+// Summary:
|
||
|
|
+// Async Logging is working on the basis of singleton AsyncLogWriter, which manages an intermediate buffer and a flushing thread.
|
||
|
|
+//
|
||
|
|
+// Interface:
|
||
|
|
+//
|
||
|
|
+// initialize() is called once when JVM is initialized. It creates and initializes the singleton instance of AsyncLogWriter.
|
||
|
|
+// Once async logging is established, there's no way to turn it off.
|
||
|
|
+//
|
||
|
|
+// instance() is MT-safe and returns the pointer of the singleton instance if and only if async logging is enabled and has well
|
||
|
|
+// initialized. Clients can use its return value to determine async logging is established or not.
|
||
|
|
+//
|
||
|
|
+// The basic operation of AsyncLogWriter is enqueue(). 2 overloading versions of it are provided to match LogOutput::write().
|
||
|
|
+// They are both MT-safe and non-blocking. Derived classes of LogOutput can invoke the corresponding enqueue() in write() and
|
||
|
|
+// return 0. AsyncLogWriter is responsible of copying neccessary data.
|
||
|
|
+//
|
||
|
|
+// The static member function flush() is designated to flush out all pending messages when JVM is terminating.
|
||
|
|
+// In normal JVM termination, flush() is invoked in LogConfiguration::finalize(). flush() is MT-safe and can be invoked arbitrary
|
||
|
|
+// times. It is no-op if async logging is not established.
|
||
|
|
+//
|
||
|
|
+class AsyncLogWriter : public NamedThread {
|
||
|
|
+ class AsyncLogLocker;
|
||
|
|
+
|
||
|
|
+ static AsyncLogWriter* _instance;
|
||
|
|
+ // _lock(1) denotes a critional region.
|
||
|
|
+ Semaphore _lock;
|
||
|
|
+ // _sem is a semaphore whose value denotes how many messages have been enqueued.
|
||
|
|
+ // It decreases in AsyncLogWriter::run()
|
||
|
|
+ Semaphore _sem;
|
||
|
|
+ // A lock of IO
|
||
|
|
+ Semaphore _io_sem;
|
||
|
|
+
|
||
|
|
+ volatile bool _initialized;
|
||
|
|
+ AsyncLogBuffer _buffer;
|
||
|
|
+
|
||
|
|
+ const size_t _buffer_max_size;
|
||
|
|
+
|
||
|
|
+ AsyncLogWriter();
|
||
|
|
+ void enqueue_locked(const AsyncLogMessage& msg);
|
||
|
|
+ void write();
|
||
|
|
+ void run();
|
||
|
|
+
|
||
|
|
+ public:
|
||
|
|
+ void enqueue(const char* msg);
|
||
|
|
+
|
||
|
|
+ static AsyncLogWriter* instance();
|
||
|
|
+ static void initialize();
|
||
|
|
+ static void flush();
|
||
|
|
+ // Printing
|
||
|
|
+ void print_on(outputStream* st) const;
|
||
|
|
+
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+#endif // SHARE_LOGGING_LOGASYNCWRITER_HPP
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp
|
||
|
|
index acc57f4..5f41e96 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/os.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/os.hpp
|
||
|
|
@@ -463,6 +463,7 @@ class os: AllStatic {
|
||
|
|
java_thread,
|
||
|
|
compiler_thread,
|
||
|
|
watcher_thread,
|
||
|
|
+ asynclog_thread, // dedicated to flushing logs
|
||
|
|
os_thread
|
||
|
|
};
|
||
|
|
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp
|
||
|
|
index cacab59..61627e4 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/thread.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/thread.cpp
|
||
|
|
@@ -57,6 +57,7 @@
|
||
|
|
#include "runtime/java.hpp"
|
||
|
|
#include "runtime/javaCalls.hpp"
|
||
|
|
#include "runtime/jniPeriodicChecker.hpp"
|
||
|
|
+#include "runtime/logAsyncWriter.hpp"
|
||
|
|
#include "runtime/memprofiler.hpp"
|
||
|
|
#include "runtime/mutexLocker.hpp"
|
||
|
|
#include "runtime/objectMonitor.hpp"
|
||
|
|
@@ -881,7 +882,9 @@ void Thread::print_on_error(outputStream* st, char* buf, int buflen) const {
|
||
|
|
else if (is_GC_task_thread()) st->print("GCTaskThread");
|
||
|
|
else if (is_Watcher_thread()) st->print("WatcherThread");
|
||
|
|
else if (is_ConcurrentGC_thread()) st->print("ConcurrentGCThread");
|
||
|
|
- else st->print("Thread");
|
||
|
|
+ else if (this == AsyncLogWriter::instance()) {
|
||
|
|
+ st->print("%s", this->name());
|
||
|
|
+ } else st->print("Thread");
|
||
|
|
|
||
|
|
st->print(" [stack: " PTR_FORMAT "," PTR_FORMAT "]",
|
||
|
|
_stack_base - _stack_size, _stack_base);
|
||
|
|
@@ -4387,6 +4390,12 @@ void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format
|
||
|
|
st->cr();
|
||
|
|
}
|
||
|
|
CompileBroker::print_compiler_threads_on(st);
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
|
||
|
|
+ if (aio_writer != NULL) {
|
||
|
|
+ aio_writer->print_on(st);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
st->flush();
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -4432,6 +4441,21 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf, int b
|
||
|
|
wt->print_on_error(st, buf, buflen);
|
||
|
|
st->cr();
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
|
||
|
|
+ if (aio_writer != NULL) {
|
||
|
|
+ bool is_current = (current == aio_writer);
|
||
|
|
+ found_current = found_current || is_current;
|
||
|
|
+ st->print("%s", is_current ? "=>" : " ");
|
||
|
|
+
|
||
|
|
+ st->print(PTR_FORMAT, aio_writer);
|
||
|
|
+ st->print(" ");
|
||
|
|
+ aio_writer->print_on_error(st, buf, buflen);
|
||
|
|
+ st->cr();
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
if (!found_current) {
|
||
|
|
st->cr();
|
||
|
|
st->print("=>" PTR_FORMAT " (exited) ", current);
|
||
|
|
diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp
|
||
|
|
index ab20f5c..5d1cf2b 100644
|
||
|
|
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp
|
||
|
|
@@ -97,6 +97,7 @@
|
||
|
|
#include "runtime/sharedRuntime.hpp"
|
||
|
|
#include "runtime/stubRoutines.hpp"
|
||
|
|
#include "runtime/thread.inline.hpp"
|
||
|
|
+#include "runtime/logAsyncWriter.hpp"
|
||
|
|
#include "runtime/virtualspace.hpp"
|
||
|
|
#include "runtime/vmStructs.hpp"
|
||
|
|
#include "utilities/array.hpp"
|
||
|
|
@@ -1599,6 +1600,7 @@ typedef TwoOopHashtable<Symbol*, mtClass> SymbolTwoOopHashtable;
|
||
|
|
declare_type(Thread, ThreadShadow) \
|
||
|
|
declare_type(NamedThread, Thread) \
|
||
|
|
declare_type(WatcherThread, Thread) \
|
||
|
|
+ declare_type(AsyncLogWriter, Thread) \
|
||
|
|
declare_type(JavaThread, Thread) \
|
||
|
|
declare_type(JvmtiAgentThread, JavaThread) \
|
||
|
|
declare_type(ServiceThread, JavaThread) \
|
||
|
|
diff --git a/hotspot/src/share/vm/utilities/linkedlist.hpp b/hotspot/src/share/vm/utilities/linkedlist.hpp
|
||
|
|
index a76c15c..f4f2a9b 100644
|
||
|
|
--- a/hotspot/src/share/vm/utilities/linkedlist.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/utilities/linkedlist.hpp
|
||
|
|
@@ -40,6 +40,25 @@ template <class E> class LinkedListNode : public ResourceObj {
|
||
|
|
E _data; // embedded content
|
||
|
|
LinkedListNode<E>* _next; // next entry
|
||
|
|
|
||
|
|
+ // Select member function 'bool U::equals(const U&) const' if 'U' is of class
|
||
|
|
+ // type. This works because of the "Substitution Failure Is Not An Error"
|
||
|
|
+ // (SFINAE) rule. Notice that this version of 'equal' will also be chosen for
|
||
|
|
+ // class types which don't define a corresponding 'equals()' method (and will
|
||
|
|
+ // result in a compilation error for them). It is not easily possible to
|
||
|
|
+ // specialize this 'equal()' function exclusively for class types which define
|
||
|
|
+ // the correct 'equals()' function because that function can be in a base
|
||
|
|
+ // class, a dependent base class or have a compatible but slightly different
|
||
|
|
+ // signature.
|
||
|
|
+ template <class U>
|
||
|
|
+ static bool equal(const U& a, const U& b, bool (U::*t)(const U&) const) {
|
||
|
|
+ return a.equals(b);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ template <class U>
|
||
|
|
+ static bool equal(const U& a, const U& b, ...) {
|
||
|
|
+ return a == b;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
protected:
|
||
|
|
LinkedListNode() : _next(NULL) { }
|
||
|
|
|
||
|
|
@@ -51,6 +70,10 @@ template <class E> class LinkedListNode : public ResourceObj {
|
||
|
|
|
||
|
|
E* data() { return &_data; }
|
||
|
|
const E* peek() const { return &_data; }
|
||
|
|
+
|
||
|
|
+ bool equals(const E& t) const {
|
||
|
|
+ return equal<E>(_data, t, NULL);
|
||
|
|
+ }
|
||
|
|
};
|
||
|
|
|
||
|
|
// A linked list interface. It does not specify
|
||
|
|
@@ -62,6 +85,7 @@ template <class E> class LinkedList : public ResourceObj {
|
||
|
|
|
||
|
|
public:
|
||
|
|
LinkedList() : _head(NULL) { }
|
||
|
|
+ virtual ~LinkedList() {}
|
||
|
|
|
||
|
|
inline void set_head(LinkedListNode<E>* h) { _head = h; }
|
||
|
|
inline LinkedListNode<E>* head() const { return _head; }
|
||
|
|
@@ -182,7 +206,7 @@ template <class E, ResourceObj::allocation_type T = ResourceObj::C_HEAP,
|
||
|
|
|
||
|
|
virtual LinkedListNode<E>* find_node(const E& e) {
|
||
|
|
LinkedListNode<E>* p = this->head();
|
||
|
|
- while (p != NULL && !p->peek()->equals(e)) {
|
||
|
|
+ while (p != NULL && !p->equals(e)) {
|
||
|
|
p = p->next();
|
||
|
|
}
|
||
|
|
return p;
|
||
|
|
@@ -229,7 +253,7 @@ template <class E, ResourceObj::allocation_type T = ResourceObj::C_HEAP,
|
||
|
|
LinkedListNode<E>* prev = NULL;
|
||
|
|
|
||
|
|
while (tmp != NULL) {
|
||
|
|
- if (tmp->peek()->equals(e)) {
|
||
|
|
+ if (tmp->equals(e)) {
|
||
|
|
return remove_after(prev);
|
||
|
|
}
|
||
|
|
prev = tmp;
|
||
|
|
@@ -396,16 +420,21 @@ template <class E, int (*FUNC)(const E&, const E&),
|
||
|
|
// Iterates all entries in the list
|
||
|
|
template <class E> class LinkedListIterator : public StackObj {
|
||
|
|
private:
|
||
|
|
- LinkedListNode<E>* _p;
|
||
|
|
- bool _is_empty;
|
||
|
|
+ mutable LinkedListNode<E>* _p;
|
||
|
|
+
|
||
|
|
public:
|
||
|
|
- LinkedListIterator(LinkedListNode<E>* head) : _p(head) {
|
||
|
|
- _is_empty = (head == NULL);
|
||
|
|
- }
|
||
|
|
+ LinkedListIterator(LinkedListNode<E>* head) : _p(head) { }
|
||
|
|
+
|
||
|
|
+ bool is_empty() const { return _p == NULL; }
|
||
|
|
|
||
|
|
- bool is_empty() const { return _is_empty; }
|
||
|
|
+ E* next() {
|
||
|
|
+ if (_p == NULL) return NULL;
|
||
|
|
+ E* e = _p->data();
|
||
|
|
+ _p = _p->next();
|
||
|
|
+ return e;
|
||
|
|
+ }
|
||
|
|
|
||
|
|
- const E* next() {
|
||
|
|
+ const E* next() const {
|
||
|
|
if (_p == NULL) return NULL;
|
||
|
|
const E* e = _p->peek();
|
||
|
|
_p = _p->next();
|
||
|
|
diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp
|
||
|
|
index 14d82ad..5d40559 100644
|
||
|
|
--- a/hotspot/src/share/vm/utilities/ostream.cpp
|
||
|
|
+++ b/hotspot/src/share/vm/utilities/ostream.cpp
|
||
|
|
@@ -30,6 +30,7 @@
|
||
|
|
#include "runtime/mutexLocker.hpp"
|
||
|
|
#include "runtime/os.hpp"
|
||
|
|
#include "runtime/vmThread.hpp"
|
||
|
|
+#include "runtime/logAsyncWriter.hpp"
|
||
|
|
#include "utilities/defaultStream.hpp"
|
||
|
|
#include "utilities/ostream.hpp"
|
||
|
|
#include "utilities/top.hpp"
|
||
|
|
@@ -876,6 +877,17 @@ gcLogFileStream::gcLogFileStream(const char* file_name) : _file_lock(NULL) {
|
||
|
|
}
|
||
|
|
|
||
|
|
void gcLogFileStream::write(const char* s, size_t len) {
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
|
||
|
|
+ if (aio_writer != NULL) {
|
||
|
|
+ aio_writer->enqueue(s);
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ write_blocking(s, len);
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+void gcLogFileStream::write_blocking(const char* s, size_t len) {
|
||
|
|
if (_file != NULL) {
|
||
|
|
// we can't use Thread::current() here because thread may be NULL
|
||
|
|
// in early stage(ostream_init_log)
|
||
|
|
@@ -1047,6 +1059,17 @@ void gcLogFileStream::rotate_log_impl(bool force, outputStream* out) {
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+void gcLogFileStream::flush() {
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
|
||
|
|
+ if (aio_writer != NULL) {
|
||
|
|
+ // do nothing
|
||
|
|
+ return;
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+ fileStream::flush();
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
defaultStream* defaultStream::instance = NULL;
|
||
|
|
int defaultStream::_output_fd = 1;
|
||
|
|
int defaultStream::_error_fd = 2;
|
||
|
|
@@ -1456,6 +1479,9 @@ void ostream_exit() {
|
||
|
|
|
||
|
|
// ostream_abort() is called by os::abort() when VM is about to die.
|
||
|
|
void ostream_abort() {
|
||
|
|
+ if (UseAsyncGCLog) {
|
||
|
|
+ AsyncLogWriter::flush();
|
||
|
|
+ }
|
||
|
|
// Here we can't delete gclog_or_tty and tty, just flush their output
|
||
|
|
if (gclog_or_tty) gclog_or_tty->flush();
|
||
|
|
if (tty) tty->flush();
|
||
|
|
diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp
|
||
|
|
index d0f9aac..85ff599 100644
|
||
|
|
--- a/hotspot/src/share/vm/utilities/ostream.hpp
|
||
|
|
+++ b/hotspot/src/share/vm/utilities/ostream.hpp
|
||
|
|
@@ -254,6 +254,7 @@ class gcLogFileStream : public fileStream {
|
||
|
|
gcLogFileStream(const char* file_name);
|
||
|
|
~gcLogFileStream();
|
||
|
|
virtual void write(const char* c, size_t len);
|
||
|
|
+ void write_blocking(const char* c, size_t len);
|
||
|
|
virtual void rotate_log(bool force, outputStream* out = NULL);
|
||
|
|
void dump_loggc_header();
|
||
|
|
|
||
|
|
@@ -263,6 +264,8 @@ class gcLogFileStream : public fileStream {
|
||
|
|
((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize));
|
||
|
|
}
|
||
|
|
|
||
|
|
+ virtual void flush();
|
||
|
|
+
|
||
|
|
};
|
||
|
|
|
||
|
|
#ifndef PRODUCT
|
||
|
|
--
|
||
|
|
1.8.3.1
|