src/share/vm/jfr/leakprofiler/chains/edgeUtils.cpp

Mon, 12 Aug 2019 18:30:40 +0300

author
apetushkov
date
Mon, 12 Aug 2019 18:30:40 +0300
changeset 9858
b985cbb00e68
child 9885
8e875c964f41
permissions
-rw-r--r--

8223147: JFR Backport
8199712: Flight Recorder
8203346: JFR: Inconsistent signature of jfr_add_string_constant
8195817: JFR.stop should require name of recording
8195818: JFR.start should increase autogenerated name by one
8195819: Remove recording=x from jcmd JFR.check output
8203921: JFR thread sampling is missing fixes from JDK-8194552
8203929: Limit amount of data for JFR.dump
8203664: JFR start failure after AppCDS archive created with JFR StartFlightRecording
8003209: JFR events for network utilization
8207392: [PPC64] Implement JFR profiling
8202835: jfr/event/os/TestSystemProcess.java fails on missing events
Summary: Backport JFR from JDK11. Initial integration
Reviewed-by: neugens

     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  */
    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.hpp"
    34 #include "oops/oopsHierarchy.hpp"
    35 #include "runtime/handles.inline.hpp"
    37 bool EdgeUtils::is_leak_edge(const Edge& edge) {
    38   return (const Edge*)edge.pointee()->mark() == &edge;
    39 }
    41 bool EdgeUtils::is_root(const Edge& edge) {
    42   return edge.is_root();
    43 }
    45 static int field_offset(const Edge& edge) {
    46   assert(!edge.is_root(), "invariant");
    47   const oop ref_owner = edge.reference_owner();
    48   assert(ref_owner != NULL, "invariant");
    49   const oop* reference = UnifiedOop::decode(edge.reference());
    50   assert(reference != NULL, "invariant");
    51   assert(!UnifiedOop::is_narrow(reference), "invariant");
    52   assert(!ref_owner->is_array(), "invariant");
    53   assert(ref_owner->is_instance(), "invariant");
    54   const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char));
    55   assert(offset < (ref_owner->size() * HeapWordSize), "invariant");
    56   return offset;
    57 }
    59 static const InstanceKlass* field_type(const Edge& edge) {
    60   assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant");
    61   return (const InstanceKlass*)edge.reference_owner_klass();
    62 }
    64 const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) {
    65   assert(!edge.is_root(), "invariant");
    66   assert(!is_array_element(edge), "invariant");
    67   const int offset = field_offset(edge);
    68   const InstanceKlass* ik = field_type(edge);
    69   while (ik != NULL) {
    70     JavaFieldStream jfs(ik);
    71     while (!jfs.done()) {
    72       if (offset == jfs.offset()) {
    73         return jfs.name();
    74       }
    75       jfs.next();
    76     }
    77     ik = (InstanceKlass*)ik->super();
    78   }
    79   return NULL;
    80 }
    82 jshort EdgeUtils::field_modifiers(const Edge& edge) {
    83   const int offset = field_offset(edge);
    84   const InstanceKlass* ik = field_type(edge);
    86   while (ik != NULL) {
    87     JavaFieldStream jfs(ik);
    88     while (!jfs.done()) {
    89       if (offset == jfs.offset()) {
    90         return jfs.access_flags().as_short();
    91       }
    92       jfs.next();
    93     }
    94     ik = (InstanceKlass*)ik->super();
    95   }
    96   return 0;
    97 }
    99 bool EdgeUtils::is_array_element(const Edge& edge) {
   100   assert(!edge.is_root(), "invariant");
   101   const oop ref_owner = edge.reference_owner();
   102   assert(ref_owner != NULL, "invariant");
   103   return ref_owner->is_objArray();
   104 }
   106 static int array_offset(const Edge& edge) {
   107   assert(!edge.is_root(), "invariant");
   108   const oop ref_owner = edge.reference_owner();
   109   assert(ref_owner != NULL, "invariant");
   110   const oop* reference = UnifiedOop::decode(edge.reference());
   111   assert(reference != NULL, "invariant");
   112   assert(!UnifiedOop::is_narrow(reference), "invariant");
   113   assert(ref_owner->is_array(), "invariant");
   114   const objArrayOop ref_owner_array = static_cast<const objArrayOop>(ref_owner);
   115   const int offset = (int)pointer_delta(reference, ref_owner_array->base(), heapOopSize);
   116   assert(offset >= 0 && offset < ref_owner_array->length(), "invariant");
   117   return offset;
   118 }
   120 int EdgeUtils::array_index(const Edge& edge) {
   121   return is_array_element(edge) ? array_offset(edge) : 0;
   122 }
   124 int EdgeUtils::array_size(const Edge& edge) {
   125   if (is_array_element(edge)) {
   126     const oop ref_owner = edge.reference_owner();
   127     assert(ref_owner != NULL, "invariant");
   128     assert(ref_owner->is_objArray(), "invariant");
   129     return ((objArrayOop)(ref_owner))->length();
   130   }
   131   return 0;
   132 }
   134 const Edge* EdgeUtils::root(const Edge& edge) {
   135   const Edge* current = &edge;
   136   const Edge* parent = current->parent();
   137   while (parent != NULL) {
   138     current = parent;
   139     parent = current->parent();
   140   }
   141   return current;
   142 }
   144 // The number of references associated with the leak node;
   145 // can be viewed as the leak node "context".
   146 // Used to provide leak context for a "capped/skipped" reference chain.
   147 static const size_t leak_context = 100;
   149 // The number of references associated with the root node;
   150 // can be viewed as the root node "context".
   151 // Used to provide root context for a "capped/skipped" reference chain.
   152 static const size_t root_context = 100;
   154 // A limit on the reference chain depth to be serialized,
   155 static const size_t max_ref_chain_depth = leak_context + root_context;
   157 const RoutableEdge* skip_to(const RoutableEdge& edge, size_t skip_length) {
   158   const RoutableEdge* current = &edge;
   159   const RoutableEdge* parent = current->physical_parent();
   160   size_t seek = 0;
   161   while (parent != NULL && seek != skip_length) {
   162     seek++;
   163     current = parent;
   164     parent = parent->physical_parent();
   165   }
   166   return current;
   167 }
   169 #ifdef ASSERT
   170 static void validate_skip_target(const RoutableEdge* skip_target) {
   171   assert(skip_target != NULL, "invariant");
   172   assert(skip_target->distance_to_root() + 1 == root_context, "invariant");
   173   assert(skip_target->is_sentinel(), "invariant");
   174 }
   176 static void validate_new_skip_edge(const RoutableEdge* new_skip_edge, const RoutableEdge* last_skip_edge, size_t adjustment) {
   177   assert(new_skip_edge != NULL, "invariant");
   178   assert(new_skip_edge->is_skip_edge(), "invariant");
   179   if (last_skip_edge != NULL) {
   180     const RoutableEdge* const target = skip_to(*new_skip_edge->logical_parent(), adjustment);
   181     validate_skip_target(target->logical_parent());
   182     return;
   183   }
   184   assert(last_skip_edge == NULL, "invariant");
   185   // only one level of logical indirection
   186   validate_skip_target(new_skip_edge->logical_parent());
   187 }
   188 #endif // ASSERT
   190 static void install_logical_route(const RoutableEdge* new_skip_edge, size_t skip_target_distance) {
   191   assert(new_skip_edge != NULL, "invariant");
   192   assert(!new_skip_edge->is_skip_edge(), "invariant");
   193   assert(!new_skip_edge->processed(), "invariant");
   194   const RoutableEdge* const skip_target = skip_to(*new_skip_edge, skip_target_distance);
   195   assert(skip_target != NULL, "invariant");
   196   new_skip_edge->set_skip_edge(skip_target);
   197   new_skip_edge->set_skip_length(skip_target_distance);
   198   assert(new_skip_edge->is_skip_edge(), "invariant");
   199   assert(new_skip_edge->logical_parent() == skip_target, "invariant");
   200 }
   202 static const RoutableEdge* find_last_skip_edge(const RoutableEdge& edge, size_t& distance) {
   203   assert(distance == 0, "invariant");
   204   const RoutableEdge* current = &edge;
   205   while (current != NULL) {
   206     if (current->is_skip_edge() && current->skip_edge()->is_sentinel()) {
   207       return current;
   208     }
   209     current = current->physical_parent();
   210     ++distance;
   211   }
   212   return current;
   213 }
   215 static void collapse_overlapping_chain(const RoutableEdge& edge,
   216                                        const RoutableEdge* first_processed_edge,
   217                                        size_t first_processed_distance) {
   218   assert(first_processed_edge != NULL, "invariant");
   219   // first_processed_edge is already processed / written
   220   assert(first_processed_edge->processed(), "invariant");
   221   assert(first_processed_distance + 1 <= leak_context, "invariant");
   223   // from this first processed edge, attempt to fetch the last skip edge
   224   size_t last_skip_edge_distance = 0;
   225   const RoutableEdge* const last_skip_edge = find_last_skip_edge(*first_processed_edge, last_skip_edge_distance);
   226   const size_t distance_discovered = first_processed_distance + last_skip_edge_distance + 1;
   228   if (distance_discovered <= leak_context || (last_skip_edge == NULL && distance_discovered <= max_ref_chain_depth)) {
   229     // complete chain can be accommodated without modification
   230     return;
   231   }
   233   // backtrack one edge from existing processed edge
   234   const RoutableEdge* const new_skip_edge = skip_to(edge, first_processed_distance - 1);
   235   assert(new_skip_edge != NULL, "invariant");
   236   assert(!new_skip_edge->processed(), "invariant");
   237   assert(new_skip_edge->parent() == first_processed_edge, "invariant");
   239   size_t adjustment = 0;
   240   if (last_skip_edge != NULL) {
   241     assert(leak_context - 1 > first_processed_distance - 1, "invariant");
   242     adjustment = leak_context - first_processed_distance - 1;
   243     assert(last_skip_edge_distance + 1 > adjustment, "invariant");
   244     install_logical_route(new_skip_edge, last_skip_edge_distance + 1 - adjustment);
   245   } else {
   246     install_logical_route(new_skip_edge, last_skip_edge_distance + 1 - root_context);
   247     new_skip_edge->logical_parent()->set_skip_length(1); // sentinel
   248   }
   250   DEBUG_ONLY(validate_new_skip_edge(new_skip_edge, last_skip_edge, adjustment);)
   251 }
   253 static void collapse_non_overlapping_chain(const RoutableEdge& edge,
   254                                            const RoutableEdge* first_processed_edge,
   255                                            size_t first_processed_distance) {
   256   assert(first_processed_edge != NULL, "invariant");
   257   assert(!first_processed_edge->processed(), "invariant");
   258   // this implies that the first "processed" edge is the leak context relative "leaf"
   259   assert(first_processed_distance + 1 == leak_context, "invariant");
   261   const size_t distance_to_root = edge.distance_to_root();
   262   if (distance_to_root + 1 <= max_ref_chain_depth) {
   263     // complete chain can be accommodated without constructing a skip edge
   264     return;
   265   }
   267   install_logical_route(first_processed_edge, distance_to_root + 1 - first_processed_distance - root_context);
   268   first_processed_edge->logical_parent()->set_skip_length(1); // sentinel
   270   DEBUG_ONLY(validate_new_skip_edge(first_processed_edge, NULL, 0);)
   271 }
   273 static const RoutableEdge* processed_edge(const RoutableEdge& edge, size_t& distance) {
   274   assert(distance == 0, "invariant");
   275   const RoutableEdge* current = &edge;
   276   while (current != NULL && distance < leak_context - 1) {
   277     if (current->processed()) {
   278       return current;
   279     }
   280     current = current->physical_parent();
   281     ++distance;
   282   }
   283   assert(distance <= leak_context - 1, "invariant");
   284   return current;
   285 }
   287 /*
   288  * Some vocabulary:
   289  * -----------
   290  * "Context" is an interval in the chain, it is associcated with an edge and it signifies a number of connected edges.
   291  * "Processed / written" means an edge that has already been serialized.
   292  * "Skip edge" is an edge that contains additional information for logical routing purposes.
   293  * "Skip target" is an edge used as a destination for a skip edge
   294  */
   295 void EdgeUtils::collapse_chain(const RoutableEdge& edge) {
   296   assert(is_leak_edge(edge), "invariant");
   298   // attempt to locate an already processed edge inside current leak context (if any)
   299   size_t first_processed_distance = 0;
   300   const RoutableEdge* const first_processed_edge = processed_edge(edge, first_processed_distance);
   301   if (first_processed_edge == NULL) {
   302     return;
   303   }
   305   if (first_processed_edge->processed()) {
   306     collapse_overlapping_chain(edge, first_processed_edge, first_processed_distance);
   307   } else {
   308     collapse_non_overlapping_chain(edge, first_processed_edge, first_processed_distance);
   309   }
   311   assert(edge.logical_distance_to_root() + 1 <= max_ref_chain_depth, "invariant");
   312 }

mercurial