src/hotspot/share/jfr/leakprofiler/chains/edgeUtils.cpp
branchJEP-349-branch
changeset 57870 00860d9caf4d
parent 50113 caf115bb98ad
child 57777 90ead0febf56
equal deleted inserted replaced
57862:84ef29ccac56 57870:00860d9caf4d
     1 /*
     1 /*
     2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
     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.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     7  * published by the Free Software Foundation.
    36 
    36 
    37 bool EdgeUtils::is_leak_edge(const Edge& edge) {
    37 bool EdgeUtils::is_leak_edge(const Edge& edge) {
    38   return (const Edge*)edge.pointee()->mark() == &edge;
    38   return (const Edge*)edge.pointee()->mark() == &edge;
    39 }
    39 }
    40 
    40 
    41 bool EdgeUtils::is_root(const Edge& edge) {
    41 static int field_offset(const StoredEdge& edge) {
    42   return edge.is_root();
       
    43 }
       
    44 
       
    45 static int field_offset(const Edge& edge) {
       
    46   assert(!edge.is_root(), "invariant");
    42   assert(!edge.is_root(), "invariant");
    47   const oop ref_owner = edge.reference_owner();
    43   const oop ref_owner = edge.reference_owner();
    48   assert(ref_owner != NULL, "invariant");
    44   assert(ref_owner != NULL, "invariant");
    49   const oop* reference = UnifiedOop::decode(edge.reference());
    45   const oop* reference = UnifiedOop::decode(edge.reference());
    50   assert(reference != NULL, "invariant");
    46   assert(reference != NULL, "invariant");
    54   const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char));
    50   const int offset = (int)pointer_delta(reference, ref_owner, sizeof(char));
    55   assert(offset < (ref_owner->size() * HeapWordSize), "invariant");
    51   assert(offset < (ref_owner->size() * HeapWordSize), "invariant");
    56   return offset;
    52   return offset;
    57 }
    53 }
    58 
    54 
    59 static const InstanceKlass* field_type(const Edge& edge) {
    55 static const InstanceKlass* field_type(const StoredEdge& edge) {
    60   assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant");
    56   assert(!edge.is_root() || !EdgeUtils::is_array_element(edge), "invariant");
    61   return (const InstanceKlass*)edge.reference_owner_klass();
    57   return (const InstanceKlass*)edge.reference_owner_klass();
    62 }
    58 }
    63 
    59 
    64 const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) {
    60 const Symbol* EdgeUtils::field_name_symbol(const Edge& edge) {
   136   const Edge* parent = current->parent();
   132   const Edge* parent = current->parent();
   137   while (parent != NULL) {
   133   while (parent != NULL) {
   138     current = parent;
   134     current = parent;
   139     parent = current->parent();
   135     parent = current->parent();
   140   }
   136   }
       
   137   assert(current != NULL, "invariant");
   141   return current;
   138   return current;
   142 }
   139 }
   143 
   140 
   144 // The number of references associated with the leak node;
   141 const Edge* EdgeUtils::ancestor(const Edge& edge, size_t distance) {
   145 // can be viewed as the leak node "context".
   142   const Edge* current = &edge;
   146 // Used to provide leak context for a "capped/skipped" reference chain.
   143   const Edge* parent = current->parent();
   147 static const size_t leak_context = 100;
       
   148 
       
   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;
       
   153 
       
   154 // A limit on the reference chain depth to be serialized,
       
   155 static const size_t max_ref_chain_depth = leak_context + root_context;
       
   156 
       
   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;
   144   size_t seek = 0;
   161   while (parent != NULL && seek != skip_length) {
   145   while (parent != NULL && seek != distance) {
   162     seek++;
   146     seek++;
   163     current = parent;
   147     current = parent;
   164     parent = parent->physical_parent();
   148     parent = parent->parent();
   165   }
   149   }
   166   return current;
   150   return current;
   167 }
   151 }
   168 
       
   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 }
       
   175 
       
   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
       
   189 
       
   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 }
       
   201 
       
   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 }
       
   214 
       
   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");
       
   222 
       
   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;
       
   227 
       
   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   }
       
   232 
       
   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");
       
   238 
       
   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   }
       
   249 
       
   250   DEBUG_ONLY(validate_new_skip_edge(new_skip_edge, last_skip_edge, adjustment);)
       
   251 }
       
   252 
       
   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");
       
   260 
       
   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   }
       
   266 
       
   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
       
   269 
       
   270   DEBUG_ONLY(validate_new_skip_edge(first_processed_edge, NULL, 0);)
       
   271 }
       
   272 
       
   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 }
       
   286 
       
   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");
       
   297 
       
   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   }
       
   304 
       
   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   }
       
   310 
       
   311   assert(edge.logical_distance_to_root() + 1 <= max_ref_chain_depth, "invariant");
       
   312 }