| 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 |  | 
|---|