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 "classfile/javaClasses.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/utilities/unifiedOop.hpp"
  31 #include "oops/fieldStreams.hpp"
  32 #include "oops/instanceKlass.hpp"
  33 #include "oops/objArrayOop.inline.hpp"
  34 #include "oops/oopsHierarchy.hpp"
  35 #include "runtime/handles.inline.hpp"
  36 
  37 bool EdgeUtils::is_leak_edge(const Edge& edge) {
  38   return (const Edge*)edge.pointee()->mark().to_pointer() == &edge;
  39 }
  40 
  41 static int field_offset(const StoredEdge& edge) {
  42   assert(!edge.is_root(), "invariant");
  43   const oop ref_owner = edge.reference_owner();
  44   assert(ref_owner != NULL, "invariant");
  45   const oop* reference = UnifiedOop::decode(edge.reference());
  46   assert(reference != NULL, "invariant");
  47   assert(!UnifiedOop::is_narrow(reference), "invariant");
  48   assert(!ref_owner->is_array(), "invariant");
  49   assert(ref_owner->is_instance(), "invariant");
  50   const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char));
  51   assert(offset < (ref_owner->size() * HeapWordSize), "invariant");
  52   return offset;
  53 }
  54 
  55 static const InstanceKlass* field_type(const StoredEdge& edge) {
  56   assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant");
  57   return (const InstanceKlass*)edge.reference_owner_klass();
  58 }
  59 
  60 const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) {
  61   assert(!edge.is_root(), "invariant");
  62   assert(!is_array_element(edge), "invariant");
  63   const int offset = field_offset(edge);
  64   const InstanceKlass* ik = field_type(edge);
  65   while (ik != NULL) {
  66     JavaFieldStream jfs(ik);
  67     while (!jfs.done()) {
  68       if (offset == jfs.offset()) {
  69         return jfs.name();
  70       }
  71       jfs.next();
  72     }
  73     ik = (InstanceKlass*)ik->super();
  74   }
  75   return NULL;
  76 }
  77 
  78 jshort EdgeUtils::field_modifiers(const Edge& edge) {
  79   const int offset = field_offset(edge);
  80   const InstanceKlass* ik = field_type(edge);
  81 
  82   while (ik != NULL) {
  83     JavaFieldStream jfs(ik);
  84     while (!jfs.done()) {
  85       if (offset == jfs.offset()) {
  86         return jfs.access_flags().as_short();
  87       }
  88       jfs.next();
  89     }
  90     ik = (InstanceKlass*)ik->super();
  91   }
  92   return 0;
  93 }
  94 
  95 bool EdgeUtils::is_array_element(const Edge& edge) {
  96   assert(!edge.is_root(), "invariant");
  97   const oop ref_owner = edge.reference_owner();
  98   assert(ref_owner != NULL, "invariant");
  99   return ref_owner->is_objArray();
 100 }
 101 
 102 static int array_offset(const Edge& edge) {
 103   assert(!edge.is_root(), "invariant");
 104   const oop ref_owner = edge.reference_owner();
 105   assert(ref_owner != NULL, "invariant");
 106   const oop* reference = UnifiedOop::decode(edge.reference());
 107   assert(reference != NULL, "invariant");
 108   assert(!UnifiedOop::is_narrow(reference), "invariant");
 109   assert(ref_owner->is_array(), "invariant");
 110   const objArrayOop ref_owner_array = static_cast<const objArrayOop>(ref_owner);
 111   const int offset = (int)pointer_delta(reference, ref_owner_array->base(), heapOopSize);
 112   assert(offset >= 0 && offset < ref_owner_array->length(), "invariant");
 113   return offset;
 114 }
 115 
 116 int EdgeUtils::array_index(const Edge& edge) {
 117   return is_array_element(edge) ? array_offset(edge) : 0;
 118 }
 119 
 120 int EdgeUtils::array_size(const Edge& edge) {
 121   if (is_array_element(edge)) {
 122     const oop ref_owner = edge.reference_owner();
 123     assert(ref_owner != NULL, "invariant");
 124     assert(ref_owner->is_objArray(), "invariant");
 125     return ((objArrayOop)(ref_owner))->length();
 126   }
 127   return 0;
 128 }
 129 
 130 const Edge* EdgeUtils::root(const Edge& edge) {
 131   const Edge* current = &edge;
 132   const Edge* parent = current->parent();
 133   while (parent != NULL) {
 134     current = parent;
 135     parent = current->parent();
 136   }
 137   assert(current != NULL, "invariant");
 138   return current;
 139 }
 140 
 141 const Edge* EdgeUtils::ancestor(const Edge& edge, size_t distance) {
 142   const Edge* current = &edge;
 143   const Edge* parent = current->parent();
 144   size_t seek = 0;
 145   while (parent != NULL && seek != distance) {
 146     seek++;
 147     current = parent;
 148     parent = parent->parent();
 149   }
 150   return current;
 151 }