1 /*
   2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "classfile/javaClasses.inline.hpp"
  27 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceIdEpoch.hpp"
  28 #include "jfr/recorder/service/jfrOptionSet.hpp"
  29 #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp"
  30 #include "jfr/recorder/repository/jfrChunkWriter.hpp"
  31 #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp"
  32 #include "jfr/recorder/stringpool/jfrStringPool.hpp"
  33 #include "jfr/recorder/stringpool/jfrStringPoolWriter.hpp"
  34 #include "jfr/utilities/jfrTypes.hpp"
  35 #include "logging/log.hpp"
  36 #include "runtime/mutexLocker.hpp"
  37 #include "runtime/orderAccess.hpp"
  38 #include "runtime/safepoint.hpp"
  39 #include "runtime/thread.inline.hpp"
  40 
  41 typedef JfrStringPool::Buffer* BufferPtr;
  42 
  43 static JfrStringPool* _instance = NULL;
  44 static uint64_t store_generation = 0;
  45 static uint64_t serialized_generation = 0;
  46 
  47 inline void set_generation(uint64_t value, uint64_t* const dest) {
  48   assert(dest != NULL, "invariant");
  49   Atomic::release_store(dest, value);
  50 }
  51 static void increment_store_generation() {
  52   const uint64_t current_serialized = Atomic::load_acquire(&serialized_generation);
  53   const uint64_t current_stored = Atomic::load_acquire(&store_generation);
  54   if (current_serialized == current_stored) {
  55     set_generation(current_serialized + 1, &store_generation);
  56   }
  57 }
  58 
  59 static bool increment_serialized_generation() {
  60   const uint64_t current_stored = Atomic::load_acquire(&store_generation);
  61   const uint64_t current_serialized = Atomic::load_acquire(&serialized_generation);
  62   if (current_stored != current_serialized) {
  63     set_generation(current_stored, &serialized_generation);
  64     return true;
  65   }
  66   return false;
  67 }
  68 
  69 bool JfrStringPool::is_modified() {
  70   return increment_serialized_generation();
  71 }
  72 
  73 JfrStringPool& JfrStringPool::instance() {
  74   return *_instance;
  75 }
  76 
  77 JfrStringPool* JfrStringPool::create(JfrChunkWriter& cw) {
  78   store_generation = 0;
  79   serialized_generation = 0;
  80   assert(_instance == NULL, "invariant");
  81   _instance = new JfrStringPool(cw);
  82   return _instance;
  83 }
  84 
  85 void JfrStringPool::destroy() {
  86   assert(_instance != NULL, "invariant");
  87   delete _instance;
  88   _instance = NULL;
  89 }
  90 
  91 JfrStringPool::JfrStringPool(JfrChunkWriter& cw) : _free_list_mspace(NULL), _lock(NULL), _chunkwriter(cw) {}
  92 
  93 JfrStringPool::~JfrStringPool() {
  94   if (_free_list_mspace != NULL) {
  95     delete _free_list_mspace;
  96   }
  97   if (_lock != NULL) {
  98     delete _lock;
  99   }
 100 }
 101 
 102 static const size_t unlimited_mspace_size = 0;
 103 static const size_t string_pool_cache_count = 2;
 104 static const size_t string_pool_buffer_size = 512 * K;
 105 
 106 bool JfrStringPool::initialize() {
 107   assert(_free_list_mspace == NULL, "invariant");
 108   _free_list_mspace = new JfrStringPoolMspace(string_pool_buffer_size, unlimited_mspace_size, string_pool_cache_count, this);
 109   if (_free_list_mspace == NULL || !_free_list_mspace->initialize()) {
 110     return false;
 111   }
 112   assert(_lock == NULL, "invariant");
 113   _lock = new Mutex(Monitor::leaf - 1, "Checkpoint mutex", Mutex::_allow_vm_block_flag, Monitor::_safepoint_check_never);
 114   return _lock != NULL;
 115 }
 116 
 117 /*
 118 * If the buffer was a "lease" from the global system, release back.
 119 *
 120 * The buffer is effectively invalidated for the thread post-return,
 121 * and the caller should take means to ensure that it is not referenced any longer.
 122 */
 123 static void release(BufferPtr buffer, Thread* thread) {
 124   assert(buffer != NULL, "invariant");
 125   assert(buffer->lease(), "invariant");
 126   assert(buffer->acquired_by_self(), "invariant");
 127   buffer->clear_lease();
 128   buffer->release();
 129 }
 130 
 131 BufferPtr JfrStringPool::flush(BufferPtr old, size_t used, size_t requested, Thread* thread) {
 132   assert(old != NULL, "invariant");
 133   assert(old->lease(), "invariant");
 134   if (0 == requested) {
 135     // indicates a lease is being returned
 136     release(old, thread);
 137     return NULL;
 138   }
 139   // migration of in-flight information
 140   BufferPtr const new_buffer = lease_buffer(thread, used + requested);
 141   if (new_buffer != NULL) {
 142     migrate_outstanding_writes(old, new_buffer, used, requested);
 143   }
 144   release(old, thread);
 145   return new_buffer; // might be NULL
 146 }
 147 
 148 static const size_t lease_retry = 10;
 149 
 150 BufferPtr JfrStringPool::lease_buffer(Thread* thread, size_t size /* 0 */) {
 151   BufferPtr buffer = mspace_get_free_lease_with_retry(size, instance()._free_list_mspace, lease_retry, thread);
 152   if (buffer == NULL) {
 153     buffer = mspace_allocate_transient_lease_to_free(size,  instance()._free_list_mspace, thread);
 154   }
 155   assert(buffer->acquired_by_self(), "invariant");
 156   assert(buffer->lease(), "invariant");
 157   return buffer;
 158 }
 159 
 160 bool JfrStringPool::add(bool epoch, jlong id, jstring string, JavaThread* jt) {
 161   assert(jt != NULL, "invariant");
 162   const bool current_epoch = JfrTraceIdEpoch::epoch();
 163   if (current_epoch != epoch) {
 164     return current_epoch;
 165   }
 166   {
 167     JfrStringPoolWriter writer(jt);
 168     writer.write(id);
 169     writer.write(string);
 170     writer.inc_nof_strings();
 171   }
 172   increment_store_generation();
 173   return current_epoch;
 174 }
 175 
 176 template <template <typename> class Operation>
 177 class StringPoolOp {
 178  public:
 179   typedef JfrStringPoolBuffer Type;
 180  private:
 181   Operation<Type> _op;
 182   Thread* _thread;
 183   size_t _strings_processed;
 184  public:
 185   StringPoolOp() : _op(), _thread(Thread::current()), _strings_processed(0) {}
 186   StringPoolOp(JfrChunkWriter& writer, Thread* thread) : _op(writer), _thread(thread), _strings_processed(0) {}
 187   bool write(Type* buffer, const u1* data, size_t size) {
 188     assert(buffer->acquired_by(_thread) || buffer->retired(), "invariant");
 189     const uint64_t nof_strings_used = buffer->string_count();
 190     assert(nof_strings_used > 0, "invariant");
 191     buffer->set_string_top(buffer->string_top() + nof_strings_used);
 192     // "size processed" for string pool buffers is the number of processed string elements
 193     _strings_processed += nof_strings_used;
 194     return _op.write(buffer, data, size);
 195   }
 196   size_t processed() { return _strings_processed; }
 197 };
 198 
 199 template <typename T>
 200 class StringPoolDiscarderStub {
 201  public:
 202   typedef T Type;
 203   bool write(Type* buffer, const u1* data, size_t size) {
 204     // stub only, discard happens at higher level
 205     return true;
 206   }
 207 };
 208 
 209 typedef StringPoolOp<UnBufferedWriteToChunk> WriteOperation;
 210 typedef StringPoolOp<StringPoolDiscarderStub> DiscardOperation;
 211 typedef ExclusiveOp<WriteOperation> ExclusiveWriteOperation;
 212 typedef ExclusiveOp<DiscardOperation> ExclusiveDiscardOperation;
 213 typedef ReleaseOp<JfrStringPoolMspace> StringPoolReleaseOperation;
 214 typedef CompositeOperation<ExclusiveWriteOperation, StringPoolReleaseOperation> StringPoolWriteOperation;
 215 typedef CompositeOperation<ExclusiveDiscardOperation, StringPoolReleaseOperation> StringPoolDiscardOperation;
 216 
 217 size_t JfrStringPool::write() {
 218   Thread* const thread = Thread::current();
 219   WriteOperation wo(_chunkwriter, thread);
 220   ExclusiveWriteOperation ewo(wo);
 221   StringPoolReleaseOperation spro(_free_list_mspace, thread, false);
 222   StringPoolWriteOperation spwo(&ewo, &spro);
 223   assert(_free_list_mspace->is_full_empty(), "invariant");
 224   process_free_list(spwo, _free_list_mspace);
 225   return wo.processed();
 226 }
 227 
 228 size_t JfrStringPool::write_at_safepoint() {
 229   assert(SafepointSynchronize::is_at_safepoint(), "invariant");
 230   return write();
 231 }
 232 
 233 size_t JfrStringPool::clear() {
 234   increment_serialized_generation();
 235   DiscardOperation discard_operation;
 236   ExclusiveDiscardOperation edo(discard_operation);
 237   StringPoolReleaseOperation spro(_free_list_mspace, Thread::current(), false);
 238   StringPoolDiscardOperation spdo(&edo, &spro);
 239   assert(_free_list_mspace->is_full_empty(), "invariant");
 240   process_free_list(spdo, _free_list_mspace);
 241   return discard_operation.processed();
 242 }
 243 
 244 void JfrStringPool::register_full(BufferPtr t, Thread* thread) {
 245   // nothing here at the moment
 246   assert(t != NULL, "invariant");
 247   assert(t->acquired_by(thread), "invariant");
 248   assert(t->retired(), "invariant");
 249 }
 250 
 251 void JfrStringPool::lock() {
 252   assert(!_lock->owned_by_self(), "invariant");
 253   _lock->lock_without_safepoint_check();
 254 }
 255 
 256 void JfrStringPool::unlock() {
 257   _lock->unlock();
 258 }
 259 
 260 #ifdef ASSERT
 261 bool JfrStringPool::is_locked() const {
 262   return _lock->owned_by_self();
 263 }
 264 #endif