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