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