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 | |