Skip to content

Commit 75ff7e1

Browse files
committed
8361712: Improve ShenandoahAsserts printing
Reviewed-by: rkennke, asmehra
1 parent 06fdb61 commit 75ff7e1

File tree

9 files changed

+152
-42
lines changed

9 files changed

+152
-42
lines changed

src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,10 @@
2929
#include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp"
3030
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
3131
#include "gc/shenandoah/shenandoahUtils.hpp"
32+
#include "oops/oop.inline.hpp"
3233
#include "memory/resourceArea.hpp"
34+
#include "runtime/os.hpp"
35+
#include "utilities/vmError.hpp"
3336

3437
void print_raw_memory(ShenandoahMessageBuffer &msg, void* loc) {
3538
// Be extra safe. Only access data that is guaranteed to be safe:
@@ -57,30 +60,42 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) {
5760

5861
ResourceMark rm;
5962
stringStream ss;
60-
r->print_on(&ss);
61-
62-
stringStream mw_ss;
63-
obj->mark().print_on(&mw_ss);
63+
StreamIndentor si(&ss);
6464

6565
ShenandoahMarkingContext* const ctx = heap->marking_context();
6666

67-
Klass* obj_klass = ShenandoahForwarding::klass(obj);
68-
69-
msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj_klass), obj_klass->external_name());
70-
msg.append(" %3s allocated after mark start\n", ctx->allocated_after_mark_start(obj) ? "" : "not");
71-
msg.append(" %3s after update watermark\n", cast_from_oop<HeapWord*>(obj) >= r->get_update_watermark() ? "" : "not");
72-
msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not");
73-
msg.append(" %3s marked weak\n", ctx->is_marked_weak(obj) ? "" : "not");
74-
msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not");
75-
if (heap->mode()->is_generational() && !obj->is_forwarded()) {
76-
msg.append(" age: %d\n", obj->age());
77-
}
78-
msg.append(" mark:%s\n", mw_ss.freeze());
79-
msg.append(" region: %s", ss.freeze());
80-
if (obj_klass == vmClasses::Class_klass()) {
81-
msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset())));
82-
msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset())));
67+
narrowKlass nk = 0;
68+
const Klass* obj_klass = nullptr;
69+
const bool klass_valid = extract_klass_safely(obj, nk, obj_klass);
70+
const char* klass_text = "(invalid)";
71+
if (klass_valid && os::is_readable_pointer(obj_klass) && Metaspace::contains(obj_klass)) {
72+
klass_text = obj_klass->external_name();
73+
}
74+
ss.print_cr(PTR_FORMAT " - nk %u klass " PTR_FORMAT " %s\n", p2i(obj), nk, p2i(obj_klass), klass_text);
75+
{
76+
StreamIndentor si(&ss);
77+
ss.print_cr("%3s allocated after mark start", ctx->allocated_after_mark_start(obj) ? "" : "not");
78+
ss.print_cr("%3s after update watermark", cast_from_oop<HeapWord*>(obj) >= r->get_update_watermark() ? "" : "not");
79+
ss.print_cr("%3s marked strong", ctx->is_marked_strong(obj) ? "" : "not");
80+
ss.print_cr("%3s marked weak", ctx->is_marked_weak(obj) ? "" : "not");
81+
ss.print_cr("%3s in collection set", heap->in_collection_set(obj) ? "" : "not");
82+
if (heap->mode()->is_generational() && !obj->is_forwarded()) {
83+
ss.print_cr("age: %d", obj->age());
84+
}
85+
ss.print_raw("mark: ");
86+
obj->mark().print_on(&ss);
87+
ss.cr();
88+
ss.print_raw("region: ");
89+
r->print_on(&ss);
90+
ss.cr();
91+
if (obj_klass == vmClasses::Class_klass()) {
92+
msg.append(" mirrored klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::klass_offset())));
93+
msg.append(" mirrored array klass: " PTR_FORMAT "\n", p2i(obj->metadata_field(java_lang_Class::array_klass_offset())));
94+
}
8395
}
96+
const_address loc = cast_from_oop<const_address>(obj);
97+
os::print_hex_dump(&ss, loc, loc + 64, 4, true, 32, loc);
98+
msg.append("%s", ss.base());
8499
}
85100

86101
void ShenandoahAsserts::print_non_obj(ShenandoahMessageBuffer& msg, void* loc) {
@@ -121,14 +136,18 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l
121136
ShenandoahHeap* heap = ShenandoahHeap::heap();
122137
ResourceMark rm;
123138

139+
if (!os::is_readable_pointer(obj)) {
140+
level = _safe_unknown;
141+
}
142+
124143
bool loc_in_heap = (loc != nullptr && heap->is_in_reserved(loc));
125144

126145
ShenandoahMessageBuffer msg("%s; %s\n\n", phase, label);
127146

128147
msg.append("Referenced from:\n");
129148
if (interior_loc != nullptr) {
130149
msg.append(" interior location: " PTR_FORMAT "\n", p2i(interior_loc));
131-
if (loc_in_heap) {
150+
if (loc_in_heap && os::is_readable_pointer(loc)) {
132151
print_obj(msg, loc);
133152
} else {
134153
print_non_obj(msg, interior_loc);
@@ -150,7 +169,7 @@ void ShenandoahAsserts::print_failure(SafeLevel level, oop obj, void* interior_l
150169
oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
151170
msg.append("Forwardee:\n");
152171
if (obj != fwd) {
153-
if (level >= _safe_oop_fwd) {
172+
if (level >= _safe_oop_fwd && os::is_readable_pointer(fwd)) {
154173
print_obj(msg, fwd);
155174
} else {
156175
print_obj_safe(msg, fwd);
@@ -205,17 +224,10 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char*
205224
file, line);
206225
}
207226

208-
Klass* obj_klass = ShenandoahForwarding::klass(obj);
209-
if (obj_klass == nullptr) {
210-
print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
211-
"Object klass pointer should not be null",
212-
file,line);
213-
}
214-
215-
if (!Metaspace::contains(obj_klass)) {
227+
if (!os::is_readable_pointer(obj)) {
216228
print_failure(_safe_unknown, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
217-
"Object klass pointer must go to metaspace",
218-
file,line);
229+
"oop within heap bounds but at unreadable location",
230+
file, line);
219231
}
220232

221233
if (!heap->is_in(obj)) {
@@ -243,9 +255,9 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char*
243255
file, line);
244256
}
245257

246-
if (obj_klass != ShenandoahForwarding::klass(fwd)) {
258+
if (!os::is_readable_pointer(fwd)) {
247259
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
248-
"Forwardee klass disagrees with object class",
260+
"Forwardee within heap bounds but at unreadable location",
249261
file, line);
250262
}
251263

@@ -271,6 +283,32 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char*
271283
}
272284
}
273285

286+
const Klass* obj_klass = nullptr;
287+
narrowKlass nk = 0;
288+
if (!extract_klass_safely(obj, nk, obj_klass)) {
289+
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
290+
"Object klass pointer invalid",
291+
file,line);
292+
}
293+
294+
if (obj_klass == nullptr) {
295+
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
296+
"Object klass pointer should not be null",
297+
file,line);
298+
}
299+
300+
if (!Metaspace::contains(obj_klass)) {
301+
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
302+
"Object klass pointer must go to metaspace",
303+
file,line);
304+
}
305+
306+
if (!UseCompactObjectHeaders && obj_klass != fwd->klass_or_null()) {
307+
print_failure(_safe_oop, obj, interior_loc, nullptr, "Shenandoah assert_correct failed",
308+
"Forwardee klass disagrees with object class",
309+
file, line);
310+
}
311+
274312
// Do additional checks for special objects: their fields can hold metadata as well.
275313
// We want to check class loading/unloading did not corrupt them. We can only reasonably
276314
// 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
519557
ShenandoahMessageBuffer msg("Active(%d) & GC(%d) Generations aren't reconciled", agen->type(), ggen->type());
520558
report_vm_error(file, line, msg.buffer());
521559
}
560+
561+
bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k) {
562+
nk = 0;
563+
k = nullptr;
564+
565+
if (!os::is_readable_pointer(obj)) {
566+
return false;
567+
}
568+
if (UseCompressedClassPointers) {
569+
if (UseCompactObjectHeaders) { // look in forwardee
570+
oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj);
571+
if (!os::is_readable_pointer(fwd)) {
572+
return false;
573+
}
574+
nk = fwd->mark().narrow_klass();
575+
} else {
576+
nk = obj->narrow_klass();
577+
}
578+
if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) {
579+
return false;
580+
}
581+
k = CompressedKlassPointers::decode_not_null_without_asserts(nk);
582+
} else {
583+
k = obj->klass();
584+
}
585+
return k != nullptr;
586+
}

src/hotspot/share/gc/shenandoah/shenandoahAsserts.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2018, 2025, Red Hat, Inc. All rights reserved.
33
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -27,6 +27,7 @@
2727
#define SHARE_GC_SHENANDOAH_SHENANDOAHASSERTS_HPP
2828

2929
#include "memory/iterator.hpp"
30+
#include "oops/compressedKlass.hpp"
3031
#include "runtime/mutex.hpp"
3132
#include "utilities/formatBuffer.hpp"
3233

@@ -77,6 +78,11 @@ class ShenandoahAsserts {
7778
static void assert_generational(const char* file, int line);
7879
static void assert_generations_reconciled(const char* file, int line);
7980

81+
// Given a possibly invalid oop, extract narrowKlass (if UCCP) and Klass*
82+
// from it safely.
83+
// Note: For -UCCP, returned nk is always 0.
84+
static bool extract_klass_safely(oop obj, narrowKlass& nk, const Klass*& k);
85+
8086
#ifdef ASSERT
8187
#define shenandoah_assert_in_heap_bounds(interior_loc, obj) \
8288
ShenandoahAsserts::assert_in_heap_bounds(interior_loc, obj, __FILE__, __LINE__)

src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2017, 2025, Red Hat, Inc. All rights reserved.
33
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
44
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
55
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -149,15 +149,21 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure {
149149
"oop must be in heap bounds");
150150
check(ShenandoahAsserts::_safe_unknown, obj, is_object_aligned(obj),
151151
"oop must be aligned");
152+
check(ShenandoahAsserts::_safe_unknown, obj, os::is_readable_pointer(obj),
153+
"oop must be accessible");
152154

153155
ShenandoahHeapRegion *obj_reg = _heap->heap_region_containing(obj);
154-
Klass* obj_klass = ShenandoahForwarding::klass(obj);
156+
157+
narrowKlass nk = 0;
158+
const Klass* obj_klass = nullptr;
159+
const bool klass_valid = ShenandoahAsserts::extract_klass_safely(obj, nk, obj_klass);
160+
161+
check(ShenandoahAsserts::_safe_unknown, obj, klass_valid,
162+
"Object klass pointer unreadable or invalid");
155163

156164
// Verify that obj is not in dead space:
157165
{
158166
// Do this before touching obj->size()
159-
check(ShenandoahAsserts::_safe_unknown, obj, obj_klass != nullptr,
160-
"Object klass pointer should not be null");
161167
check(ShenandoahAsserts::_safe_unknown, obj, Metaspace::contains(obj_klass),
162168
"Object klass pointer must go to metaspace");
163169

src/hotspot/share/oops/compressedKlass.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ class CompressedKlassPointers : public AllStatic {
251251
inline static void check_valid_narrow_klass_id(narrowKlass nk);
252252
#endif
253253

254+
// Given a narrow Klass ID, returns true if it appears to be valid
255+
inline static bool is_valid_narrow_klass_id(narrowKlass nk);
256+
254257
// Returns whether the pointer is in the memory region used for encoding compressed
255258
// class pointers. This includes CDS.
256259
static inline bool is_encodable(const void* addr) {

src/hotspot/share/oops/compressedKlass.inline.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ inline void CompressedKlassPointers::check_valid_narrow_klass_id(narrowKlass nk)
9393
}
9494
#endif // ASSERT
9595

96+
// Given a narrow Klass ID, returns true if it appears to be valid
97+
inline bool CompressedKlassPointers::is_valid_narrow_klass_id(narrowKlass nk) {
98+
return nk >= _lowest_valid_narrow_klass_id && nk < _highest_valid_narrow_klass_id;
99+
}
100+
96101
inline address CompressedKlassPointers::encoding_range_end() {
97102
const int max_bits = narrow_klass_pointer_bits() + _shift;
98103
return (address)((uintptr_t)_base + nth_bit(max_bits));

src/hotspot/share/oops/oop.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class oopDesc {
9191
inline Klass* klass_without_asserts() const;
9292

9393
void set_narrow_klass(narrowKlass nk) NOT_CDS_JAVA_HEAP_RETURN;
94+
inline narrowKlass narrow_klass() const;
9495
inline void set_klass(Klass* k);
9596
static inline void release_set_klass(HeapWord* mem, Klass* k);
9697

src/hotspot/share/oops/oop.inline.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,17 @@ Klass* oopDesc::klass_without_asserts() const {
141141
}
142142
}
143143

144+
narrowKlass oopDesc::narrow_klass() const {
145+
switch (ObjLayout::klass_mode()) {
146+
case ObjLayout::Compact:
147+
return mark().narrow_klass();
148+
case ObjLayout::Compressed:
149+
return _metadata._compressed_klass;
150+
default:
151+
ShouldNotReachHere();
152+
}
153+
}
154+
144155
void oopDesc::set_klass(Klass* k) {
145156
assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass");
146157
assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers");

src/hotspot/share/utilities/ostream.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class StreamIndentor {
182182
NONCOPYABLE(StreamIndentor);
183183

184184
public:
185-
StreamIndentor(outputStream* os, int indentation) :
185+
StreamIndentor(outputStream* os, int indentation = 2) :
186186
_stream(os),
187187
_indentation(indentation),
188188
_old_autoindent(_stream->set_autoindent(true)) {

test/hotspot/gtest/oops/test_compressedKlass.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2024, 2025, Red Hat, Inc. All rights reserved.
33
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -22,6 +22,7 @@
2222
* questions.
2323
*/
2424

25+
#include "classfile/vmClasses.hpp"
2526
#include "oops/compressedKlass.inline.hpp"
2627
#include "utilities/globalDefinitions.hpp"
2728

@@ -107,3 +108,15 @@ TEST_VM(CompressedKlass, test_good_address) {
107108
addr = CompressedKlassPointers::klass_range_end() - alignment;
108109
ASSERT_TRUE(CompressedKlassPointers::is_encodable(addr));
109110
}
111+
112+
TEST_VM(CompressedKlass, test_is_valid_narrow_klass) {
113+
if (!UseCompressedClassPointers) {
114+
return;
115+
}
116+
ASSERT_FALSE(CompressedKlassPointers::is_valid_narrow_klass_id(0));
117+
narrowKlass nk_jlC = CompressedKlassPointers::encode((Klass*)vmClasses::Class_klass());
118+
ASSERT_TRUE(CompressedKlassPointers::is_valid_narrow_klass_id(nk_jlC));
119+
if (CompressedClassSpaceSize < 4 * G && CompressedKlassPointers::base() != nullptr) {
120+
ASSERT_FALSE(CompressedKlassPointers::is_valid_narrow_klass_id(0xFFFFFFFF));
121+
}
122+
}

0 commit comments

Comments
 (0)