| 1 | /* |
| 2 | * Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved. |
| 3 | * |
| 4 | * This code is free software; you can redistribute it and/or modify it |
| 5 | * under the terms of the GNU General Public License version 2 only, as |
| 6 | * published by the Free Software Foundation. |
| 7 | * |
| 8 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 11 | * version 2 for more details (a copy is included in the LICENSE file that |
| 12 | * accompanied this code). |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License version |
| 15 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 17 | * |
| 18 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 19 | * or visit www.oracle.com if you need additional information or have any |
| 20 | * questions. |
| 21 | * |
| 22 | */ |
| 23 | |
| 24 | #include "precompiled.hpp" |
| 25 | |
| 26 | #include "gc/shenandoah/shenandoahAsserts.hpp" |
| 27 | #include "gc/shenandoah/shenandoahForwarding.hpp" |
| 28 | #include "gc/shenandoah/shenandoahHeap.inline.hpp" |
| 29 | #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" |
| 30 | #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" |
| 31 | #include "gc/shenandoah/shenandoahTraversalGC.hpp" |
| 32 | #include "gc/shenandoah/shenandoahUtils.hpp" |
| 33 | #include "memory/resourceArea.hpp" |
| 34 | |
| 35 | void print_raw_memory(ShenandoahMessageBuffer &msg, void* loc) { |
| 36 | // Be extra safe. Only access data that is guaranteed to be safe: |
| 37 | // should be in heap, in known committed region, within that region. |
| 38 | |
| 39 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 40 | if (!heap->is_in(loc)) return; |
| 41 | |
| 42 | ShenandoahHeapRegion* r = heap->heap_region_containing(loc); |
| 43 | if (r != NULL && r->is_committed()) { |
| 44 | address start = MAX2((address) r->bottom(), (address) loc - 32); |
| 45 | address end = MIN2((address) r->end(), (address) loc + 128); |
| 46 | if (start >= end) return; |
| 47 | |
| 48 | stringStream ss; |
| 49 | os::print_hex_dump(&ss, start, end, 4); |
| 50 | msg.append("\n" ); |
| 51 | msg.append("Raw heap memory:\n%s" , ss.as_string()); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) { |
| 56 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 57 | ShenandoahHeapRegion *r = heap->heap_region_containing(obj); |
| 58 | |
| 59 | ResourceMark rm; |
| 60 | stringStream ss; |
| 61 | r->print_on(&ss); |
| 62 | |
| 63 | stringStream mw_ss; |
| 64 | obj->mark()->print_on(&mw_ss); |
| 65 | |
| 66 | ShenandoahMarkingContext* const ctx = heap->marking_context(); |
| 67 | |
| 68 | msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n" , p2i(obj), p2i(obj->klass()), obj->klass()->external_name()); |
| 69 | msg.append(" %3s allocated after mark start\n" , ctx->allocated_after_mark_start((HeapWord *) obj) ? "" : "not" ); |
| 70 | msg.append(" %3s marked \n" , ctx->is_marked(obj) ? "" : "not" ); |
| 71 | msg.append(" %3s in collection set\n" , heap->in_collection_set(obj) ? "" : "not" ); |
| 72 | if (heap->traversal_gc() != NULL) { |
| 73 | msg.append(" %3s in traversal set\n" , heap->traversal_gc()->traversal_set()->is_in((HeapWord*) obj) ? "" : "not" ); |
| 74 | } |
| 75 | msg.append(" mark:%s\n" , mw_ss.as_string()); |
| 76 | msg.append(" region: %s" , ss.as_string()); |
| 77 | } |
| 78 | |
| 79 | void ShenandoahAsserts::print_non_obj(ShenandoahMessageBuffer& msg, void* loc) { |
| 80 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 81 | if (heap->is_in(loc)) { |
| 82 | msg.append(" inside Java heap\n" ); |
| 83 | ShenandoahHeapRegion *r = heap->heap_region_containing(loc); |
| 84 | stringStream ss; |
| 85 | r->print_on(&ss); |
| 86 | |
| 87 | msg.append(" %3s in collection set\n" , heap->in_collection_set(loc) ? "" : "not" ); |
| 88 | msg.append(" region: %s" , ss.as_string()); |
| 89 | } else { |
| 90 | msg.append(" outside of Java heap\n" ); |
| 91 | stringStream ss; |
| 92 | os::print_location(&ss, (intptr_t) loc, false); |
| 93 | msg.append(" %s" , ss.as_string()); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | void ShenandoahAsserts::print_obj_safe(ShenandoahMessageBuffer& msg, void* loc) { |
| 98 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 99 | msg.append(" " PTR_FORMAT " - safe print, no details\n" , p2i(loc)); |
| 100 | if (heap->is_in(loc)) { |
| 101 | ShenandoahHeapRegion* r = heap->heap_region_containing(loc); |
| 102 | if (r != NULL) { |
| 103 | stringStream ss; |
| 104 | r->print_on(&ss); |
| 105 | msg.append(" region: %s" , ss.as_string()); |
| 106 | print_raw_memory(msg, loc); |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_loc, oop loc, |
| 112 | const char* phase, const char* label, |
| 113 | const char* file, int line) { |
| 114 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 115 | ResourceMark rm; |
| 116 | |
| 117 | bool loc_in_heap = (loc != NULL && heap->is_in(loc)); |
| 118 | |
| 119 | ShenandoahMessageBuffer msg("%s; %s\n\n" , phase, label); |
| 120 | |
| 121 | msg.append("Referenced from:\n" ); |
| 122 | if (interior_loc != NULL) { |
| 123 | msg.append(" interior location: " PTR_FORMAT "\n" , p2i(interior_loc)); |
| 124 | if (loc_in_heap) { |
| 125 | print_obj(msg, loc); |
| 126 | } else { |
| 127 | print_non_obj(msg, interior_loc); |
| 128 | } |
| 129 | } else { |
| 130 | msg.append(" no interior location recorded (probably a plain heap scan, or detached oop)\n" ); |
| 131 | } |
| 132 | msg.append("\n" ); |
| 133 | |
| 134 | msg.append("Object:\n" ); |
| 135 | if (level >= _safe_oop) { |
| 136 | print_obj(msg, obj); |
| 137 | } else { |
| 138 | print_obj_safe(msg, obj); |
| 139 | } |
| 140 | msg.append("\n" ); |
| 141 | |
| 142 | if (level >= _safe_oop) { |
| 143 | oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj); |
| 144 | msg.append("Forwardee:\n" ); |
| 145 | if (!oopDesc::equals_raw(obj, fwd)) { |
| 146 | if (level >= _safe_oop_fwd) { |
| 147 | print_obj(msg, fwd); |
| 148 | } else { |
| 149 | print_obj_safe(msg, fwd); |
| 150 | } |
| 151 | } else { |
| 152 | msg.append(" (the object itself)" ); |
| 153 | } |
| 154 | msg.append("\n" ); |
| 155 | } |
| 156 | |
| 157 | if (level >= _safe_oop_fwd) { |
| 158 | oop fwd = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(obj); |
| 159 | oop fwd2 = (oop) ShenandoahForwarding::get_forwardee_raw_unchecked(fwd); |
| 160 | if (!oopDesc::equals_raw(fwd, fwd2)) { |
| 161 | msg.append("Second forwardee:\n" ); |
| 162 | print_obj_safe(msg, fwd2); |
| 163 | msg.append("\n" ); |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | report_vm_error(file, line, msg.buffer()); |
| 168 | } |
| 169 | |
| 170 | void ShenandoahAsserts::assert_in_heap(void* interior_loc, oop obj, const char *file, int line) { |
| 171 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 172 | |
| 173 | if (!heap->is_in(obj)) { |
| 174 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_in_heap failed" , |
| 175 | "oop must point to a heap address" , |
| 176 | file, line); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, int line) { |
| 181 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 182 | |
| 183 | // Step 1. Check that obj is correct. |
| 184 | // After this step, it is safe to call heap_region_containing(). |
| 185 | if (!heap->is_in(obj)) { |
| 186 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 187 | "oop must point to a heap address" , |
| 188 | file, line); |
| 189 | } |
| 190 | |
| 191 | Klass* obj_klass = obj->klass_or_null(); |
| 192 | if (obj_klass == NULL) { |
| 193 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 194 | "Object klass pointer should not be NULL" , |
| 195 | file,line); |
| 196 | } |
| 197 | |
| 198 | if (!Metaspace::contains(obj_klass)) { |
| 199 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 200 | "Object klass pointer must go to metaspace" , |
| 201 | file,line); |
| 202 | } |
| 203 | |
| 204 | oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj)); |
| 205 | |
| 206 | if (!oopDesc::equals_raw(obj, fwd)) { |
| 207 | // When Full GC moves the objects, we cannot trust fwdptrs. If we got here, it means something |
| 208 | // tries fwdptr manipulation when Full GC is running. The only exception is using the fwdptr |
| 209 | // that still points to the object itself. |
| 210 | if (heap->is_full_gc_move_in_progress()) { |
| 211 | print_failure(_safe_oop, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 212 | "Non-trivial forwarding pointer during Full GC moves, probable bug." , |
| 213 | file, line); |
| 214 | } |
| 215 | |
| 216 | // Step 2. Check that forwardee is correct |
| 217 | if (!heap->is_in(fwd)) { |
| 218 | print_failure(_safe_oop, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 219 | "Forwardee must point to a heap address" , |
| 220 | file, line); |
| 221 | } |
| 222 | |
| 223 | if (obj_klass != fwd->klass()) { |
| 224 | print_failure(_safe_oop, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 225 | "Forwardee klass disagrees with object class" , |
| 226 | file, line); |
| 227 | } |
| 228 | |
| 229 | // Step 3. Check that forwardee points to correct region |
| 230 | if (heap->heap_region_index_containing(fwd) == heap->heap_region_index_containing(obj)) { |
| 231 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 232 | "Non-trivial forwardee should in another region" , |
| 233 | file, line); |
| 234 | } |
| 235 | |
| 236 | // Step 4. Check for multiple forwardings |
| 237 | oop fwd2 = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(fwd)); |
| 238 | if (!oopDesc::equals_raw(fwd, fwd2)) { |
| 239 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_correct failed" , |
| 240 | "Multiple forwardings" , |
| 241 | file, line); |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | void ShenandoahAsserts::assert_in_correct_region(void* interior_loc, oop obj, const char* file, int line) { |
| 247 | assert_correct(interior_loc, obj, file, line); |
| 248 | |
| 249 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 250 | ShenandoahHeapRegion* r = heap->heap_region_containing(obj); |
| 251 | if (!r->is_active()) { |
| 252 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_in_correct_region failed" , |
| 253 | "Object must reside in active region" , |
| 254 | file, line); |
| 255 | } |
| 256 | |
| 257 | size_t alloc_size = obj->size(); |
| 258 | if (alloc_size > ShenandoahHeapRegion::humongous_threshold_words()) { |
| 259 | size_t idx = r->region_number(); |
| 260 | size_t num_regions = ShenandoahHeapRegion::required_regions(alloc_size * HeapWordSize); |
| 261 | for (size_t i = idx; i < idx + num_regions; i++) { |
| 262 | ShenandoahHeapRegion* chain_reg = heap->get_region(i); |
| 263 | if (i == idx && !chain_reg->is_humongous_start()) { |
| 264 | print_failure(_safe_unknown, obj, interior_loc, NULL, "Shenandoah assert_in_correct_region failed" , |
| 265 | "Object must reside in humongous start" , |
| 266 | file, line); |
| 267 | } |
| 268 | if (i != idx && !chain_reg->is_humongous_continuation()) { |
| 269 | print_failure(_safe_oop, obj, interior_loc, NULL, "Shenandoah assert_in_correct_region failed" , |
| 270 | "Humongous continuation should be of proper size" , |
| 271 | file, line); |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | void ShenandoahAsserts::assert_forwarded(void* interior_loc, oop obj, const char* file, int line) { |
| 278 | assert_correct(interior_loc, obj, file, line); |
| 279 | oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj)); |
| 280 | |
| 281 | if (oopDesc::equals_raw(obj, fwd)) { |
| 282 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_forwarded failed" , |
| 283 | "Object should be forwarded" , |
| 284 | file, line); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | void ShenandoahAsserts::assert_not_forwarded(void* interior_loc, oop obj, const char* file, int line) { |
| 289 | assert_correct(interior_loc, obj, file, line); |
| 290 | oop fwd = oop(ShenandoahForwarding::get_forwardee_raw_unchecked(obj)); |
| 291 | |
| 292 | if (!oopDesc::equals_raw(obj, fwd)) { |
| 293 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_not_forwarded failed" , |
| 294 | "Object should not be forwarded" , |
| 295 | file, line); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | void ShenandoahAsserts::assert_marked(void *interior_loc, oop obj, const char *file, int line) { |
| 300 | assert_correct(interior_loc, obj, file, line); |
| 301 | |
| 302 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 303 | if (!heap->marking_context()->is_marked(obj)) { |
| 304 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_marked failed" , |
| 305 | "Object should be marked" , |
| 306 | file, line); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | void ShenandoahAsserts::assert_in_cset(void* interior_loc, oop obj, const char* file, int line) { |
| 311 | assert_correct(interior_loc, obj, file, line); |
| 312 | |
| 313 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 314 | if (!heap->in_collection_set(obj)) { |
| 315 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_in_cset failed" , |
| 316 | "Object should be in collection set" , |
| 317 | file, line); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | void ShenandoahAsserts::assert_not_in_cset(void* interior_loc, oop obj, const char* file, int line) { |
| 322 | assert_correct(interior_loc, obj, file, line); |
| 323 | |
| 324 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 325 | if (heap->in_collection_set(obj)) { |
| 326 | print_failure(_safe_all, obj, interior_loc, NULL, "Shenandoah assert_not_in_cset failed" , |
| 327 | "Object should not be in collection set" , |
| 328 | file, line); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | void ShenandoahAsserts::assert_not_in_cset_loc(void* interior_loc, const char* file, int line) { |
| 333 | ShenandoahHeap* heap = ShenandoahHeap::heap_no_check(); |
| 334 | if (heap->in_collection_set(interior_loc)) { |
| 335 | print_failure(_safe_unknown, NULL, interior_loc, NULL, "Shenandoah assert_not_in_cset_loc failed" , |
| 336 | "Interior location should not be in collection set" , |
| 337 | file, line); |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | void ShenandoahAsserts::print_rp_failure(const char *label, BoolObjectClosure* actual, |
| 342 | const char *file, int line) { |
| 343 | ShenandoahMessageBuffer msg("%s\n" , label); |
| 344 | msg.append(" Actual: " PTR_FORMAT "\n" , p2i(actual)); |
| 345 | report_vm_error(file, line, msg.buffer()); |
| 346 | } |
| 347 | |
| 348 | void ShenandoahAsserts::assert_rp_isalive_not_installed(const char *file, int line) { |
| 349 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 350 | ReferenceProcessor* rp = heap->ref_processor(); |
| 351 | if (rp->is_alive_non_header() != NULL) { |
| 352 | print_rp_failure("Shenandoah assert_rp_isalive_not_installed failed" , rp->is_alive_non_header(), |
| 353 | file, line); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | void ShenandoahAsserts::assert_rp_isalive_installed(const char *file, int line) { |
| 358 | ShenandoahHeap* heap = ShenandoahHeap::heap(); |
| 359 | ReferenceProcessor* rp = heap->ref_processor(); |
| 360 | if (rp->is_alive_non_header() == NULL) { |
| 361 | print_rp_failure("Shenandoah assert_rp_isalive_installed failed" , rp->is_alive_non_header(), |
| 362 | file, line); |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | void ShenandoahAsserts::assert_locked_or_shenandoah_safepoint(const Monitor* lock, const char* file, int line) { |
| 367 | if (ShenandoahSafepoint::is_at_shenandoah_safepoint()) { |
| 368 | return; |
| 369 | } |
| 370 | |
| 371 | if (lock->owned_by_self()) { |
| 372 | return; |
| 373 | } |
| 374 | |
| 375 | ShenandoahMessageBuffer msg("Must ba at a Shenandoah safepoint or held %s lock" , lock->name()); |
| 376 | report_vm_error(file, line, msg.buffer()); |
| 377 | } |
| 378 | |