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 #include "precompiled.hpp"
  25 #include "jfr/leakprofiler/chains/bitset.hpp"
  26 #include "jfr/leakprofiler/chains/bfsClosure.hpp"
  27 #include "jfr/leakprofiler/chains/dfsClosure.hpp"
  28 #include "jfr/leakprofiler/chains/edge.hpp"
  29 #include "jfr/leakprofiler/chains/edgeStore.hpp"
  30 #include "jfr/leakprofiler/chains/edgeQueue.hpp"
  31 #include "jfr/leakprofiler/utilities/granularTimer.hpp"
  32 #include "jfr/leakprofiler/utilities/unifiedOop.hpp"
  33 #include "memory/resourceArea.hpp"
  34 #include "jfr/utilities/align.hpp"
  35 
  36 BFSClosure::BFSClosure(EdgeQueue* edge_queue, EdgeStore* edge_store, BitSet* mark_bits) :
  37   _edge_queue(edge_queue),
  38   _edge_store(edge_store),
  39   _mark_bits(mark_bits),
  40   _current_parent(NULL),
  41   _current_frontier_level(0),
  42   _next_frontier_idx(0),
  43   _prev_frontier_idx(0),
  44   _dfs_fallback_idx(0),
  45   _use_dfs(false) {
  46 }
  47 
  48 static void log_frontier_level_summary(size_t level,
  49                                        size_t high_idx,
  50                                        size_t low_idx,
  51                                        size_t edge_size) {
  52   const size_t nof_edges_in_frontier = high_idx - low_idx;
  53   log_trace(jfr, system)(
  54       "BFS front: " SIZE_FORMAT " edges: " SIZE_FORMAT " size: " SIZE_FORMAT " [KB]",
  55       level,
  56       nof_edges_in_frontier,
  57       (nof_edges_in_frontier * edge_size) / K
  58                         );
  59 }
  60 
  61 void BFSClosure::log_completed_frontier() const {
  62   log_frontier_level_summary(_current_frontier_level,
  63                              _next_frontier_idx,
  64                              _prev_frontier_idx,
  65                              _edge_queue->sizeof_edge());
  66 }
  67 
  68 void BFSClosure::log_dfs_fallback() const {
  69   const size_t edge_size = _edge_queue->sizeof_edge();
  70   // first complete summary for frontier in progress
  71   log_frontier_level_summary(_current_frontier_level,
  72                              _next_frontier_idx,
  73                              _prev_frontier_idx,
  74                              edge_size);
  75 
  76   // and then also complete the last frontier
  77   log_frontier_level_summary(_current_frontier_level + 1,
  78                              _edge_queue->bottom(),
  79                              _next_frontier_idx,
  80                              edge_size);
  81 
  82   // additional information about DFS fallover
  83   log_trace(jfr, system)(
  84       "BFS front: " SIZE_FORMAT " filled edge queue at edge: " SIZE_FORMAT,
  85       _current_frontier_level,
  86       _dfs_fallback_idx
  87                         );
  88 
  89   const size_t nof_dfs_completed_edges = _edge_queue->bottom() - _dfs_fallback_idx;
  90   log_trace(jfr, system)(
  91       "DFS to complete " SIZE_FORMAT " edges size: " SIZE_FORMAT " [KB]",
  92       nof_dfs_completed_edges,
  93       (nof_dfs_completed_edges * edge_size) / K
  94                         );
  95 }
  96 
  97 void BFSClosure::process() {
  98 
  99   process_root_set();
 100   process_queue();
 101 }
 102 
 103 void BFSClosure::process_root_set() {
 104   for (size_t idx = _edge_queue->bottom(); idx < _edge_queue->top(); ++idx) {
 105     const Edge* edge = _edge_queue->element_at(idx);
 106     assert(edge->parent() == NULL, "invariant");
 107     process(edge->reference(), edge->pointee());
 108   }
 109 }
 110 
 111 void BFSClosure::process(const oop* reference, const oop pointee) {
 112   closure_impl(reference, pointee);
 113 }
 114 void BFSClosure::closure_impl(const oop* reference, const oop pointee) {
 115   assert(reference != NULL, "invariant");
 116   assert(UnifiedOop::dereference(reference) == pointee, "invariant");
 117 
 118   if (GranularTimer::is_finished()) {
 119      return;
 120   }
 121 
 122   if (_use_dfs) {
 123     assert(_current_parent != NULL, "invariant");
 124     DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, _current_parent);
 125     return;
 126   }
 127 
 128   if (!_mark_bits->is_marked(pointee)) {
 129     _mark_bits->mark_obj(pointee);
 130     // is the pointee a sample object?
 131     if (NULL == pointee->mark()) {
 132       add_chain(reference, pointee);
 133     }
 134 
 135     // if we are processinig initial root set, don't add to queue
 136     if (_current_parent != NULL) {
 137       assert(_current_parent->distance_to_root() == _current_frontier_level, "invariant");
 138       _edge_queue->add(_current_parent, reference);
 139     }
 140 
 141     if (_edge_queue->is_full()) {
 142       dfs_fallback();
 143     }
 144   }
 145 }
 146 
 147 void BFSClosure::add_chain(const oop* reference, const oop pointee) {
 148   assert(pointee != NULL, "invariant");
 149   assert(NULL == pointee->mark(), "invariant");
 150 
 151   const size_t length = _current_parent == NULL ? 1 : _current_parent->distance_to_root() + 2;
 152   ResourceMark rm;
 153   Edge* const chain = NEW_RESOURCE_ARRAY(Edge, length);
 154   size_t idx = 0;
 155   chain[idx++] = Edge(NULL, reference);
 156   // aggregate from breadth-first search
 157   const Edge* current = _current_parent;
 158   while (current != NULL) {
 159     chain[idx++] = Edge(NULL, current->reference());
 160     current = current->parent();
 161   }
 162   assert(length == idx, "invariant");
 163   _edge_store->add_chain(chain, length);
 164 }
 165 
 166 void BFSClosure::dfs_fallback() {
 167   assert(_edge_queue->is_full(), "invariant");
 168   _use_dfs = true;
 169   _dfs_fallback_idx = _edge_queue->bottom();
 170   while (!_edge_queue->is_empty()) {
 171     const Edge* edge = _edge_queue->remove();
 172     if (edge->pointee() != NULL) {
 173       DFSClosure::find_leaks_from_edge(_edge_store, _mark_bits, edge);
 174     }
 175   }
 176 }
 177 
 178 void BFSClosure::process_queue() {
 179   assert(_current_frontier_level == 0, "invariant");
 180   assert(_next_frontier_idx == 0, "invariant");
 181   assert(_prev_frontier_idx == 0, "invariant");
 182 
 183   _next_frontier_idx = _edge_queue->top();
 184   while (!is_complete()) {
 185     iterate(_edge_queue->remove()); // edge_queue.remove() increments bottom
 186   }
 187 }
 188 
 189 void BFSClosure::step_frontier() const {
 190   log_completed_frontier();
 191   ++_current_frontier_level;
 192   _prev_frontier_idx = _next_frontier_idx;
 193   _next_frontier_idx = _edge_queue->top();
 194 }
 195 
 196 bool BFSClosure::is_complete() const {
 197   if (_edge_queue->bottom() < _next_frontier_idx) {
 198     return false;
 199   }
 200   if (_edge_queue->bottom() > _next_frontier_idx) {
 201     // fallback onto DFS as part of processing the frontier
 202     assert(_dfs_fallback_idx >= _prev_frontier_idx, "invariant");
 203     assert(_dfs_fallback_idx < _next_frontier_idx, "invariant");
 204     log_dfs_fallback();
 205     return true;
 206   }
 207   assert(_edge_queue->bottom() == _next_frontier_idx, "invariant");
 208   if (_edge_queue->is_empty()) {
 209     return true;
 210   }
 211   step_frontier();
 212   return false;
 213 }
 214 
 215 void BFSClosure::iterate(const Edge* parent) {
 216   assert(parent != NULL, "invariant");
 217   const oop pointee = parent->pointee();
 218   assert(pointee != NULL, "invariant");
 219   _current_parent = parent;
 220   pointee->oop_iterate(this);
 221 }
 222 
 223 void BFSClosure::do_oop(oop* ref) {
 224   assert(ref != NULL, "invariant");
 225   assert(is_aligned(ref, HeapWordSize), "invariant");
 226   const oop pointee = *ref;
 227   if (pointee != NULL) {
 228     closure_impl(ref, pointee);
 229   }
 230 }
 231 
 232 void BFSClosure::do_oop(narrowOop* ref) {
 233   assert(ref != NULL, "invariant");
 234   assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
 235   const oop pointee = oopDesc::load_decode_heap_oop(ref);
 236   if (pointee != NULL) {
 237     closure_impl(UnifiedOop::encode(ref), pointee);
 238   }
 239 }