| 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 "jfr/jni/jfrJavaSupport.hpp" |
| 27 | #include "jfr/leakprofiler/leakProfiler.hpp" |
| 28 | #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" |
| 29 | #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
| 30 | #include "jfr/recorder/jfrRecorder.hpp" |
| 31 | #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" |
| 32 | #include "jfr/recorder/checkpoint/jfrMetadataEvent.hpp" |
| 33 | #include "jfr/recorder/repository/jfrChunkRotation.hpp" |
| 34 | #include "jfr/recorder/repository/jfrChunkWriter.hpp" |
| 35 | #include "jfr/recorder/repository/jfrRepository.hpp" |
| 36 | #include "jfr/recorder/service/jfrPostBox.hpp" |
| 37 | #include "jfr/recorder/service/jfrRecorderService.hpp" |
| 38 | #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
| 39 | #include "jfr/recorder/storage/jfrStorage.hpp" |
| 40 | #include "jfr/recorder/storage/jfrStorageControl.hpp" |
| 41 | #include "jfr/recorder/stringpool/jfrStringPool.hpp" |
| 42 | #include "jfr/utilities/jfrAllocation.hpp" |
| 43 | #include "jfr/utilities/jfrTime.hpp" |
| 44 | #include "jfr/writers/jfrJavaEventWriter.hpp" |
| 45 | #include "jfr/utilities/jfrTypes.hpp" |
| 46 | #include "logging/log.hpp" |
| 47 | #include "memory/resourceArea.hpp" |
| 48 | #include "runtime/atomic.hpp" |
| 49 | #include "runtime/handles.inline.hpp" |
| 50 | #include "runtime/mutexLocker.hpp" |
| 51 | #include "runtime/orderAccess.hpp" |
| 52 | #include "runtime/os.hpp" |
| 53 | #include "runtime/safepoint.hpp" |
| 54 | #include "runtime/thread.inline.hpp" |
| 55 | #include "runtime/vmOperations.hpp" |
| 56 | #include "runtime/vmThread.hpp" |
| 57 | |
| 58 | // set data iff *dest == NULL |
| 59 | static bool try_set(void* const data, void** dest, bool clear) { |
| 60 | assert(data != NULL, "invariant" ); |
| 61 | const void* const current = OrderAccess::load_acquire(dest); |
| 62 | if (current != NULL) { |
| 63 | if (current != data) { |
| 64 | // already set |
| 65 | return false; |
| 66 | } |
| 67 | assert(current == data, "invariant" ); |
| 68 | if (!clear) { |
| 69 | // recursion disallowed |
| 70 | return false; |
| 71 | } |
| 72 | } |
| 73 | return Atomic::cmpxchg(clear ? NULL : data, dest, current) == current; |
| 74 | } |
| 75 | |
| 76 | static void* rotation_thread = NULL; |
| 77 | static const int rotation_try_limit = 1000; |
| 78 | static const int rotation_retry_sleep_millis = 10; |
| 79 | |
| 80 | class RotationLock : public StackObj { |
| 81 | private: |
| 82 | Thread* const _thread; |
| 83 | bool _acquired; |
| 84 | |
| 85 | void log(bool recursion) { |
| 86 | assert(!_acquired, "invariant" ); |
| 87 | const char* error_msg = NULL; |
| 88 | if (recursion) { |
| 89 | error_msg = "Unable to issue rotation due to recursive calls." ; |
| 90 | } |
| 91 | else { |
| 92 | error_msg = "Unable to issue rotation due to wait timeout." ; |
| 93 | } |
| 94 | log_info(jfr)( // For user, should not be "jfr, system" |
| 95 | "%s" , error_msg); |
| 96 | } |
| 97 | public: |
| 98 | RotationLock(Thread* thread) : _thread(thread), _acquired(false) { |
| 99 | assert(_thread != NULL, "invariant" ); |
| 100 | if (_thread == rotation_thread) { |
| 101 | // recursion not supported |
| 102 | log(true); |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | // limited to not spin indefinitely |
| 107 | for (int i = 0; i < rotation_try_limit; ++i) { |
| 108 | if (try_set(_thread, &rotation_thread, false)) { |
| 109 | _acquired = true; |
| 110 | assert(_thread == rotation_thread, "invariant" ); |
| 111 | return; |
| 112 | } |
| 113 | if (_thread->is_Java_thread()) { |
| 114 | // in order to allow the system to move to a safepoint |
| 115 | MutexLocker msg_lock(JfrMsg_lock); |
| 116 | JfrMsg_lock->wait(rotation_retry_sleep_millis); |
| 117 | } |
| 118 | else { |
| 119 | os::naked_short_sleep(rotation_retry_sleep_millis); |
| 120 | } |
| 121 | } |
| 122 | log(false); |
| 123 | } |
| 124 | |
| 125 | ~RotationLock() { |
| 126 | assert(_thread != NULL, "invariant" ); |
| 127 | if (_acquired) { |
| 128 | assert(_thread == rotation_thread, "invariant" ); |
| 129 | while (!try_set(_thread, &rotation_thread, true)); |
| 130 | } |
| 131 | } |
| 132 | bool not_acquired() const { return !_acquired; } |
| 133 | }; |
| 134 | |
| 135 | static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { |
| 136 | const int64_t prev_cp_offset = cw.previous_checkpoint_offset(); |
| 137 | const int64_t prev_cp_relative_offset = 0 == prev_cp_offset ? 0 : prev_cp_offset - cw.current_offset(); |
| 138 | cw.reserve(sizeof(u4)); |
| 139 | cw.write<u8>(EVENT_CHECKPOINT); |
| 140 | cw.write(JfrTicks::now()); |
| 141 | cw.write((int64_t)0); |
| 142 | cw.write(prev_cp_relative_offset); // write previous checkpoint offset delta |
| 143 | cw.write<bool>(false); // flushpoint |
| 144 | cw.write((u4)1); // nof types in this checkpoint |
| 145 | cw.write(type_id); |
| 146 | const int64_t number_of_elements_offset = cw.current_offset(); |
| 147 | cw.reserve(sizeof(u4)); |
| 148 | return number_of_elements_offset; |
| 149 | } |
| 150 | |
| 151 | template <typename ContentFunctor> |
| 152 | class WriteCheckpointEvent : public StackObj { |
| 153 | private: |
| 154 | JfrChunkWriter& _cw; |
| 155 | u8 _type_id; |
| 156 | ContentFunctor& _content_functor; |
| 157 | public: |
| 158 | WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) : |
| 159 | _cw(cw), |
| 160 | _type_id(type_id), |
| 161 | _content_functor(functor) { |
| 162 | assert(_cw.is_valid(), "invariant" ); |
| 163 | } |
| 164 | bool process() { |
| 165 | // current_cp_offset is also offset for the event size header field |
| 166 | const int64_t current_cp_offset = _cw.current_offset(); |
| 167 | const int64_t num_elements_offset = write_checkpoint_event_prologue(_cw, _type_id); |
| 168 | // invocation |
| 169 | _content_functor.process(); |
| 170 | const u4 number_of_elements = (u4)_content_functor.processed(); |
| 171 | if (number_of_elements == 0) { |
| 172 | // nothing to do, rewind writer to start |
| 173 | _cw.seek(current_cp_offset); |
| 174 | return true; |
| 175 | } |
| 176 | assert(number_of_elements > 0, "invariant" ); |
| 177 | assert(_cw.current_offset() > num_elements_offset, "invariant" ); |
| 178 | _cw.write_padded_at_offset<u4>(number_of_elements, num_elements_offset); |
| 179 | _cw.write_padded_at_offset<u4>((u4)_cw.current_offset() - current_cp_offset, current_cp_offset); |
| 180 | // update writer with last checkpoint position |
| 181 | _cw.set_previous_checkpoint_offset(current_cp_offset); |
| 182 | return true; |
| 183 | } |
| 184 | }; |
| 185 | |
| 186 | template <typename Instance, size_t(Instance::*func)()> |
| 187 | class ServiceFunctor { |
| 188 | private: |
| 189 | Instance& _instance; |
| 190 | size_t _processed; |
| 191 | public: |
| 192 | ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} |
| 193 | bool process() { |
| 194 | _processed = (_instance.*func)(); |
| 195 | return true; |
| 196 | } |
| 197 | size_t processed() const { return _processed; } |
| 198 | }; |
| 199 | |
| 200 | template <typename Instance, void(Instance::*func)()> |
| 201 | class JfrVMOperation : public VM_Operation { |
| 202 | private: |
| 203 | Instance& _instance; |
| 204 | public: |
| 205 | JfrVMOperation(Instance& instance) : _instance(instance) {} |
| 206 | void doit() { (_instance.*func)(); } |
| 207 | VMOp_Type type() const { return VMOp_JFRCheckpoint; } |
| 208 | Mode evaluation_mode() const { return _safepoint; } // default |
| 209 | }; |
| 210 | |
| 211 | class WriteStackTraceRepository : public StackObj { |
| 212 | private: |
| 213 | JfrStackTraceRepository& _repo; |
| 214 | JfrChunkWriter& _cw; |
| 215 | size_t _elements_processed; |
| 216 | bool _clear; |
| 217 | |
| 218 | public: |
| 219 | WriteStackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) : |
| 220 | _repo(repo), _cw(cw), _elements_processed(0), _clear(clear) {} |
| 221 | bool process() { |
| 222 | _elements_processed = _repo.write(_cw, _clear); |
| 223 | return true; |
| 224 | } |
| 225 | size_t processed() const { return _elements_processed; } |
| 226 | void reset() { _elements_processed = 0; } |
| 227 | }; |
| 228 | |
| 229 | static bool recording = false; |
| 230 | |
| 231 | static void set_recording_state(bool is_recording) { |
| 232 | OrderAccess::storestore(); |
| 233 | recording = is_recording; |
| 234 | } |
| 235 | |
| 236 | bool JfrRecorderService::is_recording() { |
| 237 | return recording; |
| 238 | } |
| 239 | |
| 240 | JfrRecorderService::JfrRecorderService() : |
| 241 | _checkpoint_manager(JfrCheckpointManager::instance()), |
| 242 | _chunkwriter(JfrRepository::chunkwriter()), |
| 243 | _repository(JfrRepository::instance()), |
| 244 | _stack_trace_repository(JfrStackTraceRepository::instance()), |
| 245 | _storage(JfrStorage::instance()), |
| 246 | _string_pool(JfrStringPool::instance()) {} |
| 247 | |
| 248 | void JfrRecorderService::start() { |
| 249 | RotationLock rl(Thread::current()); |
| 250 | if (rl.not_acquired()) { |
| 251 | return; |
| 252 | } |
| 253 | log_debug(jfr, system)("Request to START recording" ); |
| 254 | assert(!is_recording(), "invariant" ); |
| 255 | clear(); |
| 256 | set_recording_state(true); |
| 257 | assert(is_recording(), "invariant" ); |
| 258 | open_new_chunk(); |
| 259 | log_debug(jfr, system)("Recording STARTED" ); |
| 260 | } |
| 261 | |
| 262 | void JfrRecorderService::clear() { |
| 263 | ResourceMark rm; |
| 264 | HandleMark hm; |
| 265 | pre_safepoint_clear(); |
| 266 | invoke_safepoint_clear(); |
| 267 | post_safepoint_clear(); |
| 268 | } |
| 269 | |
| 270 | void JfrRecorderService::pre_safepoint_clear() { |
| 271 | _stack_trace_repository.clear(); |
| 272 | _string_pool.clear(); |
| 273 | _storage.clear(); |
| 274 | } |
| 275 | |
| 276 | void JfrRecorderService::invoke_safepoint_clear() { |
| 277 | JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_clear> safepoint_task(*this); |
| 278 | VMThread::execute(&safepoint_task); |
| 279 | } |
| 280 | |
| 281 | // |
| 282 | // safepoint clear sequence |
| 283 | // |
| 284 | // clear stacktrace repository -> |
| 285 | // clear string pool -> |
| 286 | // clear storage -> |
| 287 | // shift epoch -> |
| 288 | // update time |
| 289 | // |
| 290 | void JfrRecorderService::safepoint_clear() { |
| 291 | assert(SafepointSynchronize::is_at_safepoint(), "invariant" ); |
| 292 | _stack_trace_repository.clear(); |
| 293 | _string_pool.clear(); |
| 294 | _storage.clear(); |
| 295 | _checkpoint_manager.shift_epoch(); |
| 296 | _chunkwriter.time_stamp_chunk_now(); |
| 297 | } |
| 298 | |
| 299 | void JfrRecorderService::post_safepoint_clear() { |
| 300 | _checkpoint_manager.clear(); |
| 301 | } |
| 302 | |
| 303 | static void stop() { |
| 304 | assert(JfrRecorderService::is_recording(), "invariant" ); |
| 305 | log_debug(jfr, system)("Recording STOPPED" ); |
| 306 | set_recording_state(false); |
| 307 | assert(!JfrRecorderService::is_recording(), "invariant" ); |
| 308 | } |
| 309 | |
| 310 | void JfrRecorderService::rotate(int msgs) { |
| 311 | RotationLock rl(Thread::current()); |
| 312 | if (rl.not_acquired()) { |
| 313 | return; |
| 314 | } |
| 315 | static bool vm_error = false; |
| 316 | if (msgs & MSGBIT(MSG_VM_ERROR)) { |
| 317 | vm_error = true; |
| 318 | prepare_for_vm_error_rotation(); |
| 319 | } |
| 320 | if (msgs & (MSGBIT(MSG_STOP))) { |
| 321 | stop(); |
| 322 | } |
| 323 | // action determined by chunkwriter state |
| 324 | if (!_chunkwriter.is_valid()) { |
| 325 | in_memory_rotation(); |
| 326 | return; |
| 327 | } |
| 328 | if (vm_error) { |
| 329 | vm_error_rotation(); |
| 330 | return; |
| 331 | } |
| 332 | chunk_rotation(); |
| 333 | } |
| 334 | |
| 335 | void JfrRecorderService::prepare_for_vm_error_rotation() { |
| 336 | if (!_chunkwriter.is_valid()) { |
| 337 | open_new_chunk(true); |
| 338 | } |
| 339 | _checkpoint_manager.register_service_thread(Thread::current()); |
| 340 | JfrMetadataEvent::lock(); |
| 341 | } |
| 342 | |
| 343 | void JfrRecorderService::open_new_chunk(bool vm_error) { |
| 344 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 345 | assert(!JfrStream_lock->owned_by_self(), "invariant" ); |
| 346 | JfrChunkRotation::on_rotation(); |
| 347 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 348 | if (!_repository.open_chunk(vm_error)) { |
| 349 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 350 | _storage.control().set_to_disk(false); |
| 351 | return; |
| 352 | } |
| 353 | assert(_chunkwriter.is_valid(), "invariant" ); |
| 354 | _storage.control().set_to_disk(true); |
| 355 | } |
| 356 | |
| 357 | void JfrRecorderService::in_memory_rotation() { |
| 358 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 359 | // currently running an in-memory recording |
| 360 | open_new_chunk(); |
| 361 | if (_chunkwriter.is_valid()) { |
| 362 | // dump all in-memory buffer data to the newly created chunk |
| 363 | serialize_storage_from_in_memory_recording(); |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | void JfrRecorderService::serialize_storage_from_in_memory_recording() { |
| 368 | assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!" ); |
| 369 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 370 | _storage.write(); |
| 371 | } |
| 372 | |
| 373 | void JfrRecorderService::chunk_rotation() { |
| 374 | finalize_current_chunk(); |
| 375 | open_new_chunk(); |
| 376 | } |
| 377 | |
| 378 | void JfrRecorderService::finalize_current_chunk() { |
| 379 | assert(_chunkwriter.is_valid(), "invariant" ); |
| 380 | write(); |
| 381 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 382 | } |
| 383 | |
| 384 | void JfrRecorderService::write() { |
| 385 | ResourceMark rm; |
| 386 | HandleMark hm; |
| 387 | pre_safepoint_write(); |
| 388 | invoke_safepoint_write(); |
| 389 | post_safepoint_write(); |
| 390 | } |
| 391 | |
| 392 | typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write> WriteStringPool; |
| 393 | typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write_at_safepoint> WriteStringPoolSafepoint; |
| 394 | typedef WriteCheckpointEvent<WriteStackTraceRepository> WriteStackTraceCheckpoint; |
| 395 | typedef WriteCheckpointEvent<WriteStringPool> WriteStringPoolCheckpoint; |
| 396 | typedef WriteCheckpointEvent<WriteStringPoolSafepoint> WriteStringPoolCheckpointSafepoint; |
| 397 | |
| 398 | static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { |
| 399 | WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear); |
| 400 | WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo); |
| 401 | write_stack_trace_checkpoint.process(); |
| 402 | } |
| 403 | |
| 404 | static void write_object_sample_stacktrace(ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repository) { |
| 405 | WriteObjectSampleStacktrace object_sample_stacktrace(sampler, stack_trace_repository); |
| 406 | object_sample_stacktrace.process(); |
| 407 | } |
| 408 | |
| 409 | static void write_stringpool_checkpoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { |
| 410 | WriteStringPool write_string_pool(string_pool); |
| 411 | WriteStringPoolCheckpoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); |
| 412 | write_string_pool_checkpoint.process(); |
| 413 | } |
| 414 | |
| 415 | static void write_stringpool_checkpoint_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { |
| 416 | WriteStringPoolSafepoint write_string_pool(string_pool); |
| 417 | WriteStringPoolCheckpointSafepoint write_string_pool_checkpoint(chunkwriter, TYPE_STRING, write_string_pool); |
| 418 | write_string_pool_checkpoint.process(); |
| 419 | } |
| 420 | |
| 421 | // |
| 422 | // pre-safepoint write sequence |
| 423 | // |
| 424 | // lock stream lock -> |
| 425 | // write non-safepoint dependent types -> |
| 426 | // write checkpoint epoch transition list-> |
| 427 | // write stack trace checkpoint -> |
| 428 | // write string pool checkpoint -> |
| 429 | // write object sample stacktraces -> |
| 430 | // write storage -> |
| 431 | // release stream lock |
| 432 | // |
| 433 | void JfrRecorderService::pre_safepoint_write() { |
| 434 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 435 | assert(_chunkwriter.is_valid(), "invariant" ); |
| 436 | _checkpoint_manager.write_types(); |
| 437 | _checkpoint_manager.write_epoch_transition_mspace(); |
| 438 | write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false); |
| 439 | write_stringpool_checkpoint(_string_pool, _chunkwriter); |
| 440 | if (LeakProfiler::is_running()) { |
| 441 | // Exclusive access to the object sampler instance. |
| 442 | // The sampler is released (unlocked) later in post_safepoint_write. |
| 443 | ObjectSampler* const sampler = ObjectSampler::acquire(); |
| 444 | assert(sampler != NULL, "invariant" ); |
| 445 | write_object_sample_stacktrace(sampler, _stack_trace_repository); |
| 446 | } |
| 447 | _storage.write(); |
| 448 | } |
| 449 | |
| 450 | void JfrRecorderService::invoke_safepoint_write() { |
| 451 | JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_write> safepoint_task(*this); |
| 452 | VMThread::execute(&safepoint_task); |
| 453 | } |
| 454 | |
| 455 | // |
| 456 | // safepoint write sequence |
| 457 | // |
| 458 | // lock stream lock -> |
| 459 | // write stacktrace repository -> |
| 460 | // write string pool -> |
| 461 | // write safepoint dependent types -> |
| 462 | // write storage -> |
| 463 | // shift_epoch -> |
| 464 | // update time -> |
| 465 | // lock metadata descriptor -> |
| 466 | // release stream lock |
| 467 | // |
| 468 | void JfrRecorderService::safepoint_write() { |
| 469 | assert(SafepointSynchronize::is_at_safepoint(), "invariant" ); |
| 470 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 471 | write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true); |
| 472 | write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter); |
| 473 | _checkpoint_manager.write_safepoint_types(); |
| 474 | _storage.write_at_safepoint(); |
| 475 | _checkpoint_manager.shift_epoch(); |
| 476 | _chunkwriter.time_stamp_chunk_now(); |
| 477 | JfrMetadataEvent::lock(); |
| 478 | } |
| 479 | |
| 480 | static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) { |
| 481 | assert(chunkwriter.is_valid(), "invariant" ); |
| 482 | const int64_t metadata_offset = chunkwriter.current_offset(); |
| 483 | JfrMetadataEvent::write(chunkwriter, metadata_offset); |
| 484 | return metadata_offset; |
| 485 | } |
| 486 | |
| 487 | // |
| 488 | // post-safepoint write sequence |
| 489 | // |
| 490 | // write type set -> |
| 491 | // release object sampler -> |
| 492 | // lock stream lock -> |
| 493 | // write checkpoints -> |
| 494 | // write metadata event -> |
| 495 | // write chunk header -> |
| 496 | // close chunk fd -> |
| 497 | // release stream lock |
| 498 | // |
| 499 | void JfrRecorderService::post_safepoint_write() { |
| 500 | assert(_chunkwriter.is_valid(), "invariant" ); |
| 501 | // During the safepoint tasks just completed, the system transitioned to a new epoch. |
| 502 | // Type tagging is epoch relative which entails we are able to write out the |
| 503 | // already tagged artifacts for the previous epoch. We can accomplish this concurrently |
| 504 | // with threads now tagging artifacts in relation to the new, now updated, epoch and remain outside of a safepoint. |
| 505 | _checkpoint_manager.write_type_set(); |
| 506 | if (LeakProfiler::is_running()) { |
| 507 | // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. |
| 508 | // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. |
| 509 | ObjectSampler::release(); |
| 510 | } |
| 511 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 512 | // serialize any outstanding checkpoint memory |
| 513 | _checkpoint_manager.write(); |
| 514 | // serialize the metadata descriptor event and close out the chunk |
| 515 | _repository.close_chunk(write_metadata_event(_chunkwriter)); |
| 516 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 517 | } |
| 518 | |
| 519 | void JfrRecorderService::vm_error_rotation() { |
| 520 | if (_chunkwriter.is_valid()) { |
| 521 | finalize_current_chunk_on_vm_error(); |
| 522 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 523 | _repository.on_vm_error(); |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | void JfrRecorderService::finalize_current_chunk_on_vm_error() { |
| 528 | assert(_chunkwriter.is_valid(), "invariant" ); |
| 529 | pre_safepoint_write(); |
| 530 | // Do not attempt safepoint dependent operations during emergency dump. |
| 531 | // Optimistically write tagged artifacts. |
| 532 | _checkpoint_manager.shift_epoch(); |
| 533 | // update time |
| 534 | _chunkwriter.time_stamp_chunk_now(); |
| 535 | post_safepoint_write(); |
| 536 | assert(!_chunkwriter.is_valid(), "invariant" ); |
| 537 | } |
| 538 | |
| 539 | void JfrRecorderService::process_full_buffers() { |
| 540 | if (_chunkwriter.is_valid()) { |
| 541 | assert(!JfrStream_lock->owned_by_self(), "invariant" ); |
| 542 | MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
| 543 | _storage.write_full(); |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | void JfrRecorderService::scavenge() { |
| 548 | _storage.scavenge(); |
| 549 | } |
| 550 | |
| 551 | void JfrRecorderService::evaluate_chunk_size_for_rotation() { |
| 552 | JfrChunkRotation::evaluate(_chunkwriter); |
| 553 | } |
| 554 | |