1 | /* |
2 | * Copyright (c) 2014, 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 "jfrfiles/jfrTypes.hpp" |
27 | #include "jfr/leakprofiler/chains/edge.hpp" |
28 | #include "jfr/leakprofiler/chains/edgeStore.hpp" |
29 | #include "jfr/leakprofiler/chains/edgeUtils.hpp" |
30 | #include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp" |
31 | #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" |
32 | #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" |
33 | #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
34 | #include "jfr/leakprofiler/utilities/rootType.hpp" |
35 | #include "jfr/leakprofiler/utilities/unifiedOop.hpp" |
36 | #include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" |
37 | #include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" |
38 | #include "oops/oop.inline.hpp" |
39 | #include "oops/symbol.hpp" |
40 | #include "utilities/growableArray.hpp" |
41 | |
42 | template <typename Data> |
43 | class ObjectSampleAuxInfo : public ResourceObj { |
44 | public: |
45 | Data _data; |
46 | traceid _id; |
47 | ObjectSampleAuxInfo() : _data(), _id(0) {} |
48 | }; |
49 | |
50 | class ObjectSampleArrayData { |
51 | public: |
52 | int _array_size; |
53 | int _array_index; |
54 | ObjectSampleArrayData() : _array_size(0), _array_index(0) {} |
55 | }; |
56 | |
57 | class ObjectSampleFieldInfo : public ResourceObj { |
58 | public: |
59 | const Symbol* _field_name_symbol; |
60 | jshort _field_modifiers; |
61 | ObjectSampleFieldInfo() : _field_name_symbol(NULL), _field_modifiers(0) {} |
62 | }; |
63 | |
64 | class ObjectSampleRootDescriptionData { |
65 | public: |
66 | const Edge* _root_edge; |
67 | const char* _description; |
68 | OldObjectRoot::System _system; |
69 | OldObjectRoot::Type _type; |
70 | ObjectSampleRootDescriptionData() : _root_edge(NULL), |
71 | _description(NULL), |
72 | _system(OldObjectRoot::_system_undetermined), |
73 | _type(OldObjectRoot::_type_undetermined) {} |
74 | }; |
75 | |
76 | class OldObjectSampleData { |
77 | public: |
78 | oop _object; |
79 | traceid _reference_id; |
80 | }; |
81 | |
82 | class ReferenceData { |
83 | public: |
84 | traceid _field_info_id; |
85 | traceid _array_info_id; |
86 | traceid _old_object_sample_id; |
87 | size_t _skip; |
88 | }; |
89 | |
90 | static int initial_storage_size = 16; |
91 | |
92 | template <typename Data> |
93 | class SampleSet : public ResourceObj { |
94 | private: |
95 | GrowableArray<Data>* _storage; |
96 | public: |
97 | SampleSet() : _storage(NULL) {} |
98 | |
99 | traceid store(Data data) { |
100 | assert(data != NULL, "invariant" ); |
101 | if (_storage == NULL) { |
102 | _storage = new GrowableArray<Data>(initial_storage_size); |
103 | } |
104 | assert(_storage != NULL, "invariant" ); |
105 | assert(_storage->find(data) == -1, "invariant" ); |
106 | _storage->append(data); |
107 | return data->_id; |
108 | } |
109 | |
110 | size_t size() const { |
111 | return _storage != NULL ? (size_t)_storage->length() : 0; |
112 | } |
113 | |
114 | template <typename Functor> |
115 | void iterate(Functor& functor) { |
116 | if (_storage != NULL) { |
117 | for (int i = 0; i < _storage->length(); ++i) { |
118 | functor(_storage->at(i)); |
119 | } |
120 | } |
121 | } |
122 | |
123 | const GrowableArray<Data>& storage() const { |
124 | return *_storage; |
125 | } |
126 | }; |
127 | |
128 | typedef ObjectSampleAuxInfo<ObjectSampleArrayData> ObjectSampleArrayInfo; |
129 | typedef ObjectSampleAuxInfo<ObjectSampleRootDescriptionData> ObjectSampleRootDescriptionInfo; |
130 | typedef ObjectSampleAuxInfo<OldObjectSampleData> OldObjectSampleInfo; |
131 | typedef ObjectSampleAuxInfo<ReferenceData> ReferenceInfo; |
132 | |
133 | class FieldTable : public ResourceObj { |
134 | template <typename, |
135 | typename, |
136 | template<typename, typename> class, |
137 | typename, |
138 | size_t> |
139 | friend class HashTableHost; |
140 | typedef HashTableHost<const ObjectSampleFieldInfo*, traceid, Entry, FieldTable, 109> FieldInfoTable; |
141 | public: |
142 | typedef FieldInfoTable::HashEntry FieldInfoEntry; |
143 | |
144 | private: |
145 | static traceid _field_id_counter; |
146 | FieldInfoTable* _table; |
147 | |
148 | void assign_id(FieldInfoEntry* entry) { |
149 | assert(entry != NULL, "invariant" ); |
150 | entry->set_id(++_field_id_counter); |
151 | } |
152 | |
153 | bool equals(const ObjectSampleFieldInfo* query, uintptr_t hash, const FieldInfoEntry* entry) { |
154 | assert(hash == entry->hash(), "invariant" ); |
155 | assert(query != NULL, "invariant" ); |
156 | const ObjectSampleFieldInfo* stored = entry->literal(); |
157 | assert(stored != NULL, "invariant" ); |
158 | assert(stored->_field_name_symbol->identity_hash() == query->_field_name_symbol->identity_hash(), "invariant" ); |
159 | return stored->_field_modifiers == query->_field_modifiers; |
160 | } |
161 | |
162 | public: |
163 | FieldTable() : _table(new FieldInfoTable(this)) {} |
164 | ~FieldTable() { |
165 | assert(_table != NULL, "invariant" ); |
166 | delete _table; |
167 | } |
168 | |
169 | traceid store(const ObjectSampleFieldInfo* field_info) { |
170 | assert(field_info != NULL, "invariant" ); |
171 | const FieldInfoEntry& entry =_table->lookup_put(field_info, |
172 | field_info->_field_name_symbol->identity_hash()); |
173 | return entry.id(); |
174 | } |
175 | |
176 | size_t size() const { |
177 | return _table->cardinality(); |
178 | } |
179 | |
180 | template <typename T> |
181 | void iterate(T& functor) const { |
182 | _table->iterate_entry<T>(functor); |
183 | } |
184 | }; |
185 | |
186 | traceid FieldTable::_field_id_counter = 0; |
187 | |
188 | typedef SampleSet<const OldObjectSampleInfo*> SampleInfo; |
189 | typedef SampleSet<const ReferenceInfo*> RefInfo; |
190 | typedef SampleSet<const ObjectSampleArrayInfo*> ArrayInfo; |
191 | typedef SampleSet<const ObjectSampleRootDescriptionInfo*> RootDescriptionInfo; |
192 | |
193 | static SampleInfo* sample_infos = NULL; |
194 | static RefInfo* ref_infos = NULL; |
195 | static ArrayInfo* array_infos = NULL; |
196 | static FieldTable* field_infos = NULL; |
197 | static RootDescriptionInfo* root_infos = NULL; |
198 | |
199 | int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* si) { |
200 | assert(writer != NULL, "invariant" ); |
201 | assert(si != NULL, "invariant" ); |
202 | const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si; |
203 | oop object = oosi->_data._object; |
204 | assert(object != NULL, "invariant" ); |
205 | writer->write(oosi->_id); |
206 | writer->write((u8)(const HeapWord*)object); |
207 | writer->write(const_cast<const Klass*>(object->klass())); |
208 | ObjectSampleDescription od(object); |
209 | writer->write(od.description()); |
210 | writer->write(oosi->_data._reference_id); |
211 | return 1; |
212 | } |
213 | |
214 | typedef JfrArtifactWriterImplHost<const OldObjectSampleInfo*, __write_sample_info__> SampleWriterImpl; |
215 | typedef JfrArtifactWriterHost<SampleWriterImpl, TYPE_OLDOBJECT> SampleWriter; |
216 | |
217 | static void write_sample_infos(JfrCheckpointWriter& writer) { |
218 | if (sample_infos != NULL) { |
219 | SampleWriter sw(&writer, NULL, false); |
220 | sample_infos->iterate(sw); |
221 | } |
222 | } |
223 | |
224 | int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) { |
225 | assert(writer != NULL, "invariant" ); |
226 | assert(ri != NULL, "invariant" ); |
227 | const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri; |
228 | writer->write(ref_info->_id); |
229 | writer->write(ref_info->_data._array_info_id); |
230 | writer->write(ref_info->_data._field_info_id); |
231 | writer->write(ref_info->_data._old_object_sample_id); |
232 | writer->write<s4>((s4)ref_info->_data._skip); |
233 | return 1; |
234 | } |
235 | |
236 | typedef JfrArtifactWriterImplHost<const ReferenceInfo*, __write_reference_info__> ReferenceWriterImpl; |
237 | typedef JfrArtifactWriterHost<ReferenceWriterImpl, TYPE_REFERENCE> ReferenceWriter; |
238 | |
239 | static void write_reference_infos(JfrCheckpointWriter& writer) { |
240 | if (ref_infos != NULL) { |
241 | ReferenceWriter rw(&writer, NULL, false); |
242 | ref_infos->iterate(rw); |
243 | } |
244 | } |
245 | |
246 | int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) { |
247 | assert(writer != NULL, "invariant" ); |
248 | assert(ai != NULL, "invariant" ); |
249 | const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai; |
250 | writer->write(osai->_id); |
251 | writer->write(osai->_data._array_size); |
252 | writer->write(osai->_data._array_index); |
253 | return 1; |
254 | } |
255 | |
256 | static traceid get_array_info_id(const Edge& edge, traceid id) { |
257 | if (edge.is_root() || !EdgeUtils::is_array_element(edge)) { |
258 | return 0; |
259 | } |
260 | if (array_infos == NULL) { |
261 | array_infos = new ArrayInfo(); |
262 | } |
263 | assert(array_infos != NULL, "invariant" ); |
264 | |
265 | ObjectSampleArrayInfo* const osai = new ObjectSampleArrayInfo(); |
266 | assert(osai != NULL, "invariant" ); |
267 | osai->_id = id; |
268 | osai->_data._array_size = EdgeUtils::array_size(edge); |
269 | osai->_data._array_index = EdgeUtils::array_index(edge); |
270 | return array_infos->store(osai); |
271 | } |
272 | |
273 | typedef JfrArtifactWriterImplHost<const ObjectSampleArrayInfo*, __write_array_info__> ArrayWriterImpl; |
274 | typedef JfrArtifactWriterHost<ArrayWriterImpl, TYPE_OLDOBJECTARRAY> ArrayWriter; |
275 | |
276 | static void write_array_infos(JfrCheckpointWriter& writer) { |
277 | if (array_infos != NULL) { |
278 | ArrayWriter aw(&writer, NULL, false); |
279 | array_infos->iterate(aw); |
280 | } |
281 | } |
282 | |
283 | int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) { |
284 | assert(writer != NULL, "invariant" ); |
285 | assert(fi != NULL, "invariant" ); |
286 | const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi; |
287 | writer->write(field_info_entry->id()); |
288 | const ObjectSampleFieldInfo* const osfi = field_info_entry->literal(); |
289 | writer->write(osfi->_field_name_symbol->as_C_string()); |
290 | writer->write(osfi->_field_modifiers); |
291 | return 1; |
292 | } |
293 | |
294 | static traceid get_field_info_id(const Edge& edge) { |
295 | if (edge.is_root()) { |
296 | return 0; |
297 | } |
298 | |
299 | assert(!EdgeUtils::is_array_element(edge), "invariant" ); |
300 | const Symbol* const field_name_symbol = EdgeUtils::field_name_symbol(edge); |
301 | if (field_name_symbol == NULL) { |
302 | return 0; |
303 | } |
304 | |
305 | if (field_infos == NULL) { |
306 | field_infos = new FieldTable(); |
307 | } |
308 | assert(field_infos != NULL, "invariant" ); |
309 | |
310 | ObjectSampleFieldInfo* const osfi = new ObjectSampleFieldInfo(); |
311 | assert(osfi != NULL, "invariant" ); |
312 | osfi->_field_name_symbol = field_name_symbol; |
313 | osfi->_field_modifiers = EdgeUtils::field_modifiers(edge); |
314 | return field_infos->store(osfi); |
315 | } |
316 | |
317 | typedef JfrArtifactWriterImplHost<const FieldTable::FieldInfoEntry*, __write_field_info__> FieldWriterImpl; |
318 | typedef JfrArtifactWriterHost<FieldWriterImpl, TYPE_OLDOBJECTFIELD> FieldWriter; |
319 | |
320 | static void write_field_infos(JfrCheckpointWriter& writer) { |
321 | if (field_infos != NULL) { |
322 | FieldWriter fw(&writer, NULL, false); |
323 | field_infos->iterate(fw); |
324 | } |
325 | } |
326 | |
327 | static const char* description(const ObjectSampleRootDescriptionInfo* osdi) { |
328 | assert(osdi != NULL, "invariant" ); |
329 | |
330 | if (osdi->_data._description == NULL) { |
331 | return NULL; |
332 | } |
333 | |
334 | ObjectDescriptionBuilder description; |
335 | if (osdi->_data._system == OldObjectRoot::_threads) { |
336 | description.write_text("Thread Name: " ); |
337 | } |
338 | description.write_text(osdi->_data._description); |
339 | return description.description(); |
340 | } |
341 | |
342 | int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) { |
343 | assert(writer != NULL, "invariant" ); |
344 | assert(di != NULL, "invariant" ); |
345 | const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di; |
346 | writer->write(osdi->_id); |
347 | writer->write(description(osdi)); |
348 | writer->write<u8>(osdi->_data._system); |
349 | writer->write<u8>(osdi->_data._type); |
350 | return 1; |
351 | } |
352 | |
353 | static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) { |
354 | assert(edge.is_root(), "invariant" ); |
355 | if (EdgeUtils::is_leak_edge(edge)) { |
356 | return 0; |
357 | } |
358 | |
359 | if (root_infos == NULL) { |
360 | root_infos = new RootDescriptionInfo(); |
361 | } |
362 | assert(root_infos != NULL, "invariant" ); |
363 | ObjectSampleRootDescriptionInfo* const oodi = new ObjectSampleRootDescriptionInfo(); |
364 | oodi->_id = id; |
365 | oodi->_data._root_edge = &edge; |
366 | return root_infos->store(oodi); |
367 | } |
368 | |
369 | typedef JfrArtifactWriterImplHost<const ObjectSampleRootDescriptionInfo*, __write_root_description_info__> RootDescriptionWriterImpl; |
370 | typedef JfrArtifactWriterHost<RootDescriptionWriterImpl, TYPE_OLDOBJECTGCROOT> RootDescriptionWriter; |
371 | |
372 | |
373 | int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) { |
374 | return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; |
375 | } |
376 | |
377 | int _root_desc_compare_(const ObjectSampleRootDescriptionInfo*const & lhs, const ObjectSampleRootDescriptionInfo* const& rhs) { |
378 | const uintptr_t lhs_ref = (uintptr_t)lhs->_data._root_edge->reference(); |
379 | const uintptr_t rhs_ref = (uintptr_t)rhs->_data._root_edge->reference(); |
380 | return _edge_reference_compare_(lhs_ref, rhs_ref); |
381 | } |
382 | |
383 | static int find_sorted(const RootCallbackInfo& callback_info, |
384 | const GrowableArray<const ObjectSampleRootDescriptionInfo*>* arr, |
385 | int length, |
386 | bool& found) { |
387 | assert(arr != NULL, "invariant" ); |
388 | assert(length >= 0, "invariant" ); |
389 | assert(length <= arr->length(), "invariant" ); |
390 | |
391 | found = false; |
392 | int min = 0; |
393 | int max = length; |
394 | while (max >= min) { |
395 | const int mid = (int)(((uint)max + min) / 2); |
396 | int diff = _edge_reference_compare_((uintptr_t)callback_info._high, |
397 | (uintptr_t)arr->at(mid)->_data._root_edge->reference()); |
398 | if (diff > 0) { |
399 | min = mid + 1; |
400 | } else if (diff < 0) { |
401 | max = mid - 1; |
402 | } else { |
403 | found = true; |
404 | return mid; |
405 | } |
406 | } |
407 | return min; |
408 | } |
409 | |
410 | class RootResolutionSet : public ResourceObj, public RootCallback { |
411 | private: |
412 | GrowableArray<const ObjectSampleRootDescriptionInfo*>* _unresolved_roots; |
413 | |
414 | const uintptr_t high() const { |
415 | return (uintptr_t)_unresolved_roots->last()->_data._root_edge->reference(); |
416 | } |
417 | |
418 | const uintptr_t low() const { |
419 | return (uintptr_t)_unresolved_roots->first()->_data._root_edge->reference(); |
420 | } |
421 | |
422 | bool in_set_address_range(const RootCallbackInfo& callback_info) const { |
423 | assert(callback_info._low == NULL, "invariant" ); |
424 | const uintptr_t addr = (uintptr_t)callback_info._high; |
425 | return low() <= addr && high() >= addr; |
426 | } |
427 | |
428 | int compare_to_range(const RootCallbackInfo& callback_info) const { |
429 | assert(callback_info._high != NULL, "invariant" ); |
430 | assert(callback_info._low != NULL, "invariant" ); |
431 | |
432 | for (int i = 0; i < _unresolved_roots->length(); ++i) { |
433 | const uintptr_t ref_addr = (uintptr_t)_unresolved_roots->at(i)->_data._root_edge->reference(); |
434 | if ((uintptr_t)callback_info._low <= ref_addr && (uintptr_t)callback_info._high >= ref_addr) { |
435 | return i; |
436 | } |
437 | } |
438 | return -1; |
439 | } |
440 | |
441 | int exact(const RootCallbackInfo& callback_info) const { |
442 | assert(callback_info._high != NULL, "invariant" ); |
443 | assert(in_set_address_range(callback_info), "invariant" ); |
444 | |
445 | bool found; |
446 | const int idx = find_sorted(callback_info, _unresolved_roots, _unresolved_roots->length(), found); |
447 | return found ? idx : -1; |
448 | } |
449 | |
450 | bool resolve_root(const RootCallbackInfo& callback_info, int idx) const { |
451 | assert(idx >= 0, "invariant" ); |
452 | assert(idx < _unresolved_roots->length(), "invariant" ); |
453 | |
454 | ObjectSampleRootDescriptionInfo* const desc = |
455 | const_cast<ObjectSampleRootDescriptionInfo*>(_unresolved_roots->at(idx)); |
456 | assert(desc != NULL, "invariant" ); |
457 | assert((uintptr_t)callback_info._high == (uintptr_t)desc->_data._root_edge->reference(), "invariant" ); |
458 | |
459 | desc->_data._system = callback_info._system; |
460 | desc->_data._type = callback_info._type; |
461 | |
462 | if (callback_info._system == OldObjectRoot::_threads) { |
463 | const JavaThread* jt = (const JavaThread*)callback_info._context; |
464 | assert(jt != NULL, "invariant" ); |
465 | desc->_data._description = jt->name(); |
466 | } |
467 | |
468 | _unresolved_roots->remove_at(idx); |
469 | return _unresolved_roots->is_empty(); |
470 | } |
471 | |
472 | public: |
473 | RootResolutionSet(RootDescriptionInfo* info) : _unresolved_roots(NULL) { |
474 | assert(info != NULL, "invariant" ); |
475 | // construct a sorted copy |
476 | const GrowableArray<const ObjectSampleRootDescriptionInfo*>& info_storage = info->storage(); |
477 | const int length = info_storage.length(); |
478 | _unresolved_roots = new GrowableArray<const ObjectSampleRootDescriptionInfo*>(length); |
479 | assert(_unresolved_roots != NULL, "invariant" ); |
480 | |
481 | for (int i = 0; i < length; ++i) { |
482 | _unresolved_roots->insert_sorted<_root_desc_compare_>(info_storage.at(i)); |
483 | } |
484 | } |
485 | |
486 | bool process(const RootCallbackInfo& callback_info) { |
487 | if (NULL == callback_info._low) { |
488 | if (in_set_address_range(callback_info)) { |
489 | const int idx = exact(callback_info); |
490 | return idx == -1 ? false : resolve_root(callback_info, idx); |
491 | } |
492 | return false; |
493 | } |
494 | assert(callback_info._low != NULL, "invariant" ); |
495 | const int idx = compare_to_range(callback_info); |
496 | return idx == -1 ? false : resolve_root(callback_info, idx); |
497 | } |
498 | |
499 | int entries() const { |
500 | return _unresolved_roots->length(); |
501 | } |
502 | |
503 | const void* at(int idx) const { |
504 | assert(idx >= 0, "invariant" ); |
505 | assert(idx < _unresolved_roots->length(), "invariant" ); |
506 | return _unresolved_roots->at(idx)->_data._root_edge->reference(); |
507 | } |
508 | }; |
509 | |
510 | static void write_root_descriptors(JfrCheckpointWriter& writer) { |
511 | if (root_infos != NULL) { |
512 | // resolve roots |
513 | RootResolutionSet rrs(root_infos); |
514 | RootResolver::resolve(rrs); |
515 | // write roots |
516 | RootDescriptionWriter rw(&writer, NULL, false); |
517 | root_infos->iterate(rw); |
518 | } |
519 | } |
520 | |
521 | static void add_old_object_sample_info(const StoredEdge* current, traceid id) { |
522 | assert(current != NULL, "invariant" ); |
523 | if (sample_infos == NULL) { |
524 | sample_infos = new SampleInfo(); |
525 | } |
526 | assert(sample_infos != NULL, "invariant" ); |
527 | OldObjectSampleInfo* const oosi = new OldObjectSampleInfo(); |
528 | assert(oosi != NULL, "invariant" ); |
529 | oosi->_id = id; |
530 | oosi->_data._object = current->pointee(); |
531 | oosi->_data._reference_id = current->parent() == NULL ? (traceid)0 : id; |
532 | sample_infos->store(oosi); |
533 | } |
534 | |
535 | static void add_reference_info(const StoredEdge* current, traceid id, traceid parent_id) { |
536 | assert(current != NULL, "invariant" ); |
537 | if (ref_infos == NULL) { |
538 | ref_infos = new RefInfo(); |
539 | } |
540 | |
541 | assert(ref_infos != NULL, "invariant" ); |
542 | ReferenceInfo* const ri = new ReferenceInfo(); |
543 | assert(ri != NULL, "invariant" ); |
544 | |
545 | ri->_id = id; |
546 | ri->_data._array_info_id = !current->is_skip_edge() ? get_array_info_id(*current, id) : 0; |
547 | ri->_data._field_info_id = ri->_data._array_info_id == 0 && !current->is_skip_edge() ? get_field_info_id(*current) : (traceid)0; |
548 | ri->_data._old_object_sample_id = parent_id; |
549 | ri->_data._skip = current->skip_length(); |
550 | ref_infos->store(ri); |
551 | } |
552 | |
553 | static bool is_gc_root(const StoredEdge* current) { |
554 | assert(current != NULL, "invariant" ); |
555 | return current->parent() == NULL && current->gc_root_id() != 0; |
556 | } |
557 | |
558 | static traceid add_gc_root_info(const StoredEdge* root, traceid id) { |
559 | assert(root != NULL, "invariant" ); |
560 | assert(is_gc_root(root), "invariant" ); |
561 | return get_gc_root_description_info_id(*root, id); |
562 | } |
563 | |
564 | void ObjectSampleWriter::write(const StoredEdge* edge) { |
565 | assert(edge != NULL, "invariant" ); |
566 | const traceid id = _store->get_id(edge); |
567 | add_old_object_sample_info(edge, id); |
568 | const StoredEdge* const parent = edge->parent(); |
569 | if (parent != NULL) { |
570 | add_reference_info(edge, id, _store->get_id(parent)); |
571 | } else { |
572 | if (is_gc_root(edge)) { |
573 | assert(edge->gc_root_id() == id, "invariant" ); |
574 | add_gc_root_info(edge, id); |
575 | } |
576 | } |
577 | } |
578 | |
579 | ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, EdgeStore* store) : |
580 | _writer(writer), |
581 | _store(store) { |
582 | assert(store != NULL, "invariant" ); |
583 | assert(!store->is_empty(), "invariant" ); |
584 | sample_infos = NULL; |
585 | ref_infos = NULL; |
586 | array_infos = NULL; |
587 | field_infos = NULL; |
588 | root_infos = NULL; |
589 | } |
590 | |
591 | ObjectSampleWriter::~ObjectSampleWriter() { |
592 | write_sample_infos(_writer); |
593 | write_reference_infos(_writer); |
594 | write_array_infos(_writer); |
595 | write_field_infos(_writer); |
596 | write_root_descriptors(_writer); |
597 | } |
598 | |
599 | bool ObjectSampleWriter::operator()(StoredEdge& e) { |
600 | write(&e); |
601 | return true; |
602 | } |
603 | |