22 * |
22 * |
23 */ |
23 */ |
24 |
24 |
25 #include "precompiled.hpp" |
25 #include "precompiled.hpp" |
26 #include "jfr/jfrEvents.hpp" |
26 #include "jfr/jfrEvents.hpp" |
27 #include "jfr/recorder/jfrRecorder.hpp" |
|
28 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" |
|
29 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" |
|
30 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
|
31 #include "jfr/leakprofiler/chains/edgeStore.hpp" |
27 #include "jfr/leakprofiler/chains/edgeStore.hpp" |
32 #include "jfr/leakprofiler/chains/objectSampleMarker.hpp" |
28 #include "jfr/leakprofiler/chains/objectSampleMarker.hpp" |
33 #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" |
29 #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" |
34 #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" |
30 #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" |
35 #include "jfr/leakprofiler/leakProfiler.hpp" |
31 #include "jfr/leakprofiler/leakProfiler.hpp" |
36 #include "jfr/leakprofiler/sampling/objectSample.hpp" |
32 #include "jfr/leakprofiler/sampling/objectSample.hpp" |
37 #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
33 #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
38 #include "jfr/leakprofiler/utilities/rootType.hpp" |
34 #include "jfr/leakprofiler/utilities/rootType.hpp" |
39 #include "jfr/metadata/jfrSerializer.hpp" |
35 #include "jfr/metadata/jfrSerializer.hpp" |
40 #include "runtime/interfaceSupport.inline.hpp" |
36 #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" |
41 #include "runtime/mutexLocker.hpp" |
37 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" |
42 #include "runtime/thread.inline.hpp" |
38 #include "jfr/recorder/service/jfrOptionSet.hpp" |
43 |
39 #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" |
44 template <typename SampleProcessor> |
40 #include "jfr/utilities/jfrTypes.hpp" |
45 static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) { |
41 #include "runtime/safepoint.hpp" |
|
42 #include "runtime/thread.hpp" |
|
43 #include "utilities/growableArray.hpp" |
|
44 |
|
45 static bool predicate(GrowableArray<traceid>* set, traceid id) { |
|
46 assert(set != NULL, "invariant"); |
|
47 bool found = false; |
|
48 set->find_sorted<traceid, compare_traceid>(id, found); |
|
49 return found; |
|
50 } |
|
51 |
|
52 static bool mutable_predicate(GrowableArray<traceid>* set, traceid id) { |
|
53 assert(set != NULL, "invariant"); |
|
54 bool found = false; |
|
55 const int location = set->find_sorted<traceid, compare_traceid>(id, found); |
|
56 if (!found) { |
|
57 set->insert_before(location, id); |
|
58 } |
|
59 return found; |
|
60 } |
|
61 |
|
62 static bool add(GrowableArray<traceid>* set, traceid id) { |
|
63 assert(set != NULL, "invariant"); |
|
64 return mutable_predicate(set, id); |
|
65 } |
|
66 |
|
67 const int initial_array_size = 256; |
|
68 |
|
69 template <typename T> |
|
70 static GrowableArray<T>* c_heap_allocate_array(int size = initial_array_size) { |
|
71 return new (ResourceObj::C_HEAP, mtTracing) GrowableArray<T>(size, true, mtTracing); |
|
72 } |
|
73 |
|
74 template <typename T> |
|
75 static GrowableArray<T>* resource_allocate_array(int size = initial_array_size) { |
|
76 return new GrowableArray<T>(size); |
|
77 } |
|
78 |
|
79 static void sort_array(GrowableArray<traceid>* ar) { |
|
80 assert(ar != NULL, "invariant"); |
|
81 ar->sort(sort_traceid); |
|
82 } |
|
83 |
|
84 static GrowableArray<traceid>* unloaded_thread_id_set = NULL; |
|
85 |
|
86 class ThreadIdExclusiveAccess : public StackObj { |
|
87 private: |
|
88 static Semaphore _mutex_semaphore; |
|
89 public: |
|
90 ThreadIdExclusiveAccess() { _mutex_semaphore.wait(); } |
|
91 ~ThreadIdExclusiveAccess() { _mutex_semaphore.signal(); } |
|
92 }; |
|
93 |
|
94 Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1); |
|
95 |
|
96 static void add_to_unloaded_thread_set(traceid tid) { |
|
97 ThreadIdExclusiveAccess lock; |
|
98 if (unloaded_thread_id_set == NULL) { |
|
99 unloaded_thread_id_set = c_heap_allocate_array<traceid>(); |
|
100 } |
|
101 add(unloaded_thread_id_set, tid); |
|
102 } |
|
103 |
|
104 static bool has_thread_exited(traceid tid) { |
|
105 assert(tid != 0, "invariant"); |
|
106 return unloaded_thread_id_set != NULL && predicate(unloaded_thread_id_set, tid); |
|
107 } |
|
108 |
|
109 static GrowableArray<traceid>* unloaded_set = NULL; |
|
110 |
|
111 static void sort_unloaded_set() { |
|
112 if (unloaded_set != NULL) { |
|
113 sort_array(unloaded_set); |
|
114 } |
|
115 } |
|
116 |
|
117 static void add_to_unloaded_set(traceid klass_id) { |
|
118 if (unloaded_set == NULL) { |
|
119 unloaded_set = c_heap_allocate_array<traceid>(); |
|
120 } |
|
121 unloaded_set->append(klass_id); |
|
122 } |
|
123 |
|
124 void ObjectSampleCheckpoint::on_klass_unload(const Klass* k) { |
|
125 assert(k != NULL, "invariant"); |
|
126 add_to_unloaded_set(TRACE_ID(k)); |
|
127 } |
|
128 |
|
129 static bool is_klass_unloaded(traceid klass_id) { |
|
130 return unloaded_set != NULL && predicate(unloaded_set, klass_id); |
|
131 } |
|
132 |
|
133 static GrowableArray<traceid>* id_set = NULL; |
|
134 static GrowableArray<traceid>* stack_trace_id_set = NULL; |
|
135 |
|
136 static bool is_processed(traceid id) { |
|
137 assert(id != 0, "invariant"); |
|
138 assert(id_set != NULL, "invariant"); |
|
139 return mutable_predicate(id_set, id); |
|
140 } |
|
141 |
|
142 static bool is_processed_or_unloaded(traceid klass_id) { |
|
143 assert(klass_id != 0, "invariant"); |
|
144 return is_processed(klass_id) || is_klass_unloaded(klass_id); |
|
145 } |
|
146 |
|
147 static bool should_process(traceid klass_id) { |
|
148 return klass_id != 0 && !is_processed_or_unloaded(klass_id); |
|
149 } |
|
150 |
|
151 static bool is_stack_trace_processed(traceid stack_trace_id) { |
|
152 assert(stack_trace_id != 0, "invariant"); |
|
153 assert(stack_trace_id_set != NULL, "invariant"); |
|
154 return mutable_predicate(stack_trace_id_set, stack_trace_id); |
|
155 } |
|
156 |
|
157 template <typename Processor> |
|
158 static void do_samples(ObjectSample* sample, const ObjectSample* const end, Processor& processor) { |
46 assert(sample != NULL, "invariant"); |
159 assert(sample != NULL, "invariant"); |
47 while (sample != end) { |
160 while (sample != end) { |
48 processor.sample_do(sample); |
161 processor.sample_do(sample); |
49 sample = sample->next(); |
162 sample = sample->next(); |
|
163 } |
|
164 } |
|
165 |
|
166 template <typename Processor> |
|
167 static void iterate_samples(Processor& processor, bool all = false, bool update_last_resolved = false) { |
|
168 ObjectSampler* const sampler = ObjectSampler::sampler(); |
|
169 assert(sampler != NULL, "invariant"); |
|
170 ObjectSample* const last = sampler->last(); |
|
171 assert(last != NULL, "invariant"); |
|
172 do_samples(last, all ? NULL : sampler->last_resolved(), processor); |
|
173 if (update_last_resolved) { |
|
174 sampler->set_last_resolved(last); |
|
175 } |
|
176 } |
|
177 |
|
178 void ObjectSampleCheckpoint::on_thread_exit(JavaThread* jt) { |
|
179 assert(jt != NULL, "invariant"); |
|
180 if (LeakProfiler::is_running()) { |
|
181 add_to_unloaded_thread_set(jt->jfr_thread_local()->thread_id()); |
|
182 } |
|
183 } |
|
184 |
|
185 class CheckpointInstall { |
|
186 private: |
|
187 const JfrCheckpointBlobHandle& _cp; |
|
188 public: |
|
189 CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {} |
|
190 void sample_do(ObjectSample* sample) { |
|
191 assert(sample != NULL, "invariant"); |
|
192 if (!sample->is_dead()) { |
|
193 sample->set_klass_checkpoint(_cp); |
|
194 } |
|
195 } |
|
196 }; |
|
197 |
|
198 static void install_blob(JfrCheckpointWriter& writer) { |
|
199 assert(writer.has_data(), "invariant"); |
|
200 const JfrCheckpointBlobHandle h_cp = writer.copy(); |
|
201 CheckpointInstall install(h_cp); |
|
202 iterate_samples(install, true, false); |
|
203 } |
|
204 |
|
205 void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) { |
|
206 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
|
207 assert(LeakProfiler::is_running(), "invariant"); |
|
208 if (writer.has_data() && ObjectSampler::sampler()->last() != NULL) { |
|
209 install_blob(writer); |
|
210 } |
|
211 } |
|
212 |
|
213 class ObjectResolver { |
|
214 public: |
|
215 ObjectResolver() {} |
|
216 void sample_do(ObjectSample* sample) { |
|
217 assert(sample != NULL, "invariant"); |
|
218 const traceid klass_id = sample->_klass_id; |
|
219 if (klass_id != 0 || sample->is_dead() || is_klass_unloaded(klass_id)) { |
|
220 return; |
|
221 } |
|
222 sample->_klass_id = JfrTraceId::use(sample->klass()); |
|
223 } |
|
224 }; |
|
225 |
|
226 void ObjectSampleCheckpoint::resolve_sampled_objects() { |
|
227 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
|
228 assert(LeakProfiler::is_running(), "invariant"); |
|
229 if (ObjectSampler::sampler()->last() == NULL) { |
|
230 return; |
|
231 } |
|
232 ObjectResolver resolver; |
|
233 iterate_samples(resolver, false, true); |
|
234 } |
|
235 |
|
236 class SampleMark { |
|
237 private: |
|
238 ObjectSampleMarker& _marker; |
|
239 jlong _last_sweep; |
|
240 int _count; |
|
241 public: |
|
242 SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), _last_sweep(last_sweep), _count(0) {} |
|
243 void sample_do(ObjectSample* sample) { |
|
244 assert(sample != NULL, "invariant"); |
|
245 if (sample->is_alive_and_older_than(_last_sweep)) { |
|
246 _marker.mark(sample->object()); |
|
247 ++_count; |
|
248 } |
|
249 } |
|
250 int count() const { |
|
251 return _count; |
|
252 } |
|
253 }; |
|
254 |
|
255 int ObjectSampleCheckpoint::save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all) { |
|
256 assert(sampler != NULL, "invariant"); |
|
257 if (sampler->last() == NULL) { |
|
258 return 0; |
|
259 } |
|
260 SampleMark mark(marker, emit_all ? max_jlong : sampler->last_sweep().value()); |
|
261 iterate_samples(mark, true, false); |
|
262 return mark.count(); |
|
263 } |
|
264 |
|
265 void ObjectSampleCheckpoint::tag(const ObjectSample* sample) { |
|
266 assert(sample != NULL, "invariant"); |
|
267 const traceid klass_id = sample->_klass_id; |
|
268 if (should_process(sample->_klass_id)) { |
|
269 JfrTraceId::use(sample->klass()); |
|
270 } |
|
271 } |
|
272 |
|
273 #ifdef ASSERT |
|
274 static traceid get_klass_id(const Klass* k) { |
|
275 assert(k != NULL, "invariant"); |
|
276 return TRACE_ID(k); |
|
277 } |
|
278 #endif |
|
279 |
|
280 static traceid get_klass_id(traceid method_id) { |
|
281 assert(method_id != 0, "invariant"); |
|
282 return method_id >> TRACE_ID_SHIFT; |
|
283 } |
|
284 |
|
285 static int get_method_id_num(traceid method_id) { |
|
286 return (int)(method_id & METHOD_ID_NUM_MASK); |
|
287 } |
|
288 |
|
289 static Method* lookup_method_in_klasses(Klass* klass, int orig_method_id_num) { |
|
290 assert(klass != NULL, "invariant"); |
|
291 assert(!is_klass_unloaded(get_klass_id(klass)), "invariant"); |
|
292 while (klass != NULL) { |
|
293 if (klass->is_instance_klass()) { |
|
294 Method* const m = InstanceKlass::cast(klass)->method_with_orig_idnum(orig_method_id_num); |
|
295 if (m != NULL) { |
|
296 return m; |
|
297 } |
|
298 } |
|
299 klass = klass->super(); |
|
300 } |
|
301 return NULL; |
|
302 } |
|
303 |
|
304 static Method* lookup_method_in_interfaces(Klass* klass, int orig_method_id_num) { |
|
305 assert(klass != NULL, "invariant"); |
|
306 const Array<InstanceKlass*>* const all_ifs = InstanceKlass::cast(klass)->transitive_interfaces(); |
|
307 const int num_ifs = all_ifs->length(); |
|
308 for (int i = 0; i < num_ifs; i++) { |
|
309 InstanceKlass* const ik = all_ifs->at(i); |
|
310 Method* const m = ik->method_with_orig_idnum(orig_method_id_num); |
|
311 if (m != NULL) { |
|
312 return m; |
|
313 } |
|
314 } |
|
315 return NULL; |
|
316 } |
|
317 |
|
318 static Method* lookup_method(Klass* klass, int orig_method_id_num) { |
|
319 Method* m = lookup_method_in_klasses(klass, orig_method_id_num); |
|
320 if (m == NULL) { |
|
321 m = lookup_method_in_interfaces(klass, orig_method_id_num); |
|
322 } |
|
323 assert(m != NULL, "invariant"); |
|
324 return m; |
|
325 } |
|
326 |
|
327 static void write_stack_trace(traceid id, bool reached_root, u4 nr_of_frames, JfrCheckpointWriter* writer) { |
|
328 assert(writer != NULL, "invariant"); |
|
329 writer->write(id); |
|
330 writer->write((u1)!reached_root); |
|
331 writer->write(nr_of_frames); |
|
332 } |
|
333 |
|
334 static void write_stack_frame(const JfrStackFrame* frame, JfrCheckpointWriter* writer) { |
|
335 assert(frame != NULL, "invariant"); |
|
336 frame->write(*writer); |
|
337 } |
|
338 |
|
339 bool ObjectSampleCheckpoint::tag(const JfrStackTrace* trace, JfrCheckpointWriter* writer /* NULL */) { |
|
340 assert(trace != NULL, "invariant"); |
|
341 if (is_stack_trace_processed(trace->id())) { |
|
342 return false; |
|
343 } |
|
344 if (writer != NULL) { |
|
345 // JfrStackTrace |
|
346 write_stack_trace(trace->id(), trace->_reached_root, trace->_nr_of_frames, writer); |
|
347 } |
|
348 traceid last_id = 0; |
|
349 for (u4 i = 0; i < trace->_nr_of_frames; ++i) { |
|
350 if (writer != NULL) { |
|
351 // JfrStackFrame(s) |
|
352 write_stack_frame(&trace->_frames[i], writer); |
|
353 } |
|
354 const traceid method_id = trace->_frames[i]._methodid; |
|
355 if (last_id == method_id || is_processed(method_id) || is_klass_unloaded(get_klass_id(method_id))) { |
|
356 continue; |
|
357 } |
|
358 last_id = method_id; |
|
359 InstanceKlass* const ik = trace->_frames[i]._klass; |
|
360 assert(ik != NULL, "invariant"); |
|
361 JfrTraceId::use(ik, lookup_method(ik, get_method_id_num(method_id))); |
|
362 } |
|
363 return true; |
|
364 } |
|
365 |
|
366 static bool stack_trace_precondition(const ObjectSample* sample) { |
|
367 assert(sample != NULL, "invariant"); |
|
368 return sample->has_stack_trace_id() && !sample->is_dead(); |
|
369 } |
|
370 |
|
371 class Tagger { |
|
372 private: |
|
373 JfrStackTraceRepository& _stack_trace_repo; |
|
374 public: |
|
375 Tagger(JfrStackTraceRepository& stack_trace_repo) : _stack_trace_repo(stack_trace_repo) {} |
|
376 void sample_do(ObjectSample* sample) { |
|
377 ObjectSampleCheckpoint::tag(sample); |
|
378 if (stack_trace_precondition(sample)) { |
|
379 assert(sample->stack_trace_id() == sample->stack_trace()->id(), "invariant"); |
|
380 ObjectSampleCheckpoint::tag(sample->stack_trace(), NULL); |
|
381 } |
|
382 } |
|
383 }; |
|
384 |
|
385 static void tag_old_traces(ObjectSample* last_resolved, JfrStackTraceRepository& stack_trace_repo) { |
|
386 assert(last_resolved != NULL, "invariant"); |
|
387 assert(stack_trace_id_set != NULL, "invariant"); |
|
388 assert(stack_trace_id_set->is_empty(), "invariant"); |
|
389 Tagger tagger(stack_trace_repo); |
|
390 do_samples(last_resolved, NULL, tagger); |
|
391 } |
|
392 |
|
393 class StackTraceInstall { |
|
394 private: |
|
395 JfrStackTraceRepository& _stack_trace_repo; |
|
396 public: |
|
397 StackTraceInstall(JfrStackTraceRepository& stack_trace_repo) : _stack_trace_repo(stack_trace_repo) {} |
|
398 void install_to_sample(ObjectSample* sample, const JfrStackTrace* stack_trace); |
|
399 void sample_do(ObjectSample* sample) { |
|
400 ObjectSampleCheckpoint::tag(sample); |
|
401 if (stack_trace_precondition(sample)) { |
|
402 install_to_sample(sample, _stack_trace_repo.lookup(sample->stack_trace_hash(), sample->stack_trace_id())); |
|
403 } |
|
404 } |
|
405 }; |
|
406 |
|
407 #ifdef ASSERT |
|
408 static void validate_stack_trace(const ObjectSample* sample, const JfrStackTrace* trace) { |
|
409 assert(sample != NULL, "invariant"); |
|
410 assert(trace != NULL, "invariant"); |
|
411 assert(trace->hash() == sample->stack_trace_hash(), "invariant"); |
|
412 assert(trace->id() == sample->stack_trace_id(), "invariant"); |
|
413 } |
|
414 #endif |
|
415 |
|
416 void StackTraceInstall::install_to_sample(ObjectSample* sample, const JfrStackTrace* stack_trace) { |
|
417 assert(sample != NULL, "invariant"); |
|
418 assert(stack_trace != NULL, "invariant"); |
|
419 DEBUG_ONLY(validate_stack_trace(sample, stack_trace)); |
|
420 JfrStackTrace* const sample_trace = const_cast<JfrStackTrace*>(sample->stack_trace()); |
|
421 if (sample_trace != NULL) { |
|
422 *sample_trace = *stack_trace; // copy |
|
423 } else { |
|
424 sample->set_stack_trace(new JfrStackTrace(stack_trace->id(), *stack_trace, NULL)); // new |
|
425 } |
|
426 assert(sample->stack_trace() != NULL, "invariant"); |
|
427 } |
|
428 |
|
429 static void install_new_stack_traces(JfrStackTraceRepository& stack_trace_repo) { |
|
430 StackTraceInstall stack_trace_install(stack_trace_repo); |
|
431 iterate_samples(stack_trace_install); |
|
432 stack_trace_id_set->clear(); |
|
433 } |
|
434 |
|
435 static void allocate_traceid_working_sets() { |
|
436 const int set_size = JfrOptionSet::old_object_queue_size(); |
|
437 stack_trace_id_set = resource_allocate_array<traceid>(set_size); |
|
438 id_set = resource_allocate_array<traceid>(set_size); |
|
439 sort_unloaded_set(); |
|
440 } |
|
441 |
|
442 // caller needs ResourceMark |
|
443 void ObjectSampleCheckpoint::rotate(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) { |
|
444 assert(sampler != NULL, "invariant"); |
|
445 assert(LeakProfiler::is_running(), "invariant"); |
|
446 if (sampler->last() == NULL) { |
|
447 // nothing to process |
|
448 return; |
|
449 } |
|
450 allocate_traceid_working_sets(); |
|
451 install_new_stack_traces(stack_trace_repo); |
|
452 ObjectSample* const last_resolved = const_cast<ObjectSample*>(sampler->last_resolved()); |
|
453 if (last_resolved != NULL) { |
|
454 tag_old_traces(last_resolved, stack_trace_repo); |
50 } |
455 } |
51 } |
456 } |
52 |
457 |
53 class RootSystemType : public JfrSerializer { |
458 class RootSystemType : public JfrSerializer { |
54 public: |
459 public: |
72 writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i)); |
477 writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i)); |
73 } |
478 } |
74 } |
479 } |
75 }; |
480 }; |
76 |
481 |
77 class CheckpointInstall { |
482 static void register_serializers() { |
78 private: |
483 static bool is_registered = false; |
79 const JfrCheckpointBlobHandle& _cp; |
484 if (!is_registered) { |
80 public: |
485 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, true, new RootSystemType()); |
81 CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {} |
486 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, true, new RootType()); |
82 void sample_do(ObjectSample* sample) { |
487 is_registered = true; |
83 assert(sample != NULL, "invariant"); |
488 } |
84 if (!sample->is_dead()) { |
489 } |
85 sample->set_klass_checkpoint(_cp); |
490 |
86 } |
491 static void reset_blob_write_state(const ObjectSample* sample) { |
87 } |
492 assert(sample != NULL, "invariant"); |
88 }; |
493 if (sample->has_thread_checkpoint()) { |
|
494 sample->thread_checkpoint()->reset_write_state(); |
|
495 } |
|
496 if (sample->has_klass_checkpoint()) { |
|
497 sample->klass_checkpoint()->reset_write_state(); |
|
498 } |
|
499 } |
|
500 |
|
501 static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { |
|
502 if (sample->has_thread_checkpoint() && has_thread_exited(sample->thread_id())) { |
|
503 sample->thread_checkpoint()->exclusive_write(writer); |
|
504 } |
|
505 } |
|
506 |
|
507 static void write_klass_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { |
|
508 if (sample->has_klass_checkpoint()) { |
|
509 sample->klass_checkpoint()->exclusive_write(writer); |
|
510 } |
|
511 } |
|
512 |
|
513 static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer) { |
|
514 assert(sample != NULL, "invariant"); |
|
515 write_thread_blob(sample, writer); |
|
516 write_klass_blob(sample, writer); |
|
517 } |
89 |
518 |
90 class CheckpointWrite { |
519 class CheckpointWrite { |
91 private: |
520 private: |
|
521 const ObjectSampler* _sampler; |
92 JfrCheckpointWriter& _writer; |
522 JfrCheckpointWriter& _writer; |
93 const jlong _last_sweep; |
523 const jlong _last_sweep; |
94 public: |
524 public: |
95 CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {} |
525 CheckpointWrite(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : |
|
526 _sampler(sampler), _writer(writer), _last_sweep(last_sweep) {} |
96 void sample_do(ObjectSample* sample) { |
527 void sample_do(ObjectSample* sample) { |
97 assert(sample != NULL, "invariant"); |
528 assert(sample != NULL, "invariant"); |
98 if (sample->is_alive_and_older_than(_last_sweep)) { |
529 if (sample->is_alive_and_older_than(_last_sweep)) { |
99 if (sample->has_thread_checkpoint()) { |
530 write_blobs(sample, _writer); |
100 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); |
|
101 thread_cp->exclusive_write(_writer); |
|
102 } |
|
103 if (sample->has_klass_checkpoint()) { |
|
104 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); |
|
105 klass_cp->exclusive_write(_writer); |
|
106 } |
|
107 } |
531 } |
108 } |
532 } |
109 }; |
533 }; |
110 |
534 |
111 class CheckpointStateReset { |
535 class CheckpointStateReset { |
112 private: |
536 private: |
|
537 const ObjectSampler* _sampler; |
113 const jlong _last_sweep; |
538 const jlong _last_sweep; |
114 public: |
539 public: |
115 CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {} |
540 CheckpointStateReset(const ObjectSampler* sampler, jlong last_sweep) : _sampler(sampler), _last_sweep(last_sweep) {} |
116 void sample_do(ObjectSample* sample) { |
541 void sample_do(ObjectSample* sample) { |
117 assert(sample != NULL, "invariant"); |
542 assert(sample != NULL, "invariant"); |
118 if (sample->is_alive_and_older_than(_last_sweep)) { |
543 if (sample->is_alive_and_older_than(_last_sweep)) { |
119 if (sample->has_thread_checkpoint()) { |
544 reset_blob_write_state(sample); |
120 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); |
545 } |
121 thread_cp->reset_write_state(); |
546 } |
122 } |
547 }; |
123 if (sample->has_klass_checkpoint()) { |
548 |
124 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); |
549 static void reset_write_state_for_blobs(const ObjectSampler* sampler, jlong last_sweep) { |
125 klass_cp->reset_write_state(); |
550 CheckpointStateReset state_reset(sampler, last_sweep); |
126 } |
551 iterate_samples(state_reset, true, false); |
127 } |
552 } |
128 } |
553 |
129 }; |
554 static void write_sample_blobs(const ObjectSampler* sampler, jlong last_sweep, Thread* thread) { |
|
555 JfrCheckpointWriter writer(thread, false); |
|
556 CheckpointWrite checkpoint_write(sampler, writer, last_sweep); |
|
557 iterate_samples(checkpoint_write, true, false); |
|
558 reset_write_state_for_blobs(sampler, last_sweep); |
|
559 } |
130 |
560 |
131 class StackTraceWrite { |
561 class StackTraceWrite { |
132 private: |
562 private: |
133 JfrStackTraceRepository& _stack_trace_repo; |
563 JfrStackTraceRepository& _stack_trace_repo; |
134 JfrCheckpointWriter& _writer; |
564 JfrCheckpointWriter& _writer; |
|
565 const jlong _last_sweep; |
135 int _count; |
566 int _count; |
136 public: |
567 public: |
137 StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) : |
568 StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer, jlong last_sweep) : |
138 _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) { |
569 _stack_trace_repo(stack_trace_repo), _writer(writer), _last_sweep(last_sweep), _count(0) {} |
139 JfrStacktrace_lock->lock_without_safepoint_check(); |
570 void sample_do(ObjectSample* sample) { |
140 } |
571 ObjectSampleCheckpoint::tag(sample); |
141 ~StackTraceWrite() { |
572 if (stack_trace_precondition(sample) && sample->is_alive_and_older_than(_last_sweep)) { |
142 assert(JfrStacktrace_lock->owned_by_self(), "invariant"); |
573 assert(sample->stack_trace_id() == sample->stack_trace()->id(), "invariant"); |
143 JfrStacktrace_lock->unlock(); |
574 if (ObjectSampleCheckpoint::tag(sample->stack_trace(), &_writer)) { |
144 } |
|
145 |
|
146 void sample_do(ObjectSample* sample) { |
|
147 assert(sample != NULL, "invariant"); |
|
148 if (!sample->is_dead()) { |
|
149 if (sample->has_stack_trace()) { |
|
150 JfrTraceId::use(sample->klass(), true); |
|
151 _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash()); |
|
152 ++_count; |
575 ++_count; |
153 } |
576 } |
154 } |
577 } |
155 } |
578 } |
156 |
|
157 int count() const { |
579 int count() const { |
158 return _count; |
580 return _count; |
159 } |
581 } |
160 }; |
582 }; |
161 |
583 |
162 class SampleMark { |
584 static void write_and_tag_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepository& repo, jlong last_sweep, Thread* thread) { |
163 private: |
585 assert(sampler != NULL, "invariant"); |
164 ObjectSampleMarker& _marker; |
586 allocate_traceid_working_sets(); |
165 jlong _last_sweep; |
587 install_new_stack_traces(repo); |
166 int _count; |
588 JfrCheckpointWriter writer(thread); |
167 public: |
589 const JfrCheckpointContext ctx = writer.context(); |
168 SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), |
590 writer.write_type(TYPE_STACKTRACE); |
169 _last_sweep(last_sweep), |
591 const jlong count_offset = writer.reserve(sizeof(u4)); |
170 _count(0) {} |
592 StackTraceWrite sw(repo, writer, last_sweep); |
171 void sample_do(ObjectSample* sample) { |
593 do_samples(sampler->last(), NULL, sw); |
172 assert(sample != NULL, "invariant"); |
594 if (sw.count() == 0) { |
173 if (sample->is_alive_and_older_than(_last_sweep)) { |
595 writer.set_context(ctx); |
174 _marker.mark(sample->object()); |
|
175 ++_count; |
|
176 } |
|
177 } |
|
178 |
|
179 int count() const { |
|
180 return _count; |
|
181 } |
|
182 }; |
|
183 |
|
184 void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool resume) { |
|
185 assert(class_unload ? SafepointSynchronize::is_at_safepoint() : LeakProfiler::is_suspended(), "invariant"); |
|
186 |
|
187 if (!writer.has_data()) { |
|
188 if (!class_unload) { |
|
189 LeakProfiler::resume(); |
|
190 } |
|
191 assert(LeakProfiler::is_running(), "invariant"); |
|
192 return; |
596 return; |
193 } |
597 } |
194 |
598 writer.write_count((u4)sw.count(), count_offset); |
195 assert(writer.has_data(), "invariant"); |
599 } |
196 const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob(); |
600 |
197 |
601 void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { |
198 const ObjectSampler* const object_sampler = LeakProfiler::object_sampler(); |
602 assert(sampler != NULL, "invariant"); |
199 assert(object_sampler != NULL, "invariant"); |
|
200 |
|
201 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last()); |
|
202 const ObjectSample* const last_resolved = object_sampler->last_resolved(); |
|
203 CheckpointInstall install(h_cp); |
|
204 |
|
205 if (class_unload) { |
|
206 if (last != NULL) { |
|
207 // all samples need the class unload information |
|
208 do_samples(last, NULL, install); |
|
209 } |
|
210 assert(LeakProfiler::is_running(), "invariant"); |
|
211 return; |
|
212 } |
|
213 |
|
214 // only new samples since last resolved checkpoint |
|
215 if (last != last_resolved) { |
|
216 do_samples(last, last_resolved, install); |
|
217 if (resume) { |
|
218 const_cast<ObjectSampler*>(object_sampler)->set_last_resolved(last); |
|
219 } |
|
220 } |
|
221 assert(LeakProfiler::is_suspended(), "invariant"); |
|
222 if (resume) { |
|
223 LeakProfiler::resume(); |
|
224 assert(LeakProfiler::is_running(), "invariant"); |
|
225 } |
|
226 } |
|
227 |
|
228 void ObjectSampleCheckpoint::write(const EdgeStore* edge_store, bool emit_all, Thread* thread) { |
|
229 assert(edge_store != NULL, "invariant"); |
603 assert(edge_store != NULL, "invariant"); |
230 assert(thread != NULL, "invariant"); |
604 assert(thread != NULL, "invariant"); |
231 static bool types_registered = false; |
605 register_serializers(); |
232 if (!types_registered) { |
606 // sample set is predicated on time of last sweep |
233 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, true, new RootSystemType()); |
607 const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value(); |
234 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, true, new RootType()); |
608 write_and_tag_stack_traces(sampler, JfrStackTraceRepository::instance(), last_sweep, thread); |
235 types_registered = true; |
609 write_sample_blobs(sampler, last_sweep, thread); |
236 } |
610 // write reference chains |
237 const ObjectSampler* const object_sampler = LeakProfiler::object_sampler(); |
|
238 assert(object_sampler != NULL, "invariant"); |
|
239 const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); |
|
240 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last()); |
|
241 { |
|
242 JfrCheckpointWriter writer(false, false, thread); |
|
243 CheckpointWrite checkpoint_write(writer, last_sweep); |
|
244 do_samples(last, NULL, checkpoint_write); |
|
245 } |
|
246 CheckpointStateReset state_reset(last_sweep); |
|
247 do_samples(last, NULL, state_reset); |
|
248 if (!edge_store->is_empty()) { |
611 if (!edge_store->is_empty()) { |
249 // java object and chain representations |
612 JfrCheckpointWriter writer(thread); |
250 JfrCheckpointWriter writer(false, true, thread); |
|
251 ObjectSampleWriter osw(writer, edge_store); |
613 ObjectSampleWriter osw(writer, edge_store); |
252 edge_store->iterate_edges(osw); |
614 edge_store->iterate(osw); |
253 } |
615 } |
254 } |
616 } |
255 |
|
256 WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(JfrStackTraceRepository& repo) : |
|
257 _stack_trace_repo(repo) { |
|
258 } |
|
259 |
|
260 bool WriteObjectSampleStacktrace::process() { |
|
261 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); |
|
262 if (!LeakProfiler::is_running()) { |
|
263 return true; |
|
264 } |
|
265 // Suspend the LeakProfiler subsystem |
|
266 // to ensure stable samples even |
|
267 // after we return from the safepoint. |
|
268 LeakProfiler::suspend(); |
|
269 assert(!LeakProfiler::is_running(), "invariant"); |
|
270 assert(LeakProfiler::is_suspended(), "invariant"); |
|
271 |
|
272 const ObjectSampler* object_sampler = LeakProfiler::object_sampler(); |
|
273 assert(object_sampler != NULL, "invariant"); |
|
274 assert(LeakProfiler::is_suspended(), "invariant"); |
|
275 |
|
276 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last()); |
|
277 const ObjectSample* const last_resolved = object_sampler->last_resolved(); |
|
278 if (last == last_resolved) { |
|
279 assert(LeakProfiler::is_suspended(), "invariant"); |
|
280 return true; |
|
281 } |
|
282 |
|
283 JfrCheckpointWriter writer(false, true, Thread::current()); |
|
284 const JfrCheckpointContext ctx = writer.context(); |
|
285 |
|
286 writer.write_type(TYPE_STACKTRACE); |
|
287 const jlong count_offset = writer.reserve(sizeof(u4)); |
|
288 |
|
289 int count = 0; |
|
290 { |
|
291 StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock |
|
292 do_samples(last, last_resolved, stack_trace_write); |
|
293 count = stack_trace_write.count(); |
|
294 } |
|
295 if (count == 0) { |
|
296 writer.set_context(ctx); |
|
297 assert(LeakProfiler::is_suspended(), "invariant"); |
|
298 return true; |
|
299 } |
|
300 assert(count > 0, "invariant"); |
|
301 writer.write_count((u4)count, count_offset); |
|
302 JfrStackTraceRepository::write_metadata(writer); |
|
303 |
|
304 ObjectSampleCheckpoint::install(writer, false, false); |
|
305 assert(LeakProfiler::is_suspended(), "invariant"); |
|
306 return true; |
|
307 } |
|
308 |
|
309 int ObjectSampleCheckpoint::mark(ObjectSampleMarker& marker, bool emit_all) { |
|
310 const ObjectSampler* object_sampler = LeakProfiler::object_sampler(); |
|
311 assert(object_sampler != NULL, "invariant"); |
|
312 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last()); |
|
313 if (last == NULL) { |
|
314 return 0; |
|
315 } |
|
316 const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); |
|
317 SampleMark mark(marker, last_sweep); |
|
318 do_samples(last, NULL, mark); |
|
319 return mark.count(); |
|
320 } |
|