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_unloading() && !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 void DependencyContext::wipe() { 222 if (!UseZGC) { 223 assert_locked_or_safepoint(CodeCache_lock); 224 } 225 nmethodBucket* b = dependencies(); 226 set_dependencies(NULL); 227 set_has_stale_entries(false); 228 while (b != NULL) { 229 nmethodBucket* next = b->next(); 230 delete b; 231 b = next; 232 } 233 } 234 235 #ifndef PRODUCT 236 void DependencyContext::print_dependent_nmethods(bool verbose) { 237 int idx = 0; 238 for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { 239 nmethod* nm = b->get_nmethod(); 240 tty->print("[%d] count=%d { ", idx++, b->count()); 241 if (!verbose) { 242 nm->print_on(tty, "nmethod"); 243 tty->print_cr(" } "); 244 } else { 245 nm->print(); 246 nm->print_dependencies(); 247 tty->print_cr("--- } "); 248 } 249 } 250 } 251 252 bool DependencyContext::is_dependent_nmethod(nmethod* nm) { 253 for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { 254 if (nm == b->get_nmethod()) { 255 #ifdef ASSERT 256 int count = b->count(); 257 assert(count >= 0, "count shouldn't be negative: %d", count); 258 #endif 259 return true; 260 } 261 } 262 return false; 263 } 264 265 bool DependencyContext::find_stale_entries() { 266 for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { 267 if (b->count() == 0) return true; 268 } 269 return false; 270 } 271 272 #endif //PRODUCT 273 274 int nmethodBucket::decrement() { 275 return Atomic::sub(1, &_count); 276 }