diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index 6c5e57c9479..568888ac7d9 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -1135,6 +1135,26 @@ void OopStorage::BasicParState::report_num_dead() const { const char* OopStorage::name() const { return _name; } +bool OopStorage::print_containing(const oop* addr, outputStream* st) { + if (addr != nullptr) { + Block* block = find_block_or_null(addr); + if (block != nullptr && block->print_containing(addr, st)) { + st->print(" in oop storage \"%s\"", name()); + return true; + } + } + return false; +} + +bool OopStorage::Block::print_containing(const oop* addr, outputStream* st) { + if (contains(addr)) { + st->print(PTR_FORMAT " is a pointer %u/%zu into block %zu", + p2i(addr), get_index(addr), ARRAY_SIZE(_data), _active_index); + return true; + } + return false; +} + #ifndef PRODUCT void OopStorage::print_on(outputStream* st) const { diff --git a/src/hotspot/share/gc/shared/oopStorage.hpp b/src/hotspot/share/gc/shared/oopStorage.hpp index f78746fc14a..96cc5a23d6a 100644 --- a/src/hotspot/share/gc/shared/oopStorage.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.hpp @@ -213,6 +213,7 @@ public: // Debugging and logging support. const char* name() const; void print_on(outputStream* st) const PRODUCT_RETURN; + bool print_containing(const oop* addr, outputStream* st); // Provides access to storage internals, for unit testing. // Declare, but not define, the public class OopStorage::TestAccess. diff --git a/src/hotspot/share/gc/shared/oopStorage.inline.hpp b/src/hotspot/share/gc/shared/oopStorage.inline.hpp index ce78e507efc..545da0be0a7 100644 --- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp @@ -196,6 +196,8 @@ public: template bool iterate(F f); template bool iterate(F f) const; + + bool print_containing(const oop* addr, outputStream* st); }; // class Block inline OopStorage::Block* OopStorage::AllocationList::head() { diff --git a/src/hotspot/share/gc/shared/oopStorageSet.cpp b/src/hotspot/share/gc/shared/oopStorageSet.cpp index d061fc77638..c6947590d96 100644 --- a/src/hotspot/share/gc/shared/oopStorageSet.cpp +++ b/src/hotspot/share/gc/shared/oopStorageSet.cpp @@ -82,6 +82,23 @@ template OopStorage* OopStorageSet::get_storage(StrongId); template OopStorage* OopStorageSet::get_storage(WeakId); template OopStorage* OopStorageSet::get_storage(Id); +bool OopStorageSet::print_containing(const void* addr, outputStream* st) { + if (addr != nullptr) { + const void* aligned_addr = align_down(addr, alignof(oop)); + for (OopStorage* storage : Range()) { + if (storage->print_containing((oop*) aligned_addr, st)) { + if (aligned_addr != addr) { + st->print_cr(" (unaligned)"); + } else { + st->cr(); + } + return true; + } + } + } + return false; +} + #ifdef ASSERT void OopStorageSet::verify_initialized(uint index) { diff --git a/src/hotspot/share/gc/shared/oopStorageSet.hpp b/src/hotspot/share/gc/shared/oopStorageSet.hpp index 273a769dd59..867172c41ad 100644 --- a/src/hotspot/share/gc/shared/oopStorageSet.hpp +++ b/src/hotspot/share/gc/shared/oopStorageSet.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_SHARED_OOPSTORAGESET_HPP #include "nmt/memTag.hpp" +#include "oops/oop.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" @@ -89,6 +90,8 @@ public: template static void strong_oops_do(Closure* cl); + // Debugging: print location info, if in storage. + static bool print_containing(const void* addr, outputStream* st); }; ENUMERATOR_VALUE_RANGE(OopStorageSet::StrongId, diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 9c9dfbc6230..8b1c52a5d33 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -32,6 +32,7 @@ #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" #include "gc/shared/gcVMOperations.hpp" +#include "gc/shared/oopStorageSet.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -1317,6 +1318,11 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { } #endif + // Ask if any OopStorage knows about this address. + if (OopStorageSet::print_containing(addr, st)) { + return; + } + // Still nothing? If NMT is enabled, we can ask what it thinks... if (MemTracker::print_containing_region(addr, st)) { return; diff --git a/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp b/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp index c8ada2574c0..947ff120d15 100644 --- a/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp +++ b/test/hotspot/gtest/gc/shared/test_oopStorageSet.cpp @@ -23,15 +23,21 @@ */ #include "precompiled.hpp" -#include "gc/shared/oopStorage.hpp" +#include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.hpp" #include "memory/allocation.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "utilities/debug.hpp" #include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "unittest.hpp" +using ::testing::HasSubstr; +using ::testing::Not; + class OopStorageSetTest : public ::testing::Test { protected: // Returns index of s in storages, or size if not found. @@ -83,6 +89,8 @@ protected: EnumRange(), &OopStorageSet::fill_all); } + + class VM_PrintAtSafepoint; }; TEST_VM_F(OopStorageSetTest, strong_iteration) { @@ -96,3 +104,77 @@ TEST_VM_F(OopStorageSetTest, weak_iteration) { TEST_VM_F(OopStorageSetTest, all_iteration) { test_all_iteration(); } + +class OopStorageSetTest::VM_PrintAtSafepoint : public VM_GTestExecuteAtSafepoint { +private: + class PrintContainingClosure : public Closure { + public: + void do_oop(oop* addr) { + // Direct slot hit. + { + stringStream ss; + bool printed = OopStorageSet::print_containing(addr, &ss); + ASSERT_TRUE(printed); + ASSERT_THAT(ss.freeze(), HasSubstr("is a pointer")); + ASSERT_THAT(ss.freeze(), HasSubstr("into block")); + ASSERT_THAT(ss.freeze(), HasSubstr("in oop storage")); + ASSERT_THAT(ss.freeze(), Not(HasSubstr("(unaligned)"))); + } + + // Unaligned pointer to adjacent slot, should still be in oop storage range. + { + char* unaligned_addr = (char*)addr + 1; + stringStream ss; + bool printed = OopStorageSet::print_containing(unaligned_addr, &ss); + ASSERT_TRUE(printed); + ASSERT_THAT(ss.freeze(), HasSubstr("is a pointer")); + ASSERT_THAT(ss.freeze(), HasSubstr("into block")); + ASSERT_THAT(ss.freeze(), HasSubstr("in oop storage")); + ASSERT_THAT(ss.freeze(), HasSubstr("(unaligned)")); + } + } + }; + +public: + void doit() { + PrintContainingClosure cl; + for (OopStorage* storage : OopStorageSet::Range()) { + storage->oops_do(&cl); + } + } +}; + +TEST_VM_F(OopStorageSetTest, print_containing) { + // nullptrs print nothing + { + stringStream ss; + bool printed = OopStorageSet::print_containing(nullptr, &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // Goofy values print nothing: unaligned out of storage pointer. + { + stringStream ss; + bool printed = OopStorageSet::print_containing((char*)0x1, &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // Goofy values print nothing: aligned out of storage pointer. + { + stringStream ss; + bool printed = OopStorageSet::print_containing((char*)alignof(oop), &ss); + ASSERT_FALSE(printed); + EXPECT_STREQ("", ss.freeze()); + } + + // All slot addresses should print well. + { + VM_PrintAtSafepoint op; + { + ThreadInVMfromNative invm(JavaThread::current()); + VMThread::execute(&op); + } + } +}