1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "jfr/recorder/checkpoint/constant/jfrTagSet.hpp"
  27 #include "jfr/recorder/checkpoint/constant/jfrTagSetWriter.hpp"
  28 #include "jfr/leakprofiler/chains/edge.hpp"
  29 #include "jfr/leakprofiler/chains/edgeStore.hpp"
  30 #include "jfr/leakprofiler/chains/edgeUtils.hpp"
  31 #include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp"
  32 #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp"
  33 #include "jfr/leakprofiler/checkpoint/rootResolver.hpp"
  34 #include "jfr/leakprofiler/sampling/objectSampler.hpp"
  35 #include "jfr/leakprofiler/utilities/rootType.hpp"
  36 #include "jfr/leakprofiler/utilities/unifiedOop.hpp"
  37 #include "oops/oop.inline.hpp"
  38 #include "oops/symbol.hpp"
  39 #include "tracefiles/traceTypes.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, CONSTANT_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, CONSTANT_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, CONSTANT_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, CONSTANT_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_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, CONSTANT_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 Edge* 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->is_root() ? (traceid)0 : id;
 532   sample_infos->store(oosi);
 533 }
 534 
 535 static void add_reference_info(const RoutableEdge* 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() ?
 548                                get_field_info_id(*current) : (traceid)0;
 549   ri->_data._old_object_sample_id = parent_id;
 550   ri->_data._skip = current->skip_length();
 551   ref_infos->store(ri);
 552 }
 553 
 554 static traceid add_root_info(const Edge* root, traceid id) {
 555   assert(root != NULL, "invariant");
 556   assert(root->is_root(), "invariant");
 557   return get_root_description_info_id(*root, id);
 558 }
 559 
 560 void ObjectSampleWriter::write(const RoutableEdge* edge) {
 561   assert(edge != NULL, "invariant");
 562   const traceid id = _store->get_id(edge);
 563   add_old_object_sample_info(edge, id);
 564   const RoutableEdge* parent = edge->logical_parent();
 565   if (parent != NULL) {
 566     add_reference_info(edge, id, _store->get_id(parent));
 567   } else {
 568     assert(edge->is_root(), "invariant");
 569     add_root_info(edge, id);
 570   }
 571 }
 572 
 573 ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, const EdgeStore* store) :
 574   _writer(writer),
 575   _store(store) {
 576   assert(store != NULL, "invariant");
 577   assert(store->number_of_entries() > 0, "invariant");
 578   sample_infos = NULL;
 579   ref_infos = NULL;
 580   array_infos = NULL;
 581   field_infos = NULL;
 582   root_infos = NULL;
 583 }
 584 
 585 ObjectSampleWriter::~ObjectSampleWriter() {
 586   write_sample_infos(_writer);
 587   write_reference_infos(_writer);
 588   write_array_infos(_writer);
 589   write_field_infos(_writer);
 590   write_root_descriptors(_writer);
 591 }
 592 
 593 void ObjectSampleWriter::write_chain(const RoutableEdge& edge) {
 594   assert(EdgeUtils::is_leak_edge(edge), "invariant");
 595   if (edge.processed()) {
 596     return;
 597   }
 598   EdgeUtils::collapse_chain(edge);
 599   const RoutableEdge* current = &edge;
 600   while (current != NULL) {
 601     if (current->processed()) {
 602       return;
 603     }
 604     write(current);
 605     current->set_processed();
 606     current = current->logical_parent();
 607   }
 608 }
 609 
 610 bool ObjectSampleWriter::operator()(const RoutableEdge& edge) {
 611   if (EdgeUtils::is_leak_edge(edge)) {
 612     write_chain(edge);
 613   }
 614   return true;
 615 }