-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8361712: Improve ShenandoahAsserts printing #26237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
27ec3db
18de09e
2a5c9c0
c7aa454
aab9d9c
e4e4404
94fe071
c7a9c26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved. | ||
* Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
|
@@ -29,7 +29,10 @@ | |
#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" | ||
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" | ||
#include "gc/shenandoah/shenandoahUtils.hpp" | ||
#include "oops/oop.inline.hpp" | ||
#include "memory/resourceArea.hpp" | ||
#include "runtime/os.hpp" | ||
#include "utilities/vmError.hpp" | ||
|
||
void print_raw_memory(ShenandoahMessageBuffer &msg, void* loc) { | ||
// Be extra safe. Only access data that is guaranteed to be safe: | ||
|
@@ -57,30 +60,42 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) { | |
|
||
ResourceMark rm; | ||
stringStream ss; | ||
r->print_on(&ss); | ||
|
||
stringStream mw_ss; | ||
obj->mark().print_on(&mw_ss); | ||
StreamIndentor si(&ss); | ||
|
||
ShenandoahMarkingContext* const ctx = heap->marking_context(); | ||
|
||
Klass* obj_klass = ShenandoahForwarding::klass(obj); | ||
|
||
msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj_klass), obj_klass->external_name()); | ||
msg.append(" %3s allocated after mark start\n", ctx->allocated_after_mark_start(obj) ? "" : "not"); | ||
msg.append(" %3s after update watermark\n", cast_from_oop<HeapWord*>(obj) >= r->get_update_watermark() ? "" : "not"); | ||
msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not"); | ||
msg.append(" %3s marked weak\n", ctx->is_marked_weak(obj) ? "" : "not"); | ||
msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not"); | ||
if (heap->mode()->is_generational() && !obj->is_forwarded()) { | ||
msg.append(" age: %d\n", obj->age()); | ||
} | ||
msg.append(" mark:%s\n", mw_ss.freeze()); | ||
msg.append(" region: %s", ss.freeze()); | ||
if (obj_klass == vmClasses::Class_klass()) { | ||
msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset()))); | ||
msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset()))); | ||
narrowKlass nk = 0; | ||
const Klass* obj_klass = nullptr; | ||
const bool klass_valid = extract_klass_safely(obj, nk, obj_klass); | ||
const char* klass_text = "(invalid)"; | ||
if (klass_valid && os::is_readable_pointer(obj_klass) && Metaspace::contains(obj_klass)) { | ||
klass_text = obj_klass->external_name(); | ||
} | ||
ss.print_cr(PTR_FORMAT " - nk %u klass " PTR_FORMAT " %s\n", p2i(obj), nk, p2i(obj_klass), klass_text); | ||
{ | ||
StreamIndentor si(&ss); | ||
ss.print_cr("%3s allocated after mark start", ctx->allocated_after_mark_start(obj) ? "" : "not"); | ||
ss.print_cr("%3s after update watermark", cast_from_oop<HeapWord*>(obj) >= r->get_update_watermark() ? "" : "not"); | ||
ss.print_cr("%3s marked strong", ctx->is_marked_strong(obj) ? "" : "not"); | ||
ss.print_cr("%3s marked weak", ctx->is_marked_weak(obj) ? "" : "not"); | ||
ss.print_cr("%3s in collection set", heap->in_collection_set(obj) ? "" : "not"); | ||
if (heap->mode()->is_generational() && !obj->is_forwarded()) { | ||
ss.print_cr("age: %d", obj->age()); | ||
} | ||
ss.print_raw("mark: "); | ||
obj->mark().print_on(&ss); | ||
ss.cr(); | ||
ss.print_raw("region: "); | ||
r->print_on(&ss); | ||
ss.cr(); | ||
if (obj_klass == vmClasses::Class_klass()) { | ||
msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset()))); | ||
msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset()))); | ||
} | ||
} | ||
const_address loc = cast_from_oop<const_address>(obj); | ||
os::print_hex_dump(&ss, loc, loc + 64, 4, true, 32, loc); | ||
tstuefe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
msg.append("%s", ss.base()); | ||
} | ||
|
||
void ShenandoahAsserts::print_non_obj(ShenandoahMessageBuffer& msg, void* loc) { | ||
|
@@ -121,14 +136,18 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l | |
ShenandoahHeap* heap = ShenandoahHeap::heap(); | ||
ResourceMark rm; | ||
|
||
if (!os::is_readable_pointer(obj)) { | ||
level = _safe_unknown; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if I am wrong - shouldn't the caller be responsible for passing correct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The caller may have erred. Seeing how many callers there are, there are many places that can bitrot. This is just an added safety feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay |
||
} | ||
|
||
bool loc_in_heap = (loc != nullptr && heap->is_in_reserved(loc)); | ||
|
||
ShenandoahMessageBuffer msg("%s; %s\n\n", phase, label); | ||
|
||
msg.append("Referenced from:\n"); | ||
if (interior_loc != nullptr) { | ||
msg.append(" interior location: " PTR_FORMAT "\n", p2i(interior_loc)); | ||
if (loc_in_heap) { | ||
if (loc_in_heap && os::is_readable_pointer(loc)) { | ||
print_obj(msg, loc); | ||
} else { | ||
print_non_obj(msg, interior_loc); | ||
|
@@ -150,7 +169,7 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l | |
oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj); | ||
msg.append("Forwardee:\n"); | ||
if (obj != fwd) { | ||
if (level >= _safe_oop_fwd) { | ||
if (level >= _safe_oop_fwd && os::is_readable_pointer(fwd)) { | ||
print_obj(msg, fwd); | ||
} else { | ||
print_obj_safe(msg, fwd); | ||
|
@@ -205,17 +224,10 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* | |
file, line); | ||
} | ||
|
||
Klass* obj_klass = ShenandoahForwarding::klass(obj); | ||
if (obj_klass == nullptr) { | ||
print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Object klass pointer should not be null", | ||
file,line); | ||
} | ||
|
||
if (!Metaspace::contains(obj_klass)) { | ||
if (!os::is_readable_pointer(obj)) { | ||
print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Object klass pointer must go to metaspace", | ||
file,line); | ||
"oop within heap bounds but at unreadable location", | ||
file, line); | ||
} | ||
|
||
if (!heap->is_in(obj)) { | ||
|
@@ -243,9 +255,9 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* | |
file, line); | ||
} | ||
|
||
if (obj_klass != ShenandoahForwarding::klass(fwd)) { | ||
if (!os::is_readable_pointer(fwd)) { | ||
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Forwardee klass disagrees with object class", | ||
"Forwardee within heap bounds but at unreadable location", | ||
file, line); | ||
} | ||
|
||
|
@@ -271,6 +283,32 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* | |
} | ||
} | ||
|
||
const Klass* obj_klass = nullptr; | ||
narrowKlass nk = 0; | ||
if (!extract_klass_safely(obj, nk, obj_klass)) { | ||
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Object klass pointer invalid", | ||
file,line); | ||
} | ||
|
||
if (obj_klass == nullptr) { | ||
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Object klass pointer should not be null", | ||
file,line); | ||
} | ||
|
||
if (!Metaspace::contains(obj_klass)) { | ||
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Object klass pointer must go to metaspace", | ||
file,line); | ||
} | ||
|
||
if (!UseCompactObjectHeaders && obj_klass != fwd->klass_or_null()) { | ||
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", | ||
"Forwardee klass disagrees with object class", | ||
file, line); | ||
} | ||
|
||
// Do additional checks for special objects: their fields can hold metadata as well. | ||
// We want to check class loading/unloading did not corrupt them. We can only reasonably | ||
// trust the forwarded objects, as the from-space object can have the klasses effectively | ||
|
@@ -519,3 +557,30 @@ void ShenandoahAsserts::assert_generations_reconciled(const char* file, int line | |
ShenandoahMessageBuffer msg("Active(%d) & GC(%d) Generations aren't reconciled", agen->type(), ggen->type()); | ||
report_vm_error(file, line, msg.buffer()); | ||
} | ||
|
||
bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k) { | ||
nk = 0; | ||
k = nullptr; | ||
|
||
if (!os::is_readable_pointer(obj)) { | ||
return false; | ||
} | ||
if (UseCompressedClassPointers) { | ||
if (UseCompactObjectHeaders) { // look in forwardee | ||
oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj); | ||
if (!os::is_readable_pointer(fwd)) { | ||
return false; | ||
} | ||
nk = fwd->mark().narrow_klass(); | ||
} else { | ||
nk = obj->narrow_klass(); | ||
} | ||
if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { | ||
return false; | ||
} | ||
k = CompressedKlassPointers::decode_not_null_without_asserts(nk); | ||
} else { | ||
k = obj->klass(); | ||
} | ||
return k != nullptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems redundant.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
StreamIndentor indents the future output of the associated stream. We place them wherever we need indentation. We want indentation here - that avoids having to write out manual indentation like the code before did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, I missed the print statement at line 74.