blob: b9d83d675f5d87bb344c5008d87140f592a48fcc [file] [log] [blame]
/*
* Copyright (c) 2016, 2019, 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.
*
*/
#include "precompiled.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
#include "jfr/recorder/repository/jfrChunkWriter.hpp"
#include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
#include "jfr/recorder/stringpool/jfrStringPool.hpp"
#include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "logging/log.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.inline.hpp"
typedef JfrStringPool::Buffer* BufferPtr;
static JfrStringPool* _instance = NULL;
static uint64_t store_generation = 0;
static uint64_t serialized_generation = 0;
inline void set_generation(uint64_t value, uint64_t* const dest) {
assert(dest != NULL, "invariant");
OrderAccess::release_store(dest, value);
}
static void increment_store_generation() {
const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
if (current_serialized == current_stored) {
set_generation(current_serialized + 1, &store_generation);
}
}
static bool increment_serialized_generation() {
const uint64_t current_stored = OrderAccess::load_acquire(&store_generation);
const uint64_t current_serialized = OrderAccess::load_acquire(&serialized_generation);
if (current_stored != current_serialized) {
set_generation(current_stored, &serialized_generation);
return true;
}
return false;
}
bool JfrStringPool::is_modified() {
return increment_serialized_generation();
}
JfrStringPool& JfrStringPool::instance() {
return *_instance;
}
JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
store_generation = 0;
serialized_generation = 0;
assert(_instance == NULL, "invariant");
_instance = new JfrStringPool(cw);
return _instance;
}
void JfrStringPool::destroy() {
assert(_instance != NULL, "invariant");
delete _instance;
_instance = NULL;
}
JfrStringPool::JfrStringPool(JfrChunkWriter& cw) : _free_list_mspace(NULL), _lock(NULL), _chunkwriter(cw) {}
JfrStringPool::~JfrStringPool() {
if (_free_list_mspace != NULL) {
delete _free_list_mspace;
}
if (_lock != NULL) {
delete _lock;
}
}
static const size_t unlimited_mspace_size = 0;
static const size_t string_pool_cache_count = 2;
static const size_t string_pool_buffer_size = 512 * K;
bool JfrStringPool::initialize() {
assert(_free_list_mspace == NULL, "invariant");
_free_list_mspace = new JfrStringPoolMspace(string_pool_buffer_size, unlimited_mspace_size, string_pool_cache_count, this);
if (_free_list_mspace == NULL || !_free_list_mspace->initialize()) {
return false;
}
assert(_lock == NULL, "invariant");
_lock = new Mutex(Monitor::leaf - 1, "Checkpoint mutex", Mutex::_allow_vm_block_flag, Monitor::_safepoint_check_never);
return _lock != NULL;
}
/*
* If the buffer was a "lease" from the global system, release back.
*
* The buffer is effectively invalidated for the thread post-return,
* and the caller should take means to ensure that it is not referenced any longer.
*/
static void release(BufferPtr buffer, Thread* thread) {
assert(buffer != NULL, "invariant");
assert(buffer->lease(), "invariant");
assert(buffer->acquired_by_self(), "invariant");
buffer->clear_lease();
buffer->release();
}
BufferPtr JfrStringPool::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) {
assert(old != NULL, "invariant");
assert(old->lease(), "invariant");
if (0 == requested) {
// indicates a lease is being returned
release(old, thread);
return NULL;
}
// migration of in-flight information
BufferPtr const new_buffer = lease_buffer(thread, used + requested);
if (new_buffer != NULL) {
migrate_outstanding_writes(old, new_buffer, used, requested);
}
release(old, thread);
return new_buffer; // might be NULL
}
static const size_t lease_retry = 10;
BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) {
BufferPtr buffer = mspace_get_free_lease_with_retry(size, instance()._free_list_mspace, lease_retry, thread);
if (buffer == NULL) {
buffer = mspace_allocate_transient_lease_to_free(size, instance()._free_list_mspace, thread);
}
assert(buffer->acquired_by_self(), "invariant");
assert(buffer->lease(), "invariant");
return buffer;
}
bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
assert(jt != NULL, "invariant");
const bool current_epoch = JfrTraceIdEpoch::epoch();
if (current_epoch != epoch) {
return current_epoch;
}
{
JfrStringPoolWriter writer(jt);
writer.write(id);
writer.write(string);
writer.inc_nof_strings();
}
increment_store_generation();
return current_epoch;
}
template <template <typename> class Operation>
class StringPoolOp {
public:
typedef JfrStringPoolBuffer Type;
private:
Operation<Type> _op;
Thread* _thread;
size_t _strings_processed;
public:
StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {}
StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {}
bool write(Type* buffer, const u1* data, size_t size) {
assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant");
const uint64_t nof_strings_used = buffer->string_count();
assert(nof_strings_used > 0, "invariant");
buffer->set_string_top(buffer->string_top() + nof_strings_used);
// "size processed" for string pool buffers is the number of processed string elements
_strings_processed += nof_strings_used;
return _op.write(buffer, data, size);
}
size_t processed() { return _strings_processed; }
};
template <typename T>
class StringPoolDiscarderStub {
public:
typedef T Type;
bool write(Type* buffer, const u1* data, size_t size) {
// stub only, discard happens at higher level
return true;
}
};
typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
size_t JfrStringPool::write() {
Thread* const thread = Thread::current();
WriteOperation wo(_chunkwriter, thread);
ExclusiveWriteOperation ewo(wo);
StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
StringPoolWriteOperation spwo(&ewo, &spro);
assert(_free_list_mspace->is_full_empty(), "invariant");
process_free_list(spwo, _free_list_mspace);
return wo.processed();
}
size_t JfrStringPool::write_at_safepoint() {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
return write();
}
size_t JfrStringPool::clear() {
increment_serialized_generation();
DiscardOperation discard_operation;
ExclusiveDiscardOperation edo(discard_operation);
StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
StringPoolDiscardOperation spdo(&edo, &spro);
assert(_free_list_mspace->is_full_empty(), "invariant");
process_free_list(spdo, _free_list_mspace);
return discard_operation.processed();
}
void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
// nothing here at the moment
assert(t != NULL, "invariant");
assert(t->acquired_by(thread), "invariant");
assert(t->retired(), "invariant");
}
void JfrStringPool::lock() {
assert(!_lock->owned_by_self(), "invariant");
_lock->lock_without_safepoint_check();
}
void JfrStringPool::unlock() {
_lock->unlock();
}
#ifdef ASSERT
bool JfrStringPool::is_locked() const {
return _lock->owned_by_self();
}
#endif