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