1 /* 2 * Copyright (c) 2012, 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 "jfr/jfrEvents.hpp" 27 #include "jfr/jni/jfrJavaSupport.hpp" 28 #include "jfr/recorder/jfrRecorder.hpp" 29 #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" 30 #include "jfr/recorder/repository/jfrChunkWriter.hpp" 31 #include "jfr/recorder/service/jfrOptionSet.hpp" 32 #include "jfr/recorder/service/jfrPostBox.hpp" 33 #include "jfr/recorder/storage/jfrMemorySpace.inline.hpp" 34 #include "jfr/recorder/storage/jfrStorage.hpp" 35 #include "jfr/recorder/storage/jfrStorageControl.hpp" 36 #include "jfr/recorder/storage/jfrStorageUtils.inline.hpp" 37 #include "jfr/utilities/jfrIterator.hpp" 38 #include "jfr/utilities/jfrTime.hpp" 39 #include "jfr/writers/jfrNativeEventWriter.hpp" 40 #include "logging/log.hpp" 41 #include "runtime/mutexLocker.hpp" 42 #include "runtime/os.inline.hpp" 43 #include "runtime/safepoint.hpp" 44 #include "runtime/thread.hpp" 45 46 typedef JfrStorage::Buffer* BufferPtr; 47 48 static JfrStorage* _instance = NULL; 49 static JfrStorageControl* _control; 50 51 JfrStorage& JfrStorage::instance() { 52 return *_instance; 53 } 54 55 JfrStorage* JfrStorage::create(JfrChunkWriter& chunkwriter, JfrPostBox& post_box) { 56 assert(_instance == NULL, "invariant"); 57 _instance = new JfrStorage(chunkwriter, post_box); 58 return _instance; 59 } 60 61 void JfrStorage::destroy() { 62 if (_instance != NULL) { 63 delete _instance; 64 _instance = NULL; 65 } 66 } 67 68 JfrStorage::JfrStorage(JfrChunkWriter& chunkwriter, JfrPostBox& post_box) : 69 _control(NULL), 70 _global_mspace(NULL), 71 _thread_local_mspace(NULL), 72 _transient_mspace(NULL), 73 _age_mspace(NULL), 74 _chunkwriter(chunkwriter), 75 _post_box(post_box) {} 76 77 JfrStorage::~JfrStorage() { 78 if (_control != NULL) { 79 delete _control; 80 } 81 if (_global_mspace != NULL) { 82 delete _global_mspace; 83 } 84 if (_thread_local_mspace != NULL) { 85 delete _thread_local_mspace; 86 } 87 if (_transient_mspace != NULL) { 88 delete _transient_mspace; 89 } 90 if (_age_mspace != NULL) { 91 delete _age_mspace; 92 } 93 _instance = NULL; 94 } 95 96 static const size_t in_memory_discard_threshold_delta = 2; // start to discard data when the only this number of free buffers are left 97 static const size_t unlimited_mspace_size = 0; 98 static const size_t thread_local_cache_count = 8; 99 static const size_t thread_local_scavenge_threshold = thread_local_cache_count / 2; 100 static const size_t transient_buffer_size_multiplier = 8; // against thread local buffer size 101 102 template <typename Mspace> 103 static Mspace* create_mspace(size_t buffer_size, size_t limit, size_t cache_count, JfrStorage* storage_instance) { 104 Mspace* mspace = new Mspace(buffer_size, limit, cache_count, storage_instance); 105 if (mspace != NULL) { 106 mspace->initialize(); 107 } 108 return mspace; 109 } 110 111 bool JfrStorage::initialize() { 112 assert(_control == NULL, "invariant"); 113 assert(_global_mspace == NULL, "invariant"); 114 assert(_thread_local_mspace == NULL, "invariant"); 115 assert(_transient_mspace == NULL, "invariant"); 116 assert(_age_mspace == NULL, "invariant"); 117 118 const size_t num_global_buffers = (size_t)JfrOptionSet::num_global_buffers(); 119 assert(num_global_buffers >= in_memory_discard_threshold_delta, "invariant"); 120 const size_t memory_size = (size_t)JfrOptionSet::memory_size(); 121 const size_t global_buffer_size = (size_t)JfrOptionSet::global_buffer_size(); 122 const size_t thread_buffer_size = (size_t)JfrOptionSet::thread_buffer_size(); 123 124 _control = new JfrStorageControl(num_global_buffers, num_global_buffers - in_memory_discard_threshold_delta); 125 if (_control == NULL) { 126 return false; 127 } 128 _global_mspace = create_mspace<JfrStorageMspace>(global_buffer_size, memory_size, num_global_buffers, this); 129 if (_global_mspace == NULL) { 130 return false; 131 } 132 _thread_local_mspace = create_mspace<JfrThreadLocalMspace>(thread_buffer_size, unlimited_mspace_size, thread_local_cache_count, this); 133 if (_thread_local_mspace == NULL) { 134 return false; 135 } 136 _transient_mspace = create_mspace<JfrStorageMspace>(thread_buffer_size * transient_buffer_size_multiplier, unlimited_mspace_size, 0, this); 137 if (_transient_mspace == NULL) { 138 return false; 139 } 140 _age_mspace = create_mspace<JfrStorageAgeMspace>(0 /* no extra size except header */, unlimited_mspace_size, num_global_buffers, this); 141 if (_age_mspace == NULL) { 142 return false; 143 } 144 control().set_scavenge_threshold(thread_local_scavenge_threshold); 145 return true; 146 } 147 148 JfrStorageControl& JfrStorage::control() { 149 return *instance()._control; 150 } 151 152 static void log_allocation_failure(const char* msg, size_t size) { 153 log_warning(jfr)("Unable to allocate " SIZE_FORMAT " bytes of %s.", size, msg); 154 } 155 156 BufferPtr JfrStorage::acquire_thread_local(Thread* thread, size_t size /* 0 */) { 157 BufferPtr buffer = mspace_get_to_full(size, instance()._thread_local_mspace, thread); 158 if (buffer == NULL) { 159 log_allocation_failure("thread local_memory", size); 160 return NULL; 161 } 162 assert(buffer->acquired_by_self(), "invariant"); 163 return buffer; 164 } 165 166 BufferPtr JfrStorage::acquire_transient(size_t size, Thread* thread) { 167 BufferPtr buffer = mspace_allocate_transient_lease_to_full(size, instance()._transient_mspace, thread); 168 if (buffer == NULL) { 169 log_allocation_failure("transient memory", size); 170 return NULL; 171 } 172 assert(buffer->acquired_by_self(), "invariant"); 173 assert(buffer->transient(), "invariant"); 174 assert(buffer->lease(), "invariant"); 175 return buffer; 176 } 177 178 static BufferPtr get_lease(size_t size, JfrStorageMspace* mspace, JfrStorage& storage_instance, size_t retry_count, Thread* thread) { 179 assert(size <= mspace->min_elem_size(), "invariant"); 180 while (true) { 181 BufferPtr t = mspace_get_free_lease_with_retry(size, mspace, retry_count, thread); 182 if (t == NULL && storage_instance.control().should_discard()) { 183 storage_instance.discard_oldest(thread); 184 continue; 185 } 186 return t; 187 } 188 } 189 190 static BufferPtr get_promotion_buffer(size_t size, JfrStorageMspace* mspace, JfrStorage& storage_instance, size_t retry_count, Thread* thread) { 191 assert(size <= mspace->min_elem_size(), "invariant"); 192 while (true) { 193 BufferPtr t = mspace_get_free_with_retry(size, mspace, retry_count, thread); 194 if (t == NULL && storage_instance.control().should_discard()) { 195 storage_instance.discard_oldest(thread); 196 continue; 197 } 198 return t; 199 } 200 } 201 202 static const size_t lease_retry = 10; 203 204 BufferPtr JfrStorage::acquire_large(size_t size, Thread* thread) { 205 JfrStorage& storage_instance = instance(); 206 const size_t max_elem_size = storage_instance._global_mspace->min_elem_size(); // min is also max 207 // if not too large and capacity is still available, ask for a lease from the global system 208 if (size < max_elem_size && storage_instance.control().is_global_lease_allowed()) { 209 BufferPtr const buffer = get_lease(size, storage_instance._global_mspace, storage_instance, lease_retry, thread); 210 if (buffer != NULL) { 211 assert(buffer->acquired_by_self(), "invariant"); 212 assert(!buffer->transient(), "invariant"); 213 assert(buffer->lease(), "invariant"); 214 storage_instance.control().increment_leased(); 215 return buffer; 216 } 217 } 218 return acquire_transient(size, thread); 219 } 220 221 static void write_data_loss_event(JfrBuffer* buffer, u8 unflushed_size, Thread* thread) { 222 assert(buffer != NULL, "invariant"); 223 assert(buffer->empty(), "invariant"); 224 const u8 total_data_loss = thread->jfr_thread_local()->add_data_lost(unflushed_size); 225 if (EventDataLoss::is_enabled()) { 226 JfrNativeEventWriter writer(buffer, thread); 227 writer.write<u8>(EventDataLoss::eventId); 228 writer.write(JfrTicks::now()); 229 writer.write(unflushed_size); 230 writer.write(total_data_loss); 231 } 232 } 233 234 static void write_data_loss(BufferPtr buffer, Thread* thread) { 235 assert(buffer != NULL, "invariant"); 236 const size_t unflushed_size = buffer->unflushed_size(); 237 buffer->concurrent_reinitialization(); 238 if (unflushed_size == 0) { 239 return; 240 } 241 write_data_loss_event(buffer, unflushed_size, thread); 242 } 243 244 static const size_t promotion_retry = 100; 245 246 bool JfrStorage::flush_regular_buffer(BufferPtr buffer, Thread* thread) { 247 assert(buffer != NULL, "invariant"); 248 assert(!buffer->lease(), "invariant"); 249 assert(!buffer->transient(), "invariant"); 250 const size_t unflushed_size = buffer->unflushed_size(); 251 if (unflushed_size == 0) { 252 buffer->concurrent_reinitialization(); 253 assert(buffer->empty(), "invariant"); 254 return true; 255 } 256 257 if (buffer->excluded()) { 258 const bool thread_is_excluded = thread->jfr_thread_local()->is_excluded(); 259 buffer->reinitialize(thread_is_excluded); 260 assert(buffer->empty(), "invariant"); 261 if (!thread_is_excluded) { 262 // state change from exclusion to inclusion requires a thread checkpoint 263 JfrCheckpointManager::write_thread_checkpoint(thread); 264 } 265 return true; 266 } 267 268 BufferPtr const promotion_buffer = get_promotion_buffer(unflushed_size, _global_mspace, *this, promotion_retry, thread); 269 if (promotion_buffer == NULL) { 270 write_data_loss(buffer, thread); 271 return false; 272 } 273 assert(promotion_buffer->acquired_by_self(), "invariant"); 274 assert(promotion_buffer->free_size() >= unflushed_size, "invariant"); 275 buffer->concurrent_move_and_reinitialize(promotion_buffer, unflushed_size); 276 assert(buffer->empty(), "invariant"); 277 return true; 278 } 279 280 /* 281 * 1. If the buffer was a "lease" from the global system, release back. 282 * 2. If the buffer is transient (temporal dynamically allocated), retire and register full. 283 * 284 * The buffer is effectively invalidated for the thread post-return, 285 * and the caller should take means to ensure that it is not referenced any longer. 286 */ 287 void JfrStorage::release_large(BufferPtr buffer, Thread* thread) { 288 assert(buffer != NULL, "invariant"); 289 assert(buffer->lease(), "invariant"); 290 assert(buffer->acquired_by_self(), "invariant"); 291 buffer->clear_lease(); 292 if (buffer->transient()) { 293 buffer->set_retired(); 294 register_full(buffer, thread); 295 } else { 296 buffer->release(); 297 control().decrement_leased(); 298 } 299 } 300 301 static JfrAgeNode* new_age_node(BufferPtr buffer, JfrStorageAgeMspace* age_mspace, Thread* thread) { 302 assert(buffer != NULL, "invariant"); 303 assert(age_mspace != NULL, "invariant"); 304 return mspace_allocate_transient(0, age_mspace, thread); 305 } 306 307 static void log_registration_failure(size_t unflushed_size) { 308 log_warning(jfr)("Unable to register a full buffer of " SIZE_FORMAT " bytes.", unflushed_size); 309 log_debug(jfr, system)("Cleared 1 full buffer of " SIZE_FORMAT " bytes.", unflushed_size); 310 } 311 312 static void handle_registration_failure(BufferPtr buffer) { 313 assert(buffer != NULL, "invariant"); 314 assert(buffer->retired(), "invariant"); 315 const size_t unflushed_size = buffer->unflushed_size(); 316 buffer->concurrent_reinitialization(); 317 log_registration_failure(unflushed_size); 318 } 319 320 static JfrAgeNode* get_free_age_node(JfrStorageAgeMspace* age_mspace, Thread* thread) { 321 assert(JfrBuffer_lock->owned_by_self(), "invariant"); 322 return mspace_get_free_with_detach(0, age_mspace, thread); 323 } 324 325 static bool insert_full_age_node(JfrAgeNode* age_node, JfrStorageAgeMspace* age_mspace, Thread* thread) { 326 assert(JfrBuffer_lock->owned_by_self(), "invariant"); 327 assert(age_node != NULL, "invariant"); 328 assert(age_node->acquired_by_self(), "invariant"); 329 assert(age_node->retired_buffer()->retired(), "invariant"); 330 age_node->release(); // drop identity claim on age node when inserting to full list 331 assert(age_node->identity() == NULL, "invariant"); 332 age_mspace->insert_full_head(age_node); 333 return true; 334 } 335 336 static bool full_buffer_registration(BufferPtr buffer, JfrStorageAgeMspace* age_mspace, JfrStorageControl& control, Thread* thread) { 337 assert(buffer != NULL, "invariant"); 338 assert(buffer->retired(), "invariant"); 339 assert(age_mspace != NULL, "invariant"); 340 MutexLocker lock(JfrBuffer_lock, Mutex::_no_safepoint_check_flag); 341 JfrAgeNode* age_node = get_free_age_node(age_mspace, thread); 342 if (age_node == NULL) { 343 age_node = new_age_node(buffer, age_mspace, thread); 344 if (age_node == NULL) { 345 return false; 346 } 347 } 348 assert(age_node != NULL, "invariant"); 349 assert(age_node->acquired_by_self(), "invariant"); 350 age_node->set_retired_buffer(buffer); 351 control.increment_full(); 352 return insert_full_age_node(age_node, age_mspace, thread); 353 } 354 355 void JfrStorage::register_full(BufferPtr buffer, Thread* thread) { 356 assert(buffer != NULL, "invariant"); 357 assert(buffer->retired(), "invariant"); 358 assert(buffer->acquired_by(thread), "invariant"); 359 if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) { 360 handle_registration_failure(buffer); 361 } 362 if (control().should_post_buffer_full_message()) { 363 _post_box.post(MSG_FULLBUFFER); 364 } 365 } 366 367 void JfrStorage::lock() { 368 assert(!JfrBuffer_lock->owned_by_self(), "invariant"); 369 JfrBuffer_lock->lock_without_safepoint_check(); 370 } 371 372 void JfrStorage::unlock() { 373 assert(JfrBuffer_lock->owned_by_self(), "invariant"); 374 JfrBuffer_lock->unlock(); 375 } 376 377 #ifdef ASSERT 378 bool JfrStorage::is_locked() const { 379 return JfrBuffer_lock->owned_by_self(); 380 } 381 #endif 382 383 // don't use buffer on return, it is gone 384 void JfrStorage::release(BufferPtr buffer, Thread* thread) { 385 assert(buffer != NULL, "invariant"); 386 assert(!buffer->lease(), "invariant"); 387 assert(!buffer->transient(), "invariant"); 388 assert(!buffer->retired(), "invariant"); 389 if (!buffer->empty()) { 390 if (!flush_regular_buffer(buffer, thread)) { 391 buffer->concurrent_reinitialization(); 392 } 393 } 394 assert(buffer->empty(), "invariant"); 395 assert(buffer->identity() != NULL, "invariant"); 396 control().increment_dead(); 397 buffer->set_retired(); 398 } 399 400 void JfrStorage::release_thread_local(BufferPtr buffer, Thread* thread) { 401 assert(buffer != NULL, "invariant"); 402 JfrStorage& storage_instance = instance(); 403 storage_instance.release(buffer, thread); 404 if (storage_instance.control().should_scavenge()) { 405 storage_instance._post_box.post(MSG_DEADBUFFER); 406 } 407 } 408 409 static void log_discard(size_t count, size_t amount, size_t current) { 410 if (log_is_enabled(Debug, jfr, system)) { 411 assert(count > 0, "invariant"); 412 log_debug(jfr, system)("Cleared " SIZE_FORMAT " full buffer(s) of " SIZE_FORMAT" bytes.", count, amount); 413 log_debug(jfr, system)("Current number of full buffers " SIZE_FORMAT "", current); 414 } 415 } 416 417 void JfrStorage::discard_oldest(Thread* thread) { 418 if (JfrBuffer_lock->try_lock()) { 419 if (!control().should_discard()) { 420 // another thread handled it 421 return; 422 } 423 const size_t num_full_pre_discard = control().full_count(); 424 size_t num_full_post_discard = 0; 425 size_t discarded_size = 0; 426 while (true) { 427 JfrAgeNode* const oldest_age_node = _age_mspace->full_tail(); 428 if (oldest_age_node == NULL) { 429 break; 430 } 431 assert(oldest_age_node->identity() == NULL, "invariant"); 432 BufferPtr const buffer = oldest_age_node->retired_buffer(); 433 assert(buffer->retired(), "invariant"); 434 discarded_size += buffer->discard(); 435 assert(buffer->unflushed_size() == 0, "invariant"); 436 num_full_post_discard = control().decrement_full(); 437 mspace_release_full(oldest_age_node, _age_mspace); 438 if (buffer->transient()) { 439 mspace_release_full(buffer, _transient_mspace); 440 continue; 441 } 442 buffer->reinitialize(); 443 buffer->release(); // publish 444 break; 445 } 446 JfrBuffer_lock->unlock(); 447 const size_t number_of_discards = num_full_pre_discard - num_full_post_discard; 448 if (number_of_discards > 0) { 449 log_discard(number_of_discards, discarded_size, num_full_post_discard); 450 } 451 } 452 } 453 454 #ifdef ASSERT 455 typedef const BufferPtr ConstBufferPtr; 456 457 static void assert_flush_precondition(ConstBufferPtr cur, size_t used, bool native, const Thread* t) { 458 assert(t != NULL, "invariant"); 459 assert(cur != NULL, "invariant"); 460 assert(cur->pos() + used <= cur->end(), "invariant"); 461 assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant"); 462 } 463 464 static void assert_flush_regular_precondition(ConstBufferPtr cur, const u1* const cur_pos, size_t used, size_t req, const Thread* t) { 465 assert(t != NULL, "invariant"); 466 assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant"); 467 assert(cur != NULL, "invariant"); 468 assert(!cur->lease(), "invariant"); 469 assert(cur_pos != NULL, "invariant"); 470 assert(req >= used, "invariant"); 471 } 472 473 static void assert_provision_large_precondition(ConstBufferPtr cur, size_t used, size_t req, const Thread* t) { 474 assert(cur != NULL, "invariant"); 475 assert(t != NULL, "invariant"); 476 assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant"); 477 assert(req >= used, "invariant"); 478 } 479 480 static void assert_flush_large_precondition(ConstBufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { 481 assert(t != NULL, "invariant"); 482 assert(cur != NULL, "invariant"); 483 assert(cur->lease(), "invariant"); 484 assert(!cur->excluded(), "invariant"); 485 assert(cur_pos != NULL, "invariant"); 486 assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant"); 487 assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant"); 488 assert(req >= used, "invariant"); 489 assert(cur != t->jfr_thread_local()->shelved_buffer(), "invariant"); 490 } 491 #endif // ASSERT 492 493 BufferPtr JfrStorage::flush(BufferPtr cur, size_t used, size_t req, bool native, Thread* t) { 494 debug_only(assert_flush_precondition(cur, used, native, t);) 495 const u1* const cur_pos = cur->pos(); 496 req += used; 497 // requested size now encompass the outstanding used size 498 return cur->lease() ? instance().flush_large(cur, cur_pos, used, req, native, t) : 499 instance().flush_regular(cur, cur_pos, used, req, native, t); 500 } 501 502 BufferPtr JfrStorage::flush_regular(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { 503 debug_only(assert_flush_regular_precondition(cur, cur_pos, used, req, t);) 504 // A flush is needed before memcpy since a non-large buffer is thread stable 505 // (thread local). The flush will not modify memory in addresses above pos() 506 // which is where the "used / uncommitted" data resides. It is therefore both 507 // possible and valid to migrate data after the flush. This is however only 508 // the case for stable thread local buffers; it is not the case for large buffers. 509 if (!cur->empty()) { 510 flush_regular_buffer(cur, t); 511 if (cur->excluded()) { 512 return cur; 513 } 514 } 515 assert(t->jfr_thread_local()->shelved_buffer() == NULL, "invariant"); 516 if (cur->free_size() >= req) { 517 // simplest case, no switching of buffers 518 if (used > 0) { 519 memcpy(cur->pos(), (void*)cur_pos, used); 520 } 521 assert(native ? t->jfr_thread_local()->native_buffer() == cur : t->jfr_thread_local()->java_buffer() == cur, "invariant"); 522 return cur; 523 } 524 // Going for a "larger-than-regular" buffer. 525 // Shelve the current buffer to make room for a temporary lease. 526 t->jfr_thread_local()->shelve_buffer(cur); 527 return provision_large(cur, cur_pos, used, req, native, t); 528 } 529 530 static BufferPtr store_buffer_to_thread_local(BufferPtr buffer, JfrThreadLocal* jfr_thread_local, bool native) { 531 assert(buffer != NULL, "invariant"); 532 if (native) { 533 jfr_thread_local->set_native_buffer(buffer); 534 } else { 535 jfr_thread_local->set_java_buffer(buffer); 536 } 537 return buffer; 538 } 539 540 static BufferPtr restore_shelved_buffer(bool native, Thread* t) { 541 JfrThreadLocal* const tl = t->jfr_thread_local(); 542 BufferPtr shelved = tl->shelved_buffer(); 543 assert(shelved != NULL, "invariant"); 544 tl->shelve_buffer(NULL); 545 // restore shelved buffer back as primary 546 return store_buffer_to_thread_local(shelved, tl, native); 547 } 548 549 BufferPtr JfrStorage::flush_large(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { 550 debug_only(assert_flush_large_precondition(cur, cur_pos, used, req, native, t);) 551 // Can the "regular" buffer (now shelved) accommodate the requested size? 552 BufferPtr shelved = t->jfr_thread_local()->shelved_buffer(); 553 assert(shelved != NULL, "invariant"); 554 if (shelved->free_size() >= req) { 555 if (req > 0) { 556 memcpy(shelved->pos(), (void*)cur_pos, (size_t)used); 557 } 558 // release and invalidate 559 release_large(cur, t); 560 return restore_shelved_buffer(native, t); 561 } 562 // regular too small 563 return provision_large(cur, cur_pos, used, req, native, t); 564 } 565 566 static BufferPtr large_fail(BufferPtr cur, bool native, JfrStorage& storage_instance, Thread* t) { 567 assert(cur != NULL, "invariant"); 568 assert(t != NULL, "invariant"); 569 if (cur->lease()) { 570 storage_instance.release_large(cur, t); 571 } 572 return restore_shelved_buffer(native, t); 573 } 574 575 // Always returns a non-null buffer. 576 // If accommodating the large request fails, the shelved buffer is returned 577 // even though it might be smaller than the requested size. 578 // Caller needs to ensure if the size was successfully accommodated. 579 BufferPtr JfrStorage::provision_large(BufferPtr cur, const u1* const cur_pos, size_t used, size_t req, bool native, Thread* t) { 580 debug_only(assert_provision_large_precondition(cur, used, req, t);) 581 assert(t->jfr_thread_local()->shelved_buffer() != NULL, "invariant"); 582 BufferPtr const buffer = acquire_large(req, t); 583 if (buffer == NULL) { 584 // unable to allocate and serve the request 585 return large_fail(cur, native, *this, t); 586 } 587 // ok managed to acquire a "large" buffer for the requested size 588 assert(buffer->free_size() >= req, "invariant"); 589 assert(buffer->lease(), "invariant"); 590 // transfer outstanding data 591 memcpy(buffer->pos(), (void*)cur_pos, used); 592 if (cur->lease()) { 593 release_large(cur, t); 594 // don't use current anymore, it is gone 595 } 596 return store_buffer_to_thread_local(buffer, t->jfr_thread_local(), native); 597 } 598 599 typedef UnBufferedWriteToChunk<JfrBuffer> WriteOperation; 600 typedef MutexedWriteOp<WriteOperation> MutexedWriteOperation; 601 typedef ConcurrentWriteOp<WriteOperation> ConcurrentWriteOperation; 602 603 typedef Retired<JfrBuffer, true> NonRetired; 604 typedef Excluded<JfrBuffer, true> NonExcluded; 605 typedef CompositeOperation<NonRetired, NonExcluded> BufferPredicate; 606 typedef PredicatedMutexedWriteOp<WriteOperation, BufferPredicate> ThreadLocalMutexedWriteOperation; 607 typedef PredicatedConcurrentWriteOp<WriteOperation, BufferPredicate> ThreadLocalConcurrentWriteOperation; 608 609 size_t JfrStorage::write() { 610 const size_t full_elements = write_full(); 611 WriteOperation wo(_chunkwriter); 612 NonRetired nr; 613 NonExcluded ne; 614 BufferPredicate bp(&nr, &ne); 615 ThreadLocalConcurrentWriteOperation tlwo(wo, bp); 616 process_full_list(tlwo, _thread_local_mspace); 617 ConcurrentWriteOperation cwo(wo); 618 process_free_list(cwo, _global_mspace); 619 return full_elements + wo.elements(); 620 } 621 622 size_t JfrStorage::write_at_safepoint() { 623 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); 624 WriteOperation wo(_chunkwriter); 625 MutexedWriteOperation writer(wo); // mutexed write mode 626 NonRetired nr; 627 NonExcluded ne; 628 BufferPredicate bp(&nr, &ne); 629 ThreadLocalMutexedWriteOperation tlmwo(wo, bp); 630 process_full_list(tlmwo, _thread_local_mspace); 631 assert(_transient_mspace->is_free_empty(), "invariant"); 632 process_full_list(writer, _transient_mspace); 633 assert(_global_mspace->is_full_empty(), "invariant"); 634 process_free_list(writer, _global_mspace); 635 return wo.elements(); 636 } 637 638 typedef DiscardOp<DefaultDiscarder<JfrStorage::Buffer> > DiscardOperation; 639 typedef ReleaseOp<JfrStorageMspace> ReleaseOperation; 640 typedef CompositeOperation<MutexedWriteOperation, ReleaseOperation> FullOperation; 641 642 size_t JfrStorage::clear() { 643 const size_t full_elements = clear_full(); 644 DiscardOperation discarder(concurrent); // concurrent discard mode 645 process_full_list(discarder, _thread_local_mspace); 646 assert(_transient_mspace->is_free_empty(), "invariant"); 647 process_full_list(discarder, _transient_mspace); 648 assert(_global_mspace->is_full_empty(), "invariant"); 649 process_free_list(discarder, _global_mspace); 650 return full_elements + discarder.elements(); 651 } 652 653 static void insert_free_age_nodes(JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, JfrAgeNode* tail, size_t count) { 654 if (tail != NULL) { 655 assert(tail->next() == NULL, "invariant"); 656 assert(head != NULL, "invariant"); 657 assert(head->prev() == NULL, "invariant"); 658 MutexLocker buffer_lock(JfrBuffer_lock, Mutex::_no_safepoint_check_flag); 659 age_mspace->insert_free_tail(head, tail, count); 660 } 661 } 662 663 template <typename Processor> 664 static void process_age_list(Processor& processor, JfrStorageAgeMspace* age_mspace, JfrAgeNode* head, size_t count) { 665 assert(age_mspace != NULL, "invariant"); 666 assert(head != NULL, "invariant"); 667 assert(count > 0, "invariant"); 668 JfrAgeNode* node = head; 669 JfrAgeNode* last = NULL; 670 while (node != NULL) { 671 last = node; 672 assert(node->identity() == NULL, "invariant"); 673 BufferPtr const buffer = node->retired_buffer(); 674 assert(buffer != NULL, "invariant"); 675 assert(buffer->retired(), "invariant"); 676 processor.process(buffer); 677 // at this point, buffer is already live or destroyed 678 JfrAgeNode* const next = (JfrAgeNode*)node->next(); 679 if (node->transient()) { 680 // detach 681 last = (JfrAgeNode*)last->prev(); 682 if (last != NULL) { 683 last->set_next(next); 684 } else { 685 head = next; 686 } 687 if (next != NULL) { 688 next->set_prev(last); 689 } 690 --count; 691 age_mspace->deallocate(node); 692 } 693 node = next; 694 } 695 insert_free_age_nodes(age_mspace, head, last, count); 696 } 697 698 template <typename Processor> 699 static size_t process_full(Processor& processor, JfrStorageControl& control, JfrStorageAgeMspace* age_mspace) { 700 assert(age_mspace != NULL, "invariant"); 701 if (age_mspace->is_full_empty()) { 702 // nothing to do 703 return 0; 704 } 705 size_t count; 706 JfrAgeNode* head; 707 { 708 // fetch age list 709 MutexLocker buffer_lock(JfrBuffer_lock, Mutex::_no_safepoint_check_flag); 710 count = age_mspace->full_count(); 711 head = age_mspace->clear_full(); 712 control.reset_full(); 713 } 714 assert(head != NULL, "invariant"); 715 assert(count > 0, "invariant"); 716 process_age_list(processor, age_mspace, head, count); 717 return count; 718 } 719 720 static void log(size_t count, size_t amount, bool clear = false) { 721 if (log_is_enabled(Debug, jfr, system)) { 722 if (count > 0) { 723 log_debug(jfr, system)("%s " SIZE_FORMAT " full buffer(s) of " SIZE_FORMAT" B of data%s", 724 clear ? "Discarded" : "Wrote", count, amount, clear ? "." : " to chunk."); 725 } 726 } 727 } 728 729 // full writer 730 // Assumption is retired only; exclusive access 731 // MutexedWriter -> ReleaseOp 732 // 733 size_t JfrStorage::write_full() { 734 assert(_chunkwriter.is_valid(), "invariant"); 735 Thread* const thread = Thread::current(); 736 WriteOperation wo(_chunkwriter); 737 MutexedWriteOperation writer(wo); // a retired buffer implies mutexed access 738 ReleaseOperation ro(_transient_mspace, thread); 739 FullOperation cmd(&writer, &ro); 740 const size_t count = process_full(cmd, control(), _age_mspace); 741 if (0 == count) { 742 assert(0 == writer.elements(), "invariant"); 743 return 0; 744 } 745 const size_t size = writer.size(); 746 log(count, size); 747 return count; 748 } 749 750 size_t JfrStorage::clear_full() { 751 DiscardOperation discarder(mutexed); // a retired buffer implies mutexed access 752 const size_t count = process_full(discarder, control(), _age_mspace); 753 if (0 == count) { 754 assert(0 == discarder.elements(), "invariant"); 755 return 0; 756 } 757 const size_t size = discarder.size(); 758 log(count, size, true); 759 return count; 760 } 761 762 static void scavenge_log(size_t count, size_t amount, size_t current) { 763 if (count > 0) { 764 if (log_is_enabled(Debug, jfr, system)) { 765 log_debug(jfr, system)("Released " SIZE_FORMAT " dead buffer(s) of " SIZE_FORMAT" B of data.", count, amount); 766 log_debug(jfr, system)("Current number of dead buffers " SIZE_FORMAT "", current); 767 } 768 } 769 } 770 771 template <typename Mspace> 772 class Scavenger { 773 private: 774 JfrStorageControl& _control; 775 Mspace* _mspace; 776 size_t _count; 777 size_t _amount; 778 public: 779 typedef typename Mspace::Type Type; 780 Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {} 781 bool process(Type* t) { 782 if (t->retired()) { 783 assert(t->identity() != NULL, "invariant"); 784 assert(t->empty(), "invariant"); 785 assert(!t->transient(), "invariant"); 786 assert(!t->lease(), "invariant"); 787 ++_count; 788 _amount += t->total_size(); 789 if (t->excluded()) { 790 t->clear_excluded(); 791 } 792 assert(!t->excluded(), "invariant"); 793 t->clear_retired(); 794 t->release(); 795 _control.decrement_dead(); 796 mspace_release_full_critical(t, _mspace); 797 } 798 return true; 799 } 800 size_t processed() const { return _count; } 801 size_t amount() const { return _amount; } 802 }; 803 804 size_t JfrStorage::scavenge() { 805 JfrStorageControl& ctrl = control(); 806 if (ctrl.dead_count() == 0) { 807 return 0; 808 } 809 Scavenger<JfrThreadLocalMspace> scavenger(ctrl, _thread_local_mspace); 810 process_full_list(scavenger, _thread_local_mspace); 811 const size_t count = scavenger.processed(); 812 if (0 == count) { 813 assert(0 == scavenger.amount(), "invariant"); 814 return 0; 815 } 816 scavenge_log(count, scavenger.amount(), ctrl.dead_count()); 817 return count; 818 }