1 /*
   2  * Copyright (c) 2015, 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 "code/nmethod.hpp"
  27 #include "code/dependencies.hpp"
  28 #include "code/dependencyContext.hpp"
  29 #include "memory/resourceArea.hpp"
  30 #include "runtime/atomic.hpp"
  31 #include "runtime/perfData.hpp"
  32 #include "utilities/exceptions.hpp"
  33 
  34 PerfCounter* DependencyContext::_perf_total_buckets_allocated_count   = NULL;
  35 PerfCounter* DependencyContext::_perf_total_buckets_deallocated_count = NULL;
  36 PerfCounter* DependencyContext::_perf_total_buckets_stale_count       = NULL;
  37 PerfCounter* DependencyContext::_perf_total_buckets_stale_acc_count   = NULL;
  38 
  39 void dependencyContext_init() {
  40   DependencyContext::init();
  41 }
  42 
  43 void DependencyContext::init() {
  44   if (UsePerfData) {
  45     EXCEPTION_MARK;
  46     _perf_total_buckets_allocated_count =
  47         PerfDataManager::create_counter(SUN_CI, "nmethodBucketsAllocated", PerfData::U_Events, CHECK);
  48     _perf_total_buckets_deallocated_count =
  49         PerfDataManager::create_counter(SUN_CI, "nmethodBucketsDeallocated", PerfData::U_Events, CHECK);
  50     _perf_total_buckets_stale_count =
  51         PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStale", PerfData::U_Events, CHECK);
  52     _perf_total_buckets_stale_acc_count =
  53         PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStaleAccumulated", PerfData::U_Events, CHECK);
  54   }
  55 }
  56 
  57 //
  58 // Walk the list of dependent nmethods searching for nmethods which
  59 // are dependent on the changes that were passed in and mark them for
  60 // deoptimization.  Returns the number of nmethods found.
  61 //
  62 int DependencyContext::mark_dependent_nmethods(DepChange& changes) {
  63   int found = 0;
  64   for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) {
  65     nmethod* nm = b->get_nmethod();
  66     // since dependencies aren't removed until an nmethod becomes a zombie,
  67     // the dependency list may contain nmethods which aren't alive.
  68     if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) {
  69       if (TraceDependencies) {
  70         ResourceMark rm;
  71         tty->print_cr("Marked for deoptimization");
  72         changes.print();
  73         nm->print();
  74         nm->print_dependencies();
  75       }
  76       changes.mark_for_deoptimization(nm);
  77       found++;
  78     }
  79   }
  80   return found;
  81 }
  82 
  83 //
  84 // Add an nmethod to the dependency context.
  85 // It's possible that an nmethod has multiple dependencies on a klass
  86 // so a count is kept for each bucket to guarantee that creation and
  87 // deletion of dependencies is consistent.
  88 //
  89 void DependencyContext::add_dependent_nmethod(nmethod* nm, bool expunge) {
  90   assert_lock_strong(CodeCache_lock);
  91   for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) {
  92     if (nm == b->get_nmethod()) {
  93       b->increment();
  94       return;
  95     }
  96   }
  97   set_dependencies(new nmethodBucket(nm, dependencies()));
  98   if (UsePerfData) {
  99     _perf_total_buckets_allocated_count->inc();
 100   }
 101   if (expunge) {
 102     // Remove stale entries from the list.
 103     expunge_stale_entries();
 104   }
 105 }
 106 
 107 //
 108 // Remove an nmethod dependency from the context.
 109 // Decrement count of the nmethod in the dependency list and, optionally, remove
 110 // the bucket completely when the count goes to 0.  This method must find
 111 // a corresponding bucket otherwise there's a bug in the recording of dependencies.
 112 // Can be called concurrently by parallel GC threads.
 113 //
 114 void DependencyContext::remove_dependent_nmethod(nmethod* nm, bool expunge) {
 115   assert_locked_or_safepoint(CodeCache_lock);
 116   nmethodBucket* first = dependencies();
 117   nmethodBucket* last = NULL;
 118   for (nmethodBucket* b = first; b != NULL; b = b->next()) {
 119     if (nm == b->get_nmethod()) {
 120       int val = b->decrement();
 121       guarantee(val >= 0, "Underflow: %d", val);
 122       if (val == 0) {
 123         if (expunge) {
 124           if (last == NULL) {
 125             set_dependencies(b->next());
 126           } else {
 127             last->set_next(b->next());
 128           }
 129           delete b;
 130           if (UsePerfData) {
 131             _perf_total_buckets_deallocated_count->inc();
 132           }
 133         } else {
 134           // Mark the context as having stale entries, since it is not safe to
 135           // expunge the list right now.
 136           set_has_stale_entries(true);
 137           if (UsePerfData) {
 138             _perf_total_buckets_stale_count->inc();
 139             _perf_total_buckets_stale_acc_count->inc();
 140           }
 141         }
 142       }
 143       if (expunge) {
 144         // Remove stale entries from the list.
 145         expunge_stale_entries();
 146       }
 147       return;
 148     }
 149     last = b;
 150   }
 151 #ifdef ASSERT
 152   tty->print_raw_cr("### can't find dependent nmethod");
 153   nm->print();
 154 #endif // ASSERT
 155   ShouldNotReachHere();
 156 }
 157 
 158 //
 159 // Reclaim all unused buckets.
 160 //
 161 void DependencyContext::expunge_stale_entries() {
 162   assert_locked_or_safepoint(CodeCache_lock);
 163   if (!has_stale_entries()) {
 164     assert(!find_stale_entries(), "inconsistent info");
 165     return;
 166   }
 167   nmethodBucket* first = dependencies();
 168   nmethodBucket* last = NULL;
 169   int removed = 0;
 170   for (nmethodBucket* b = first; b != NULL;) {
 171     assert(b->count() >= 0, "bucket count: %d", b->count());
 172     nmethodBucket* next = b->next();
 173     if (b->count() == 0) {
 174       if (last == NULL) {
 175         first = next;
 176       } else {
 177         last->set_next(next);
 178       }
 179       removed++;
 180       delete b;
 181       // last stays the same.
 182     } else {
 183       last = b;
 184     }
 185     b = next;
 186   }
 187   set_dependencies(first);
 188   set_has_stale_entries(false);
 189   if (UsePerfData && removed > 0) {
 190     _perf_total_buckets_deallocated_count->inc(removed);
 191     _perf_total_buckets_stale_count->dec(removed);
 192   }
 193 }
 194 
 195 //
 196 // Invalidate all dependencies in the context
 197 int DependencyContext::remove_all_dependents() {
 198   assert_locked_or_safepoint(CodeCache_lock);
 199   nmethodBucket* b = dependencies();
 200   set_dependencies(NULL);
 201   int marked = 0;
 202   int removed = 0;
 203   while (b != NULL) {
 204     nmethod* nm = b->get_nmethod();
 205     if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) {
 206       nm->mark_for_deoptimization();
 207       marked++;
 208     }
 209     nmethodBucket* next = b->next();
 210     removed++;
 211     delete b;
 212     b = next;
 213   }
 214   set_has_stale_entries(false);
 215   if (UsePerfData && removed > 0) {
 216     _perf_total_buckets_deallocated_count->inc(removed);
 217   }
 218   return marked;
 219 }
 220 
 221 #ifndef PRODUCT
 222 void DependencyContext::print_dependent_nmethods(bool verbose) {
 223   int idx = 0;
 224   for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) {
 225     nmethod* nm = b->get_nmethod();
 226     tty->print("[%d] count=%d { ", idx++, b->count());
 227     if (!verbose) {
 228       nm->print_on(tty, "nmethod");
 229       tty->print_cr(" } ");
 230     } else {
 231       nm->print();
 232       nm->print_dependencies();
 233       tty->print_cr("--- } ");
 234     }
 235   }
 236 }
 237 
 238 bool DependencyContext::is_dependent_nmethod(nmethod* nm) {
 239   for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) {
 240     if (nm == b->get_nmethod()) {
 241 #ifdef ASSERT
 242       int count = b->count();
 243       assert(count >= 0, "count shouldn't be negative: %d", count);
 244 #endif
 245       return true;
 246     }
 247   }
 248   return false;
 249 }
 250 
 251 bool DependencyContext::find_stale_entries() {
 252   for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) {
 253     if (b->count() == 0)  return true;
 254   }
 255   return false;
 256 }
 257 
 258 #endif //PRODUCT
 259 
 260 int nmethodBucket::decrement() {
 261   return Atomic::sub(1, &_count);
 262 }