53 #include "runtime/safepoint.hpp" |
54 #include "runtime/safepoint.hpp" |
54 #include "runtime/thread.inline.hpp" |
55 #include "runtime/thread.inline.hpp" |
55 #include "runtime/vmOperations.hpp" |
56 #include "runtime/vmOperations.hpp" |
56 #include "runtime/vmThread.hpp" |
57 #include "runtime/vmThread.hpp" |
57 |
58 |
58 // set data iff *dest == NULL |
59 // incremented on each flushpoint |
59 static bool try_set(void* const data, void** dest, bool clear) { |
60 static u8 flushpoint_id = 0; |
60 assert(data != NULL, "invariant"); |
61 |
61 const void* const current = OrderAccess::load_acquire(dest); |
62 template <typename E, typename Instance, size_t(Instance::*func)()> |
62 if (current != NULL) { |
63 class Content { |
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: |
64 private: |
82 Thread* const _thread; |
65 Instance& _instance; |
83 bool _acquired; |
66 u4 _elements; |
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: |
67 public: |
98 RotationLock(Thread* thread) : _thread(thread), _acquired(false) { |
68 typedef E EventType; |
99 assert(_thread != NULL, "invariant"); |
69 Content(Instance& instance) : _instance(instance), _elements(0) {} |
100 if (_thread == rotation_thread) { |
70 bool process() { |
101 // recursion not supported |
71 _elements = (u4)(_instance.*func)(); |
102 log(true); |
72 return true; |
103 return; |
73 } |
104 } |
74 u4 elements() const { return _elements; } |
105 |
75 }; |
106 // limited to not spin indefinitely |
76 |
107 for (int i = 0; i < rotation_try_limit; ++i) { |
77 template <typename Content> |
108 if (try_set(_thread, &rotation_thread, false)) { |
78 class WriteContent : public StackObj { |
109 _acquired = true; |
79 protected: |
110 assert(_thread == rotation_thread, "invariant"); |
80 const JfrTicks _start_time; |
111 return; |
81 JfrTicks _end_time; |
112 } |
82 JfrChunkWriter& _cw; |
113 if (_thread->is_Java_thread()) { |
83 Content& _content; |
114 // in order to allow the system to move to a safepoint |
84 const int64_t _start_offset; |
115 MutexLocker msg_lock(JfrMsg_lock); |
85 public: |
116 JfrMsg_lock->wait(rotation_retry_sleep_millis); |
86 typedef typename Content::EventType EventType; |
117 } |
87 |
118 else { |
88 WriteContent(JfrChunkWriter& cw, Content& content) : |
119 os::naked_short_sleep(rotation_retry_sleep_millis); |
89 _start_time(JfrTicks::now()), |
120 } |
90 _end_time(), |
121 } |
91 _cw(cw), |
122 log(false); |
92 _content(content), |
123 } |
93 _start_offset(_cw.current_offset()) { |
124 |
94 assert(_cw.is_valid(), "invariant"); |
125 ~RotationLock() { |
95 } |
126 assert(_thread != NULL, "invariant"); |
96 |
127 if (_acquired) { |
97 bool process() { |
128 assert(_thread == rotation_thread, "invariant"); |
98 // invocation |
129 while (!try_set(_thread, &rotation_thread, true)); |
99 _content.process(); |
130 } |
100 _end_time = JfrTicks::now(); |
131 } |
101 return 0 != _content.elements(); |
132 bool not_acquired() const { return !_acquired; } |
102 } |
|
103 |
|
104 const JfrTicks& start_time() const { |
|
105 return _start_time; |
|
106 } |
|
107 |
|
108 const JfrTicks& end_time() const { |
|
109 return _end_time; |
|
110 } |
|
111 |
|
112 int64_t start_offset() const { |
|
113 return _start_offset; |
|
114 } |
|
115 |
|
116 int64_t end_offset() const { |
|
117 return current_offset(); |
|
118 } |
|
119 |
|
120 int64_t current_offset() const { |
|
121 return _cw.current_offset(); |
|
122 } |
|
123 |
|
124 u4 elements() const { |
|
125 return (u4) _content.elements(); |
|
126 } |
|
127 |
|
128 u4 size() const { |
|
129 return (u4)(end_offset() - start_offset()); |
|
130 } |
|
131 |
|
132 static bool is_event_enabled() { |
|
133 return EventType::is_enabled(); |
|
134 } |
|
135 |
|
136 static u8 event_id() { |
|
137 return EventType::eventId; |
|
138 } |
|
139 |
|
140 void write_elements(int64_t offset) { |
|
141 _cw.write_padded_at_offset<u4>(elements(), offset); |
|
142 } |
|
143 |
|
144 void write_size() { |
|
145 _cw.write_padded_at_offset<u4>(size(), start_offset()); |
|
146 } |
|
147 |
|
148 void set_last_checkpoint() { |
|
149 _cw.set_last_checkpoint_offset(start_offset()); |
|
150 } |
|
151 |
|
152 void rewind() { |
|
153 _cw.seek(start_offset()); |
|
154 } |
133 }; |
155 }; |
134 |
156 |
135 static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { |
157 static int64_t write_checkpoint_event_prologue(JfrChunkWriter& cw, u8 type_id) { |
136 const int64_t last_cp_offset = cw.last_checkpoint_offset(); |
158 const int64_t last_cp_offset = cw.last_checkpoint_offset(); |
137 const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset(); |
159 const int64_t delta_to_last_checkpoint = 0 == last_cp_offset ? 0 : last_cp_offset - cw.current_offset(); |
138 cw.reserve(sizeof(u4)); |
160 cw.reserve(sizeof(u4)); |
139 cw.write<u8>(EVENT_CHECKPOINT); |
161 cw.write<u8>(EVENT_CHECKPOINT); |
140 cw.write(JfrTicks::now()); |
162 cw.write(JfrTicks::now()); |
141 cw.write((int64_t)0); // duration |
163 cw.write<u8>(0); // duration |
142 cw.write(delta_to_last_checkpoint); |
164 cw.write(delta_to_last_checkpoint); |
143 cw.write<bool>(false); // flushpoint |
165 cw.write<u4>(GENERIC); // checkpoint type |
144 cw.write((u4)1); // nof types in this checkpoint |
166 cw.write<u4>(1); // nof types in this checkpoint |
145 cw.write(type_id); |
167 cw.write(type_id); |
146 const int64_t number_of_elements_offset = cw.current_offset(); |
168 return cw.reserve(sizeof(u4)); |
147 cw.reserve(sizeof(u4)); |
169 } |
148 return number_of_elements_offset; |
170 |
149 } |
171 template <typename Content> |
150 |
172 class WriteCheckpointEvent : public WriteContent<Content> { |
151 template <typename ContentFunctor> |
173 private: |
152 class WriteCheckpointEvent : public StackObj { |
174 const u8 _type_id; |
|
175 public: |
|
176 WriteCheckpointEvent(JfrChunkWriter& cw, Content& content, u8 type_id) : |
|
177 WriteContent<Content>(cw, content), _type_id(type_id) {} |
|
178 |
|
179 bool process() { |
|
180 const int64_t num_elements_offset = write_checkpoint_event_prologue(this->_cw, _type_id); |
|
181 if (!WriteContent<Content>::process()) { |
|
182 // nothing to do, rewind writer to start |
|
183 this->rewind(); |
|
184 assert(this->current_offset() == this->start_offset(), "invariant"); |
|
185 return false; |
|
186 } |
|
187 assert(this->elements() > 0, "invariant"); |
|
188 assert(this->current_offset() > num_elements_offset, "invariant"); |
|
189 this->write_elements(num_elements_offset); |
|
190 this->write_size(); |
|
191 this->set_last_checkpoint(); |
|
192 return true; |
|
193 } |
|
194 }; |
|
195 |
|
196 template <typename Functor> |
|
197 static u4 invoke(Functor& f) { |
|
198 f.process(); |
|
199 return f.elements(); |
|
200 } |
|
201 |
|
202 template <typename Functor> |
|
203 static void write_flush_event(Functor& f) { |
|
204 if (Functor::is_event_enabled()) { |
|
205 typename Functor::EventType e(UNTIMED); |
|
206 e.set_starttime(f.start_time()); |
|
207 e.set_endtime(f.end_time()); |
|
208 e.set_flushId(flushpoint_id); |
|
209 e.set_elements(f.elements()); |
|
210 e.set_size(f.size()); |
|
211 e.commit(); |
|
212 } |
|
213 } |
|
214 |
|
215 template <typename Functor> |
|
216 static u4 invoke_with_flush_event(Functor& f) { |
|
217 const u4 elements = invoke(f); |
|
218 write_flush_event(f); |
|
219 return elements; |
|
220 } |
|
221 |
|
222 class StackTraceRepository : public StackObj { |
|
223 private: |
|
224 JfrStackTraceRepository& _repo; |
|
225 JfrChunkWriter& _cw; |
|
226 size_t _elements; |
|
227 bool _clear; |
|
228 |
|
229 public: |
|
230 typedef EventFlushStacktrace EventType; |
|
231 StackTraceRepository(JfrStackTraceRepository& repo, JfrChunkWriter& cw, bool clear) : |
|
232 _repo(repo), _cw(cw), _elements(0), _clear(clear) {} |
|
233 bool process() { |
|
234 _elements = _repo.write(_cw, _clear); |
|
235 return true; |
|
236 } |
|
237 size_t elements() const { return _elements; } |
|
238 void reset() { _elements = 0; } |
|
239 }; |
|
240 |
|
241 typedef WriteCheckpointEvent<StackTraceRepository> WriteStackTrace; |
|
242 |
|
243 static u4 flush_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter) { |
|
244 StackTraceRepository str(stack_trace_repo, chunkwriter, false); |
|
245 WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE); |
|
246 return invoke_with_flush_event(wst); |
|
247 } |
|
248 |
|
249 static u4 write_stacktrace(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { |
|
250 StackTraceRepository str(stack_trace_repo, chunkwriter, clear); |
|
251 WriteStackTrace wst(chunkwriter, str, TYPE_STACKTRACE); |
|
252 return invoke(wst); |
|
253 } |
|
254 |
|
255 typedef Content<EventFlushStorage, JfrStorage, &JfrStorage::write> Storage; |
|
256 typedef WriteContent<Storage> WriteStorage; |
|
257 |
|
258 static size_t flush_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) { |
|
259 assert(chunkwriter.is_valid(), "invariant"); |
|
260 Storage fsf(storage); |
|
261 WriteStorage fs(chunkwriter, fsf); |
|
262 return invoke_with_flush_event(fs); |
|
263 } |
|
264 |
|
265 static size_t write_storage(JfrStorage& storage, JfrChunkWriter& chunkwriter) { |
|
266 assert(chunkwriter.is_valid(), "invariant"); |
|
267 Storage fsf(storage); |
|
268 WriteStorage fs(chunkwriter, fsf); |
|
269 return invoke(fs); |
|
270 } |
|
271 |
|
272 typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write> StringPool; |
|
273 typedef Content<EventFlushStringPool, JfrStringPool, &JfrStringPool::write_at_safepoint> StringPoolSafepoint; |
|
274 typedef WriteCheckpointEvent<StringPool> WriteStringPool; |
|
275 typedef WriteCheckpointEvent<StringPoolSafepoint> WriteStringPoolSafepoint; |
|
276 |
|
277 static u4 flush_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { |
|
278 StringPool sp(string_pool); |
|
279 WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); |
|
280 return invoke_with_flush_event(wsp); |
|
281 } |
|
282 |
|
283 static u4 write_stringpool(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { |
|
284 StringPool sp(string_pool); |
|
285 WriteStringPool wsp(chunkwriter, sp, TYPE_STRING); |
|
286 return invoke(wsp); |
|
287 } |
|
288 |
|
289 static u4 write_stringpool_safepoint(JfrStringPool& string_pool, JfrChunkWriter& chunkwriter) { |
|
290 StringPoolSafepoint sps(string_pool); |
|
291 WriteStringPoolSafepoint wsps(chunkwriter, sps, TYPE_STRING); |
|
292 return invoke(wsps); |
|
293 } |
|
294 |
|
295 typedef Content<EventFlushTypeSet, JfrCheckpointManager, &JfrCheckpointManager::flush_type_set> FlushTypeSetFunctor; |
|
296 typedef WriteContent<FlushTypeSetFunctor> FlushTypeSet; |
|
297 |
|
298 static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter& chunkwriter) { |
|
299 FlushTypeSetFunctor flush_type_set(checkpoint_manager); |
|
300 FlushTypeSet fts(chunkwriter, flush_type_set); |
|
301 return invoke_with_flush_event(fts); |
|
302 } |
|
303 |
|
304 class MetadataEvent : public StackObj { |
153 private: |
305 private: |
154 JfrChunkWriter& _cw; |
306 JfrChunkWriter& _cw; |
155 u8 _type_id; |
|
156 ContentFunctor& _content_functor; |
|
157 public: |
307 public: |
158 WriteCheckpointEvent(JfrChunkWriter& cw, u8 type_id, ContentFunctor& functor) : |
308 typedef EventFlushMetadata EventType; |
159 _cw(cw), |
309 MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {} |
160 _type_id(type_id), |
|
161 _content_functor(functor) { |
|
162 assert(_cw.is_valid(), "invariant"); |
|
163 } |
|
164 bool process() { |
310 bool process() { |
165 // current_cp_offset is also offset for the event size header field |
311 JfrMetadataEvent::write(_cw); |
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_last_checkpoint_offset(current_cp_offset); |
|
182 return true; |
312 return true; |
183 } |
313 } |
|
314 size_t elements() const { return 1; } |
184 }; |
315 }; |
185 |
316 |
186 template <typename Instance, size_t(Instance::*func)()> |
317 typedef WriteContent<MetadataEvent> WriteMetadata; |
187 class ServiceFunctor { |
318 |
188 private: |
319 static u4 flush_metadata(JfrChunkWriter& chunkwriter) { |
189 Instance& _instance; |
320 assert(chunkwriter.is_valid(), "invariant"); |
190 size_t _processed; |
321 MetadataEvent me(chunkwriter); |
191 public: |
322 WriteMetadata wm(chunkwriter, me); |
192 ServiceFunctor(Instance& instance) : _instance(instance), _processed(0) {} |
323 return invoke_with_flush_event(wm); |
193 bool process() { |
324 } |
194 _processed = (_instance.*func)(); |
325 |
195 return true; |
326 static u4 write_metadata(JfrChunkWriter& chunkwriter) { |
196 } |
327 assert(chunkwriter.is_valid(), "invariant"); |
197 size_t processed() const { return _processed; } |
328 MetadataEvent me(chunkwriter); |
198 }; |
329 WriteMetadata wm(chunkwriter, me); |
|
330 return invoke(wm); |
|
331 } |
199 |
332 |
200 template <typename Instance, void(Instance::*func)()> |
333 template <typename Instance, void(Instance::*func)()> |
201 class JfrVMOperation : public VM_Operation { |
334 class JfrVMOperation : public VM_Operation { |
202 private: |
335 private: |
203 Instance& _instance; |
336 Instance& _instance; |
206 void doit() { (_instance.*func)(); } |
339 void doit() { (_instance.*func)(); } |
207 VMOp_Type type() const { return VMOp_JFRCheckpoint; } |
340 VMOp_Type type() const { return VMOp_JFRCheckpoint; } |
208 Mode evaluation_mode() const { return _safepoint; } // default |
341 Mode evaluation_mode() const { return _safepoint; } // default |
209 }; |
342 }; |
210 |
343 |
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() : |
344 JfrRecorderService::JfrRecorderService() : |
241 _checkpoint_manager(JfrCheckpointManager::instance()), |
345 _checkpoint_manager(JfrCheckpointManager::instance()), |
242 _chunkwriter(JfrRepository::chunkwriter()), |
346 _chunkwriter(JfrRepository::chunkwriter()), |
243 _repository(JfrRepository::instance()), |
347 _repository(JfrRepository::instance()), |
244 _stack_trace_repository(JfrStackTraceRepository::instance()), |
348 _stack_trace_repository(JfrStackTraceRepository::instance()), |
245 _storage(JfrStorage::instance()), |
349 _storage(JfrStorage::instance()), |
246 _string_pool(JfrStringPool::instance()) {} |
350 _string_pool(JfrStringPool::instance()) {} |
247 |
351 |
|
352 static bool recording = false; |
|
353 |
|
354 static void set_recording_state(bool is_recording) { |
|
355 OrderAccess::storestore(); |
|
356 recording = is_recording; |
|
357 } |
|
358 |
|
359 bool JfrRecorderService::is_recording() { |
|
360 return recording; |
|
361 } |
|
362 |
248 void JfrRecorderService::start() { |
363 void JfrRecorderService::start() { |
249 RotationLock rl(Thread::current()); |
364 MutexLocker lock(JfrStream_lock); |
250 if (rl.not_acquired()) { |
|
251 return; |
|
252 } |
|
253 log_debug(jfr, system)("Request to START recording"); |
365 log_debug(jfr, system)("Request to START recording"); |
254 assert(!is_recording(), "invariant"); |
366 assert(!is_recording(), "invariant"); |
255 clear(); |
367 clear(); |
256 set_recording_state(true); |
368 set_recording_state(true); |
257 assert(is_recording(), "invariant"); |
369 assert(is_recording(), "invariant"); |
266 invoke_safepoint_clear(); |
378 invoke_safepoint_clear(); |
267 post_safepoint_clear(); |
379 post_safepoint_clear(); |
268 } |
380 } |
269 |
381 |
270 void JfrRecorderService::pre_safepoint_clear() { |
382 void JfrRecorderService::pre_safepoint_clear() { |
271 _stack_trace_repository.clear(); |
|
272 _string_pool.clear(); |
383 _string_pool.clear(); |
273 _storage.clear(); |
384 _storage.clear(); |
|
385 _stack_trace_repository.clear(); |
274 } |
386 } |
275 |
387 |
276 void JfrRecorderService::invoke_safepoint_clear() { |
388 void JfrRecorderService::invoke_safepoint_clear() { |
277 JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_clear> safepoint_task(*this); |
389 JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_clear> safepoint_task(*this); |
278 VMThread::execute(&safepoint_task); |
390 VMThread::execute(&safepoint_task); |
279 } |
391 } |
280 |
392 |
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() { |
393 void JfrRecorderService::safepoint_clear() { |
291 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
394 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
292 _stack_trace_repository.clear(); |
|
293 _string_pool.clear(); |
395 _string_pool.clear(); |
294 _storage.clear(); |
396 _storage.clear(); |
295 _checkpoint_manager.shift_epoch(); |
397 _checkpoint_manager.shift_epoch(); |
296 _chunkwriter.time_stamp_chunk_now(); |
398 _chunkwriter.set_time_stamp(); |
|
399 _stack_trace_repository.clear(); |
297 } |
400 } |
298 |
401 |
299 void JfrRecorderService::post_safepoint_clear() { |
402 void JfrRecorderService::post_safepoint_clear() { |
300 _checkpoint_manager.clear(); |
403 _checkpoint_manager.clear(); |
|
404 } |
|
405 |
|
406 void JfrRecorderService::open_new_chunk(bool vm_error) { |
|
407 JfrChunkRotation::on_rotation(); |
|
408 const bool valid_chunk = _repository.open_chunk(vm_error); |
|
409 _storage.control().set_to_disk(valid_chunk); |
|
410 if (valid_chunk) { |
|
411 _checkpoint_manager.write_static_type_set_and_threads(); |
|
412 } |
301 } |
413 } |
302 |
414 |
303 static void stop() { |
415 static void stop() { |
304 assert(JfrRecorderService::is_recording(), "invariant"); |
416 assert(JfrRecorderService::is_recording(), "invariant"); |
305 log_debug(jfr, system)("Recording STOPPED"); |
417 log_debug(jfr, system)("Recording STOPPED"); |
306 set_recording_state(false); |
418 set_recording_state(false); |
307 assert(!JfrRecorderService::is_recording(), "invariant"); |
419 assert(!JfrRecorderService::is_recording(), "invariant"); |
308 } |
420 } |
309 |
421 |
|
422 void JfrRecorderService::prepare_for_vm_error_rotation() { |
|
423 assert(JfrStream_lock->owned_by_self(), "invariant"); |
|
424 if (!_chunkwriter.is_valid()) { |
|
425 open_new_chunk(true); |
|
426 } |
|
427 _checkpoint_manager.register_service_thread(Thread::current()); |
|
428 } |
|
429 |
|
430 void JfrRecorderService::vm_error_rotation() { |
|
431 assert(JfrStream_lock->owned_by_self(), "invariant"); |
|
432 if (_chunkwriter.is_valid()) { |
|
433 Thread* const t = Thread::current(); |
|
434 _storage.flush_regular_buffer(t->jfr_thread_local()->native_buffer(), t); |
|
435 invoke_flush(); |
|
436 _chunkwriter.set_time_stamp(); |
|
437 _repository.close_chunk(); |
|
438 assert(!_chunkwriter.is_valid(), "invariant"); |
|
439 _repository.on_vm_error(); |
|
440 } |
|
441 } |
|
442 |
310 void JfrRecorderService::rotate(int msgs) { |
443 void JfrRecorderService::rotate(int msgs) { |
311 RotationLock rl(Thread::current()); |
444 assert(!JfrStream_lock->owned_by_self(), "invariant"); |
312 if (rl.not_acquired()) { |
445 MutexLocker lock(JfrStream_lock); |
313 return; |
|
314 } |
|
315 static bool vm_error = false; |
446 static bool vm_error = false; |
316 if (msgs & MSGBIT(MSG_VM_ERROR)) { |
447 if (msgs & MSGBIT(MSG_VM_ERROR)) { |
317 vm_error = true; |
448 vm_error = true; |
318 prepare_for_vm_error_rotation(); |
449 prepare_for_vm_error_rotation(); |
319 } |
450 } |
327 if (msgs & (MSGBIT(MSG_STOP))) { |
458 if (msgs & (MSGBIT(MSG_STOP))) { |
328 stop(); |
459 stop(); |
329 } |
460 } |
330 } |
461 } |
331 |
462 |
332 void JfrRecorderService::prepare_for_vm_error_rotation() { |
|
333 if (!_chunkwriter.is_valid()) { |
|
334 open_new_chunk(true); |
|
335 } |
|
336 _checkpoint_manager.register_service_thread(Thread::current()); |
|
337 JfrMetadataEvent::lock(); |
|
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 JfrChunkRotation::on_rotation(); |
|
344 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
|
345 if (!_repository.open_chunk(vm_error)) { |
|
346 assert(!_chunkwriter.is_valid(), "invariant"); |
|
347 _storage.control().set_to_disk(false); |
|
348 return; |
|
349 } |
|
350 assert(_chunkwriter.is_valid(), "invariant"); |
|
351 _storage.control().set_to_disk(true); |
|
352 } |
|
353 |
|
354 void JfrRecorderService::in_memory_rotation() { |
463 void JfrRecorderService::in_memory_rotation() { |
355 assert(!_chunkwriter.is_valid(), "invariant"); |
464 assert(JfrStream_lock->owned_by_self(), "invariant"); |
356 // currently running an in-memory recording |
465 // currently running an in-memory recording |
|
466 assert(!_storage.control().to_disk(), "invariant"); |
357 open_new_chunk(); |
467 open_new_chunk(); |
358 if (_chunkwriter.is_valid()) { |
468 if (_chunkwriter.is_valid()) { |
359 // dump all in-memory buffer data to the newly created chunk |
469 // dump all in-memory buffer data to the newly created chunk |
360 serialize_storage_from_in_memory_recording(); |
470 write_storage(_storage, _chunkwriter); |
361 } |
471 } |
362 } |
|
363 |
|
364 void JfrRecorderService::serialize_storage_from_in_memory_recording() { |
|
365 assert(!JfrStream_lock->owned_by_self(), "not holding stream lock!"); |
|
366 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
|
367 _storage.write(); |
|
368 } |
472 } |
369 |
473 |
370 void JfrRecorderService::chunk_rotation() { |
474 void JfrRecorderService::chunk_rotation() { |
|
475 assert(JfrStream_lock->owned_by_self(), "invariant"); |
371 finalize_current_chunk(); |
476 finalize_current_chunk(); |
372 open_new_chunk(); |
477 open_new_chunk(); |
373 } |
478 } |
374 |
479 |
375 void JfrRecorderService::finalize_current_chunk() { |
480 void JfrRecorderService::finalize_current_chunk() { |
376 assert(_chunkwriter.is_valid(), "invariant"); |
481 assert(_chunkwriter.is_valid(), "invariant"); |
377 write(); |
482 write(); |
378 assert(!_chunkwriter.is_valid(), "invariant"); |
|
379 } |
483 } |
380 |
484 |
381 void JfrRecorderService::write() { |
485 void JfrRecorderService::write() { |
382 ResourceMark rm; |
486 ResourceMark rm; |
383 HandleMark hm; |
487 HandleMark hm; |
384 pre_safepoint_write(); |
488 pre_safepoint_write(); |
385 invoke_safepoint_write(); |
489 invoke_safepoint_write(); |
386 post_safepoint_write(); |
490 post_safepoint_write(); |
387 } |
491 } |
388 |
492 |
389 typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write> WriteStringPool; |
|
390 typedef ServiceFunctor<JfrStringPool, &JfrStringPool::write_at_safepoint> WriteStringPoolSafepoint; |
|
391 typedef WriteCheckpointEvent<WriteStackTraceRepository> WriteStackTraceCheckpoint; |
|
392 typedef WriteCheckpointEvent<WriteStringPool> WriteStringPoolCheckpoint; |
|
393 typedef WriteCheckpointEvent<WriteStringPoolSafepoint> WriteStringPoolCheckpointSafepoint; |
|
394 |
|
395 static void write_stacktrace_checkpoint(JfrStackTraceRepository& stack_trace_repo, JfrChunkWriter& chunkwriter, bool clear) { |
|
396 WriteStackTraceRepository write_stacktrace_repo(stack_trace_repo, chunkwriter, clear); |
|
397 WriteStackTraceCheckpoint write_stack_trace_checkpoint(chunkwriter, TYPE_STACKTRACE, write_stacktrace_repo); |
|
398 write_stack_trace_checkpoint.process(); |
|
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 object sample stacktraces -> |
|
421 // write storage -> |
|
422 // release stream lock |
|
423 // |
|
424 void JfrRecorderService::pre_safepoint_write() { |
493 void JfrRecorderService::pre_safepoint_write() { |
425 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
|
426 assert(_chunkwriter.is_valid(), "invariant"); |
494 assert(_chunkwriter.is_valid(), "invariant"); |
427 _checkpoint_manager.write_types(); |
|
428 _checkpoint_manager.write_epoch_transition_mspace(); |
|
429 write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, false); |
|
430 write_stringpool_checkpoint(_string_pool, _chunkwriter); |
|
431 if (LeakProfiler::is_running()) { |
495 if (LeakProfiler::is_running()) { |
432 // Exclusive access to the object sampler instance. |
496 // Exclusive access to the object sampler instance. |
433 // The sampler is released (unlocked) later in post_safepoint_write. |
497 // The sampler is released (unlocked) later in post_safepoint_write. |
434 ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository); |
498 ObjectSampleCheckpoint::on_rotation(ObjectSampler::acquire(), _stack_trace_repository); |
435 } |
499 } |
436 _storage.write(); |
500 if (_string_pool.is_modified()) { |
|
501 write_stringpool(_string_pool, _chunkwriter); |
|
502 } |
|
503 write_storage(_storage, _chunkwriter); |
|
504 if (_stack_trace_repository.is_modified()) { |
|
505 write_stacktrace(_stack_trace_repository, _chunkwriter, false); |
|
506 } |
437 } |
507 } |
438 |
508 |
439 void JfrRecorderService::invoke_safepoint_write() { |
509 void JfrRecorderService::invoke_safepoint_write() { |
440 JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_write> safepoint_task(*this); |
510 JfrVMOperation<JfrRecorderService, &JfrRecorderService::safepoint_write> safepoint_task(*this); |
441 VMThread::execute(&safepoint_task); |
511 VMThread::execute(&safepoint_task); |
442 } |
512 } |
443 |
513 |
444 // |
|
445 // safepoint write sequence |
|
446 // |
|
447 // lock stream lock -> |
|
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() { |
514 void JfrRecorderService::safepoint_write() { |
458 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
515 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
459 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
516 if (_string_pool.is_modified()) { |
460 write_stacktrace_checkpoint(_stack_trace_repository, _chunkwriter, true); |
517 write_stringpool_safepoint(_string_pool, _chunkwriter); |
461 write_stringpool_checkpoint_safepoint(_string_pool, _chunkwriter); |
518 } |
462 _checkpoint_manager.write_safepoint_types(); |
519 _checkpoint_manager.on_rotation(); |
463 _storage.write_at_safepoint(); |
520 _storage.write_at_safepoint(); |
464 _checkpoint_manager.shift_epoch(); |
521 _checkpoint_manager.shift_epoch(); |
465 _chunkwriter.time_stamp_chunk_now(); |
522 _chunkwriter.set_time_stamp(); |
466 JfrMetadataEvent::lock(); |
523 write_stacktrace(_stack_trace_repository, _chunkwriter, true); |
467 } |
524 } |
468 |
525 |
469 static int64_t write_metadata_event(JfrChunkWriter& chunkwriter) { |
|
470 assert(chunkwriter.is_valid(), "invariant"); |
|
471 const int64_t metadata_offset = chunkwriter.current_offset(); |
|
472 JfrMetadataEvent::write(chunkwriter, metadata_offset); |
|
473 return metadata_offset; |
|
474 } |
|
475 |
|
476 // |
|
477 // post-safepoint write sequence |
|
478 // |
|
479 // write type set -> |
|
480 // release object sampler -> |
|
481 // lock stream lock -> |
|
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() { |
526 void JfrRecorderService::post_safepoint_write() { |
489 assert(_chunkwriter.is_valid(), "invariant"); |
527 assert(_chunkwriter.is_valid(), "invariant"); |
490 // During the safepoint tasks just completed, the system transitioned to a new epoch. |
528 // 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 |
529 // 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 |
530 // already tagged artifacts for the previous epoch. We can accomplish this concurrently |
495 if (LeakProfiler::is_running()) { |
533 if (LeakProfiler::is_running()) { |
496 // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. |
534 // The object sampler instance was exclusively acquired and locked in pre_safepoint_write. |
497 // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. |
535 // Note: There is a dependency on write_type_set() above, ensure the release is subsequent. |
498 ObjectSampler::release(); |
536 ObjectSampler::release(); |
499 } |
537 } |
500 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
|
501 // serialize any outstanding checkpoint memory |
|
502 _checkpoint_manager.write(); |
|
503 // serialize the metadata descriptor event and close out the chunk |
538 // serialize the metadata descriptor event and close out the chunk |
504 _repository.close_chunk(write_metadata_event(_chunkwriter)); |
539 write_metadata(_chunkwriter); |
505 assert(!_chunkwriter.is_valid(), "invariant"); |
540 _repository.close_chunk(); |
506 } |
541 } |
507 |
542 |
508 void JfrRecorderService::vm_error_rotation() { |
543 static JfrBuffer* thread_local_buffer(Thread* t) { |
509 if (_chunkwriter.is_valid()) { |
544 assert(t != NULL, "invariant"); |
510 finalize_current_chunk_on_vm_error(); |
545 return t->jfr_thread_local()->native_buffer(); |
511 assert(!_chunkwriter.is_valid(), "invariant"); |
546 } |
512 _repository.on_vm_error(); |
547 |
513 } |
548 static void reset_buffer(JfrBuffer* buffer, Thread* t) { |
514 } |
549 assert(buffer != NULL, "invariant"); |
515 |
550 assert(t != NULL, "invariant"); |
516 void JfrRecorderService::finalize_current_chunk_on_vm_error() { |
551 assert(buffer == thread_local_buffer(t), "invariant"); |
|
552 buffer->set_pos(const_cast<u1*>(buffer->top())); |
|
553 } |
|
554 |
|
555 static void reset_thread_local_buffer(Thread* t) { |
|
556 reset_buffer(thread_local_buffer(t), t); |
|
557 } |
|
558 |
|
559 static void write_thread_local_buffer(JfrChunkWriter& chunkwriter, Thread* t) { |
|
560 JfrBuffer * const buffer = thread_local_buffer(t); |
|
561 assert(buffer != NULL, "invariant"); |
|
562 if (!buffer->empty()) { |
|
563 chunkwriter.write_unbuffered(buffer->top(), buffer->pos() - buffer->top()); |
|
564 reset_buffer(buffer, t); |
|
565 } |
|
566 } |
|
567 |
|
568 size_t JfrRecorderService::flush() { |
|
569 assert(JfrStream_lock->owned_by_self(), "invariant"); |
|
570 size_t total_elements = flush_metadata(_chunkwriter); |
|
571 const size_t storage_elements = flush_storage(_storage, _chunkwriter); |
|
572 if (0 == storage_elements) { |
|
573 return total_elements; |
|
574 } |
|
575 total_elements += storage_elements; |
|
576 if (_string_pool.is_modified()) { |
|
577 total_elements += flush_stringpool(_string_pool, _chunkwriter); |
|
578 } |
|
579 if (_stack_trace_repository.is_modified()) { |
|
580 total_elements += flush_stacktrace(_stack_trace_repository, _chunkwriter); |
|
581 } |
|
582 if (_checkpoint_manager.is_type_set_required()) { |
|
583 total_elements += flush_typeset(_checkpoint_manager, _chunkwriter); |
|
584 } else if (_checkpoint_manager.is_static_type_set_required()) { |
|
585 // don't tally this, it is only in order to flush the waiting constants |
|
586 _checkpoint_manager.flush_static_type_set(); |
|
587 } |
|
588 return total_elements; |
|
589 } |
|
590 |
|
591 typedef Content<EventFlush, JfrRecorderService, &JfrRecorderService::flush> FlushFunctor; |
|
592 typedef WriteContent<FlushFunctor> Flush; |
|
593 |
|
594 void JfrRecorderService::invoke_flush() { |
|
595 assert(JfrStream_lock->owned_by_self(), "invariant"); |
517 assert(_chunkwriter.is_valid(), "invariant"); |
596 assert(_chunkwriter.is_valid(), "invariant"); |
518 pre_safepoint_write(); |
597 Thread* const t = Thread::current(); |
519 // Do not attempt safepoint dependent operations during emergency dump. |
598 ResourceMark rm(t); |
520 // Optimistically write tagged artifacts. |
599 HandleMark hm(t); |
521 _checkpoint_manager.shift_epoch(); |
600 ++flushpoint_id; |
522 // update time |
601 reset_thread_local_buffer(t); |
523 _chunkwriter.time_stamp_chunk_now(); |
602 FlushFunctor flushpoint(*this); |
524 post_safepoint_write(); |
603 Flush fl(_chunkwriter, flushpoint); |
525 assert(!_chunkwriter.is_valid(), "invariant"); |
604 invoke_with_flush_event(fl); |
|
605 write_thread_local_buffer(_chunkwriter, t); |
|
606 _repository.flush_chunk(); |
|
607 } |
|
608 |
|
609 void JfrRecorderService::flushpoint() { |
|
610 MutexLocker lock(JfrStream_lock); |
|
611 invoke_flush(); |
526 } |
612 } |
527 |
613 |
528 void JfrRecorderService::process_full_buffers() { |
614 void JfrRecorderService::process_full_buffers() { |
529 if (_chunkwriter.is_valid()) { |
615 if (_chunkwriter.is_valid()) { |
530 assert(!JfrStream_lock->owned_by_self(), "invariant"); |
|
531 MutexLocker stream_lock(JfrStream_lock, Mutex::_no_safepoint_check_flag); |
|
532 _storage.write_full(); |
616 _storage.write_full(); |
533 } |
617 } |
534 } |
618 } |
535 |
619 |
536 void JfrRecorderService::scavenge() { |
620 void JfrRecorderService::scavenge() { |